feat(aio): add footer links with GoogleFeedbackService (#15605)
This commit is contained in:
parent
9e883f5873
commit
28bf222a6a
|
@ -0,0 +1,21 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2014-2017 Google, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -4,6 +4,15 @@
|
|||
"url": "features",
|
||||
"title": "Features"
|
||||
},
|
||||
{
|
||||
"url": "docs",
|
||||
"title": "Docs",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"url": "resources",
|
||||
"title": "Resources"
|
||||
},
|
||||
{
|
||||
"url": "events",
|
||||
"title": "Events"
|
||||
|
@ -12,8 +21,9 @@
|
|||
|
||||
"SideNav": [
|
||||
{
|
||||
"url": "overview",
|
||||
"url": "docs",
|
||||
"title": "Docs",
|
||||
"tooltip": "Angular Documentation",
|
||||
"hidden": true
|
||||
},
|
||||
|
||||
|
@ -359,8 +369,7 @@
|
|||
},
|
||||
|
||||
{
|
||||
"url": "resources",
|
||||
"title": "Resources",
|
||||
"title": "References",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/change-log",
|
||||
|
@ -386,8 +395,30 @@
|
|||
"tooltip": "Write Angular with style."
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
],
|
||||
|
||||
"Footer": [
|
||||
{
|
||||
"title": "Resources",
|
||||
"children": [
|
||||
{
|
||||
"url": "about",
|
||||
"title": "About",
|
||||
"tooltip": "Angular contributors."
|
||||
},
|
||||
{
|
||||
"url": "resources",
|
||||
"title": "Resource Listing",
|
||||
"tooltip": "Angular tools, training, and blogs from around the web."
|
||||
},
|
||||
{
|
||||
"url": "presskit",
|
||||
"title": "Press Kit",
|
||||
"tooltip": "Press contacts, logos, and branding."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Help",
|
||||
"children": [
|
||||
|
@ -400,6 +431,54 @@
|
|||
"url": "https://gitter.im/angular/angular",
|
||||
"title": "Gitter",
|
||||
"tooltip": "Chat about Angular with other birds of a feather."
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/angular/angular/issues",
|
||||
"title": "Report Issues",
|
||||
"tooltip": "Post issues and suggestions on github."
|
||||
},
|
||||
{
|
||||
"title": "Site Feedback",
|
||||
"tooltip": "Submit feedback on this page."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Community",
|
||||
"children": [
|
||||
{
|
||||
"url": "events",
|
||||
"title": "Events",
|
||||
"tooltip": "Post issues and suggestions on github."
|
||||
},
|
||||
{
|
||||
"url": "http://www.meetup.com/topics/angularjs/",
|
||||
"title": "Meetups",
|
||||
"tooltip": "Attend a meetup and learn from fellow developers."
|
||||
},
|
||||
{
|
||||
"url": "https://twitter.com/angular",
|
||||
"title": "Twitter",
|
||||
"tooltip": "Twitter"
|
||||
},
|
||||
{
|
||||
"url": "hhttps://github.com/angular/angular",
|
||||
"title": "GitHub",
|
||||
"tooltip": "GitHub"
|
||||
},
|
||||
{
|
||||
"url": "contribute",
|
||||
"title": "Contribute",
|
||||
"tooltip": "Contribute to Angular"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Languages",
|
||||
"children": [
|
||||
{
|
||||
"title": "中文版",
|
||||
"url": "https://angular.cn/"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -24,4 +24,4 @@
|
|||
|
||||
<aio-search-results #searchResults></aio-search-results>
|
||||
|
||||
<aio-footer [versionInfo]="versionInfo" ></aio-footer>
|
||||
<aio-footer [nodes]="footerNodes" [versionInfo]="versionInfo" ></aio-footer>
|
||||
|
|
|
@ -171,7 +171,7 @@ describe('AppComponent', () => {
|
|||
|
||||
describe('footer', () => {
|
||||
it('should have version number', () => {
|
||||
const versionEl: HTMLElement = fixture.debugElement.query(By.css('aio-footer p.version-info')).nativeElement;
|
||||
const versionEl: HTMLElement = fixture.debugElement.query(By.css('aio-footer p')).nativeElement;
|
||||
expect(versionEl.innerText).toContain(TestHttp.versionFull);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -24,6 +24,7 @@ import { ApiService } from 'app/embedded/api/api.service';
|
|||
import { DocViewerComponent } from 'app/layout/doc-viewer/doc-viewer.component';
|
||||
import { EmbeddedModule } from 'app/embedded/embedded.module';
|
||||
import { GaService } from 'app/shared/ga.service';
|
||||
import { GoogleFeedbackService } from 'app/shared/google-feedback.service';
|
||||
import { Logger } from 'app/shared/logger.service';
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
import { NavigationService } from 'app/navigation/navigation.service';
|
||||
|
@ -64,6 +65,7 @@ import { AutoScrollService } from 'app/shared/auto-scroll.service';
|
|||
providers: [
|
||||
ApiService,
|
||||
GaService,
|
||||
GoogleFeedbackService,
|
||||
Logger,
|
||||
Location,
|
||||
{ provide: LocationStrategy, useClass: PathLocationStrategy },
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<footer>
|
||||
<div class="grid-fluid">
|
||||
<div *ngFor="let node of nodes">
|
||||
<h3>{{node.title}}</h3>
|
||||
<ul>
|
||||
<li *ngFor="let item of node.children">
|
||||
<a *ngIf="item.url" class="link" [href]="item.url"
|
||||
[title]="item.tooltip || item.title">{{ item.title }}</a>
|
||||
<a *ngIf="!item.url" class="action" (click)="action(item)"
|
||||
[title]="item.tooltip || item.title"
|
||||
[attr.aria-label]="item.tooltip || item.title">{{ item.title}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Powered by Google ©2010-2017.
|
||||
Code licensed under an <a href="license" title="License text" >MIT-style License</a>.
|
||||
Documentation licensed under
|
||||
<a href="http://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>.
|
||||
Version {{versionInfo?.full}}.
|
||||
</p>
|
||||
<!-- TODO: twitter widget (but only on pages that use twitter) -->
|
||||
</footer>
|
|
@ -1,19 +1,21 @@
|
|||
import { Component, Input } from '@angular/core';
|
||||
import { VersionInfo } from 'app/navigation/navigation.service';
|
||||
|
||||
import { GoogleFeedbackService } from 'app/shared/google-feedback.service';
|
||||
import { NavigationNode, VersionInfo } from 'app/navigation/navigation.service';
|
||||
|
||||
@Component({
|
||||
selector: 'aio-footer',
|
||||
template: `
|
||||
<footer>
|
||||
<div class="footer">
|
||||
<p>Powered by Google ©2010-2017. Code licensed under an <a href="/license">MIT-style License</a>.</p>
|
||||
<p>Documentation licensed under <a href="http://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>.
|
||||
</p>
|
||||
<p class="version-info">Version Info | {{ versionInfo?.full }}</p>
|
||||
</div>
|
||||
</footer>`
|
||||
templateUrl: 'footer.component.html'
|
||||
})
|
||||
export class FooterComponent {
|
||||
@Input() nodes: NavigationNode[];
|
||||
@Input() versionInfo: VersionInfo;
|
||||
}
|
||||
|
||||
constructor(private feedback: GoogleFeedbackService) { }
|
||||
|
||||
action(node: NavigationNode) {
|
||||
// There is only one action at this time, site feedback
|
||||
// so don't bother to analyze the node; just do the action.
|
||||
this.feedback.openFeedback();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
.fill-remaining-space {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
margin-right: 10px;
|
||||
margin-left: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav-link.home img {
|
||||
position: relative;
|
||||
margin-top: -15px;
|
||||
top: 12px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.nav-link {
|
||||
margin-right: 8px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.nav-link {
|
||||
font-size: 80%;
|
||||
margin-right: 8px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
}
|
|
@ -5,11 +5,8 @@ import { NavigationNode } from 'app/navigation/navigation.service';
|
|||
selector: 'aio-top-menu',
|
||||
template: `
|
||||
<ul role="navigation">
|
||||
<li><a class="nav-link" href="overview" title="Angular Documentation">Docs</a></li>
|
||||
<li *ngFor="let node of nodes"><a class="nav-link" [href]="node.path || node.url" [title]="node.title">{{ node.title }}</a></li>
|
||||
</ul>`,
|
||||
|
||||
styleUrls: ['top-menu.component.scss']
|
||||
<li *ngFor="let node of nodes"><a class="nav-link" [href]="node.url" [title]="node.title">{{ node.title }}</a></li>
|
||||
</ul>`
|
||||
})
|
||||
export class TopMenuComponent {
|
||||
@Input() nodes: NavigationNode[];
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Logger } from 'app/shared/logger.service';
|
||||
|
||||
interface UserFeedback {
|
||||
api: { startFeedback: (config: any) => void };
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
/**
|
||||
* Google Feedback Service - opens a Google Feedback facility.
|
||||
* Presupposes that tiny Google Feedback has been loaded from
|
||||
* //www.gstatic.com/feedback/api.js with a script on the host web page.
|
||||
*/
|
||||
export class GoogleFeedbackService {
|
||||
|
||||
// ms to wait before acquiring window.userfeedback after library loads
|
||||
// empirically determined to allow time for e2e test setup
|
||||
static initializeDelay = 1000;
|
||||
|
||||
private userFeedback: UserFeedback;
|
||||
|
||||
constructor(private logger: Logger) {
|
||||
// fallback userFeedback
|
||||
this.userFeedback = {
|
||||
api: { startFeedback: () => {
|
||||
logger.error('Google Feedback service is not available.');
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
openFeedback() {
|
||||
this.initializeGoogleFeedback().then(ufb => {
|
||||
const configuration = {
|
||||
'productId': '410509', // Google's Angular Docs key?
|
||||
'authuser': '1',
|
||||
'bucket': 'angulario'
|
||||
};
|
||||
ufb.api.startFeedback(configuration);
|
||||
});
|
||||
};
|
||||
|
||||
private initializeGoogleFeedback() {
|
||||
let ufb = window['userfeedback'];
|
||||
if (ufb) {
|
||||
return Promise.resolve<UserFeedback>(this.userFeedback = ufb);
|
||||
} else {
|
||||
// Give script more time to async load.
|
||||
// Useful in e2e tests.
|
||||
return new Promise<UserFeedback>(resolve => {
|
||||
setTimeout(() => {
|
||||
ufb = window['userfeedback'];
|
||||
if (ufb) { this.userFeedback = ufb; }
|
||||
resolve(this.userFeedback);
|
||||
}, GoogleFeedbackService.initializeDelay);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,27 +22,20 @@
|
|||
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css?family=Droid+Sans+Mono" rel="stylesheet">
|
||||
|
||||
<link rel="manifest" href="pwa-manifest.json">
|
||||
<meta name="theme-color" content="#1976d2">
|
||||
|
||||
<!-- Google Analytics -->
|
||||
<script>
|
||||
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
||||
// let GaService set the following
|
||||
// ga('create', 'UA-xxxxxxx-1', 'auto');
|
||||
// ga('send', 'pageview');
|
||||
</script>
|
||||
<script async src='https://www.google-analytics.com/analytics.js'></script>
|
||||
<!-- End Google Analytics -->
|
||||
|
||||
<!-- Google Feedback -->
|
||||
<script async src="//www.gstatic.com/feedback/api.js" type="text/javascript"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<aio-shell>Loading...</aio-shell>
|
||||
|
||||
<!-- TODO: google feedback -->
|
||||
<!-- TODO: twitter widget (but only on pages that use twitter) -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,40 +1,91 @@
|
|||
footer {
|
||||
position: relative;
|
||||
line-height: 24px;
|
||||
}
|
||||
position: relative;
|
||||
line-height: 24px;
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
z-index: 10;
|
||||
background-color: $blue;
|
||||
color: $offwhite;
|
||||
font-weight: 300;
|
||||
|
||||
footer .footer {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
z-index: 10;
|
||||
background-color: $blue;
|
||||
color: $offwhite;
|
||||
a {
|
||||
color: $offwhite;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
&:visited {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
a.action {
|
||||
cursor: pointer;
|
||||
}
|
||||
h3 {
|
||||
font-size: 130%;
|
||||
}
|
||||
p {
|
||||
text-align: center;
|
||||
font-weight: 300;
|
||||
a {
|
||||
color: $offwhite;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
&:visited {
|
||||
text-decoration: none;
|
||||
}
|
||||
margin: 10px 0px 5px;
|
||||
}
|
||||
|
||||
div.grid-fluid {
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
|
||||
-webkit-flex-direction: row;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
|
||||
-webkit-justify-content: space-around;
|
||||
-ms-flex-pack: distribute;
|
||||
justify-content: space-around;
|
||||
|
||||
-webkit-align-items: flex-start;
|
||||
-ms-flex-align: start;
|
||||
align-items: flex-start;
|
||||
|
||||
-webkit-align-content: flex-start;
|
||||
-ms-flex-line-pack: start;
|
||||
align-content: flex-start;
|
||||
|
||||
text-align: left;
|
||||
|
||||
ul {
|
||||
list-style-position: inside;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
padding: 0px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
p {
|
||||
margin: 0 0 5px;
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
h3 {
|
||||
font-size: 110%;
|
||||
}
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
h3 {
|
||||
font-size: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer::after {
|
||||
content: "";
|
||||
// content: "";
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background:
|
||||
background:
|
||||
url('../src/assets/images/logos/angular2/angular_whiteTransparent.png') top left repeat-y,
|
||||
url('../src/assets/images/logos/angular2/angular_whiteTransparent.png') top 80px left 168px repeat-y,
|
||||
url('../src/assets/images/logos/angular2/angular_whiteTransparent.png') top left 336px repeat-y,
|
||||
|
@ -49,4 +100,4 @@ footer::after {
|
|||
url('../src/assets/images/logos/angular2/angular_whiteTransparent.png') top 80px left 1848px repeat-y;
|
||||
opacity: 0.1;
|
||||
background-size: 160px auto;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,50 @@
|
|||
/** STEF: HELP WITH LOGO SPACING **/
|
||||
.nav-link.home {
|
||||
margin: 0 20px 0 10px;
|
||||
/************************************
|
||||
|
||||
Media queries
|
||||
|
||||
To use these, put this snippet in the approriate selector:
|
||||
|
||||
@include bp(tiny) {
|
||||
background-color: purple;
|
||||
}
|
||||
|
||||
Replace "tiny" with "medium" or "big" as necessary.
|
||||
*************************************/
|
||||
|
||||
@mixin bp($point) {
|
||||
|
||||
$bp-xsmall: "(min-width: 320px)";
|
||||
$bp-teeny: "(min-width: 480px)";
|
||||
$bp-tiny: "(min-width: 600px)";
|
||||
$bp-small: "(min-width: 650px)";
|
||||
$bp-medium: "(min-width: 800px)";
|
||||
$bp-big: "(min-width: 1000px)";
|
||||
|
||||
@if $point == big {
|
||||
@media #{$bp-big} { @content; }
|
||||
}
|
||||
@else if $point == medium {
|
||||
@media #{$bp-medium} { @content; }
|
||||
}
|
||||
@else if $point == small {
|
||||
@media #{$bp-small} { @content; }
|
||||
}
|
||||
@else if $point == tiny {
|
||||
@media #{$bp-tiny} { @content; }
|
||||
}
|
||||
@else if $point == teeny {
|
||||
@media #{$bp-teeny} { @content; }
|
||||
}
|
||||
@else if $point == xsmall {
|
||||
@media #{$bp-xsmall} { @content; }
|
||||
}
|
||||
}
|
||||
/** STEF: MENU AT TOP OF SIDE-NAV HELP! **/
|
||||
|
||||
/************************************/
|
||||
|
||||
aio-nav-menu.top-menu .vertical-menu-item {
|
||||
background-color: $lightgray;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.mat-sidenav.sidenav {
|
||||
|
|
|
@ -1,22 +1,56 @@
|
|||
|
||||
.fill-remaining-space {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
margin-right: 10px;
|
||||
margin-left: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav-link.home img {
|
||||
position: relative;
|
||||
margin-top: -15px;
|
||||
top: 12px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.nav-link {
|
||||
font-size: 80%;
|
||||
margin-right: 8px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.nav-link {
|
||||
font-size: 80%;
|
||||
margin-right: 8px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
aio-top-menu {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
aio-top-menu ul {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
list-style-position: inside;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
list-style-position: inside;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
|
||||
aio-top-menu li {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
li {
|
||||
list-style-type: none;
|
||||
padding: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
md-toolbar.mat-toolbar {
|
||||
|
@ -48,12 +82,12 @@ aio-search-box input {
|
|||
transition: width 0.4s ease-in-out;
|
||||
&:focus {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@include bp(medium) {
|
||||
transition: width 0.4s ease-in-out;
|
||||
&:focus {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue