From 28bf222a6ab5aee1d8b8d2449a92b71007bc75ff Mon Sep 17 00:00:00 2001 From: Ward Bell Date: Thu, 30 Mar 2017 14:46:25 -0700 Subject: [PATCH] feat(aio): add footer links with GoogleFeedbackService (#15605) --- aio/content/license.md | 21 ++++ aio/content/navigation.json | 87 +++++++++++++++- aio/src/app/app.component.html | 2 +- aio/src/app/app.component.spec.ts | 2 +- aio/src/app/app.module.ts | 2 + .../app/layout/footer/footer.component.html | 25 +++++ aio/src/app/layout/footer/footer.component.ts | 24 ++--- .../layout/top-menu/top-menu.component.scss | 30 ------ .../app/layout/top-menu/top-menu.component.ts | 7 +- aio/src/app/shared/google-feedback.service.ts | 59 +++++++++++ aio/src/index.html | 11 +-- aio/src/styles/1-layouts/_footer.scss | 99 ++++++++++++++----- aio/src/styles/1-layouts/_sidenav.scss | 48 ++++++++- aio/src/styles/1-layouts/_top-menu.scss | 64 +++++++++--- 14 files changed, 377 insertions(+), 104 deletions(-) create mode 100644 aio/content/license.md create mode 100644 aio/src/app/layout/footer/footer.component.html delete mode 100644 aio/src/app/layout/top-menu/top-menu.component.scss create mode 100644 aio/src/app/shared/google-feedback.service.ts diff --git a/aio/content/license.md b/aio/content/license.md new file mode 100644 index 0000000000..62345da08d --- /dev/null +++ b/aio/content/license.md @@ -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. diff --git a/aio/content/navigation.json b/aio/content/navigation.json index f4f50794a0..6da9256b62 100644 --- a/aio/content/navigation.json +++ b/aio/content/navigation.json @@ -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/" } ] } diff --git a/aio/src/app/app.component.html b/aio/src/app/app.component.html index ea443d173f..8ce39fa43f 100644 --- a/aio/src/app/app.component.html +++ b/aio/src/app/app.component.html @@ -24,4 +24,4 @@ - + diff --git a/aio/src/app/app.component.spec.ts b/aio/src/app/app.component.spec.ts index a996da143a..a6895e09de 100644 --- a/aio/src/app/app.component.spec.ts +++ b/aio/src/app/app.component.spec.ts @@ -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); }); }); diff --git a/aio/src/app/app.module.ts b/aio/src/app/app.module.ts index f53de92cbf..deb77e41a8 100644 --- a/aio/src/app/app.module.ts +++ b/aio/src/app/app.module.ts @@ -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 }, diff --git a/aio/src/app/layout/footer/footer.component.html b/aio/src/app/layout/footer/footer.component.html new file mode 100644 index 0000000000..36f0b6b9ab --- /dev/null +++ b/aio/src/app/layout/footer/footer.component.html @@ -0,0 +1,25 @@ + diff --git a/aio/src/app/layout/footer/footer.component.ts b/aio/src/app/layout/footer/footer.component.ts index 5505a5a48f..fb45d1d046 100644 --- a/aio/src/app/layout/footer/footer.component.ts +++ b/aio/src/app/layout/footer/footer.component.ts @@ -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: ` - ` + 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(); + } +} diff --git a/aio/src/app/layout/top-menu/top-menu.component.scss b/aio/src/app/layout/top-menu/top-menu.component.scss deleted file mode 100644 index 3f193367a3..0000000000 --- a/aio/src/app/layout/top-menu/top-menu.component.scss +++ /dev/null @@ -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; - } -} diff --git a/aio/src/app/layout/top-menu/top-menu.component.ts b/aio/src/app/layout/top-menu/top-menu.component.ts index 05e2a4026b..7cf09713e2 100644 --- a/aio/src/app/layout/top-menu/top-menu.component.ts +++ b/aio/src/app/layout/top-menu/top-menu.component.ts @@ -5,11 +5,8 @@ import { NavigationNode } from 'app/navigation/navigation.service'; selector: 'aio-top-menu', template: ` `, - - styleUrls: ['top-menu.component.scss'] +
  • {{ node.title }}
  • + ` }) export class TopMenuComponent { @Input() nodes: NavigationNode[]; diff --git a/aio/src/app/shared/google-feedback.service.ts b/aio/src/app/shared/google-feedback.service.ts new file mode 100644 index 0000000000..cebd532db7 --- /dev/null +++ b/aio/src/app/shared/google-feedback.service.ts @@ -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(this.userFeedback = ufb); + } else { + // Give script more time to async load. + // Useful in e2e tests. + return new Promise(resolve => { + setTimeout(() => { + ufb = window['userfeedback']; + if (ufb) { this.userFeedback = ufb; } + resolve(this.userFeedback); + }, GoogleFeedbackService.initializeDelay); + }); + } + } +} diff --git a/aio/src/index.html b/aio/src/index.html index f6d816fba0..cc62897b55 100644 --- a/aio/src/index.html +++ b/aio/src/index.html @@ -22,27 +22,20 @@ - + + - - Loading... - - - diff --git a/aio/src/styles/1-layouts/_footer.scss b/aio/src/styles/1-layouts/_footer.scss index 01b07787a0..a1ce0d9b06 100644 --- a/aio/src/styles/1-layouts/_footer.scss +++ b/aio/src/styles/1-layouts/_footer.scss @@ -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; -} \ No newline at end of file +} diff --git a/aio/src/styles/1-layouts/_sidenav.scss b/aio/src/styles/1-layouts/_sidenav.scss index c463319f0f..4ef29f59a1 100644 --- a/aio/src/styles/1-layouts/_sidenav.scss +++ b/aio/src/styles/1-layouts/_sidenav.scss @@ -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 { diff --git a/aio/src/styles/1-layouts/_top-menu.scss b/aio/src/styles/1-layouts/_top-menu.scss index c1e80e908b..0344ea7ed8 100644 --- a/aio/src/styles/1-layouts/_top-menu.scss +++ b/aio/src/styles/1-layouts/_top-menu.scss @@ -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%; - } + } } -} \ No newline at end of file +}