From 3bec04c183e269f4158a00ed6902292ff57c798d Mon Sep 17 00:00:00 2001 From: rowa-audil Date: Wed, 19 Aug 2020 15:25:55 +0200 Subject: [PATCH] chore: Adds aria support for the switch --- .../switch/src/lib/switch.spec.ts | 36 ++++++++++++++++ libs/fluid-elements/switch/src/lib/switch.ts | 43 ++++++++++++++++++- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/libs/fluid-elements/switch/src/lib/switch.spec.ts b/libs/fluid-elements/switch/src/lib/switch.spec.ts index f4f8ae3531..bf871ec5cf 100644 --- a/libs/fluid-elements/switch/src/lib/switch.spec.ts +++ b/libs/fluid-elements/switch/src/lib/switch.spec.ts @@ -112,6 +112,42 @@ describe('Fluid switch', () => { }); }); + describe('aria-checked attribute', () => { + it('should have aria-checked set to false by default', () => { + expect(fixture.hasAttribute('aria-checked')).toBeTruthy(); + expect(fixture.getAttribute('aria-checked')).toBe('false'); + }); + + it('should have aria-checked set to false when the switch is not checked', async () => { + fixture.checked = false; + await tick(); + expect(fixture.hasAttribute('aria-checked')).toBeTruthy(); + expect(fixture.getAttribute('aria-checked')).toBe('false'); + }); + + it('should have aria-checked set to true when the switch is checked', async () => { + fixture.checked = true; + await tick(); + expect(fixture.hasAttribute('aria-checked')).toBeTruthy(); + expect(fixture.getAttribute('aria-checked')).toBe('true'); + }); + + it('should have aria-checked set to true when the switch is clicked', async () => { + const label = fixture.shadowRoot?.querySelector('label'); + label?.click(); + await tick(); + expect(fixture.hasAttribute('aria-checked')).toBeTruthy(); + expect(fixture.getAttribute('aria-checked')).toBe('true'); + }); + it('should have aria-checked set to true when the switch is focused and space is pressed', async () => { + const checkbox = fixture.shadowRoot?.querySelector('svg'); + dispatchKeyboardEvent(checkbox!, 'keyup', SPACE); + await tick(); + expect(fixture.hasAttribute('aria-checked')).toBeTruthy(); + expect(fixture.getAttribute('aria-checked')).toBe('true'); + }); + }); + describe('disabled attribute', () => { it('should set the disabled state when the attribute is present', async () => { fixture.setAttribute('disabled', ''); diff --git a/libs/fluid-elements/switch/src/lib/switch.ts b/libs/fluid-elements/switch/src/lib/switch.ts index 540a23a96f..f437081d10 100644 --- a/libs/fluid-elements/switch/src/lib/switch.ts +++ b/libs/fluid-elements/switch/src/lib/switch.ts @@ -53,7 +53,7 @@ let uniqueCounter = 0; export class FluidSwitch extends LitElement { /** * Unique identifier used for the id and label connection - * within the checkbox. + * within the switch. */ private _unique = `fluid-switch-${uniqueCounter++}`; @@ -89,7 +89,7 @@ export class FluidSwitch extends LitElement { display: flex; position: relative; } - :host([disabled]) .fluid-switch-input, /* Checkbox should be hidden in the disabled state as well. */ + :host([disabled]) .fluid-switch-input, /* Switch should be hidden in the disabled state as well. */ .fluid-switch-input { position: absolute; width: 35px; @@ -193,12 +193,51 @@ export class FluidSwitch extends LitElement { } private _checked = false; + /** + * Role of the switch. + * @private - An internal prop that should not appear in the readme and should + * not be set by the outside. + */ + @property({ + type: String, + reflect: true, + }) + role: string = 'switch'; + + /** + * Aria-checked attribute of the switch. + * @private - An internal prop that should not appear in the readme and should + * not be set by the outside. + */ + @property({ + type: String, + reflect: true, + attribute: 'aria-checked', + }) + ariaChecked: string = 'false'; + /** First updated lifecycle */ firstUpdated(props: Map): void { super.firstUpdated(props); this._inputElement = this.shadowRoot?.querySelector('input')!; } + /** Update lifecycle */ + update(props: Map): void { + // Aria-checked depends on the value of checked, but is never actually + // set by the litElement reactivity. In the updated lifeCycle + // we need to manually update the ariaChecked attribute here. + if (props.has('checked')) { + this.ariaChecked = this.checked.toString(); + } + // Changing the aria-checked or any observed property in the update, will + // add it to the updated properties. When calling super first in, the change + // of properties in the update call will trigger an update, as the properties + // will have changed after the super.update() call. To prevent an additional + // cycle, we make the modifications before calling the super lifecycle + super.update(props); + } + /** * Render function. */