diff --git a/src/elements/autocomplete-grid/autocomplete-grid-option/autocomplete-grid-option.scss b/src/elements/autocomplete-grid/autocomplete-grid-option/autocomplete-grid-option.scss index acb964f983..00d6afc1ec 100644 --- a/src/elements/autocomplete-grid/autocomplete-grid-option/autocomplete-grid-option.scss +++ b/src/elements/autocomplete-grid/autocomplete-grid-option/autocomplete-grid-option.scss @@ -14,7 +14,7 @@ display: block; } -:host([active]) { +:host([data-active]) { --sbb-focus-outline-offset: calc(-1 * var(--sbb-spacing-fixed-1x)); } @@ -41,7 +41,7 @@ padding-inline: var(--sbb-option-padding-inline); color: var(--sbb-option-color); - :host([active]) & { + :host([data-active]) & { @include sbb.focus-outline; border-radius: var(--sbb-option-border-radius); diff --git a/src/elements/autocomplete-grid/autocomplete-grid-option/autocomplete-grid-option.visual.spec.ts b/src/elements/autocomplete-grid/autocomplete-grid-option/autocomplete-grid-option.visual.spec.ts index dcd04e83ec..d809b6b7e7 100644 --- a/src/elements/autocomplete-grid/autocomplete-grid-option/autocomplete-grid-option.visual.spec.ts +++ b/src/elements/autocomplete-grid/autocomplete-grid-option/autocomplete-grid-option.visual.spec.ts @@ -25,7 +25,7 @@ describe(`sbb-autocomplete-grid-option`, () => { Value ${i + 1}Option label -Option label - Option label ``` @@ -86,13 +84,12 @@ which is needed to correctly set the `aria-activedescendant` on the related `inp ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| ---------- | ----------- | ------- | ---------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | -| `active` | `active` | public | `boolean \| undefined` | | Whether the option is currently active. | -| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | -| `iconName` | `icon-name` | public | `string \| undefined` | | The icon name we want to use, choose from the small icon variants from the ui-icons category from here https://icons.app.sbb.ch. | -| `selected` | `selected` | public | `boolean` | | Whether the option is selected. | -| `value` | `value` | public | `string` | | Value of the option. | +| Name | Attribute | Privacy | Type | Default | Description | +| ---------- | ----------- | ------- | --------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | +| `iconName` | `icon-name` | public | `string \| undefined` | | The icon name we want to use, choose from the small icon variants from the ui-icons category from here https://icons.app.sbb.ch. | +| `selected` | `selected` | public | `boolean` | | Whether the option is selected. | +| `value` | `value` | public | `string` | | Value of the option. | ## Events diff --git a/src/elements/autocomplete-grid/autocomplete-grid/autocomplete-grid.spec.ts b/src/elements/autocomplete-grid/autocomplete-grid/autocomplete-grid.spec.ts index aebd7ced75..50e0d663b7 100644 --- a/src/elements/autocomplete-grid/autocomplete-grid/autocomplete-grid.spec.ts +++ b/src/elements/autocomplete-grid/autocomplete-grid/autocomplete-grid.spec.ts @@ -208,30 +208,30 @@ describe(`sbb-autocomplete-grid`, () => { await sendKeys({ press: 'ArrowDown' }); await sendKeys({ press: 'ArrowDown' }); await waitForLitRender(element); - expect(optTwo).to.have.attribute('active'); + expect(optTwo).to.have.attribute('data-active'); expect(buttonTwo).not.to.have.attribute('data-focus-visible'); expect(buttonThree).not.to.have.attribute('data-focus-visible'); expect(input).to.have.attribute('aria-activedescendant', 'option-2'); await sendKeys({ press: 'ArrowRight' }); await waitForLitRender(element); - expect(optTwo).not.to.have.attribute('active'); + expect(optTwo).not.to.have.attribute('data-active'); expect(buttonTwo).to.have.attribute('data-focus-visible'); expect(buttonThree).not.to.have.attribute('data-focus-visible'); expect(input).to.have.attribute('aria-activedescendant', 'button-2'); await sendKeys({ press: 'ArrowRight' }); await waitForLitRender(element); - expect(optTwo).not.to.have.attribute('active'); + expect(optTwo).not.to.have.attribute('data-active'); expect(buttonTwo).not.to.have.attribute('data-focus-visible'); expect(buttonThree).to.have.attribute('data-focus-visible'); expect(input).to.have.attribute('aria-activedescendant', 'button-3'); await sendKeys({ press: 'ArrowDown' }); await waitForLitRender(element); - expect(optOne).to.have.attribute('active'); + expect(optOne).to.have.attribute('data-active'); expect(buttonOne).not.to.have.attribute('data-focus-visible'); - expect(optTwo).not.to.have.attribute('active'); + expect(optTwo).not.to.have.attribute('data-active'); expect(buttonTwo).not.to.have.attribute('data-focus-visible'); expect(buttonThree).not.to.have.attribute('data-focus-visible'); expect(input).to.have.attribute('aria-activedescendant', 'option-1'); @@ -253,9 +253,9 @@ describe(`sbb-autocomplete-grid`, () => { await sendKeys({ press: 'ArrowDown' }); await sendKeys({ press: 'ArrowDown' }); await waitForLitRender(element); - expect(optOne).not.to.have.attribute('active'); + expect(optOne).not.to.have.attribute('data-active'); expect(optOne).not.to.have.attribute('selected'); - expect(optTwo).to.have.attribute('active'); + expect(optTwo).to.have.attribute('data-active'); expect(optTwo).not.to.have.attribute('selected'); expect(input).to.have.attribute('aria-activedescendant', 'option-2'); @@ -263,7 +263,7 @@ describe(`sbb-autocomplete-grid`, () => { await waitForCondition(() => didCloseEventSpy.events.length === 1); expect(didCloseEventSpy.count).to.be.equal(1); - expect(optTwo).not.to.have.attribute('active'); + expect(optTwo).not.to.have.attribute('data-active'); expect(optTwo).to.have.attribute('selected'); expect(optionSelectedEventSpy.count).to.be.equal(1); expect(input).to.have.attribute('aria-expanded', 'false'); @@ -283,10 +283,10 @@ describe(`sbb-autocomplete-grid`, () => { await sendKeys({ press: 'ArrowDown' }); await waitForLitRender(element); - expect(optOne).to.have.attribute('active'); + expect(optOne).to.have.attribute('data-active'); expect(buttonOne).not.to.have.attribute('data-focus-visible'); await sendKeys({ press: 'ArrowRight' }); - expect(optOne).not.to.have.attribute('active'); + expect(optOne).not.to.have.attribute('data-active'); expect(buttonOne).to.have.attribute('data-focus-visible'); expect(input).to.have.attribute('aria-activedescendant', 'button-1'); await sendKeys({ press: 'Enter' }); @@ -296,7 +296,7 @@ describe(`sbb-autocomplete-grid`, () => { await sendKeys({ press: 'ArrowDown' }); await sendKeys({ press: 'ArrowRight' }); await waitForLitRender(element); - expect(optOne).not.to.have.attribute('active'); + expect(optOne).not.to.have.attribute('data-active'); expect(buttonOne).not.to.have.attribute('data-focus-visible'); expect(buttonTwo).to.have.attribute('data-focus-visible'); expect(input).to.have.attribute('aria-activedescendant', 'button-2'); diff --git a/src/elements/autocomplete-grid/autocomplete-grid/autocomplete-grid.ts b/src/elements/autocomplete-grid/autocomplete-grid/autocomplete-grid.ts index 3ff62585f1..d61c1fa928 100644 --- a/src/elements/autocomplete-grid/autocomplete-grid/autocomplete-grid.ts +++ b/src/elements/autocomplete-grid/autocomplete-grid/autocomplete-grid.ts @@ -141,7 +141,7 @@ export class SbbAutocompleteGridElement extends SbbAutocompleteBaseElement { return; } const nextActiveOption = filteredOptions[next]; - nextActiveOption.active = true; + nextActiveOption.setActive(true); this.triggerElement?.setAttribute('aria-activedescendant', nextActiveOption.id); nextActiveOption.scrollIntoView({ block: 'nearest' }); @@ -153,7 +153,7 @@ export class SbbAutocompleteGridElement extends SbbAutocompleteBaseElement { } else { const lastActiveOption = filteredOptions[this._activeItemIndex]; if (lastActiveOption) { - lastActiveOption.active = false; + lastActiveOption.setActive(false); } } this._activeItemIndex = next; @@ -178,7 +178,7 @@ export class SbbAutocompleteGridElement extends SbbAutocompleteBaseElement { const nextElement: SbbAutocompleteGridOptionElement | SbbAutocompleteGridButtonElement = elementsInRow[next]; if (nextElement instanceof SbbAutocompleteGridOptionElement) { - nextElement.active = true; + nextElement.setActive(true); } else { nextElement.toggleAttribute('data-focus-visible', true); } @@ -186,7 +186,7 @@ export class SbbAutocompleteGridElement extends SbbAutocompleteBaseElement { const lastActiveElement: SbbAutocompleteGridOptionElement | SbbAutocompleteGridButtonElement = elementsInRow[this._activeColumnIndex]; if (lastActiveElement instanceof SbbAutocompleteGridOptionElement) { - lastActiveElement.active = false; + lastActiveElement.setActive(false); } else { lastActiveElement.toggleAttribute('data-focus-visible', false); } @@ -205,7 +205,7 @@ export class SbbAutocompleteGridElement extends SbbAutocompleteBaseElement { (opt) => !opt.disabled && !opt.hasAttribute('data-group-disabled'), )[this._activeItemIndex]; if (activeElement) { - activeElement.active = false; + activeElement.setActive(false); } } this._activeItemIndex = -1; diff --git a/src/elements/autocomplete/autocomplete.spec.ts b/src/elements/autocomplete/autocomplete.spec.ts index 1a8e1dcfc1..f1ba6d79df 100644 --- a/src/elements/autocomplete/autocomplete.spec.ts +++ b/src/elements/autocomplete/autocomplete.spec.ts @@ -150,9 +150,9 @@ describe(`sbb-autocomplete`, () => { await sendKeys({ press: 'ArrowDown' }); await sendKeys({ press: 'ArrowDown' }); await waitForLitRender(element); - expect(optOne).not.to.have.attribute('active'); + expect(optOne).not.to.have.attribute('data-active'); expect(optOne).not.to.have.attribute('selected'); - expect(optTwo).to.have.attribute('active'); + expect(optTwo).to.have.attribute('data-active'); expect(optTwo).not.to.have.attribute('selected'); expect(input).to.have.attribute('aria-activedescendant', 'option-2'); @@ -160,7 +160,7 @@ describe(`sbb-autocomplete`, () => { await waitForCondition(() => didCloseEventSpy.events.length === 1); expect(didCloseEventSpy.count).to.be.equal(1); - expect(optTwo).not.to.have.attribute('active'); + expect(optTwo).not.to.have.attribute('data-active'); expect(optTwo).to.have.attribute('selected'); expect(optionSelectedEventSpy.count).to.be.equal(1); expect(input).to.have.attribute('aria-expanded', 'false'); diff --git a/src/elements/autocomplete/autocomplete.ts b/src/elements/autocomplete/autocomplete.ts index 0fd7309355..423f1d26c7 100644 --- a/src/elements/autocomplete/autocomplete.ts +++ b/src/elements/autocomplete/autocomplete.ts @@ -108,14 +108,14 @@ export class SbbAutocompleteElement extends SbbAutocompleteBaseElement { // Get and activate the next active option const next = getNextElementIndex(event, this._activeItemIndex, filteredOptions.length); const nextActiveOption = filteredOptions[next]; - nextActiveOption.active = true; + nextActiveOption.setActive(true); this.triggerElement?.setAttribute('aria-activedescendant', nextActiveOption.id); nextActiveOption.scrollIntoView({ block: 'nearest' }); // Reset the previous active option const lastActiveOption = filteredOptions[this._activeItemIndex]; if (lastActiveOption) { - lastActiveOption.active = false; + lastActiveOption.setActive(false); } this._activeItemIndex = next; @@ -125,7 +125,7 @@ export class SbbAutocompleteElement extends SbbAutocompleteBaseElement { const activeElement = this.options[this._activeItemIndex]; if (activeElement) { - activeElement.active = false; + activeElement.setActive(false); } this._activeItemIndex = -1; this.triggerElement?.removeAttribute('aria-activedescendant'); diff --git a/src/elements/option/option/__snapshots__/option.snapshot.spec.snap.js b/src/elements/option/option/__snapshots__/option.snapshot.spec.snap.js index 7b75540806..6b4b95400b 100644 --- a/src/elements/option/option/__snapshots__/option.snapshot.spec.snap.js +++ b/src/elements/option/option/__snapshots__/option.snapshot.spec.snap.js @@ -1,9 +1,8 @@ /* @web/test-runner snapshot v1 */ export const snapshots = {}; -snapshots["sbb-option autocomplete renders selected and active DOM"] = +snapshots["sbb-option autocomplete renders selected DOM"] = ` `; -/* end snapshot sbb-option autocomplete renders selected and active DOM */ +/* end snapshot sbb-option autocomplete renders selected DOM */ -snapshots["sbb-option autocomplete renders selected and active Shadow DOM"] = +snapshots["sbb-option autocomplete renders selected Shadow DOM"] = `
@@ -32,7 +31,7 @@ snapshots["sbb-option autocomplete renders selected and active Shadow DOM"] =
`; -/* end snapshot sbb-option autocomplete renders selected and active Shadow DOM */ +/* end snapshot sbb-option autocomplete renders selected Shadow DOM */ snapshots["sbb-option autocomplete renders disabled DOM"] = ` { "role": "WebArea", @@ -75,27 +74,27 @@ snapshots["sbb-option selected active Chrome"] = }

`; -/* end snapshot sbb-option selected active Chrome */ +/* end snapshot sbb-option selected Chrome */ -snapshots["sbb-option disabled Chrome"] = +snapshots["sbb-option selected Firefox"] = `

{ - "role": "WebArea", + "role": "document", "name": "" }

`; -/* end snapshot sbb-option disabled Chrome */ +/* end snapshot sbb-option selected Firefox */ -snapshots["sbb-option selected active Firefox"] = +snapshots["sbb-option disabled Chrome"] = `

{ - "role": "document", + "role": "WebArea", "name": "" }

`; -/* end snapshot sbb-option selected active Firefox */ +/* end snapshot sbb-option disabled Chrome */ snapshots["sbb-option disabled Firefox"] = `

diff --git a/src/elements/option/option/option-base-element.ts b/src/elements/option/option/option-base-element.ts index 52cd5e4f42..e63638e429 100644 --- a/src/elements/option/option/option-base-element.ts +++ b/src/elements/option/option/option-base-element.ts @@ -44,7 +44,12 @@ export abstract class SbbOptionBaseElement extends SbbDisabledMixin( return this.getAttribute('value') ?? ''; } - /** Whether the option is currently active. */ + /** + * Whether the option is currently active. + * TODO: remove with next major version. + * @deprecated + * @internal + */ @property({ reflect: true, type: Boolean }) public active?: boolean; /** Whether the option is selected. */ @@ -166,6 +171,14 @@ export abstract class SbbOptionBaseElement extends SbbDisabledMixin( this._optionAttributeObserver.disconnect(); } + /** + * Whether the option is currently active. + * @internal + */ + public setActive(value: boolean): void { + this.toggleAttribute('data-active', value); + } + protected init(): void { this.setAttributeFromParent(); this._optionAttributeObserver.observe(this, optionObserverConfig); diff --git a/src/elements/option/option/option.scss b/src/elements/option/option/option.scss index 9e1dbb3c1f..5bf9e6899f 100644 --- a/src/elements/option/option/option.scss +++ b/src/elements/option/option/option.scss @@ -31,7 +31,7 @@ --sbb-focus-outline-color: var(--sbb-focus-outline-color-dark); } -:host([active]) { +:host([data-active]) { --sbb-focus-outline-offset: calc(-1 * var(--sbb-spacing-fixed-1x)); } @@ -97,7 +97,7 @@ -webkit-tap-highlight-color: transparent; -webkit-text-fill-color: var(--sbb-option-color); - :host([active]) & { + :host([data-active]) & { @include sbb.focus-outline; border-radius: var(--sbb-option-border-radius); diff --git a/src/elements/option/option/option.snapshot.spec.ts b/src/elements/option/option/option.snapshot.spec.ts index 571f51f56e..1401b94303 100644 --- a/src/elements/option/option/option.snapshot.spec.ts +++ b/src/elements/option/option/option.snapshot.spec.ts @@ -12,12 +12,12 @@ describe(`sbb-option`, () => { describe('autocomplete', () => { let element: SbbOptionElement; - describe('renders selected and active', async () => { + describe('renders selected', async () => { beforeEach(async () => { element = ( await fixture(html` - Option 1 + Option 1

`) @@ -55,10 +55,7 @@ describe(`sbb-option`, () => { }); }); - testA11yTreeSnapshot( - html``, - 'selected active', - ); + testA11yTreeSnapshot(html``, 'selected'); testA11yTreeSnapshot(html``, 'disabled'); }); diff --git a/src/elements/option/option/option.visual.spec.ts b/src/elements/option/option/option.visual.spec.ts index 0401bc13ab..3a2dbf269f 100644 --- a/src/elements/option/option/option.visual.spec.ts +++ b/src/elements/option/option/option.visual.spec.ts @@ -31,7 +31,7 @@ describe(`sbb-option`, () => { Value ${i + 1}Option label
-Option label - Option label ``` @@ -58,13 +56,12 @@ If the label slot contains only a **text node**, it is possible to search for te ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| ---------- | ----------- | ------- | ---------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | -| `active` | `active` | public | `boolean \| undefined` | | Whether the option is currently active. | -| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | -| `iconName` | `icon-name` | public | `string \| undefined` | | The icon name we want to use, choose from the small icon variants from the ui-icons category from here https://icons.app.sbb.ch. | -| `selected` | `selected` | public | `boolean` | | Whether the option is selected. | -| `value` | `value` | public | `string` | | Value of the option. | +| Name | Attribute | Privacy | Type | Default | Description | +| ---------- | ----------- | ------- | --------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | +| `iconName` | `icon-name` | public | `string \| undefined` | | The icon name we want to use, choose from the small icon variants from the ui-icons category from here https://icons.app.sbb.ch. | +| `selected` | `selected` | public | `boolean` | | Whether the option is selected. | +| `value` | `value` | public | `string` | | Value of the option. | ## Events diff --git a/src/elements/select/select.spec.ts b/src/elements/select/select.spec.ts index 4408c4b54b..6b45d56260 100644 --- a/src/elements/select/select.spec.ts +++ b/src/elements/select/select.spec.ts @@ -169,10 +169,10 @@ describe(`sbb-select`, () => { await waitForLitRender(element); firstOption = element.querySelector('#option-1')!; - expect(firstOption).not.to.have.attribute('active'); + expect(firstOption).not.to.have.attribute('data-active'); expect(firstOption).to.have.attribute('selected'); secondOption = element.querySelector('#option-2')!; - expect(secondOption).not.to.have.attribute('active'); + expect(secondOption).not.to.have.attribute('data-active'); expect(secondOption).not.to.have.attribute('selected'); const selectionChange = new EventSpy(SbbOptionElement.events.selectionChange); @@ -210,9 +210,9 @@ describe(`sbb-select`, () => { await waitForCondition(() => didOpen.events.length === 1); expect(didOpen.count).to.be.equal(1); await waitForLitRender(element); - expect(firstOption).not.to.have.attribute('active'); + expect(firstOption).not.to.have.attribute('data-active'); expect(firstOption).not.to.have.attribute('selected'); - expect(secondOption).not.to.have.attribute('active'); + expect(secondOption).not.to.have.attribute('data-active'); expect(secondOption).not.to.have.attribute('selected'); const selectionChange = new EventSpy(SbbOptionElement.events.selectionChange); @@ -288,12 +288,12 @@ describe(`sbb-select`, () => { await sendKeys({ press: ' ' }); await waitForCondition(() => didOpen.events.length === 1); expect(didOpen.count).to.be.equal(1); - expect(firstOption).not.to.have.attribute('active'); + expect(firstOption).not.to.have.attribute('data-active'); expect(firstOption).not.to.have.attribute('selected'); focusableElement.focus(); await sendKeys({ press: 'ArrowDown' }); - expect(firstOption).to.have.attribute('active'); + expect(firstOption).to.have.attribute('data-active'); expect(firstOption).to.have.attribute('selected'); expect(element.value).to.be.equal('1'); expect(displayValue).to.have.trimmed.text('First'); @@ -304,7 +304,7 @@ describe(`sbb-select`, () => { await waitForLitRender(element); expect(didOpen.count).to.be.equal(1); expect(displayValue).to.have.trimmed.text('Third'); - expect(thirdOption).to.have.attribute('active'); + expect(thirdOption).to.have.attribute('data-active'); expect(thirdOption).to.have.attribute('selected'); expect(element.value).to.be.equal('3'); @@ -315,7 +315,7 @@ describe(`sbb-select`, () => { await waitForLitRender(element); expect(didOpen.count).to.be.equal(1); expect(displayValue).to.have.trimmed.text('Second'); - expect(secondOption).to.have.attribute('active'); + expect(secondOption).to.have.attribute('data-active'); expect(secondOption).to.have.attribute('selected'); expect(element.value).to.be.equal('2'); }); @@ -331,13 +331,13 @@ describe(`sbb-select`, () => { await waitForCondition(() => didOpen.events.length === 1); expect(didOpen.count).to.be.equal(1); - expect(secondOption).not.to.have.attribute('active'); + expect(secondOption).not.to.have.attribute('data-active'); expect(secondOption).not.to.have.attribute('selected'); focusableElement.focus(); await sendKeys({ press: 'ArrowDown' }); await sendKeys({ press: 'ArrowDown' }); await sendKeys({ press: 'Enter' }); - expect(secondOption).to.have.attribute('active'); + expect(secondOption).to.have.attribute('data-active'); expect(secondOption).to.have.attribute('selected'); expect(element.value).to.be.eql(['2']); expect(displayValue).to.have.trimmed.text('Second'); @@ -351,7 +351,7 @@ describe(`sbb-select`, () => { await waitForLitRender(element); await waitForCondition(() => didOpen.events.length === 2); expect(didOpen.count).to.be.equal(2); - expect(secondOption).not.to.have.attribute('active'); + expect(secondOption).not.to.have.attribute('data-active'); expect(secondOption).to.have.attribute('selected'); expect(comboBoxElement).to.have.attribute('aria-expanded', 'true'); }); diff --git a/src/elements/select/select.ts b/src/elements/select/select.ts index 3897fa8431..e0402f59d2 100644 --- a/src/elements/select/select.ts +++ b/src/elements/select/select.ts @@ -598,7 +598,7 @@ export class SbbSelectElement extends SbbUpdateSchedulerMixin( lastActiveOption: SbbOptionElement | null = null, setActiveDescendant = true, ): void { - nextActiveOption.active = true; + nextActiveOption.setActive(true); nextActiveOption.scrollIntoView({ block: 'nearest' }); if (setActiveDescendant) { @@ -607,7 +607,7 @@ export class SbbSelectElement extends SbbUpdateSchedulerMixin( // Reset the previous if (lastActiveOption && lastActiveOption !== nextActiveOption) { - lastActiveOption.active = false; + lastActiveOption.setActive(false); } } @@ -626,7 +626,7 @@ export class SbbSelectElement extends SbbUpdateSchedulerMixin( const activeElement = this._filteredOptions[this._activeItemIndex]; if (activeElement) { - activeElement.active = false; + activeElement.setActive(false); } this._activeItemIndex = -1; this._triggerElement.removeAttribute('aria-activedescendant');