feat(docs-infra): allow notification bar to show arbitrary content (#25020)

This change generalises the notification bar rendering to allow
more complex content to be displayed.

Now you must provide the full HTML of the notification message
when using `<aio-notification>`.

Also you can control whether clicking the content triggers the
notification to close or not.

This will support the new notification specified in "Other Items : 3" of
[#24140](https://github.com/angular/angular/issues/24140#issuecomment-397480410)

PR Close #25020
This commit is contained in:
Pete Bacon Darwin 2018-07-22 07:28:54 +01:00 committed by Igor Minar
parent 6a4d66d432
commit e8d4211d5c
5 changed files with 63 additions and 44 deletions

View File

@ -7,14 +7,15 @@
<mat-toolbar color="primary" class="app-toolbar no-print" [class.transitioning]="isTransitioning"> <mat-toolbar color="primary" class="app-toolbar no-print" [class.transitioning]="isTransitioning">
<mat-toolbar-row class="notification-container"> <mat-toolbar-row class="notification-container">
<aio-notification <aio-notification
icon="insert_comment"
iconLabel="Announcement"
buttonText="Learn More"
actionUrl="https://blog.angular.io/version-6-0-0-of-angular-now-available-cc56b0efa7a4"
notificationId="angular-v6-announcement" notificationId="angular-v6-announcement"
expirationDate="2018-07-01" expirationDate="2018-07-01"
[dismissOnContentClick]="true"
(dismissed)="notificationDismissed()"> (dismissed)="notificationDismissed()">
Version 6 of Angular Now Available! <a href="https://blog.angular.io/version-6-0-0-of-angular-now-available-cc56b0efa7a4">
<mat-icon class="icon" svgIcon="insert_comment" aria-label="Announcement"></mat-icon>
<span class="message">Version 6 of Angular Now Available!</span>
<span class="action-button">Learn More</span>
</a>
</aio-notification> </aio-notification>
</mat-toolbar-row> </mat-toolbar-row>
<mat-toolbar-row> <mat-toolbar-row>

View File

@ -1,8 +1,6 @@
<a href="{{actionUrl}}" class="content" (click)="dismiss()"> <span class="content" (click)="contentClick()">
<mat-icon class="icon" [svgIcon]="icon" [attr.aria-label]="iconLabel"></mat-icon> <ng-content></ng-content>
<span class="message"><ng-content></ng-content></span> </span>
<span class="action-button">{{buttonText}}</span>
</a>
<button mat-icon-button class="close-button" aria-label="Close" (click)="dismiss()"> <button mat-icon-button class="close-button" aria-label="Close" (click)="dismiss()">
<mat-icon svgIcon="close" aria-label="Dismiss notification"></mat-icon> <mat-icon svgIcon="close" aria-label="Dismiss notification"></mat-icon>

View File

@ -30,37 +30,49 @@ describe('NotificationComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
} }
it('should display the message', () => { describe('content projection', () => {
it('should display the message text', () => {
configTestingModule(); configTestingModule();
createComponent(); createComponent();
expect(fixture.nativeElement.innerHTML).toContain('Help Angular by taking a <strong>1 minute survey</strong>!'); expect(fixture.nativeElement.innerHTML).toContain('Version 6 of Angular Now Available!');
}); });
it('should display an icon', () => { it('should render HTML elements', () => {
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(); configTestingModule();
createComponent(); createComponent();
const button = fixture.debugElement.query(By.css('.action-button')); const button = fixture.debugElement.query(By.css('.action-button'));
expect(button.nativeElement.textContent).toEqual('Go to survey'); expect(button.nativeElement.textContent).toEqual('Learn More');
}); });
it('should call dismiss when the message link is clicked', () => { it('should process Angular directives', () => {
configTestingModule();
createComponent();
const badSpans = fixture.debugElement.queryAll(By.css('.bad'));
expect(badSpans.length).toEqual(0);
});
});
it('should call dismiss() when the message link is clicked, if dismissOnContentClick is true', () => {
configTestingModule(); configTestingModule();
createComponent(); createComponent();
spyOn(component, 'dismiss'); spyOn(component, 'dismiss');
fixture.debugElement.query(By.css('a')).triggerEventHandler('click', null); component.dismissOnContentClick = true;
fixture.detectChanges(); const message: HTMLSpanElement = fixture.debugElement.query(By.css('.messageholder')).nativeElement;
message.click();
expect(component.dismiss).toHaveBeenCalled(); expect(component.dismiss).toHaveBeenCalled();
}); });
it('should call dismiss when the close button is clicked', () => { it('should not call dismiss() when the message link is clicked, if dismissOnContentClick is false', () => {
configTestingModule();
createComponent();
spyOn(component, 'dismiss');
component.dismissOnContentClick = false;
const message: HTMLSpanElement = fixture.debugElement.query(By.css('.messageholder')).nativeElement;
message.click();
expect(component.dismiss).not.toHaveBeenCalled();
});
it('should call dismiss() when the close button is clicked', () => {
configTestingModule(); configTestingModule();
createComponent(); createComponent();
spyOn(component, 'dismiss'); spyOn(component, 'dismiss');
@ -104,13 +116,15 @@ describe('NotificationComponent', () => {
@Component({ @Component({
template: ` template: `
<aio-notification <aio-notification
icon="insert_comment"
iconLabel="Survey"
buttonText="Go to survey"
actionUrl="https://bit.ly/angular-survey-2018"
notificationId="survey-january-2018" notificationId="survey-january-2018"
expirationDate="2018-01-22"> expirationDate="2018-01-22">
Help Angular by taking a <strong>1 minute survey</strong>! <span class="messageholder">
<a href="https://blog.angular.io/version-6-0-0-of-angular-now-available-cc56b0efa7a4">
<span *ngIf="false" class="bad">This should not appear</span>
<span class="message">Version 6 of Angular Now Available!</span>
<span class="action-button">Learn More</span>
</a>
</span>
</aio-notification>` </aio-notification>`
}) })
class TestComponent { class TestComponent {

View File

@ -22,10 +22,7 @@ const LOCAL_STORAGE_NAMESPACE = 'aio-notification/';
export class NotificationComponent implements OnInit { export class NotificationComponent implements OnInit {
private get localStorage() { return this.window.localStorage; } private get localStorage() { return this.window.localStorage; }
@Input() icon: string; @Input() dismissOnContentClick: boolean;
@Input() iconLabel: string;
@Input() buttonText: string;
@Input() actionUrl: string;
@Input() notificationId: string; @Input() notificationId: string;
@Input() expirationDate: string; @Input() expirationDate: string;
@Output() dismissed = new EventEmitter(); @Output() dismissed = new EventEmitter();
@ -44,6 +41,12 @@ export class NotificationComponent implements OnInit {
this.showNotification = previouslyHidden || expired ? 'hide' : 'show'; this.showNotification = previouslyHidden || expired ? 'hide' : 'show';
} }
contentClick() {
if (this.dismissOnContentClick) {
this.dismiss();
}
}
dismiss() { dismiss() {
this.localStorage.setItem(LOCAL_STORAGE_NAMESPACE + this.notificationId, 'hide'); this.localStorage.setItem(LOCAL_STORAGE_NAMESPACE + this.notificationId, 'hide');
this.showNotification = 'hide'; this.showNotification = 'hide';

View File

@ -31,11 +31,14 @@ aio-notification {
} }
.content { .content {
display: flex;
max-width: calc(100% - #{$notificationHeight}); max-width: calc(100% - #{$notificationHeight});
text-transform: none; text-transform: none;
padding: 0; padding: 0;
> * {
display: flex;
}
.icon { .icon {
margin-right: 10px; margin-right: 10px;
@media (max-width: 464px) { @media (max-width: 464px) {
@ -46,10 +49,10 @@ aio-notification {
.message { .message {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
margin-right: 10px;
} }
.action-button { .action-button {
margin-left: 10px;
background: $brightred; background: $brightred;
border-radius: 15px; border-radius: 15px;
text-transform: uppercase; text-transform: uppercase;