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:
parent
6a4d66d432
commit
e8d4211d5c
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue