diff --git a/src/components/radio-button/radio-button/radio-button.e2e.ts b/src/components/radio-button/radio-button/radio-button.e2e.ts index ed85e79385..6a7e47b9e6 100644 --- a/src/components/radio-button/radio-button/radio-button.e2e.ts +++ b/src/components/radio-button/radio-button/radio-button.e2e.ts @@ -9,72 +9,186 @@ import { SbbRadioButtonElement } from './radio-button.js'; describe(`sbb-radio-button`, () => { let element: SbbRadioButtonElement; - beforeEach(async () => { - element = await fixture(html`Value label`); - }); + describe('general', () => { + beforeEach(async () => { + element = await fixture(html`Value label`); + }); + + it('renders', async () => { + assert.instanceOf(element, SbbRadioButtonElement); + }); + + it('should have corresponding aria values set', async () => { + expect(element).to.have.attribute('aria-checked', 'false'); + expect(element).to.have.attribute('aria-required', 'false'); + expect(element).not.to.have.attribute('aria-disabled'); + }); + + it('should update aria values', async () => { + element.checked = true; + element.required = true; + element.disabled = true; + + await waitForLitRender(element); - it('renders', async () => { - assert.instanceOf(element, SbbRadioButtonElement); - }); + expect(element).to.have.attribute('aria-checked', 'true'); + expect(element).to.have.attribute('aria-required', 'true'); + expect(element).to.have.attribute('aria-disabled', 'true'); + }); - it('should not render accessibility label about containing state', async () => { - element = element.shadowRoot!.querySelector('.sbb-screen-reader-only:not(input)')!; - expect(element).not.to.be.ok; - }); + it('should not render accessibility label about containing state', async () => { + element = element.shadowRoot!.querySelector('.sbb-screen-reader-only:not(input)')!; + expect(element).not.to.be.ok; + }); - it('selects radio on click', async () => { - const stateChange = new EventSpy(SbbRadioButtonElement.events.stateChange); + it('selects radio on click', async () => { + const stateChange = new EventSpy(SbbRadioButtonElement.events.stateChange); - element.click(); - await waitForLitRender(element); + element.click(); + await waitForLitRender(element); - expect(element).to.have.attribute('checked'); - await waitForCondition(() => stateChange.events.length === 1); - expect(stateChange.count).to.be.equal(1); - }); + expect(element).to.have.attribute('checked'); + await waitForCondition(() => stateChange.events.length === 1); + expect(stateChange.count).to.be.equal(1); + }); - it('does not deselect radio if already checked', async () => { - const stateChange = new EventSpy(SbbRadioButtonElement.events.stateChange); + it('does not deselect radio if already checked', async () => { + const stateChange = new EventSpy(SbbRadioButtonElement.events.stateChange); - element.click(); - await waitForLitRender(element); - expect(element).to.have.attribute('checked'); - await waitForCondition(() => stateChange.events.length === 1); - expect(stateChange.count).to.be.equal(1); + element.click(); + await waitForLitRender(element); + expect(element).to.have.attribute('checked'); + await waitForCondition(() => stateChange.events.length === 1); + expect(stateChange.count).to.be.equal(1); - element.click(); - await waitForLitRender(element); - expect(element).to.have.attribute('checked'); - await waitForCondition(() => stateChange.events.length === 1); - expect(stateChange.count).to.be.equal(1); - }); + element.click(); + await waitForLitRender(element); + expect(element).to.have.attribute('checked'); + await waitForCondition(() => stateChange.events.length === 1); + expect(stateChange.count).to.be.equal(1); + }); - it('allows empty selection', async () => { - const stateChange = new EventSpy(SbbRadioButtonElement.events.stateChange); - - element.allowEmptySelection = true; - element.click(); - await waitForLitRender(element); - expect(element).to.have.attribute('checked'); - await waitForCondition(() => stateChange.events.length === 1); - expect(stateChange.count).to.be.equal(1); - - element.click(); - await waitForLitRender(element); - expect(element).not.to.have.attribute('checked'); - await waitForCondition(() => stateChange.events.length === 2); - expect(stateChange.count).to.be.equal(2); - }); + it('allows empty selection', async () => { + const stateChange = new EventSpy(SbbRadioButtonElement.events.stateChange); + + element.allowEmptySelection = true; + element.click(); + await waitForLitRender(element); + expect(element).to.have.attribute('checked'); + await waitForCondition(() => stateChange.events.length === 1); + expect(stateChange.count).to.be.equal(1); + + element.click(); + await waitForLitRender(element); + expect(element).not.to.have.attribute('checked'); + await waitForCondition(() => stateChange.events.length === 2); + expect(stateChange.count).to.be.equal(2); + }); + + it('should convert falsy checked to false', async () => { + element.checked = true; + (element.checked as any) = undefined; + + await waitForLitRender(element); + + expect(element.checked).to.equal(false); + expect(element).to.have.attribute('aria-checked', 'false'); + }); + + it('should convert truthy checked to true', async () => { + element.checked = true; + (element.checked as any) = 2; + + await waitForLitRender(element); + + expect(element.checked).to.equal(true); + expect(element).to.have.attribute('aria-checked', 'true'); + }); + + it('should convert falsy disabled to false', async () => { + element.disabled = true; + (element.disabled as any) = undefined; + + await waitForLitRender(element); + + expect(element.disabled).to.equal(false); + expect(element).not.to.have.attribute('aria-disabled'); + }); + + it('should convert truthy disabled to true', async () => { + element.disabled = true; + (element.disabled as any) = 2; + + await waitForLitRender(element); + + expect(element.disabled).to.equal(true); + expect(element).to.have.attribute('aria-disabled', 'true'); + }); + + it('should convert falsy required to false', async () => { + element.required = true; + (element.required as any) = undefined; + + await waitForLitRender(element); + + expect(element.required).to.equal(false); + expect(element).to.have.attribute('aria-required', 'false'); + }); + + it('should convert truthy required to true', async () => { + element.required = true; + (element.required as any) = 2; + + await waitForLitRender(element); + + expect(element.required).to.equal(true); + expect(element).to.have.attribute('aria-required', 'true'); + }); + + it('should convert falsy allowEmptySelection to false', async () => { + element.allowEmptySelection = true; + (element.allowEmptySelection as any) = undefined; + + await waitForLitRender(element); + + expect(element.allowEmptySelection).to.equal(false); + }); + + it('should convert truthy allowEmptySelection to true', async () => { + element.allowEmptySelection = true; + (element.allowEmptySelection as any) = 2; + + await waitForLitRender(element); - it('should convert falsy checked to false', async () => { - element.checked = true; - (element.checked as any) = undefined; - expect(element.checked).to.equal(false); + expect(element.allowEmptySelection).to.equal(true); + }); }); - it('should convert truthy checked to true', async () => { - element.checked = true; - (element.checked as any) = 2; - expect(element.checked).to.equal(true); + describe('with initial attributes', () => { + beforeEach(async () => { + element = await fixture( + html` + Value label + `, + ); + }); + + it('should have corresponding aria values set', async () => { + expect(element).to.have.attribute('aria-checked', 'true'); + expect(element).to.have.attribute('aria-required', 'true'); + expect(element).to.have.attribute('aria-disabled', 'true'); + }); + + it('should update aria values', async () => { + element.checked = false; + element.required = false; + element.disabled = false; + + await waitForLitRender(element); + + expect(element).to.have.attribute('aria-checked', 'false'); + expect(element).to.have.attribute('aria-required', 'false'); + expect(element).not.to.have.attribute('aria-disabled'); + }); }); }); diff --git a/src/components/radio-button/radio-button/radio-button.ts b/src/components/radio-button/radio-button/radio-button.ts index 5636d200c0..77b91b739a 100644 --- a/src/components/radio-button/radio-button/radio-button.ts +++ b/src/components/radio-button/radio-button/radio-button.ts @@ -52,7 +52,7 @@ export class SbbRadioButtonElement extends SbbUpdateSchedulerMixin(LitElement) { */ @property({ attribute: 'allow-empty-selection', type: Boolean }) public set allowEmptySelection(value: boolean) { - this._allowEmptySelection = value; + this._allowEmptySelection = Boolean(value); } public get allowEmptySelection(): boolean { return this._allowEmptySelection || (this.group?.allowEmptySelection ?? false); @@ -69,7 +69,7 @@ export class SbbRadioButtonElement extends SbbUpdateSchedulerMixin(LitElement) { */ @property({ reflect: true, type: Boolean }) public set disabled(value: boolean) { - this._disabled = value; + this._disabled = Boolean(value); } public get disabled(): boolean { return this._disabled || (this.group?.disabled ?? false); @@ -81,7 +81,7 @@ export class SbbRadioButtonElement extends SbbUpdateSchedulerMixin(LitElement) { */ @property({ reflect: true, type: Boolean }) public set required(value: boolean) { - this._required = value; + this._required = Boolean(value); } public get required(): boolean { return this._required || (this.group?.required ?? false); @@ -158,22 +158,6 @@ export class SbbRadioButtonElement extends SbbUpdateSchedulerMixin(LitElement) { SbbRadioButtonElement.events.radioButtonLoaded, { bubbles: true }, ); - - private _handleCheckedChange(currentValue: boolean, previousValue: boolean): void { - if (currentValue !== previousValue) { - this.setAttribute('aria-checked', `${currentValue}`); - this._stateChange.emit({ type: 'checked', checked: currentValue }); - this.isSelectionPanelInput && this._updateExpandedLabel(); - } - } - - private _handleDisabledChange(currentValue: boolean, previousValue: boolean): void { - if (currentValue !== previousValue) { - setOrRemoveAttribute(this, 'aria-disabled', currentValue ? 'true' : null); - this._stateChange.emit({ type: 'disabled', disabled: currentValue }); - } - } - private _handleClick(event: Event): void { event.preventDefault(); this.select(); @@ -221,10 +205,17 @@ export class SbbRadioButtonElement extends SbbUpdateSchedulerMixin(LitElement) { super.willUpdate(changedProperties); if (changedProperties.has('checked')) { - this._handleCheckedChange(this.checked, changedProperties.get('checked')!); + this.setAttribute('aria-checked', `${this.checked}`); + if (this.checked !== changedProperties.get('checked')!) { + this._stateChange.emit({ type: 'checked', checked: this.checked }); + this.isSelectionPanelInput && this._updateExpandedLabel(); + } } if (changedProperties.has('disabled')) { - this._handleDisabledChange(this.disabled, changedProperties.get('disabled')!); + setOrRemoveAttribute(this, 'aria-disabled', this.disabled ? 'true' : null); + if (this.disabled !== changedProperties.get('disabled')!) { + this._stateChange.emit({ type: 'disabled', disabled: this.disabled }); + } } if (changedProperties.has('required')) { this.setAttribute('aria-required', `${this.required}`);