diff --git a/packages/notification/src/vaadin-notification.d.ts b/packages/notification/src/vaadin-notification.d.ts index 88c6c29fbf..3ebc31fb9e 100644 --- a/packages/notification/src/vaadin-notification.d.ts +++ b/packages/notification/src/vaadin-notification.d.ts @@ -40,6 +40,7 @@ export interface NotificationCustomEventMap { export interface NotificationEventMap extends HTMLElementEventMap, NotificationCustomEventMap {} export interface ShowOptions { + assertive?: boolean; duration?: number; position?: NotificationPosition; theme?: string; @@ -116,6 +117,7 @@ declare class Notification extends OverlayClassMixin(ThemePropertyMixin(ElementM * * ``` * { + * assertive?: boolean * position?: string * duration?: number * theme?: string @@ -123,6 +125,7 @@ declare class Notification extends OverlayClassMixin(ThemePropertyMixin(ElementM * ``` * * See the individual documentation for: + * - [`assertive`](#/elements/vaadin-notification#property-assertive) * - [`position`](#/elements/vaadin-notification#property-position) * - [`duration`](#/elements/vaadin-notification#property-duration) * @@ -131,6 +134,13 @@ declare class Notification extends OverlayClassMixin(ThemePropertyMixin(ElementM */ static show(contents: TemplateResult | string, options?: ShowOptions): Notification; + /** + * When true, the notification card has `aria-live` attribute set to + * `assertive` instead of `polite`. This makes screen readers announce + * the notification content immediately when it appears. + */ + assertive: boolean; + /** * The duration in milliseconds to show the notification. * Set to `0` or a negative number to disable the notification auto-closing. diff --git a/packages/notification/src/vaadin-notification.js b/packages/notification/src/vaadin-notification.js index 852164ea59..5db86c1343 100644 --- a/packages/notification/src/vaadin-notification.js +++ b/packages/notification/src/vaadin-notification.js @@ -208,7 +208,6 @@ class NotificationCard extends ThemableMixin(PolymerElement) { ready() { super.ready(); this.setAttribute('role', 'alert'); - this.setAttribute('aria-live', 'polite'); } } @@ -273,7 +272,10 @@ class Notification extends OverlayClassMixin(ThemePropertyMixin(ElementMixin(Pol display: none !important; } - + `; } @@ -283,6 +285,16 @@ class Notification extends OverlayClassMixin(ThemePropertyMixin(ElementMixin(Pol static get properties() { return { + /** + * When true, the notification card has `aria-live` attribute set to + * `assertive` instead of `polite`. This makes screen readers announce + * the notification content immediately when it appears. + */ + assertive: { + type: Boolean, + value: false, + }, + /** * The duration in milliseconds to show the notification. * Set to `0` or a negative number to disable the notification auto-closing. @@ -340,6 +352,7 @@ class Notification extends OverlayClassMixin(ThemePropertyMixin(ElementMixin(Pol * * ``` * { + * assertive?: boolean * position?: string * duration?: number * theme?: string @@ -347,6 +360,7 @@ class Notification extends OverlayClassMixin(ThemePropertyMixin(ElementMixin(Pol * ``` * * See the individual documentation for: + * - [`assertive`](#/elements/vaadin-notification#property-assertive) * - [`position`](#/elements/vaadin-notification#property-position) * - [`duration`](#/elements/vaadin-notification#property-duration) * @@ -373,6 +387,9 @@ class Notification extends OverlayClassMixin(ThemePropertyMixin(ElementMixin(Pol if (options && options.position) { notification.position = options.position; } + if (options && options.assertive) { + notification.assertive = options.assertive; + } if (options && options.theme) { notification.setAttribute('theme', options.theme); } @@ -436,6 +453,11 @@ class Notification extends OverlayClassMixin(ThemePropertyMixin(ElementMixin(Pol this.renderer(this._card, this); } + /** @private */ + __computeAriaLive(assertive) { + return assertive ? 'assertive' : 'polite'; + } + /** @private */ _rendererChanged(renderer, opened, card) { if (!card) { diff --git a/packages/notification/test/dom/__snapshots__/notification.test.snap.js b/packages/notification/test/dom/__snapshots__/notification.test.snap.js index 5643e71d5a..2477558526 100644 --- a/packages/notification/test/dom/__snapshots__/notification.test.snap.js +++ b/packages/notification/test/dom/__snapshots__/notification.test.snap.js @@ -36,3 +36,14 @@ snapshots["vaadin-notification card class"] = `; /* end snapshot vaadin-notification card class */ +snapshots["vaadin-notification assertive"] = +` + content + +`; +/* end snapshot vaadin-notification assertive */ + diff --git a/packages/notification/test/dom/notification.test.js b/packages/notification/test/dom/notification.test.js index b5c80e43ac..f267d6b0fe 100644 --- a/packages/notification/test/dom/notification.test.js +++ b/packages/notification/test/dom/notification.test.js @@ -29,4 +29,9 @@ describe('vaadin-notification', () => { notification.overlayClass = 'custom'; await expect(card).dom.to.equalSnapshot(); }); + + it('assertive', async () => { + notification.assertive = true; + await expect(card).dom.to.equalSnapshot(); + }); }); diff --git a/packages/notification/test/notification.test.js b/packages/notification/test/notification.test.js index 10193fe45a..5208f7ada2 100644 --- a/packages/notification/test/notification.test.js +++ b/packages/notification/test/notification.test.js @@ -173,6 +173,18 @@ describe('vaadin-notification', () => { it('notification card should have `aria-live="polite"`', () => { expect(notification._card.getAttribute('aria-live')).to.be.equal('polite'); }); + + it('should update `aria-live` to "assertive" when assertive is set to true', () => { + notification.assertive = true; + expect(notification._card.getAttribute('aria-live')).to.be.equal('assertive'); + }); + + it('should update `aria-live` to "polite" when assertive is set to false', () => { + notification.assertive = true; + + notification.assertive = false; + expect(notification._card.getAttribute('aria-live')).to.be.equal('polite'); + }); }); describe('methods', () => { diff --git a/packages/notification/test/statichelper.test.js b/packages/notification/test/statichelper.test.js index 4b8bd63697..8da8e4938b 100644 --- a/packages/notification/test/statichelper.test.js +++ b/packages/notification/test/statichelper.test.js @@ -35,6 +35,11 @@ describe('static helpers', () => { expect(notification.position).to.equal('top-center'); }); + it('show should use assertive property when set to true', () => { + const notification = Notification.show('Hello world', { assertive: true }); + expect(notification.assertive).to.be.true; + }); + it('show should set the given theme attribute', () => { const notification = Notification.show('Hello world', { theme: 'error' }); expect(notification.getAttribute('theme')).to.equal('error'); diff --git a/packages/notification/test/typings/notification.types.ts b/packages/notification/test/typings/notification.types.ts index c3875b45e7..4f9295026c 100644 --- a/packages/notification/test/typings/notification.types.ts +++ b/packages/notification/test/typings/notification.types.ts @@ -20,6 +20,7 @@ assertType(notification); assertType(notification); // Properties +assertType(notification.assertive); assertType(notification.duration); assertType(notification.opened); assertType(notification.position); @@ -36,7 +37,7 @@ notification.addEventListener('closed', (event) => { assertType(event); }); -Notification.show('Hello world', { position: 'middle', duration: 7000, theme: 'error' }); +Notification.show('Hello world', { assertive: true, position: 'middle', duration: 7000, theme: 'error' }); const renderer: NotificationRenderer = (root, owner) => { assertType(root);