feat(aio): add footer links with GoogleFeedbackService (#15605)

This commit is contained in:
Ward Bell
2017-03-30 14:46:25 -07:00
committed by Alex Rickabaugh
parent 9e883f5873
commit 28bf222a6a
14 changed files with 377 additions and 104 deletions

View File

@ -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>

View File

@ -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);
});
});

View File

@ -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 },

View File

@ -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>

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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[];

View File

@ -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);
});
}
}
}

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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 {

View File

@ -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%;
}
}
}
}
}