Skip to content

Commit

Permalink
feat(design): add accessibility considerations to the notification co…
Browse files Browse the repository at this point in the history
…mponent (#2900)
  • Loading branch information
xelaint authored Jul 10, 2024
1 parent f58693e commit 8b5130a
Show file tree
Hide file tree
Showing 17 changed files with 184 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<h1>Notification</h1>
<p>Notifications provides a way to display and communicate information related to user actions within a page's content.</p>
<p>Notifications provide a way to display and communicate information related to user actions within a page's content.</p>

<h2>Overview</h2>
<p>Notifications are used to display short messages that provide context in close promixity to a piece of content. They're often used to provide feedback or to notify users about an action they performed within a page. Notifications should not be used to display app-level alerts. Instead, use the <a routerLink="/toast">Toast</a> component.</p>
Expand All @@ -21,6 +21,8 @@ <h3>Subtitle</h3>
<h3>Actions</h3>
<p>Buttons can be included in notifications to resolve the notification or navigate them to a page with more information. It can be added by using the <code>daffNotificationActions</code> selector.</p>

<design-land-example-viewer-container example="notification-with-actions"></design-land-example-viewer-container>

<h2>Properties</h2>

<h3>Statuses</h3>
Expand All @@ -44,4 +46,4 @@ <h3>Dismissing a notification</h3>
<design-land-example-viewer-container example="dismissible-notification"></design-land-example-viewer-container>

<h2>Accessibility</h2>
<p>Notifications sets <code>aria-live</code> to <code>polite</code> by default. It does not interrupt a user's current activity and waits until they are idle to make the announcement.</p>
<p>Notifications with a <code>danger</code> or <code>warn</code> status have a <code>role="alert"</code> so that it can be announced by assistive technologies. All other notifications have a <code>role="status"</code>. See <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles#4._live_region_roles" target="_blank">live region roles</a> for more information. Notifications have a <code>tabindex="0"</code> so users can discover them while tabbing through a page.</p>
8 changes: 5 additions & 3 deletions libs/design/notification/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Notifications
Notifications provides a way to display and communicate information related to user actions within a page's content.
# Notification
Notifications provide a way to display and communicate information related to user actions within a page's content.

## Overview
Notifications are used to display short messages that provide context in close promixity to a piece of content. They're often used to provide feedback or to notify users about an action they performed within a page. Notifications should not be used to display app-level alerts. Instead, use the [Toast](/libs/design/toast/README.md) component.
Expand All @@ -21,6 +21,8 @@ Subtitle provides additional details about the notification that should be limit
### Actions
Buttons can be included in notifications to resolve the notification or navigate them to a page with more information. It can be added by using the `daffNotificationActions` selector.

<design-land-example-viewer-container example="notification-with-actions"></design-land-example-viewer-container>d

## Properties

### Statuses
Expand All @@ -44,4 +46,4 @@ The close button is hidden by default but can be visible by setting the `dismiss
<design-land-example-viewer-container example="dismissible-notification"></design-land-example-viewer-container>

## Accessibility
Notifications sets `aria-live` to `polite` by default. It does not interrupt a user's current activity and waits until they are idle to make the announcement.
Notifications with a `danger` or `warn` status have a `role="alert"` so that it can be announced by assistive technologies. See (live region roles)[https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles#4._live_region_roles] for more information. All other notifications have a `role="status"`. Notifications have a `tabindex="0"` so users can discover them while tabbing through a page.
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<daff-notification>
<button daff-button (click)="toggleNotification()">Show Notification</button>

<daff-notification *ngIf="showNotification" status="success">
<fa-icon daffPrefix [icon]="faInfoCircle"></fa-icon>
<div daffNotificationTitle>Title</div>
<div daffNotificationSubtitle>This is the subtitle with information</div>
<div daffNotificationActions>
<button daff-button size="sm">Confirm</button>
<button daff-button size="sm" color="theme-contrast">Confirm</button>
<button daff-flat-button size="sm" color="theme-contrast">Cancel</button>
</div>
</daff-notification>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
:host {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'default-notification',
templateUrl: './default-notification.component.html',
styles: [`
:host {
display: flex;
justify-content: center;
}
`],
styleUrls: ['./default-notification.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DefaultNotificationComponent {
faInfoCircle = faInfoCircle;

showNotification = false;

toggleNotification() {
this.showNotification = !this.showNotification;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<daff-notification dismissible="true">
<daff-notification *ngIf="!hidden" dismissible="true" (closeNotification)="hideNotification()">
<fa-icon daffPrefix [icon]="faInfoCircle"></fa-icon>
<div daffNotificationTitle>Title</div>
<div daffNotificationSubtitle>This is the subtitle with information</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,10 @@ import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
})
export class DismissibleNotificationComponent {
faInfoCircle = faInfoCircle;

hidden = false;

hideNotification() {
this.hidden = true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
<fa-icon daffPrefix [icon]="faCheck" [fixedWidth]="true"></fa-icon>
<div daffNotificationTitle>Title</div>
<div daffNotificationSubtitle>This is the subtitle with information</div>
<div daffNotificationActions>
<button daff-button size="sm" color="theme-contrast">Confirm</button>
</div>
</daff-notification>

<select [formControl]="orientationControl">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
<fa-icon *ngIf="statusControl.value === 'error'" daffPrefix [icon]="faExclamation" [fixedWidth]="true"></fa-icon>
<div daffNotificationTitle>Title</div>
<div daffNotificationSubtitle>This is the subtitle with information</div>
<div daffNotificationActions>
<button daff-button size="sm" color="theme-contrast">Confirm</button>
</div>
</daff-notification>

<select [formControl]="statusControl">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<button daff-button (click)="toggleNotification()">
<ng-container *ngIf="showNotification">Show Notification</ng-container>
<ng-container *ngIf="!showNotification">Hide Notification</ng-container>
</button>

<daff-notification status="error" *ngIf="showNotification">
<fa-icon daffPrefix [icon]="faExclamationCircle"></fa-icon>
<div daffNotificationTitle>Title</div>
<div daffNotificationSubtitle>This is the subtitle with information</div>
<div daffNotificationActions>
<button daff-button size="sm" color="theme-contrast">Confirm</button>
<button daff-flat-button size="sm" color="theme-contrast">Cancel</button>
</div>
</daff-notification>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
ChangeDetectionStrategy,
Component,
} from '@angular/core';
import { faExclamationCircle } from '@fortawesome/free-solid-svg-icons';

@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'notification-with-actions',
templateUrl: './notification-with-actions.component.html',
styles: [`
:host {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
`],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NotificationWithActionsComponent {
faExclamationCircle = faExclamationCircle;

showNotification = false;

toggleNotification() {
this.showNotification = !this.showNotification;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';

import { DaffButtonModule } from '@daffodil/design/button';
import { DaffNotificationModule } from '@daffodil/design/notification';

import { NotificationWithActionsComponent } from './notification-with-actions.component';

@NgModule({
declarations: [
NotificationWithActionsComponent,
],
imports: [
CommonModule,
DaffNotificationModule,
FontAwesomeModule,
DaffButtonModule,
],
exports: [
NotificationWithActionsComponent,
],
})
export class NotificationWithActionsModule { }
3 changes: 3 additions & 0 deletions libs/design/notification/examples/src/public_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ import { NotificationOrientationsComponent } from './notification-orientations/n
import { NotificationOrientationsModule } from './notification-orientations/notification-orientations.module';
import { NotificationStatusComponent } from './notification-status/notification-status.component';
import { NotificationStatusModule } from './notification-status/notification-status.module';
import { NotificationWithActionsComponent } from './notification-with-actions/notification-with-actions.component';
import { NotificationWithActionsModule } from './notification-with-actions/notification-with-actions.module';

export const NOTIFICATION_EXAMPLES: ComponentExample[] = [
{ component: DefaultNotificationComponent, module: DefaultNotificationModule },
{ component: NotificationStatusComponent, module: NotificationStatusModule },
{ component: NotificationOrientationsComponent, module: NotificationOrientationsModule },
{ component: DismissibleNotificationComponent, module: DismissibleNotificationModule },
{ component: NotificationWithActionsComponent, module: NotificationWithActionsModule },
];
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
<ng-container *ngIf="_prefix">
<ng-content select="[daffPrefix]"></ng-content>
</ng-container>
<div class="daff-notification__details">
<div class="daff-notification__body">
<div class="daff-notification__content">
<ng-content select="[daffNotificationTitle]"></ng-content>
<ng-content select="[daffNotificationSubtitle]"></ng-content>
</div>
<ng-content select="[daffNotificationActions]"></ng-content>
<ng-container *ngIf="_actions">
<ng-content select="[daffNotificationActions]"></ng-content>
</ng-container>
</div>
<button class="daff-notification__close-icon" *ngIf="dismissible" (click)="onCloseNotification($event)">
<fa-icon [icon]="faTimes" [fixedWidth]="true"></fa-icon>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
@use '../../../scss/interactions';
@use '../../../scss/typography' as t;

.daff-notification {
$root: &;
display: flex;
border-radius: 4px;
font-size: 1rem;
font-size: t.$font-size-base;
line-height: 1.5rem;
position: relative;

Expand All @@ -25,7 +26,7 @@
width: 3rem;
}

&__details {
&__body {
display: flex;
font-size: 1rem;
line-height: 1.5rem;
Expand All @@ -40,19 +41,20 @@
}

&__title {
font-size: t.$font-size-base;
font-weight: 600;
line-height: 1.5rem;
}

&__subtitle {
align-self: center;
font-size: t.$font-size-base;
font-weight: normal;
line-height: 1.25rem;
}

&__actions {
display: flex;
gap: 8px;
margin: 16px 0 0;
}

&.dismissible {
Expand All @@ -64,8 +66,9 @@
}

&.vertical {
#{$root}__details {
#{$root}__body {
flex-direction: column;
gap: 16px;
}
}

Expand All @@ -74,7 +77,11 @@
padding: 12px 0 12px 16px;
}

#{$root}__details {
#{$root}__subtitle {
align-self: center;
}

#{$root}__body {
flex-direction: row;
gap: 0;
padding: 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import {
Component,
ContentChild,
DebugElement,
Input,
ViewChild,
} from '@angular/core';
import {
waitForAsync,
Expand All @@ -15,6 +18,7 @@ import {
DaffNotificationComponent,
DaffNotificationOrientation,
} from './notification.component';
import { DaffNotificationActionsDirective } from '../notification-actions/notification-actions.directive';

@Component ({
template: `
Expand Down Expand Up @@ -75,6 +79,20 @@ describe('@daffodil/design/notification | DaffNotificationComponent', () => {
expect(component.dismissible).toEqual(wrapper.dismissible);
});

it('should set dismissible to false by default', () => {
expect(component.dismissible).toBeFalse();
});

describe('when dismissible is set to false', () => {
it('should not add a class of "dismissible" to the host element', () => {
expect(de.classes['dismissible']).toBeUndefined();
});

it('should not show the close icon button', () => {
expect(fixture.debugElement.query(By.css('.daff-notification__close-icon'))).toBeFalsy();
});
});

describe('when dismissible is set to true', () => {
beforeEach(() => {
wrapper.dismissible = true;
Expand All @@ -91,14 +109,30 @@ describe('@daffodil/design/notification | DaffNotificationComponent', () => {
});
});

it('should set aria-live to polite', () => {
expect(de.attributes['aria-live']).toEqual('polite');
});

it('should set the tabindex to 0', () => {
expect(de.attributes['tabindex']).toEqual('0');
});

describe('setting the role', () => {
it('should set role to status', () => {
expect(component.role).toBe('status');
});

it('should set role to alert if status is warn', () => {
wrapper.status = 'warn';
fixture.detectChanges();

expect(component.role).toBe('alert');
});

it('should set role to alert if status is danger', () => {
wrapper.status = 'danger';
fixture.detectChanges();

expect(component.role).toBe('alert');
});
});

describe('using the status property of a notification', () => {
it('should not set a default status', () => {
expect(component.status).toBeFalsy();
Expand Down
Loading

0 comments on commit 8b5130a

Please sign in to comment.