-
Notifications
You must be signed in to change notification settings - Fork 24.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
10 changed files
with
280 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
aio/src/app/layout/notification/notification.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<span class="space"></span> | ||
|
||
<a href="{{actionUrl}}" class="content"> | ||
<mat-icon class="icon" [svgIcon]="icon" [attr.aria-label]="iconLabel"></mat-icon> | ||
<span class="message"><ng-content></ng-content></span> | ||
<span class="action-button">{{buttonText}}</span> | ||
</a> | ||
|
||
<button mat-icon-button class="close-button" aria-label="Close"> | ||
<mat-icon svgIcon="close" aria-label="Dismiss notification"></mat-icon> | ||
</button> |
114 changes: 114 additions & 0 deletions
114
aio/src/app/layout/notification/notification.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; | ||
import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
import { By } from '@angular/platform-browser'; | ||
import { CurrentDateToken } from 'app/shared/current-date'; | ||
import { NotificationComponent } from './notification.component'; | ||
import { WindowToken } from 'app/shared/window'; | ||
|
||
describe('NotificationComponent', () => { | ||
let element: HTMLElement; | ||
let component: NotificationComponent; | ||
let fixture: ComponentFixture<TestComponent>; | ||
|
||
function configTestingModule(now = new Date('2018-01-20')) { | ||
TestBed.configureTestingModule({ | ||
declarations: [TestComponent, NotificationComponent], | ||
providers: [ | ||
{ provide: WindowToken, useClass: MockWindow }, | ||
{ provide: CurrentDateToken, useValue: now }, | ||
], | ||
imports: [NoopAnimationsModule], | ||
schemas: [NO_ERRORS_SCHEMA] | ||
}); | ||
} | ||
|
||
function createComponent() { | ||
fixture = TestBed.createComponent(TestComponent); | ||
const debugElement = fixture.debugElement.query(By.directive(NotificationComponent)); | ||
element = debugElement.nativeElement; | ||
component = debugElement.componentInstance; | ||
component.ngOnInit(); | ||
fixture.detectChanges(); | ||
} | ||
|
||
it('should display the message', () => { | ||
configTestingModule(); | ||
createComponent(); | ||
expect(fixture.nativeElement.innerHTML).toContain('Help Angular by taking a <strong>1 minute survey</strong>!'); | ||
}); | ||
|
||
it('should display an icon', () => { | ||
configTestingModule(); | ||
createComponent(); | ||
const iconElement = fixture.debugElement.query(By.css('.icon')); | ||
expect(iconElement.properties['svgIcon']).toEqual('insert_comment'); | ||
expect(iconElement.attributes['aria-label']).toEqual('Survey'); | ||
}); | ||
|
||
it('should display a button', () => { | ||
configTestingModule(); | ||
createComponent(); | ||
const button = fixture.debugElement.query(By.css('.action-button')); | ||
expect(button.nativeElement.textContent).toEqual('Go to survey'); | ||
}); | ||
|
||
it('should call hideNotification when the component is clicked', () => { | ||
configTestingModule(); | ||
createComponent(); | ||
spyOn(component, 'hideNotification'); | ||
element.click(); | ||
fixture.detectChanges(); | ||
expect(component.hideNotification).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should hide the notification when hideNotification is called', () => { | ||
configTestingModule(); | ||
createComponent(); | ||
expect(component.showNotification).toBe(true); | ||
component.hideNotification(); | ||
expect(component.showNotification).toBe(false); | ||
}); | ||
|
||
it('should update localStorage key when hideNotification is called', () => { | ||
configTestingModule(); | ||
createComponent(); | ||
const setItemSpy: jasmine.Spy = TestBed.get(WindowToken).localStorage.setItem; | ||
component.hideNotification(); | ||
expect(setItemSpy).toHaveBeenCalledWith('survey-january-2018', 'hide'); | ||
}); | ||
|
||
it('should not show the notification if the date is after the expiry date', () => { | ||
configTestingModule(new Date('2018-01-23')); | ||
createComponent(); | ||
expect(component.showNotification).toBe(false); | ||
}); | ||
|
||
it('should not show the notification if the there is a "hide" flag in localStorage', () => { | ||
configTestingModule(); | ||
const getItemSpy: jasmine.Spy = TestBed.get(WindowToken).localStorage.getItem; | ||
getItemSpy.and.returnValue('hide'); | ||
createComponent(); | ||
expect(getItemSpy).toHaveBeenCalledWith('survey-january-2018'); | ||
expect(component.showNotification).toBe(false); | ||
}); | ||
}); | ||
|
||
@Component({ | ||
template: ` | ||
<aio-notification | ||
icon="insert_comment" | ||
iconLabel="Survey" | ||
buttonText="Go to survey" | ||
actionUrl="https://bit.ly/angular-survey-2018" | ||
notificationId="survey-january-2018" | ||
expirationDate="2018-01-22"> | ||
Help Angular by taking a <strong>1 minute survey</strong>! | ||
</aio-notification>` | ||
}) | ||
class TestComponent { | ||
} | ||
|
||
class MockWindow { | ||
localStorage = jasmine.createSpyObj('localStorage', ['getItem', 'setItem']); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { animate, state, style, trigger, transition } from '@angular/animations'; | ||
import { Component, HostBinding, HostListener, Inject, Input, OnInit } from '@angular/core'; | ||
import { CurrentDateToken } from 'app/shared/current-date'; | ||
import { WindowToken } from 'app/shared/window'; | ||
|
||
@Component({ | ||
selector: 'aio-notification', | ||
templateUrl: 'notification.component.html', | ||
animations: [ | ||
trigger('accordion', [ | ||
state('true', style({height: '*'})), | ||
state('false', style({height: 0})), | ||
transition('true => false', animate(250)) | ||
]) | ||
] | ||
}) | ||
export class NotificationComponent implements OnInit { | ||
private get localStorage() { return this.window.localStorage; } | ||
|
||
@Input() icon: string; | ||
@Input() iconLabel: string; | ||
@Input() buttonText: string; | ||
@Input() actionUrl: string; | ||
@Input() notificationId: string; | ||
@Input() expirationDate: string; | ||
|
||
@HostBinding('@accordion') | ||
showNotification: boolean; | ||
|
||
constructor( | ||
@Inject(WindowToken) private window: Window, | ||
@Inject(CurrentDateToken) private currentDate: Date | ||
) {} | ||
|
||
ngOnInit() { | ||
this.showNotification = this.localStorage.getItem(this.notificationId) !== 'hide' && new Date(this.expirationDate) > this.currentDate; | ||
} | ||
|
||
@HostListener('click') | ||
hideNotification() { | ||
this.localStorage.setItem(this.notificationId, 'hide'); | ||
this.showNotification = false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { InjectionToken } from '@angular/core'; | ||
|
||
export const CurrentDateToken = new InjectionToken('CurrentDate'); | ||
export function currentDateProvider() { return new Date(); } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,3 +29,4 @@ | |
@import 'toc'; | ||
@import 'select-menu'; | ||
@import 'deploy-theme'; | ||
@import 'notification'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// we need to override some of the toolbar styling | ||
mat-toolbar-row.notification-container { | ||
padding: 0; | ||
height: auto; | ||
overflow: hidden; | ||
} | ||
|
||
aio-notification { | ||
background: $darkgray; | ||
display: flex; | ||
align-items: center; | ||
width: 100%; | ||
height: 56px; | ||
|
||
.space, .close-button { | ||
// prevent the spacer and close button from growing to fill | ||
// but allow them to disappear as the screen width gets narrow | ||
flex: 0 1 auto; | ||
width: 56px; | ||
} | ||
|
||
.content { | ||
align-items: center; | ||
justify-content: center; | ||
flex: 1 1 auto; // allow the content to fill the space | ||
display: flex; | ||
text-transform: none; | ||
padding: 0; | ||
|
||
.icon { | ||
margin-right: 10px; | ||
@media (max-width: 400px) { | ||
display: none; | ||
} | ||
} | ||
|
||
.action-button { | ||
margin-left: 10px; | ||
background: $brightred; | ||
border-radius: 15px; | ||
text-transform: uppercase; | ||
padding: 0 10px; | ||
font-size: 12px; | ||
@media (max-width: 780px) { | ||
display: none; | ||
} | ||
} | ||
} | ||
} |