Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(vwc-banner): add aria live polite (#1093)
* add aria live and role * story fix * add aria-live and role * render aria live * ui test * directive * inport accessible-snackbar-label-directive * ui test * dependency * ui test * directive * add dep * aria property * update versions * yarn * yarn * reflect * aria * set role attribute * add constants * merge * merge * test * test * tests * a11y test * test * test Co-authored-by: yinonov <yinon@hotmail.com>
- Loading branch information
Showing
9 changed files
with
165 additions
and
12 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,4 +40,4 @@ | |
"lit-html": "^1.3.0", | ||
"typescript": "^4.3.2" | ||
} | ||
} | ||
} |
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,82 @@ | ||
import { html, TemplateResult } from 'lit-element'; | ||
import { render } from 'lit-html'; | ||
import { AsyncDirective } from 'lit-html/async-directive.js'; | ||
import { | ||
ChildPart, directive, DirectiveParameters, PartInfo, PartType | ||
} from 'lit-html/directive.js'; | ||
|
||
class AccessibleBannerDirective extends AsyncDirective { | ||
protected labelEl: HTMLElement|null = null; | ||
protected timerId: number|null = null; | ||
protected previousPart: ChildPart|null = null; | ||
|
||
constructor(partInfo: PartInfo) { | ||
super(partInfo); | ||
|
||
if (partInfo.type !== PartType.CHILD) { | ||
throw new Error('AccessibleBannerDirective only supports child parts.'); | ||
} | ||
} | ||
|
||
override update(part: ChildPart, [ | ||
message, open, role, ariaLive | ||
]: DirectiveParameters<this>) { | ||
if (!open) { | ||
return; | ||
} | ||
|
||
if (this.labelEl === null) { | ||
const wrapperEl = document.createElement('div'); | ||
const messageTemplate = | ||
html`<div class="banner--message" role=${role} aria-live=${ariaLive}></div>`; | ||
|
||
render(messageTemplate, wrapperEl); | ||
|
||
const labelEl = wrapperEl.firstElementChild! as HTMLElement; | ||
labelEl.textContent = message; | ||
|
||
part.endNode?.parentNode!.insertBefore(labelEl, part.endNode); | ||
this.labelEl = labelEl; | ||
return labelEl; | ||
} | ||
|
||
const messageEl = this.labelEl; | ||
messageEl.setAttribute('role', ''); | ||
messageEl.setAttribute('aria-live', 'off'); | ||
messageEl.textContent = ''; | ||
|
||
const spaceSpan = document.createElement('span'); | ||
spaceSpan.style.display = 'inline-block'; | ||
spaceSpan.style.width = '0'; | ||
spaceSpan.style.height = '1px'; | ||
spaceSpan.textContent = '\u00A0'; // U+00A0 is | ||
messageEl.appendChild(spaceSpan); | ||
messageEl.setAttribute('message-text', message); | ||
|
||
if (this.timerId !== null) { | ||
clearTimeout(this.timerId); | ||
} | ||
|
||
this.timerId = window.setTimeout(() => { | ||
this.timerId = null; | ||
messageEl.setAttribute('role', role); | ||
messageEl.setAttribute('aria-live', ariaLive); | ||
messageEl.removeAttribute('message-text'); | ||
messageEl.textContent = message; | ||
this.setValue(this.labelEl); | ||
}, 1000); | ||
|
||
return messageEl; | ||
} | ||
|
||
render(message: string, isOpen: boolean, role: string, ariaLive: string): TemplateResult { | ||
if (!isOpen) { | ||
return html``; | ||
} | ||
|
||
return html` | ||
<div class="banner--message" role=${role} aria-live=${ariaLive}>${message}</div>`; | ||
} | ||
} | ||
|
||
export const accessibleBannerDirective = directive(AccessibleBannerDirective); |
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
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 |
---|---|---|
@@ -1,10 +1,30 @@ | ||
import 'chai-a11y-axe'; | ||
import { html } from 'lit-html'; | ||
import { fixture } from '@open-wc/testing-helpers'; | ||
import { fixture, aTimeout } from '@open-wc/testing-helpers'; | ||
|
||
describe('banner a11y', function () { | ||
const TRANSITION_TIME = 200; | ||
it('should adhere to accessibility guidelines', async function () { | ||
const bannerEl = await fixture(html`<vwc-banner></vwc-banner>`); | ||
const bannerEl = await fixture(html`<vwc-banner message="Hello" open></vwc-banner>`); | ||
await expect(bannerEl).shadowDom.to.be.accessible(); | ||
}); | ||
it('should be with default role and aria-live values', async function () { | ||
const bannerEl = await fixture(html`<vwc-banner message="Hello" open></vwc-banner>`); | ||
expect(bannerEl.shadowRoot.querySelector('.banner--message')).to.have.attribute('role', 'status'); | ||
expect(bannerEl.shadowRoot.querySelector('.banner--message')).to.have.attribute('aria-live', 'polite'); | ||
}); | ||
it('should be with reflected role and aria-live values', async function () { | ||
const bannerEl = await fixture(html`<vwc-banner message="Hello" open role="alert" aria-live="assertive" dismissible></vwc-banner>`); | ||
expect(bannerEl.shadowRoot.querySelector('.banner--message')).to.have.attribute('role', 'alert'); | ||
expect(bannerEl.shadowRoot.querySelector('.banner--message')).to.have.attribute('aria-live', 'assertive'); | ||
bannerEl.shadowRoot.querySelector('vwc-icon-button')?.click(); | ||
await aTimeout(TRANSITION_TIME * 1.1); | ||
bannerEl.setAttribute('open', 'true'); | ||
expect(bannerEl.shadowRoot.querySelector('.banner--message')).to.equal(null); | ||
expect(bannerEl.shadowRoot.querySelector('.banner--message')).to.equal(null); | ||
}); | ||
it('should be without role and aria-live values when closed', async function () { | ||
const bannerEl = await fixture(html`<vwc-banner message="Hello"></vwc-banner>`); | ||
expect(bannerEl.shadowRoot.querySelector('.banner--message')).to.equal(null); | ||
}); | ||
}); |
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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