diff --git a/src/components/action-group/__snapshots__/action-group.spec.snap.js b/src/components/action-group/__snapshots__/action-group.spec.snap.js index b20e5791f8..14aa4f73ff 100644 --- a/src/components/action-group/__snapshots__/action-group.spec.snap.js +++ b/src/components/action-group/__snapshots__/action-group.spec.snap.js @@ -30,9 +30,7 @@ snapshots["sbb-action-group renders renders - Dom"] = href="https://github.com/lyne-design-system/lyne-components" icon-name="chevron-small-left-small" icon-placement="start" - role="link" size="m" - tabindex="0" > Link @@ -60,13 +58,7 @@ snapshots["sbb-action-group renders A11y tree Chrome"] = }, { "role": "link", - "name": "Link", - "children": [ - { - "role": "link", - "name": "Link" - } - ] + "name": "Link" } ] } @@ -87,13 +79,7 @@ snapshots["sbb-action-group renders A11y tree Firefox"] = { "role": "link", "name": "Link", - "children": [ - { - "role": "link", - "name": "Link", - "value": "https://github.com/lyne-design-system/lyne-components" - } - ] + "value": "https://github.com/lyne-design-system/lyne-components" } ] } diff --git a/src/components/alert/alert-group/__snapshots__/alert-group.spec.snap.js b/src/components/alert/alert-group/__snapshots__/alert-group.spec.snap.js index 6bd037fc8f..99952b64a1 100644 --- a/src/components/alert/alert-group/__snapshots__/alert-group.spec.snap.js +++ b/src/components/alert/alert-group/__snapshots__/alert-group.spec.snap.js @@ -55,13 +55,7 @@ snapshots["sbb-alert-group should render A11y tree Chrome"] = }, { "role": "link", - "name": "Find out more", - "children": [ - { - "role": "link", - "name": "Find out more" - } - ] + "name": "Find out more" }, { "role": "button", @@ -96,13 +90,7 @@ snapshots["sbb-alert-group should render A11y tree Firefox"] = { "role": "link", "name": "Find out more", - "children": [ - { - "role": "link", - "name": "Find out more", - "value": "https://www.sbb.ch/" - } - ] + "value": "https://www.sbb.ch/" }, { "role": "button", diff --git a/src/components/alert/alert/__snapshots__/alert.spec.snap.js b/src/components/alert/alert/__snapshots__/alert.spec.snap.js index 16e243cb5a..4777002ef5 100644 --- a/src/components/alert/alert/__snapshots__/alert.spec.snap.js +++ b/src/components/alert/alert/__snapshots__/alert.spec.snap.js @@ -97,7 +97,7 @@ snapshots["sbb-alert should render customized properties"] =

Show much more @@ -161,13 +159,7 @@ snapshots["sbb-alert A11y tree Chrome"] = }, { "role": "link", - "name": "test-a11y-label", - "children": [ - { - "role": "link", - "name": "Find out more" - } - ] + "name": "test-a11y-label" }, { "role": "button", @@ -197,13 +189,7 @@ snapshots["sbb-alert A11y tree Firefox"] = { "role": "link", "name": "test-a11y-label", - "children": [ - { - "role": "link", - "name": "Find out more", - "value": "https://www.sbb.ch/" - } - ] + "value": "https://www.sbb.ch/" }, { "role": "button", diff --git a/src/components/alert/alert/alert.ts b/src/components/alert/alert/alert.ts index 4d9f842c84..3ac77d23b9 100644 --- a/src/components/alert/alert/alert.ts +++ b/src/components/alert/alert/alert.ts @@ -151,7 +151,7 @@ export class SbbAlertElement extends SbbIconNameMixin(LitElement) {

${this.href ? html` One @@ -34,9 +30,7 @@ snapshots["sbb-breadcrumb-group renders - Dom"] = data-link="" dir="ltr" href="https://example.com/one" - role="link" slot="li-2" - tabindex="0" > Two @@ -90,33 +84,15 @@ snapshots["sbb-breadcrumb-group A11y tree Chrome"] = "children": [ { "role": "link", - "name": "", - "children": [ - { - "role": "link", - "name": "" - } - ] + "name": "" }, { "role": "link", - "name": "One", - "children": [ - { - "role": "link", - "name": "One" - } - ] + "name": "One" }, { "role": "link", - "name": "Two", - "children": [ - { - "role": "link", - "name": "Two" - } - ] + "name": "Two" } ] } @@ -133,35 +109,17 @@ snapshots["sbb-breadcrumb-group A11y tree Firefox"] = { "role": "link", "name": "", - "children": [ - { - "role": "link", - "name": "", - "value": "https://example.com/" - } - ] + "value": "https://example.com/" }, { "role": "link", "name": "One", - "children": [ - { - "role": "link", - "name": "One", - "value": "https://example.com/one" - } - ] + "value": "https://example.com/one" }, { "role": "link", "name": "Two", - "children": [ - { - "role": "link", - "name": "Two", - "value": "https://example.com/one" - } - ] + "value": "https://example.com/one" } ] } diff --git a/src/components/breadcrumb/breadcrumb/__snapshots__/breadcrumb.spec.snap.js b/src/components/breadcrumb/breadcrumb/__snapshots__/breadcrumb.spec.snap.js index 08c17da30a..dfd92886a5 100644 --- a/src/components/breadcrumb/breadcrumb/__snapshots__/breadcrumb.spec.snap.js +++ b/src/components/breadcrumb/breadcrumb/__snapshots__/breadcrumb.spec.snap.js @@ -7,8 +7,6 @@ snapshots["sbb-breadcrumb renders with text"] = download="" href="https://example.com/test" rel="subsection" - role="presentation" - tabindex="-1" target="_blank" > @@ -28,8 +26,6 @@ snapshots["sbb-breadcrumb renders with icon"] = ` { expect(root).dom.to.be.equal(` { expect(root).dom.to.be.equal(` { expect(root).dom.to.be.equal(` Home diff --git a/src/components/breadcrumb/breadcrumb/readme.md b/src/components/breadcrumb/breadcrumb/readme.md index 3c7f2a9cb1..599ee8b16e 100644 --- a/src/components/breadcrumb/breadcrumb/readme.md +++ b/src/components/breadcrumb/breadcrumb/readme.md @@ -39,13 +39,14 @@ By default, the `sbb-breadcrumb-group` component sets `aria-current="page"` on t ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| ---------- | ----------- | ------- | --------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | -| `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. | -| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | -| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | -| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | -| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| Name | Attribute | Privacy | Type | Default | Description | +| -------------------- | --------------------- | ------- | --------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `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. | +| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | +| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | +| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | +| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the inner anchor element. | ## Slots diff --git a/src/components/button/button-link/__snapshots__/button-link.spec.snap.js b/src/components/button/button-link/__snapshots__/button-link.spec.snap.js index 1fe04e9fa9..99910f2d32 100644 --- a/src/components/button/button-link/__snapshots__/button-link.spec.snap.js +++ b/src/components/button/button-link/__snapshots__/button-link.spec.snap.js @@ -11,9 +11,7 @@ snapshots["sbb-button-link renders a sbb-button-link without icon Dom"] = download="" href="https://www.sbb.ch" rel="noopener" - role="link" size="m" - tabindex="0" target="_blank" > Label Text @@ -27,8 +25,6 @@ snapshots["sbb-button-link renders a sbb-button-link without icon ShadowDom"] = download="" href="https://www.sbb.ch" rel="noopener" - role="presentation" - tabindex="-1" target="_blank" > @@ -52,13 +48,7 @@ snapshots["sbb-button-link renders a sbb-button-link without icon A11y tree Chro "children": [ { "role": "link", - "name": "Label Text . Link target opens in a new window.", - "children": [ - { - "role": "link", - "name": "Label Text . Link target opens in a new window." - } - ] + "name": "Label Text . Link target opens in a new window." } ] } @@ -68,7 +58,6 @@ snapshots["sbb-button-link renders a sbb-button-link without icon A11y tree Chro snapshots["sbb-button-link renders a disabled sbb-button-link with slotted icon Dom"] = ` @@ -118,13 +106,7 @@ snapshots["sbb-button-link renders a sbb-button-link without icon A11y tree Fire { "role": "link", "name": "Label Text . Link target opens in a new window.", - "children": [ - { - "role": "link", - "name": "Label Text . Link target opens in a new window.", - "value": "https://www.sbb.ch/" - } - ] + "value": "https://www.sbb.ch/" } ] } diff --git a/src/components/button/button-link/button-link.ts b/src/components/button/button-link/button-link.ts index cc434a29fe..37a1b5d487 100644 --- a/src/components/button/button-link/button-link.ts +++ b/src/components/button/button-link/button-link.ts @@ -2,7 +2,7 @@ import type { CSSResultGroup } from 'lit'; import { customElement } from 'lit/decorators.js'; import { SbbLinkBaseElement } from '../../core/base-elements.js'; -import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js'; +import { SbbDisabledMixin } from '../../core/mixins.js'; import { buttonCommonStyle, buttonPrimaryStyle, SbbButtonCommonElementMixin } from '../common.js'; /** @@ -13,7 +13,7 @@ import { buttonCommonStyle, buttonPrimaryStyle, SbbButtonCommonElementMixin } fr */ @customElement('sbb-button-link') export class SbbButtonLinkElement extends SbbButtonCommonElementMixin( - SbbDisabledTabIndexActionMixin(SbbLinkBaseElement), + SbbDisabledMixin(SbbLinkBaseElement), ) { public static override styles: CSSResultGroup = [buttonCommonStyle, buttonPrimaryStyle]; } diff --git a/src/components/button/button-link/readme.md b/src/components/button/button-link/readme.md index e5fc472656..92fd3491bc 100644 --- a/src/components/button/button-link/readme.md +++ b/src/components/button/button-link/readme.md @@ -22,7 +22,7 @@ At least one is mandatory, so you can have a `sbb-button-link` with icon only, t ``` @@ -72,16 +72,17 @@ Use the accessibility properties in case of an icon-only button to describe the ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| ---------- | ----------- | ------- | --------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | -| `size` | `size` | public | `SbbButtonSize \| undefined` | `'l'` | Size variant, either l or m. | -| `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. | -| `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. | -| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | -| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | -| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | -| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | -| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| Name | Attribute | Privacy | Type | Default | Description | +| -------------------- | --------------------- | ------- | --------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `size` | `size` | public | `SbbButtonSize \| undefined` | `'l'` | Size variant, either l or m. | +| `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. | +| `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. | +| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | +| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | +| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | +| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | +| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the inner anchor element. | ## Slots diff --git a/src/components/button/common/button-common-stories.ts b/src/components/button/common/button-common-stories.ts index 09a0d56b14..8c4bb9cb75 100644 --- a/src/components/button/common/button-common-stories.ts +++ b/src/components/button/common/button-common-stories.ts @@ -66,6 +66,12 @@ const form: InputType = { }, }; +const ariaLabel: InputType = { + control: { + type: 'text', + }, +}; + export const buttonDefaultArgTypes: ArgTypes = { ...commonDefaultArgTypes, type, @@ -73,6 +79,7 @@ export const buttonDefaultArgTypes: ArgTypes = { name, value, form, + 'aria-label': ariaLabel, }; export const buttonDefaultArgs: Args = { @@ -82,6 +89,7 @@ export const buttonDefaultArgs: Args = { name: 'Button Name', value: undefined, form: undefined, + 'aria-label': undefined, }; export const requestSubmit: StoryObj = { diff --git a/src/components/button/common/button-common.scss b/src/components/button/common/button-common.scss index 7492646833..6f04586e62 100644 --- a/src/components/button/common/button-common.scss +++ b/src/components/button/common/button-common.scss @@ -140,6 +140,7 @@ $icon-only: ':where([data-slot-names~=icon], [icon-name]):not([data-slot-names~= color: var(--sbb-button-color-default-text); cursor: pointer; user-select: none; + outline: none; // Renders background and border in the background absolute to enable the hover animation &::before { @@ -159,17 +160,6 @@ $icon-only: ':where([data-slot-names~=icon], [icon-name]):not([data-slot-names~= border-color: var(--sbb-button-color-disabled-border); } - // Hide focus outline when focus origin is mouse or touch. This is being used as a workaround in various components. - :host( - :is( - [data-focus-visible], - :focus-visible:not([data-focus-origin='mouse'], [data-focus-origin='touch']) - ) - ) - & { - @include sbb.focus-outline; - } - :host(:not([disabled], :active, [data-active]):hover) & { @include sbb.hover-mq($hover: true) { inset: calc(var(--sbb-button-border-width) * -1); @@ -202,6 +192,19 @@ $icon-only: ':where([data-slot-names~=icon], [icon-name]):not([data-slot-names~= color: var(--sbb-button-color-hover-text); } } + + /* stylelint-disable no-descending-specificity */ + :host([data-focus-visible]) &, + // Hide focus outline when focus origin is mouse or touch. This is being used as a workaround in various components. + // Handle focus on the host (button variant) + :host(:focus-visible:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) &, + // Handle focus on the action (link variant) + :host(:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) &:focus-visible { + &::before { + @include sbb.focus-outline; + } + } + /* stylelint-enable no-descending-specificity */ } .sbb-button__label, diff --git a/src/components/button/common/button-link-common-stories.ts b/src/components/button/common/button-link-common-stories.ts index 811960b338..69dbc86b09 100644 --- a/src/components/button/common/button-link-common-stories.ts +++ b/src/components/button/common/button-link-common-stories.ts @@ -55,6 +55,12 @@ const disabled: InputType = { }, }; +const accessibilityLabel: InputType = { + control: { + type: 'text', + }, +}; + export const buttonLinkDefaultArgTypes: ArgTypes = { ...commonDefaultArgTypes, href, @@ -62,6 +68,7 @@ export const buttonLinkDefaultArgTypes: ArgTypes = { rel, download, disabled, + 'accessibility-label': accessibilityLabel, }; export const buttonLinkDefaultArgs: Args = { @@ -71,4 +78,5 @@ export const buttonLinkDefaultArgs: Args = { rel: 'noopener', download: false, disabled: false, + 'accessibility-label': undefined, }; diff --git a/src/components/button/common/common-stories.ts b/src/components/button/common/common-stories.ts index dc7cbe0015..45bf52f00d 100644 --- a/src/components/button/common/common-stories.ts +++ b/src/components/button/common/common-stories.ts @@ -96,12 +96,6 @@ const iconName: InputType = { }, }; -const ariaLabel: InputType = { - control: { - type: 'text', - }, -}; - const tag: InputType = { control: { type: 'text', @@ -117,7 +111,6 @@ export const commonDefaultArgTypes: ArgTypes = { negative, size, 'icon-name': iconName, - 'aria-label': ariaLabel, }; export const commonDefaultArgs: Args = { @@ -126,7 +119,6 @@ export const commonDefaultArgs: Args = { negative: false, size: size.options![0], 'icon-name': 'arrow-right-small', - 'aria-label': undefined, }; export const primary: StoryObj = { diff --git a/src/components/button/secondary-button-link/__snapshots__/secondary-button-link.spec.snap.js b/src/components/button/secondary-button-link/__snapshots__/secondary-button-link.spec.snap.js index de586909b4..e03596b670 100644 --- a/src/components/button/secondary-button-link/__snapshots__/secondary-button-link.spec.snap.js +++ b/src/components/button/secondary-button-link/__snapshots__/secondary-button-link.spec.snap.js @@ -11,9 +11,7 @@ snapshots["sbb-secondary-button-link renders a sbb-secondary-button-link without download="" href="https://www.sbb.ch" rel="noopener" - role="link" size="m" - tabindex="0" target="_blank" > Label Text @@ -27,8 +25,6 @@ snapshots["sbb-secondary-button-link renders a sbb-secondary-button-link without download="" href="https://www.sbb.ch" rel="noopener" - role="presentation" - tabindex="-1" target="_blank" > @@ -52,13 +48,7 @@ snapshots["sbb-secondary-button-link renders a sbb-secondary-button-link without "children": [ { "role": "link", - "name": "Label Text . Link target opens in a new window.", - "children": [ - { - "role": "link", - "name": "Label Text . Link target opens in a new window." - } - ] + "name": "Label Text . Link target opens in a new window." } ] } @@ -68,7 +58,6 @@ snapshots["sbb-secondary-button-link renders a sbb-secondary-button-link without snapshots["sbb-secondary-button-link renders a disabled sbb-secondary-button-link with slotted icon Dom"] = ` @@ -118,13 +106,7 @@ snapshots["sbb-secondary-button-link renders a sbb-secondary-button-link without { "role": "link", "name": "Label Text . Link target opens in a new window.", - "children": [ - { - "role": "link", - "name": "Label Text . Link target opens in a new window.", - "value": "https://www.sbb.ch/" - } - ] + "value": "https://www.sbb.ch/" } ] } diff --git a/src/components/button/secondary-button-link/readme.md b/src/components/button/secondary-button-link/readme.md index e74bfe2e60..c3c80a07bb 100644 --- a/src/components/button/secondary-button-link/readme.md +++ b/src/components/button/secondary-button-link/readme.md @@ -24,7 +24,7 @@ At least one is mandatory, so you can have a `sbb-secondary-button-link` with ic ``` @@ -77,16 +77,17 @@ Use the accessibility properties in case of an icon-only button to describe the ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| ---------- | ----------- | ------- | --------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | -| `size` | `size` | public | `SbbButtonSize \| undefined` | `'l'` | Size variant, either l or m. | -| `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. | -| `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. | -| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | -| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | -| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | -| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | -| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| Name | Attribute | Privacy | Type | Default | Description | +| -------------------- | --------------------- | ------- | --------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `size` | `size` | public | `SbbButtonSize \| undefined` | `'l'` | Size variant, either l or m. | +| `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. | +| `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. | +| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | +| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | +| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | +| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | +| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the inner anchor element. | ## Slots diff --git a/src/components/button/secondary-button-link/secondary-button-link.ts b/src/components/button/secondary-button-link/secondary-button-link.ts index f9cd60eec4..a6db60cfbc 100644 --- a/src/components/button/secondary-button-link/secondary-button-link.ts +++ b/src/components/button/secondary-button-link/secondary-button-link.ts @@ -2,7 +2,7 @@ import type { CSSResultGroup } from 'lit'; import { customElement } from 'lit/decorators.js'; import { SbbLinkBaseElement } from '../../core/base-elements.js'; -import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js'; +import { SbbDisabledMixin } from '../../core/mixins.js'; import { buttonCommonStyle, buttonSecondaryStyle, SbbButtonCommonElementMixin } from '../common.js'; /** @@ -13,7 +13,7 @@ import { buttonCommonStyle, buttonSecondaryStyle, SbbButtonCommonElementMixin } */ @customElement('sbb-secondary-button-link') export class SbbSecondaryButtonLinkElement extends SbbButtonCommonElementMixin( - SbbDisabledTabIndexActionMixin(SbbLinkBaseElement), + SbbDisabledMixin(SbbLinkBaseElement), ) { public static override styles: CSSResultGroup = [buttonCommonStyle, buttonSecondaryStyle]; } diff --git a/src/components/button/tertiary-button-link/__snapshots__/tertiary-button-link.spec.snap.js b/src/components/button/tertiary-button-link/__snapshots__/tertiary-button-link.spec.snap.js index 18fc01e439..b4fa2f42a5 100644 --- a/src/components/button/tertiary-button-link/__snapshots__/tertiary-button-link.spec.snap.js +++ b/src/components/button/tertiary-button-link/__snapshots__/tertiary-button-link.spec.snap.js @@ -11,9 +11,7 @@ snapshots["sbb-tertiary-button-link renders a sbb-tertiary-button-link without i download="" href="https://www.sbb.ch" rel="noopener" - role="link" size="m" - tabindex="0" target="_blank" > Label Text @@ -27,8 +25,6 @@ snapshots["sbb-tertiary-button-link renders a sbb-tertiary-button-link without i download="" href="https://www.sbb.ch" rel="noopener" - role="presentation" - tabindex="-1" target="_blank" > @@ -52,13 +48,7 @@ snapshots["sbb-tertiary-button-link renders a sbb-tertiary-button-link without i "children": [ { "role": "link", - "name": "Label Text . Link target opens in a new window.", - "children": [ - { - "role": "link", - "name": "Label Text . Link target opens in a new window." - } - ] + "name": "Label Text . Link target opens in a new window." } ] } @@ -68,7 +58,6 @@ snapshots["sbb-tertiary-button-link renders a sbb-tertiary-button-link without i snapshots["sbb-tertiary-button-link renders a disabled sbb-tertiary-button-link with slotted icon Dom"] = ` @@ -118,13 +106,7 @@ snapshots["sbb-tertiary-button-link renders a sbb-tertiary-button-link without i { "role": "link", "name": "Label Text . Link target opens in a new window.", - "children": [ - { - "role": "link", - "name": "Label Text . Link target opens in a new window.", - "value": "https://www.sbb.ch/" - } - ] + "value": "https://www.sbb.ch/" } ] } diff --git a/src/components/button/tertiary-button-link/readme.md b/src/components/button/tertiary-button-link/readme.md index b5b668263c..adadac6614 100644 --- a/src/components/button/tertiary-button-link/readme.md +++ b/src/components/button/tertiary-button-link/readme.md @@ -24,7 +24,7 @@ At least one is mandatory, so you can have a `sbb-tertiary-button-link` with ico ``` @@ -77,16 +77,17 @@ Use the accessibility properties in case of an icon-only button to describe the ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| ---------- | ----------- | ------- | --------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | -| `size` | `size` | public | `SbbButtonSize \| undefined` | `'l'` | Size variant, either l or m. | -| `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. | -| `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. | -| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | -| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | -| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | -| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | -| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| Name | Attribute | Privacy | Type | Default | Description | +| -------------------- | --------------------- | ------- | --------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `size` | `size` | public | `SbbButtonSize \| undefined` | `'l'` | Size variant, either l or m. | +| `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. | +| `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. | +| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | +| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | +| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | +| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | +| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the inner anchor element. | ## Slots diff --git a/src/components/button/tertiary-button-link/tertiary-button-link.ts b/src/components/button/tertiary-button-link/tertiary-button-link.ts index 114dfe6881..1cfc6d810a 100644 --- a/src/components/button/tertiary-button-link/tertiary-button-link.ts +++ b/src/components/button/tertiary-button-link/tertiary-button-link.ts @@ -2,7 +2,7 @@ import type { CSSResultGroup } from 'lit'; import { customElement } from 'lit/decorators.js'; import { SbbLinkBaseElement } from '../../core/base-elements.js'; -import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js'; +import { SbbDisabledMixin } from '../../core/mixins.js'; import { buttonCommonStyle, buttonTertiaryStyle, SbbButtonCommonElementMixin } from '../common.js'; /** @@ -13,7 +13,7 @@ import { buttonCommonStyle, buttonTertiaryStyle, SbbButtonCommonElementMixin } f */ @customElement('sbb-tertiary-button-link') export class SbbTertiaryButtonLinkElement extends SbbButtonCommonElementMixin( - SbbDisabledTabIndexActionMixin(SbbLinkBaseElement), + SbbDisabledMixin(SbbLinkBaseElement), ) { public static override styles: CSSResultGroup = [buttonCommonStyle, buttonTertiaryStyle]; } diff --git a/src/components/button/transparent-button-link/__snapshots__/transparent-button-link.spec.snap.js b/src/components/button/transparent-button-link/__snapshots__/transparent-button-link.spec.snap.js index 3665721fc5..155f424291 100644 --- a/src/components/button/transparent-button-link/__snapshots__/transparent-button-link.spec.snap.js +++ b/src/components/button/transparent-button-link/__snapshots__/transparent-button-link.spec.snap.js @@ -11,9 +11,7 @@ snapshots["sbb-transparent-button-link renders a sbb-transparent-button-link wit download="" href="https://www.sbb.ch" rel="noopener" - role="link" size="m" - tabindex="0" target="_blank" > Label Text @@ -27,8 +25,6 @@ snapshots["sbb-transparent-button-link renders a sbb-transparent-button-link wit download="" href="https://www.sbb.ch" rel="noopener" - role="presentation" - tabindex="-1" target="_blank" > @@ -52,13 +48,7 @@ snapshots["sbb-transparent-button-link renders a sbb-transparent-button-link wit "children": [ { "role": "link", - "name": "Label Text . Link target opens in a new window.", - "children": [ - { - "role": "link", - "name": "Label Text . Link target opens in a new window." - } - ] + "name": "Label Text . Link target opens in a new window." } ] } @@ -68,7 +58,6 @@ snapshots["sbb-transparent-button-link renders a sbb-transparent-button-link wit snapshots["sbb-transparent-button-link renders a disabled sbb-transparent-button-link with slotted icon Dom"] = ` @@ -118,13 +106,7 @@ snapshots["sbb-transparent-button-link renders a sbb-transparent-button-link wit { "role": "link", "name": "Label Text . Link target opens in a new window.", - "children": [ - { - "role": "link", - "name": "Label Text . Link target opens in a new window.", - "value": "https://www.sbb.ch/" - } - ] + "value": "https://www.sbb.ch/" } ] } diff --git a/src/components/button/transparent-button-link/readme.md b/src/components/button/transparent-button-link/readme.md index b43e4140f0..5fa8f363cc 100644 --- a/src/components/button/transparent-button-link/readme.md +++ b/src/components/button/transparent-button-link/readme.md @@ -24,7 +24,7 @@ At least one is mandatory, so you can have a `sbb-transparent-button-link` with ``` @@ -77,16 +77,17 @@ Use the accessibility properties in case of an icon-only button to describe the ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| ---------- | ----------- | ------- | --------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | -| `size` | `size` | public | `SbbButtonSize \| undefined` | `'l'` | Size variant, either l or m. | -| `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. | -| `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. | -| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | -| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | -| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | -| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | -| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| Name | Attribute | Privacy | Type | Default | Description | +| -------------------- | --------------------- | ------- | --------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `size` | `size` | public | `SbbButtonSize \| undefined` | `'l'` | Size variant, either l or m. | +| `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. | +| `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. | +| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | +| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | +| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | +| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | +| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the inner anchor element. | ## Slots diff --git a/src/components/button/transparent-button-link/transparent-button-link.ts b/src/components/button/transparent-button-link/transparent-button-link.ts index 06f22a5a24..ff9322b9a6 100644 --- a/src/components/button/transparent-button-link/transparent-button-link.ts +++ b/src/components/button/transparent-button-link/transparent-button-link.ts @@ -2,7 +2,7 @@ import type { CSSResultGroup } from 'lit'; import { customElement } from 'lit/decorators.js'; import { SbbLinkBaseElement } from '../../core/base-elements.js'; -import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js'; +import { SbbDisabledMixin } from '../../core/mixins.js'; import { buttonCommonStyle, buttonTransparentStyle, @@ -17,7 +17,7 @@ import { */ @customElement('sbb-transparent-button-link') export class SbbTransparentButtonLinkElement extends SbbButtonCommonElementMixin( - SbbDisabledTabIndexActionMixin(SbbLinkBaseElement), + SbbDisabledMixin(SbbLinkBaseElement), ) { public static override styles: CSSResultGroup = [buttonCommonStyle, buttonTransparentStyle]; } diff --git a/src/components/card/card-button/card-button.ts b/src/components/card/card-button/card-button.ts index a9746d5b16..fdaab1a5a1 100644 --- a/src/components/card/card-button/card-button.ts +++ b/src/components/card/card-button/card-button.ts @@ -10,7 +10,9 @@ import { SbbCardActionCommonElementMixin } from '../common.js'; * This is relevant for SEO and screen readers. */ @customElement('sbb-card-button') -export class SbbCardButtonElement extends SbbCardActionCommonElementMixin(SbbButtonBaseElement) {} +export class SbbCardButtonElement extends SbbCardActionCommonElementMixin(SbbButtonBaseElement) { + protected override actionRole: 'link' | 'button' = 'button'; +} declare global { interface HTMLElementTagNameMap { diff --git a/src/components/card/card-link/__snapshots__/card-link.e2e.snap.js b/src/components/card/card-link/__snapshots__/card-link.e2e.snap.js index 050528796f..f838a0d078 100644 --- a/src/components/card/card-link/__snapshots__/card-link.e2e.snap.js +++ b/src/components/card/card-link/__snapshots__/card-link.e2e.snap.js @@ -6,8 +6,6 @@ snapshots["sbb-card-link with csrFixture should render an sbb-card-link as a lin class="sbb-action-base sbb-card-link" href="https://github.com/lyne-design-system/lyne-components" rel="external noopener nofollow" - role="presentation" - tabindex="-1" target="_blank" > diff --git a/src/components/card/card-link/card-link.e2e.ts b/src/components/card/card-link/card-link.e2e.ts index 1453216785..c0340281d4 100644 --- a/src/components/card/card-link/card-link.e2e.ts +++ b/src/components/card/card-link/card-link.e2e.ts @@ -40,9 +40,7 @@ describe(`sbb-card-link with ${fixture.name}`, () => { diff --git a/src/components/card/card-link/card-link.ts b/src/components/card/card-link/card-link.ts index fe5a221ec7..b248c99d46 100644 --- a/src/components/card/card-link/card-link.ts +++ b/src/components/card/card-link/card-link.ts @@ -10,7 +10,9 @@ import { SbbCardActionCommonElementMixin } from '../common.js'; * This is relevant for SEO and screen readers. */ @customElement('sbb-card-link') -export class SbbCardLinkElement extends SbbCardActionCommonElementMixin(SbbLinkBaseElement) {} +export class SbbCardLinkElement extends SbbCardActionCommonElementMixin(SbbLinkBaseElement) { + protected override actionRole: 'link' | 'button' = 'link'; +} declare global { interface HTMLElementTagNameMap { diff --git a/src/components/card/card-link/readme.md b/src/components/card/card-link/readme.md index bbf2e2a780..e719a796c1 100644 --- a/src/components/card/card-link/readme.md +++ b/src/components/card/card-link/readme.md @@ -22,13 +22,14 @@ as it is used for search engines and screen-reader users. ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| ---------- | ---------- | ------- | --------------------------------------- | ------- | ----------------------------------------------------------------- | -| `active` | `active` | public | `boolean` | `false` | Whether the card is active. | -| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | -| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | -| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | -| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| Name | Attribute | Privacy | Type | Default | Description | +| -------------------- | --------------------- | ------- | --------------------------------------- | ------- | ----------------------------------------------------------------- | +| `active` | `active` | public | `boolean` | `false` | Whether the card is active. | +| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | +| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | +| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | +| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the inner anchor element. | ## Slots diff --git a/src/components/card/common/card-action-common.ts b/src/components/card/common/card-action-common.ts index d587e27f3c..5cdc1d4633 100644 --- a/src/components/card/common/card-action-common.ts +++ b/src/components/card/common/card-action-common.ts @@ -15,6 +15,7 @@ import '../../screen-reader-only.js'; export declare class SbbCardActionCommonElementMixinType { public active: boolean; + protected actionRole: 'link' | 'button'; } // eslint-disable-next-line @typescript-eslint/naming-convention @@ -43,6 +44,8 @@ export const SbbCardActionCommonElementMixin = < } private _active: boolean = false; + protected abstract actionRole: 'link' | 'button'; + private _card: SbbCardElement | null = null; private _cardMutationObserver = new AgnosticMutationObserver(() => this._checkForSlottedActions(), @@ -73,7 +76,7 @@ export const SbbCardActionCommonElementMixin = < if (this._card) { this._card.toggleAttribute('data-has-action', true); this._card.toggleAttribute('data-has-active-action', this.active); - this._card.setAttribute('data-action-role', this.getAttribute('role')!); + this._card.setAttribute('data-action-role', this.actionRole); this._checkForSlottedActions(); this._cardMutationObserver.observe(this._card, { diff --git a/src/components/card/common/card-action.scss b/src/components/card/common/card-action.scss index bc9da7d11a..a7ab3fb8ca 100644 --- a/src/components/card/common/card-action.scss +++ b/src/components/card/common/card-action.scss @@ -19,9 +19,13 @@ inset: 0; border-radius: var(--sbb-card-border-radius); cursor: pointer; + outline: none; // Hide focus outline when focus origin is mouse or touch. This is being used as a workaround in various components. - :host(:focus-visible:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) & { + // Handle focus on the host (button variant) + :host(:focus-visible:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) &, + // Handle focus on the action (link variant) + :host(:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) &:focus-visible { @include sbb.focus-outline; } } diff --git a/src/components/core/base-elements/action-base-element.ts b/src/components/core/base-elements/action-base-element.ts index 2146fd139a..d8a8f073ff 100644 --- a/src/components/core/base-elements/action-base-element.ts +++ b/src/components/core/base-elements/action-base-element.ts @@ -18,12 +18,16 @@ type MaybeDisabled = { 'data-action': '', }) export abstract class SbbActionBaseElement extends LitElement { + protected get maybeDisabled(): boolean | undefined { + const maybeDisabled = this as MaybeDisabled; + return maybeDisabled.disabled || maybeDisabled.formDisabled; + } + protected setupBaseEventHandlers(): void { this.addEventListener( 'click', (event) => { - const maybeDisabled = this as MaybeDisabled; - if (maybeDisabled.disabled || maybeDisabled.formDisabled) { + if (this.maybeDisabled) { event.preventDefault(); event.stopImmediatePropagation(); } @@ -32,32 +36,6 @@ export abstract class SbbActionBaseElement extends LitElement { // in order to stop immediate propagation in the disabled case. { capture: true }, ); - this.addEventListener( - 'keypress', - (event: KeyboardEvent): void => { - if (event.key === 'Enter' || event.key === '\n') { - this.dispatchClickEvent(event); - } - }, - { passive: true }, - ); - } - - protected dispatchClickEvent(event: KeyboardEvent): void { - const { altKey, ctrlKey, metaKey, shiftKey } = event; - (event.target as Element).dispatchEvent( - new PointerEvent('click', { - bubbles: true, - cancelable: true, - composed: true, - pointerId: -1, - pointerType: '', - altKey, - ctrlKey, - metaKey, - shiftKey, - }), - ); } /** Override this method to render the component template. */ diff --git a/src/components/core/base-elements/button-base-element.ts b/src/components/core/base-elements/button-base-element.ts index 87a30d2056..05a1541d5e 100644 --- a/src/components/core/base-elements/button-base-element.ts +++ b/src/components/core/base-elements/button-base-element.ts @@ -86,10 +86,27 @@ export abstract class SbbButtonBaseElement extends SbbActionBaseElement { private _dispatchClickEventOnSpaceKeyup = (event: KeyboardEvent): void => { if (event.key === ' ') { this._removeActiveMarker(event); - this.dispatchClickEvent(event); + this._dispatchClickEvent(event); } }; + private _dispatchClickEvent = (event: KeyboardEvent): void => { + const { altKey, ctrlKey, metaKey, shiftKey } = event; + (event.target as Element).dispatchEvent( + new PointerEvent('click', { + bubbles: true, + cancelable: true, + composed: true, + pointerId: -1, + pointerType: '', + altKey, + ctrlKey, + metaKey, + shiftKey, + }), + ); + }; + public constructor() { super(); if (!isServer) { @@ -100,6 +117,15 @@ export abstract class SbbButtonBaseElement extends SbbActionBaseElement { this.addEventListener('keydown', this._preventScrollOnSpaceKeydown); this.addEventListener('keyup', this._dispatchClickEventOnSpaceKeyup, passiveOptions); this.addEventListener('blur', this._removeActiveMarker, passiveOptions); + this.addEventListener( + 'keypress', + (event: KeyboardEvent): void => { + if (event.key === 'Enter' || event.key === '\n') { + this._dispatchClickEvent(event); + } + }, + passiveOptions, + ); } } } diff --git a/src/components/core/base-elements/link-base-element.e2e.ts b/src/components/core/base-elements/link-base-element.e2e.ts index fe51d7564d..ada8fb32ac 100644 --- a/src/components/core/base-elements/link-base-element.e2e.ts +++ b/src/components/core/base-elements/link-base-element.e2e.ts @@ -21,7 +21,7 @@ describe(`SbbLinkBaseElement with ${fixture.name}`, () => { let element: GenericLink; beforeEach(async () => { - element = await fixture(html``); + element = await fixture(html``); }); it('renders', async () => { @@ -30,8 +30,6 @@ describe(`SbbLinkBaseElement with ${fixture.name}`, () => { it('check host attributes and content', () => { expect(element.getAttribute('dir')).to.be.equal('ltr'); - expect(element.getAttribute('role')).to.be.equal('link'); - expect(element.getAttribute('tabindex')).to.be.equal('0'); expect(element.shadowRoot!.firstElementChild!.classList.contains('generic-link')).to.be.true; expect(element.shadowRoot!.textContent!.trim()).to.be.equal('Link'); }); @@ -41,7 +39,7 @@ describe(`SbbLinkBaseElement with ${fixture.name}`, () => { let element: GenericLink; beforeEach(async () => { - element = await fixture(html` `); + element = await fixture(html``); }); it('no click dispatch if disabled', async () => { @@ -54,8 +52,6 @@ describe(`SbbLinkBaseElement with ${fixture.name}`, () => { }); it('dispatch event', async () => { - element.href = '#'; - await waitForLitRender(element); const clickSpy = new EventSpy('click'); element.click(); await waitForLitRender(element); @@ -63,9 +59,8 @@ describe(`SbbLinkBaseElement with ${fixture.name}`, () => { }); it('enter keydown', async () => { - element.focus(); - await waitForLitRender(element); const clickSpy = new EventSpy('click'); + element.focus(); await sendKeys({ down: 'Enter' }); await waitForLitRender(element); diff --git a/src/components/core/base-elements/link-base-element.ts b/src/components/core/base-elements/link-base-element.ts index c8e60ff5bf..baaf479936 100644 --- a/src/components/core/base-elements/link-base-element.ts +++ b/src/components/core/base-elements/link-base-element.ts @@ -4,7 +4,6 @@ import { property } from 'lit/decorators.js'; import { SbbLanguageController } from '../controllers.js'; import { hostAttributes } from '../decorators.js'; import { getLocalName } from '../dom.js'; -import { isEventPrevented } from '../eventing.js'; import { i18nTargetOpensInNewWindow } from '../i18n.js'; import { SbbActionBaseElement } from './action-base-element.js'; @@ -16,8 +15,6 @@ export type LinkTargetType = '_blank' | '_self' | '_parent' | '_top'; /** Link base class. */ @hostAttributes({ - role: 'link', - tabindex: '0', 'data-link': '', }) export abstract class SbbLinkBaseElement extends SbbActionBaseElement { @@ -33,48 +30,31 @@ export abstract class SbbLinkBaseElement extends SbbActionBaseElement { /** Whether the browser will show the download dialog on click. */ @property({ type: Boolean }) public download?: boolean; + /** This will be forwarded as aria-label to the inner anchor element. */ + @property({ attribute: 'accessibility-label' }) public accessibilityLabel: string | undefined; + protected language = new SbbLanguageController(this); public constructor() { super(); if (!isServer) { this.setupBaseEventHandlers(); - this.addEventListener('click', this._triggerAnchorWhenNecessary); } } - /** - * Trigger an anchor element click after the event has finished the bubbling phase and - * preventDefault() has not been called for the event. - */ - private async _triggerAnchorWhenNecessary(event: MouseEvent): Promise { - const target = event.target as Element; - const composedTarget = event.composedPath()[0] as Element; - // We only want to trigger a click event on the inner anchor element, if the host element is the - // event origin, which means the inner anchor element has not actually been activated/clicked. - if ( - !this.href || - !target.localName.startsWith('sbb-') || - target !== composedTarget || - (await isEventPrevented(event)) - ) { - return; - } + /** @internal */ + public override focus(options?: FocusOptions | undefined): void { + this.shadowRoot!.querySelector('a')?.focus(options); + } + + /** @internal */ + public override blur(): void { + this.shadowRoot!.querySelector('a')?.blur(); + } - // We are using dispatchEvent here, instead of just .click() in order to - // prevent another click event from bubbling up the DOM tree. - // TODO: The CTRL case does not work exactly the same as with a use interaction PointerEvent - // as the newly created tab immediately receives focus, instead of remaining on the current page. - const { altKey, ctrlKey, metaKey, shiftKey } = event; - target.shadowRoot?.querySelector('a')?.dispatchEvent( - // We need to use a MouseEvent here, as PointerEvent does not work on Firefox. - new MouseEvent('click', { - altKey, - ctrlKey, - metaKey, - shiftKey, - }), - ); + /** @internal */ + public override click(): void { + this.shadowRoot!.querySelector('a')?.click(); } private _evaluateRelAttribute = (): string | typeof nothing => { @@ -86,12 +66,13 @@ export abstract class SbbLinkBaseElement extends SbbActionBaseElement { return html` ${this.renderTemplate()} ${!!this.href && this.target === '_blank' diff --git a/src/components/header/common/header-action.scss b/src/components/header/common/header-action.scss index 8375a93db5..45fa546bb9 100644 --- a/src/components/header/common/header-action.scss +++ b/src/components/header/common/header-action.scss @@ -75,6 +75,7 @@ padding-inline: var(--sbb-header-action-padding-inline); cursor: pointer; user-select: none; + outline: none; &::before { position: absolute; @@ -90,9 +91,14 @@ @include sbb.if-forced-colors { border-width: var(--sbb-border-width-2x); } + } - // Hide focus outline when focus origin is mouse or touch. This is being used as a workaround in various components. - :host(:focus-visible:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) & { + // Hide focus outline when focus origin is mouse or touch. This is being used as a workaround in various components. + // Handle focus on the host (button variant) + :host(:focus-visible:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) &, + // Handle focus on the action (link variant) + :host(:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) &:focus-visible { + &::before { @include sbb.focus-outline; } } diff --git a/src/components/header/header-button/__snapshots__/header-button.spec.snap.js b/src/components/header/header-button/__snapshots__/header-button.spec.snap.js index 8fbcb3c511..6748977ed7 100644 --- a/src/components/header/header-button/__snapshots__/header-button.spec.snap.js +++ b/src/components/header/header-button/__snapshots__/header-button.spec.snap.js @@ -3,6 +3,7 @@ export const snapshots = {}; snapshots["sbb-header-button renders the component as a button with icon Light DOM"] = ` { type="reset" value="value" expand-from="zero" + aria-label="a11y label" > Action diff --git a/src/components/header/header-link/__snapshots__/header-link.spec.snap.js b/src/components/header/header-link/__snapshots__/header-link.spec.snap.js index c9475f925e..e3862d184d 100644 --- a/src/components/header/header-link/__snapshots__/header-link.spec.snap.js +++ b/src/components/header/header-link/__snapshots__/header-link.spec.snap.js @@ -3,14 +3,13 @@ export const snapshots = {}; snapshots["sbb-header-link renders the component as a button with icon Light DOM"] = ` Action @@ -20,11 +19,10 @@ snapshots["sbb-header-link renders the component as a button with icon Light DOM snapshots["sbb-header-link renders the component as a button with icon Shadow DOM"] = ` @@ -59,13 +57,7 @@ snapshots["sbb-header-link renders the component as a button with icon A11y tree "children": [ { "role": "link", - "name": "Action . Link target opens in a new window.", - "children": [ - { - "role": "link", - "name": "Action . Link target opens in a new window." - } - ] + "name": "a11y label" } ] } @@ -81,14 +73,8 @@ snapshots["sbb-header-link renders the component as a button with icon A11y tree "children": [ { "role": "link", - "name": "Action . Link target opens in a new window.", - "children": [ - { - "role": "link", - "name": "Action . Link target opens in a new window.", - "value": "https://github.com/lyne-design-system/lyne-components" - } - ] + "name": "a11y label", + "value": "https://github.com/lyne-design-system/lyne-components" } ] } diff --git a/src/components/header/header-link/header-link.spec.ts b/src/components/header/header-link/header-link.spec.ts index ff9a493b60..19770c63d7 100644 --- a/src/components/header/header-link/header-link.spec.ts +++ b/src/components/header/header-link/header-link.spec.ts @@ -17,6 +17,7 @@ describe(`sbb-header-link`, () => { href="https://github.com/lyne-design-system/lyne-components" target="_blank" icon-name="pie-small" + accessibility-label="a11y label" >Action`, ); diff --git a/src/components/header/header-link/header-link.stories.ts b/src/components/header/header-link/header-link.stories.ts index 801dfc3d00..5fbaab360e 100644 --- a/src/components/header/header-link/header-link.stories.ts +++ b/src/components/header/header-link/header-link.stories.ts @@ -84,7 +84,7 @@ const download: InputType = { }, }; -const ariaLabel: InputType = { +const accessibilityLabel: InputType = { control: { type: 'text' }, }; @@ -96,7 +96,7 @@ const basicArgTypes: ArgTypes = { target, rel, download, - 'aria-label': ariaLabel, + 'accessibility-label': accessibilityLabel, }; const basicArgs: Args = { @@ -107,7 +107,7 @@ const basicArgs: Args = { target: '_blank', rel: undefined, download: false, - 'aria-label': undefined, + 'accessibility-label': undefined, }; export const sbbHeaderActionLink: StoryObj = { diff --git a/src/components/header/header-link/readme.md b/src/components/header/header-link/readme.md index 87de692ec0..489eda7382 100644 --- a/src/components/header/header-link/readme.md +++ b/src/components/header/header-link/readme.md @@ -31,14 +31,15 @@ accepting its associated properties (`href`, `target`, `rel` and `download`). ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| ------------ | ------------- | ------- | --------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `expandFrom` | `expand-from` | public | `SbbHorizontalFrom` | `'medium'` | Used to set the minimum breakpoint from which the text is displayed. E.g. if set to 'large', the text will be visible for breakpoints large, wide, ultra, and hidden for all the others. | -| `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. | -| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | -| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | -| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | -| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| Name | Attribute | Privacy | Type | Default | Description | +| -------------------- | --------------------- | ------- | --------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `expandFrom` | `expand-from` | public | `SbbHorizontalFrom` | `'medium'` | Used to set the minimum breakpoint from which the text is displayed. E.g. if set to 'large', the text will be visible for breakpoints large, wide, ultra, and hidden for all the others. | +| `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. | +| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | +| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | +| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | +| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the inner anchor element. | ## Slots diff --git a/src/components/header/header/__snapshots__/header.spec.snap.js b/src/components/header/header/__snapshots__/header.spec.snap.js index 30ec687ccc..8aaeb50026 100644 --- a/src/components/header/header/__snapshots__/header.spec.snap.js +++ b/src/components/header/header/__snapshots__/header.spec.snap.js @@ -26,8 +26,6 @@ snapshots["sbb-header renders actions and logo Dom"] = expand-from="medium" href="https://github.com/lyne-design-system/lyne-components" icon-name="hamburger-menu-small" - role="link" - tabindex="0" > Menu @@ -67,13 +65,7 @@ snapshots["sbb-header renders actions and logo A11y tree Chrome"] = "children": [ { "role": "link", - "name": "Menu", - "children": [ - { - "role": "link", - "name": "Menu" - } - ] + "name": "Menu" } ] } @@ -90,13 +82,7 @@ snapshots["sbb-header renders actions and logo A11y tree Firefox"] = { "role": "link", "name": "Menu", - "children": [ - { - "role": "link", - "name": "Menu", - "value": "https://github.com/lyne-design-system/lyne-components" - } - ] + "value": "https://github.com/lyne-design-system/lyne-components" } ] } diff --git a/src/components/link-list/__snapshots__/link-list.spec.snap.js b/src/components/link-list/__snapshots__/link-list.spec.snap.js index ef4a404f77..658a5c26fb 100644 --- a/src/components/link-list/__snapshots__/link-list.spec.snap.js +++ b/src/components/link-list/__snapshots__/link-list.spec.snap.js @@ -57,10 +57,8 @@ snapshots["sbb-link-list rendered with a slotted title in light DOM"] = dir="ltr" href="https://www.sbb.ch/de/hilfe-und-kontakt/erstattung-entschaedigung/rueckerstattung-von-billetten.html" icon-placement="start" - role="link" size="s" slot="li-0" - tabindex="0" > Rückerstattungen @@ -72,10 +70,8 @@ snapshots["sbb-link-list rendered with a slotted title in light DOM"] = dir="ltr" href="https://www.sbb.ch/de/hilfe-und-kontakt/erstattung-entschaedigung/rueckerstattung-von-billetten.html" icon-placement="start" - role="link" size="s" slot="li-1" - tabindex="0" > Fundbüro @@ -87,10 +83,8 @@ snapshots["sbb-link-list rendered with a slotted title in light DOM"] = dir="ltr" href="https://www.sbb.ch/de/hilfe-und-kontakt/erstattung-entschaedigung/rueckerstattung-von-billetten.html" icon-placement="start" - role="link" size="s" slot="li-2" - tabindex="0" > Beschwerden @@ -102,10 +96,8 @@ snapshots["sbb-link-list rendered with a slotted title in light DOM"] = dir="ltr" href="https://www.sbb.ch/de/hilfe-und-kontakt/erstattung-entschaedigung/rueckerstattung-von-billetten.html" icon-placement="start" - role="link" size="s" slot="li-3" - tabindex="0" > Lob aussprechen @@ -117,10 +109,8 @@ snapshots["sbb-link-list rendered with a slotted title in light DOM"] = dir="ltr" href="https://www.sbb.ch/de/hilfe-und-kontakt/erstattung-entschaedigung/rueckerstattung-von-billetten.html" icon-placement="start" - role="link" size="s" slot="li-4" - tabindex="0" > Sachbeschädigung melden @@ -200,10 +190,8 @@ snapshots["sbb-link-list rendered with a title from properties in light DOM"] = dir="ltr" href="https://www.sbb.ch/de/hilfe-und-kontakt/erstattung-entschaedigung/rueckerstattung-von-billetten.html" icon-placement="start" - role="link" size="s" slot="li-0" - tabindex="0" > Rückerstattungen @@ -215,10 +203,8 @@ snapshots["sbb-link-list rendered with a title from properties in light DOM"] = dir="ltr" href="https://www.sbb.ch/de/hilfe-und-kontakt/erstattung-entschaedigung/rueckerstattung-von-billetten.html" icon-placement="start" - role="link" size="s" slot="li-1" - tabindex="0" > Fundbüro @@ -230,10 +216,8 @@ snapshots["sbb-link-list rendered with a title from properties in light DOM"] = dir="ltr" href="https://www.sbb.ch/de/hilfe-und-kontakt/erstattung-entschaedigung/rueckerstattung-von-billetten.html" icon-placement="start" - role="link" size="s" slot="li-2" - tabindex="0" > Beschwerden @@ -245,10 +229,8 @@ snapshots["sbb-link-list rendered with a title from properties in light DOM"] = dir="ltr" href="https://www.sbb.ch/de/hilfe-und-kontakt/erstattung-entschaedigung/rueckerstattung-von-billetten.html" icon-placement="start" - role="link" size="s" slot="li-3" - tabindex="0" > Lob aussprechen @@ -260,10 +242,8 @@ snapshots["sbb-link-list rendered with a title from properties in light DOM"] = dir="ltr" href="https://www.sbb.ch/de/hilfe-und-kontakt/erstattung-entschaedigung/rueckerstattung-von-billetten.html" icon-placement="start" - role="link" size="s" slot="li-4" - tabindex="0" > Sachbeschädigung melden @@ -332,10 +312,8 @@ snapshots["sbb-link-list rendered without a title in light DOM"] = dir="ltr" href="https://www.sbb.ch/de/hilfe-und-kontakt/erstattung-entschaedigung/rueckerstattung-von-billetten.html" icon-placement="start" - role="link" size="s" slot="li-0" - tabindex="0" > Rückerstattungen @@ -347,10 +325,8 @@ snapshots["sbb-link-list rendered without a title in light DOM"] = dir="ltr" href="https://www.sbb.ch/de/hilfe-und-kontakt/erstattung-entschaedigung/rueckerstattung-von-billetten.html" icon-placement="start" - role="link" size="s" slot="li-1" - tabindex="0" > Fundbüro @@ -362,10 +338,8 @@ snapshots["sbb-link-list rendered without a title in light DOM"] = dir="ltr" href="https://www.sbb.ch/de/hilfe-und-kontakt/erstattung-entschaedigung/rueckerstattung-von-billetten.html" icon-placement="start" - role="link" size="s" slot="li-2" - tabindex="0" > Beschwerden @@ -377,10 +351,8 @@ snapshots["sbb-link-list rendered without a title in light DOM"] = dir="ltr" href="https://www.sbb.ch/de/hilfe-und-kontakt/erstattung-entschaedigung/rueckerstattung-von-billetten.html" icon-placement="start" - role="link" size="s" slot="li-3" - tabindex="0" > Lob aussprechen @@ -392,10 +364,8 @@ snapshots["sbb-link-list rendered without a title in light DOM"] = dir="ltr" href="https://www.sbb.ch/de/hilfe-und-kontakt/erstattung-entschaedigung/rueckerstattung-von-billetten.html" icon-placement="start" - role="link" size="s" slot="li-4" - tabindex="0" > Sachbeschädigung melden diff --git a/src/components/link/block-link/__snapshots__/block-link.spec.snap.js b/src/components/link/block-link/__snapshots__/block-link.spec.snap.js index 74cf3ab2b0..ed3d5a1306 100644 --- a/src/components/link/block-link/__snapshots__/block-link.spec.snap.js +++ b/src/components/link/block-link/__snapshots__/block-link.spec.snap.js @@ -3,7 +3,7 @@ export const snapshots = {}; snapshots["sbb-block-link renders - DOM"] = ` Travelcards & tickets. @@ -23,11 +21,10 @@ snapshots["sbb-block-link renders - DOM"] = snapshots["sbb-block-link renders - ShadowDOM"] = ` @@ -47,13 +44,7 @@ snapshots["sbb-block-link A11y tree Chrome"] = "children": [ { "role": "link", - "name": "Travelcards & tickets", - "children": [ - { - "role": "link", - "name": "Travelcards & tickets." - } - ] + "name": "Travelcards & tickets" } ] } @@ -70,13 +61,7 @@ snapshots["sbb-block-link A11y tree Firefox"] = { "role": "link", "name": "Travelcards & tickets", - "children": [ - { - "role": "link", - "name": "Travelcards & tickets.", - "value": "https://github.com/lyne-design-system/lyne-components" - } - ] + "value": "https://github.com/lyne-design-system/lyne-components" } ] } diff --git a/src/components/link/block-link/block-link.spec.ts b/src/components/link/block-link/block-link.spec.ts index 341bf75c55..49c4390b24 100644 --- a/src/components/link/block-link/block-link.spec.ts +++ b/src/components/link/block-link/block-link.spec.ts @@ -16,7 +16,7 @@ describe(`sbb-block-link`, () => { href="https://github.com/lyne-design-system/lyne-components" size="m" download - aria-label="Travelcards & tickets" + accessibility-label="Travelcards & tickets" > Travelcards & tickets. diff --git a/src/components/link/block-link/block-link.ts b/src/components/link/block-link/block-link.ts index dd43bae516..a5cdda7286 100644 --- a/src/components/link/block-link/block-link.ts +++ b/src/components/link/block-link/block-link.ts @@ -1,7 +1,7 @@ import { customElement } from 'lit/decorators.js'; import { SbbLinkBaseElement } from '../../core/base-elements.js'; -import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js'; +import { SbbDisabledMixin } from '../../core/mixins.js'; import { SbbBlockLinkCommonElementMixin } from '../common.js'; /** @@ -12,7 +12,7 @@ import { SbbBlockLinkCommonElementMixin } from '../common.js'; */ @customElement('sbb-block-link') export class SbbBlockLinkElement extends SbbBlockLinkCommonElementMixin( - SbbDisabledTabIndexActionMixin(SbbLinkBaseElement), + SbbDisabledMixin(SbbLinkBaseElement), ) {} declare global { diff --git a/src/components/link/block-link/readme.md b/src/components/link/block-link/readme.md index 0d693d670e..7bfafa1e6c 100644 --- a/src/components/link/block-link/readme.md +++ b/src/components/link/block-link/readme.md @@ -51,16 +51,17 @@ The component has three sizes (`xs`, `s`, which is the default, and `m`). ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| --------------- | ---------------- | ------- | --------------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------- | -| `iconPlacement` | `icon-placement` | public | `SbbIconPlacement \| undefined` | `'start'` | Moves the icon to the end of the component if set to true. | -| `size` | `size` | public | `SbbLinkSize` | `'s'` | Text size, the link should get in the non-button variation. With inline variant, the text size adapts to where it is used. | -| `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. | -| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | -| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | -| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | -| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | -| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| Name | Attribute | Privacy | Type | Default | Description | +| -------------------- | --------------------- | ------- | --------------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `iconPlacement` | `icon-placement` | public | `SbbIconPlacement \| undefined` | `'start'` | Moves the icon to the end of the component if set to true. | +| `size` | `size` | public | `SbbLinkSize` | `'s'` | Text size, the link should get in the non-button variation. With inline variant, the text size adapts to where it is used. | +| `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. | +| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | +| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | +| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | +| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | +| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the inner anchor element. | ## Slots diff --git a/src/components/link/common/link-common-stories.ts b/src/components/link/common/link-common-stories.ts index b5cf1ab084..80f97cdf0d 100644 --- a/src/components/link/common/link-common-stories.ts +++ b/src/components/link/common/link-common-stories.ts @@ -54,12 +54,6 @@ const negative: InputType = { }, }; -const ariaLabel: InputType = { - control: { - type: 'text', - }, -}; - const disabled: InputType = { control: { type: 'boolean', @@ -81,14 +75,12 @@ const tag: InputType = { export const linkCommonDefaultArgTypes: ArgTypes = { text, negative, - 'aria-label': ariaLabel, tag, }; export const linkCommonDefaultArgs: Args = { text: 'Travelcards & tickets', negative: false, - 'aria-label': undefined, tag: 'TBD', }; @@ -177,6 +169,12 @@ const download: InputType = { }, }; +const accessibilityLabel: InputType = { + control: { + type: 'text', + }, +}; + export const linkDefaultArgTypes: ArgTypes = { ...linkCommonDefaultArgTypes, href, @@ -184,6 +182,7 @@ export const linkDefaultArgTypes: ArgTypes = { rel, download, disabled, + 'accessibility-label': accessibilityLabel, }; export const linkDefaultArgs: Args = { @@ -193,6 +192,7 @@ export const linkDefaultArgs: Args = { rel: undefined, download: false, disabled: false, + 'accessibility-label': undefined, }; // link-button params @@ -234,6 +234,12 @@ const form: InputType = { }, }; +const ariaLabel: InputType = { + control: { + type: 'text', + }, +}; + export const linkButtonDefaultArgTypes: ArgTypes = { ...linkCommonDefaultArgTypes, type, @@ -241,6 +247,7 @@ export const linkButtonDefaultArgTypes: ArgTypes = { name, value, form, + 'aria-label': ariaLabel, }; export const linkButtonDefaultArgs: Args = { @@ -250,6 +257,7 @@ export const linkButtonDefaultArgs: Args = { name: 'Button name', value: undefined, form: undefined, + 'aria-label': undefined, }; // Stories diff --git a/src/components/link/common/link.scss b/src/components/link/common/link.scss index 2c5f20c2e6..5b9d0decc8 100644 --- a/src/components/link/common/link.scss +++ b/src/components/link/common/link.scss @@ -22,6 +22,7 @@ width: 100%; cursor: pointer; padding: var(--sbb-link-padding, 0); + outline: none; :host([disabled]) & { pointer-events: none; @@ -33,7 +34,10 @@ } // Hide focus outline when focus origin is mouse or touch. This is being used as a workaround in various components. - :host(:focus-visible:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) & { + // Handle focus on the host (button variant) + :host(:focus-visible:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) &, + // Handle focus on the action (link variant) + :host(:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) &:focus-visible { @include sbb.focus-outline; border-radius: calc(var(--sbb-border-radius-4x) - var(--sbb-focus-outline-offset)); diff --git a/src/components/link/link/__snapshots__/link.spec.snap.js b/src/components/link/link/__snapshots__/link.spec.snap.js index e78820d6fa..2235bad3cb 100644 --- a/src/components/link/link/__snapshots__/link.spec.snap.js +++ b/src/components/link/link/__snapshots__/link.spec.snap.js @@ -9,9 +9,7 @@ snapshots["sbb-link renders - DOM"] = data-slot-names="unnamed" dir="ltr" href="https://sbb.ch" - role="link" size="m" - tabindex="0" target="_blank" > Travelcards & tickets. @@ -24,8 +22,6 @@ snapshots["sbb-link renders - ShadowDOM"] = class="sbb-action-base sbb-link" href="https://sbb.ch" rel="external noopener nofollow" - role="presentation" - tabindex="-1" target="_blank" > @@ -45,13 +41,7 @@ snapshots["sbb-link A11y tree Chrome"] = "children": [ { "role": "link", - "name": "Travelcards & tickets. . Link target opens in a new window.", - "children": [ - { - "role": "link", - "name": "Travelcards & tickets. . Link target opens in a new window." - } - ] + "name": "Travelcards & tickets. . Link target opens in a new window." } ] } @@ -68,13 +58,7 @@ snapshots["sbb-link A11y tree Firefox"] = { "role": "link", "name": "Travelcards & tickets. . Link target opens in a new window.", - "children": [ - { - "role": "link", - "name": "Travelcards & tickets. . Link target opens in a new window.", - "value": "https://sbb.ch/" - } - ] + "value": "https://sbb.ch/" } ] } diff --git a/src/components/link/link/link.ts b/src/components/link/link/link.ts index 8f5a421fb0..408c0f2aad 100644 --- a/src/components/link/link/link.ts +++ b/src/components/link/link/link.ts @@ -1,7 +1,7 @@ import { customElement } from 'lit/decorators.js'; import { SbbLinkBaseElement } from '../../core/base-elements.js'; -import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js'; +import { SbbDisabledMixin } from '../../core/mixins.js'; import { SbbInlineLinkCommonElementMixin } from '../common.js'; /** @@ -11,7 +11,7 @@ import { SbbInlineLinkCommonElementMixin } from '../common.js'; */ @customElement('sbb-link') export class SbbLinkElement extends SbbInlineLinkCommonElementMixin( - SbbDisabledTabIndexActionMixin(SbbLinkBaseElement), + SbbDisabledMixin(SbbLinkBaseElement), ) {} declare global { diff --git a/src/components/link/link/readme.md b/src/components/link/link/readme.md index 4ec2210f44..cd60200b71 100644 --- a/src/components/link/link/readme.md +++ b/src/components/link/link/readme.md @@ -31,15 +31,16 @@ accepting its associated properties (`href`, `target`, `rel` and `download`). ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| ---------- | ---------- | ------- | --------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------- | -| `size` | `size` | public | `SbbLinkSize` | `'s'` | Text size, the link should get in the non-button variation. With inline variant, the text size adapts to where it is used. | -| `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. | -| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | -| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | -| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | -| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | -| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| Name | Attribute | Privacy | Type | Default | Description | +| -------------------- | --------------------- | ------- | --------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------- | +| `size` | `size` | public | `SbbLinkSize` | `'s'` | Text size, the link should get in the non-button variation. With inline variant, the text size adapts to where it is used. | +| `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. | +| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | +| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | +| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | +| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | +| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the inner anchor element. | ## Slots diff --git a/src/components/menu/common/menu-action-common.ts b/src/components/menu/common/menu-action-common.ts index 197768f6f3..fa13f7cb4b 100644 --- a/src/components/menu/common/menu-action-common.ts +++ b/src/components/menu/common/menu-action-common.ts @@ -4,7 +4,7 @@ import { html } from 'lit/static-html.js'; import type { SbbActionBaseElement } from '../../core/base-elements.js'; import { - SbbDisabledTabIndexActionMixin, + SbbDisabledMixin, type AbstractConstructor, type SbbDisabledMixinType, } from '../../core/mixins.js'; @@ -27,7 +27,7 @@ export const SbbMenuActionCommonElementMixin = < superClass: T, ): AbstractConstructor & T => { abstract class SbbMenuActionCommonElement - extends SbbIconNameMixin(SbbDisabledTabIndexActionMixin(superClass)) + extends SbbIconNameMixin(SbbDisabledMixin(superClass)) implements Partial { public static styles: CSSResultGroup = style; diff --git a/src/components/menu/common/menu-action.scss b/src/components/menu/common/menu-action.scss index e6134d0fcf..40f0fac485 100644 --- a/src/components/menu/common/menu-action.scss +++ b/src/components/menu/common/menu-action.scss @@ -51,6 +51,7 @@ color: var(--sbb-menu-action-color); padding: var(--sbb-spacing-fixed-1x) var(--sbb-menu-action-outer-horizontal-padding); cursor: var(--sbb-menu-action-cursor); + outline: none; } .sbb-menu-action__content { @@ -66,7 +67,10 @@ background-color: var(--sbb-menu-background-color); // Hide focus outline when focus origin is mouse or touch. This is being used as a workaround in various components. - :host(:focus-visible:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) & { + // Handle focus on the host (button variant) + :host(:focus-visible:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) & , + // Handle focus on the action (link variant) + :host(:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) :focus-visible & { --sbb-focus-outline-color: var(--sbb-focus-outline-color-dark); @include sbb.focus-outline; diff --git a/src/components/menu/menu-button/__snapshots__/menu-button.spec.snap.js b/src/components/menu/menu-button/__snapshots__/menu-button.spec.snap.js new file mode 100644 index 0000000000..a9aa7f8792 --- /dev/null +++ b/src/components/menu/menu-button/__snapshots__/menu-button.spec.snap.js @@ -0,0 +1,113 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["sbb-menu-button renders Light DOM"] = +` + + Action + + +`; +/* end snapshot sbb-menu-button renders Light DOM */ + +snapshots["sbb-menu-button renders Shadow DOM"] = +` + + + + + + + + + + + +`; +/* end snapshot sbb-menu-button renders Shadow DOM */ + +snapshots["sbb-menu-button renders A11y tree Chrome"] = +`

+ { + "role": "WebArea", + "name": "", + "children": [ + { + "role": "button", + "name": "a11y label" + } + ] +} +

+`; +/* end snapshot sbb-menu-button renders A11y tree Chrome */ + +snapshots["sbb-menu-button renders component with icon and amount Light DOM"] = +` + + Action + + +`; +/* end snapshot sbb-menu-button renders component with icon and amount Light DOM */ + +snapshots["sbb-menu-button renders component with icon and amount Shadow DOM"] = +` + + + + + + + + + + + + 123456 + + + +`; +/* end snapshot sbb-menu-button renders component with icon and amount Shadow DOM */ + +snapshots["sbb-menu-button renders A11y tree Firefox"] = +`

+ { + "role": "document", + "name": "", + "children": [ + { + "role": "button", + "name": "a11y label" + } + ] +} +

+`; +/* end snapshot sbb-menu-button renders A11y tree Firefox */ + diff --git a/src/components/menu/menu-button/menu-button.spec.ts b/src/components/menu/menu-button/menu-button.spec.ts index 677158e99e..d0f5ea950a 100644 --- a/src/components/menu/menu-button/menu-button.spec.ts +++ b/src/components/menu/menu-button/menu-button.spec.ts @@ -1,70 +1,52 @@ import { expect } from '@open-wc/testing'; import { html } from 'lit/static-html.js'; -import { fixture } from '../../core/testing/private.js'; +import { fixture, testA11yTreeSnapshot } from '../../core/testing/private.js'; + +import type { SbbMenuButtonElement } from './menu-button.js'; import './menu-button.js'; describe(`sbb-menu-button`, () => { - it('renders', async () => { - const root = await fixture(html` - - Action - - `); + describe('renders', () => { + let element: SbbMenuButtonElement; + + beforeEach(async () => { + element = await fixture(html` + + Action + + `); + }); + + it('Light DOM', async () => { + await expect(element).dom.to.be.equalSnapshot(); + }); - expect(root).dom.to.be.equal(` - - Action - - `); - expect(root).shadowDom.to.be.equal(` - - - - - - - - - - - `); + it('Shadow DOM', async () => { + await expect(element).shadowDom.to.be.equalSnapshot(); + }); + + testA11yTreeSnapshot(); }); - it('renders component with icon and amount', async () => { - const root = await fixture(html` - - Action - - `); + describe('renders component with icon and amount', () => { + let element: SbbMenuButtonElement; + + beforeEach(async () => { + element = await fixture(html` + + Action + + `); + }); + + it('Light DOM', async () => { + await expect(element).dom.to.be.equalSnapshot(); + }); - expect(root).dom.to.be.equal(` - - Action - - `); - expect(root).shadowDom.to.be.equal(` - - - - - - - - - - - 123456 - - - - `); + it('Shadow DOM', async () => { + await expect(element).shadowDom.to.be.equalSnapshot(); + }); }); }); diff --git a/src/components/menu/menu-button/menu-button.ts b/src/components/menu/menu-button/menu-button.ts index f6ef86c048..1602155c61 100644 --- a/src/components/menu/menu-button/menu-button.ts +++ b/src/components/menu/menu-button/menu-button.ts @@ -1,6 +1,7 @@ import { customElement } from 'lit/decorators.js'; import { SbbButtonBaseElement } from '../../core/base-elements.js'; +import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js'; import { SbbMenuActionCommonElementMixin } from '../common.js'; /** @@ -12,7 +13,9 @@ import { SbbMenuActionCommonElementMixin } from '../common.js'; * to modify horizontal padding. */ @customElement('sbb-menu-button') -export class SbbMenuButtonElement extends SbbMenuActionCommonElementMixin(SbbButtonBaseElement) {} +export class SbbMenuButtonElement extends SbbDisabledTabIndexActionMixin( + SbbMenuActionCommonElementMixin(SbbButtonBaseElement), +) {} declare global { interface HTMLElementTagNameMap { diff --git a/src/components/menu/menu-button/readme.md b/src/components/menu/menu-button/readme.md index 348d8b15b1..4cac4277b1 100644 --- a/src/components/menu/menu-button/readme.md +++ b/src/components/menu/menu-button/readme.md @@ -32,9 +32,9 @@ accepting its associated properties (`type`, `name`, `value` and `form`). | Name | Attribute | Privacy | Type | Default | Description | | ---------- | ----------- | ------- | --------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | | `amount` | `amount` | public | `string \| undefined` | | Value shown as badge at component end. | | `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. | -| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | | `type` | `type` | public | `SbbButtonType` | `'button'` | The type attribute to use for the button. | | `name` | `name` | public | `string` | | The name of the button element. | | `value` | `value` | public | `string` | | The value of the button element. | diff --git a/src/components/menu/menu-link/__snapshots__/menu-link.spec.snap.js b/src/components/menu/menu-link/__snapshots__/menu-link.spec.snap.js new file mode 100644 index 0000000000..1027511164 --- /dev/null +++ b/src/components/menu/menu-link/__snapshots__/menu-link.spec.snap.js @@ -0,0 +1,89 @@ +/* @web/test-runner snapshot v1 */ +export const snapshots = {}; + +snapshots["sbb-menu-link renders component with icon and amount Light DOM"] = +` + + Action + + +`; +/* end snapshot sbb-menu-link renders component with icon and amount Light DOM */ + +snapshots["sbb-menu-link renders component with icon and amount Shadow DOM"] = +`
+ + + + + + + + + + + + 123456 + + + + . Link target opens in a new window. + + +`; +/* end snapshot sbb-menu-link renders component with icon and amount Shadow DOM */ + +snapshots["sbb-menu-link renders component with icon and amount A11y tree Chrome"] = +`

+ { + "role": "WebArea", + "name": "", + "children": [ + { + "role": "link", + "name": "a11y label" + } + ] +} +

+`; +/* end snapshot sbb-menu-link renders component with icon and amount A11y tree Chrome */ + +snapshots["sbb-menu-link renders component with icon and amount A11y tree Firefox"] = +`

+ { + "role": "document", + "name": "", + "children": [ + { + "role": "link", + "name": "a11y label", + "value": "https://github.com/lyne-design-system/lyne-components" + } + ] +} +

+`; +/* end snapshot sbb-menu-link renders component with icon and amount A11y tree Firefox */ + diff --git a/src/components/menu/menu-link/menu-link.spec.ts b/src/components/menu/menu-link/menu-link.spec.ts index 4937efe409..ec6d9197b9 100644 --- a/src/components/menu/menu-link/menu-link.spec.ts +++ b/src/components/menu/menu-link/menu-link.spec.ts @@ -1,52 +1,38 @@ import { expect } from '@open-wc/testing'; import { html } from 'lit/static-html.js'; -import { fixture } from '../../core/testing/private.js'; +import { fixture, testA11yTreeSnapshot } from '../../core/testing/private.js'; + +import type { SbbMenuLinkElement } from './menu-link.js'; import './menu-link.js'; describe(`sbb-menu-link`, () => { - it('renders component with icon and amount', async () => { - const root = await fixture(html` - - Action - - `); - - expect(root).dom.to.be.equal(` - - Action - - `); - expect(root).shadowDom.to.be.equal(` - - - - - - - - - - - 123456 - - - - . Link target opens in a new window. - - - `); + describe('renders component with icon and amount', () => { + let element: SbbMenuLinkElement; + + beforeEach(async () => { + element = await fixture(html` + + Action + + `); + }); + + it('Light DOM', async () => { + await expect(element).dom.to.be.equalSnapshot(); + }); + + it('Shadow DOM', async () => { + await expect(element).shadowDom.to.be.equalSnapshot(); + }); + + testA11yTreeSnapshot(); }); }); diff --git a/src/components/menu/menu-link/menu-link.stories.ts b/src/components/menu/menu-link/menu-link.stories.ts index c4c5018712..08983288bd 100644 --- a/src/components/menu/menu-link/menu-link.stories.ts +++ b/src/components/menu/menu-link/menu-link.stories.ts @@ -103,7 +103,7 @@ const disabled: InputType = { }, }; -const ariaLabel: InputType = { +const accessibilityLabel: InputType = { control: { type: 'text', }, @@ -118,7 +118,7 @@ const defaultArgTypes: ArgTypes = { rel, download, disabled, - 'aria-label': ariaLabel, + 'accessibility-label': accessibilityLabel, }; const defaultArgs: Args = { @@ -130,7 +130,7 @@ const defaultArgs: Args = { rel: undefined, download: false, disabled: false, - 'aria-label': ariaLabel, + 'accessibility-label': accessibilityLabel, }; export const menuLink: StoryObj = { diff --git a/src/components/menu/menu-link/readme.md b/src/components/menu/menu-link/readme.md index 381092dd46..7121709566 100644 --- a/src/components/menu/menu-link/readme.md +++ b/src/components/menu/menu-link/readme.md @@ -30,15 +30,16 @@ accepting its associated properties (`href`, `target`, `rel` and `download`). ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| ---------- | ----------- | ------- | --------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | -| `amount` | `amount` | public | `string \| undefined` | | Value shown as badge at component end. | -| `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. | -| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | -| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | -| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | -| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | -| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| Name | Attribute | Privacy | Type | Default | Description | +| -------------------- | --------------------- | ------- | --------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `amount` | `amount` | public | `string \| undefined` | | Value shown as badge at component end. | +| `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. | +| `disabled` | `disabled` | public | `boolean` | `false` | Whether the component is disabled. | +| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | +| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | +| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | +| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the inner anchor element. | ## CSS Properties diff --git a/src/components/menu/menu/__snapshots__/menu.spec.snap.js b/src/components/menu/menu/__snapshots__/menu.spec.snap.js index fcead6b9ed..bc36348581 100644 --- a/src/components/menu/menu/__snapshots__/menu.spec.snap.js +++ b/src/components/menu/menu/__snapshots__/menu.spec.snap.js @@ -15,9 +15,7 @@ snapshots["sbb-menu renders DOM"] = dir="ltr" href="https://www.sbb.ch/en" icon-placement="start" - role="link" size="s" - tabindex="0" > Profile diff --git a/src/components/menu/menu/menu.e2e.ts b/src/components/menu/menu/menu.e2e.ts index c31df4ea3a..48785e8cf9 100644 --- a/src/components/menu/menu/menu.e2e.ts +++ b/src/components/menu/menu/menu.e2e.ts @@ -240,8 +240,7 @@ describe(`sbb-menu with ${fixture.name}`, () => { const willOpenEventSpy = new EventSpy(SbbMenuElement.events.willOpen); const didOpenEventSpy = new EventSpy(SbbMenuElement.events.didOpen); - await sendKeys({ down: 'Tab' }); - await waitForLitRender(element); + trigger.focus(); await sendKeys({ down: 'Enter' }); await waitForLitRender(element); diff --git a/src/components/menu/menu/menu.ts b/src/components/menu/menu/menu.ts index b731f4047e..b997a37414 100644 --- a/src/components/menu/menu/menu.ts +++ b/src/components/menu/menu/menu.ts @@ -178,7 +178,10 @@ export class SbbMenuElement extends SbbNamedSlotListMixin< this.querySelectorAll( 'sbb-menu-button, sbb-menu-link', ), - ).filter((el: HTMLElement) => el.tabIndex === 0 && interactivityChecker.isVisible(el)); + ).filter( + (el: SbbMenuButtonElement | SbbMenuLinkElement) => + !el.disabled && interactivityChecker.isVisible(el), + ); const current = enabledActions.findIndex((e: Element) => e === evt.target); const nextIndex = getNextElementIndex(evt, current, enabledActions.length); diff --git a/src/components/navigation/common/navigation-action.scss b/src/components/navigation/common/navigation-action.scss index 74e1703323..918ecca2fd 100644 --- a/src/components/navigation/common/navigation-action.scss +++ b/src/components/navigation/common/navigation-action.scss @@ -59,6 +59,7 @@ sbb-icon { hyphens: auto; text-align: left; color: var(--sbb-navigation-action-color); + outline: none; @include sbb.if-forced-colors { transition: none; @@ -73,7 +74,10 @@ sbb-icon { } // Hide focus outline when focus origin is mouse or touch. This is being used as a workaround in various components. - :host(:focus-visible:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) & { + // Handle focus on the host (button variant) + :host(:focus-visible:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) &, + // Handle focus on the action (link variant) + :host(:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) &:focus-visible { @include sbb.focus-outline; border-radius: calc(var(--sbb-border-radius-4x) - var(--sbb-focus-outline-offset)); diff --git a/src/components/navigation/navigation-link/__snapshots__/navigation-link.spec.snap.js b/src/components/navigation/navigation-link/__snapshots__/navigation-link.spec.snap.js index e4c9a06105..7b046892af 100644 --- a/src/components/navigation/navigation-link/__snapshots__/navigation-link.spec.snap.js +++ b/src/components/navigation/navigation-link/__snapshots__/navigation-link.spec.snap.js @@ -3,13 +3,12 @@ export const snapshots = {}; snapshots["sbb-navigation-link renders DOM"] = ` This is a link @@ -19,11 +18,10 @@ snapshots["sbb-navigation-link renders DOM"] = snapshots["sbb-navigation-link renders Shadow DOM"] = ` { describe('renders', async () => { beforeEach(async () => { element = await fixture( - html`This is a link`, ); diff --git a/src/components/navigation/navigation-link/navigation-link.stories.ts b/src/components/navigation/navigation-link/navigation-link.stories.ts index da5c648211..51ba7d3054 100644 --- a/src/components/navigation/navigation-link/navigation-link.stories.ts +++ b/src/components/navigation/navigation-link/navigation-link.stories.ts @@ -15,7 +15,7 @@ const size: InputType = { options: ['l', 'm', 's'], }; -const ariaLabel: InputType = { +const accessibilityLabel: InputType = { control: { type: 'text', }, @@ -70,7 +70,7 @@ const defaultArgTypes: ArgTypes = { target, rel, download, - 'aria-label': ariaLabel, + 'accessibility-label': accessibilityLabel, }; const defaultArgs: Args = { @@ -79,7 +79,7 @@ const defaultArgs: Args = { target: '_blank', rel: undefined, download: false, - 'aria-label': undefined, + 'accessibility-label': undefined, }; const Template = (args: Args): TemplateResult => html` diff --git a/src/components/navigation/navigation-link/readme.md b/src/components/navigation/navigation-link/readme.md index 941e606495..b9fb86ab66 100644 --- a/src/components/navigation/navigation-link/readme.md +++ b/src/components/navigation/navigation-link/readme.md @@ -31,16 +31,17 @@ The component has three different sizes, which can be changed using the `size` p ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| ------------------ | ---------- | ------- | ------------------------------------------ | ------- | ----------------------------------------------------------------- | -| `size` | `size` | public | `SbbNavigationActionSize \| undefined` | `'l'` | Action size variant. | -| `connectedSection` | - | public | `SbbNavigationSectionElement \| undefined` | | The section that is beign controlled by the action, if any. | -| `marker` | - | public | `SbbNavigationMarkerElement \| null` | | The navigation marker in which the action is nested. | -| `section` | - | public | `SbbNavigationSectionElement \| null` | | The section in which the action is nested. | -| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | -| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | -| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | -| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| Name | Attribute | Privacy | Type | Default | Description | +| -------------------- | --------------------- | ------- | ------------------------------------------ | ------- | ----------------------------------------------------------------- | +| `size` | `size` | public | `SbbNavigationActionSize \| undefined` | `'l'` | Action size variant. | +| `connectedSection` | - | public | `SbbNavigationSectionElement \| undefined` | | The section that is beign controlled by the action, if any. | +| `marker` | - | public | `SbbNavigationMarkerElement \| null` | | The navigation marker in which the action is nested. | +| `section` | - | public | `SbbNavigationSectionElement \| null` | | The section in which the action is nested. | +| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | +| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | +| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | +| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the inner anchor element. | ## Slots diff --git a/src/components/skiplink-list/__snapshots__/skiplink-list.spec.snap.js b/src/components/skiplink-list/__snapshots__/skiplink-list.spec.snap.js index 02292a566b..333e936a01 100644 --- a/src/components/skiplink-list/__snapshots__/skiplink-list.spec.snap.js +++ b/src/components/skiplink-list/__snapshots__/skiplink-list.spec.snap.js @@ -52,10 +52,8 @@ snapshots["sbb-skiplink-list renders Dom"] = href="#" icon-placement="start" negative="" - role="link" size="m" slot="li-0" - tabindex="0" > Link 1 @@ -68,10 +66,8 @@ snapshots["sbb-skiplink-list renders Dom"] = href="#" icon-placement="start" negative="" - role="link" size="m" slot="li-1" - tabindex="0" > Link 2 @@ -84,10 +80,8 @@ snapshots["sbb-skiplink-list renders Dom"] = href="#" icon-placement="start" negative="" - role="link" size="m" slot="li-2" - tabindex="0" > Link 3 @@ -150,10 +144,8 @@ snapshots["sbb-skiplink-list renders with title Dom"] = href="https://www.sbb.ch" icon-placement="start" negative="" - role="link" size="m" slot="li-0" - tabindex="0" > Link 1 @@ -166,10 +158,8 @@ snapshots["sbb-skiplink-list renders with title Dom"] = href="https://www.sbb.ch" icon-placement="start" negative="" - role="link" size="m" slot="li-1" - tabindex="0" > Link 2 @@ -182,10 +172,8 @@ snapshots["sbb-skiplink-list renders with title Dom"] = href="https://www.sbb.ch" icon-placement="start" negative="" - role="link" size="m" slot="li-2" - tabindex="0" > Link 3 @@ -247,33 +235,15 @@ snapshots["sbb-skiplink-list renders with title A11y tree Chrome"] = }, { "role": "link", - "name": "Link 1", - "children": [ - { - "role": "link", - "name": "Link 1" - } - ] + "name": "Link 1" }, { "role": "link", - "name": "Link 2", - "children": [ - { - "role": "link", - "name": "Link 2" - } - ] + "name": "Link 2" }, { "role": "link", - "name": "Link 3", - "children": [ - { - "role": "link", - "name": "Link 3" - } - ] + "name": "Link 3" } ] } @@ -295,35 +265,17 @@ snapshots["sbb-skiplink-list renders with title A11y tree Firefox"] = { "role": "link", "name": "Link 1", - "children": [ - { - "role": "link", - "name": "Link 1", - "value": "https://www.sbb.ch/" - } - ] + "value": "https://www.sbb.ch/" }, { "role": "link", "name": "Link 2", - "children": [ - { - "role": "link", - "name": "Link 2", - "value": "https://www.sbb.ch/" - } - ] + "value": "https://www.sbb.ch/" }, { "role": "link", "name": "Link 3", - "children": [ - { - "role": "link", - "name": "Link 3", - "value": "https://www.sbb.ch/" - } - ] + "value": "https://www.sbb.ch/" } ] } diff --git a/src/components/skiplink-list/skiplink-list.stories.ts b/src/components/skiplink-list/skiplink-list.stories.ts index 8743a7df7c..6959aba11f 100644 --- a/src/components/skiplink-list/skiplink-list.stories.ts +++ b/src/components/skiplink-list/skiplink-list.stories.ts @@ -1,4 +1,4 @@ -import { userEvent, within } from '@storybook/test'; +import { within } from '@storybook/test'; import type { InputType } from '@storybook/types'; import type { Meta, StoryObj, ArgTypes, Args, StoryContext } from '@storybook/web-components'; import isChromatic from 'chromatic/isChromatic'; @@ -106,7 +106,7 @@ const playStory = async ({ canvasElement }: StoryContext): Promise => { canvas.getByTestId('skiplink')?.shadowRoot?.querySelectorAll('.sbb-skiplink-list__wrapper'), ); await waitForStablePosition(() => canvas.getByTestId('skiplink')); - userEvent.tab(); + document.querySelector('sbb-block-link')?.focus(); }; const Template = ({ diff --git a/src/components/teaser-hero/__snapshots__/teaser-hero.spec.snap.js b/src/components/teaser-hero/__snapshots__/teaser-hero.spec.snap.js index 5064e9c634..a395faacd4 100644 --- a/src/components/teaser-hero/__snapshots__/teaser-hero.spec.snap.js +++ b/src/components/teaser-hero/__snapshots__/teaser-hero.spec.snap.js @@ -3,10 +3,9 @@ export const snapshots = {}; snapshots["sbb-teaser-hero should render with slots"] = `

@@ -36,7 +35,7 @@ snapshots["sbb-teaser-hero should render with slots"] = snapshots["sbb-teaser-hero should render all properties Dom"] = ` Break out and explore castles and palaces. @@ -56,11 +53,10 @@ snapshots["sbb-teaser-hero should render all properties Dom"] = snapshots["sbb-teaser-hero should render all properties ShadowDom"] = ` @@ -106,13 +102,7 @@ snapshots["sbb-teaser-hero should render all properties A11y tree Chrome"] = "children": [ { "role": "link", - "name": "label", - "children": [ - { - "role": "link", - "name": "Break out and explore castles and palaces. Find out more . Link target opens in a new window." - } - ] + "name": "label" } ] } @@ -129,13 +119,7 @@ snapshots["sbb-teaser-hero should render all properties A11y tree Firefox"] = { "role": "link", "name": "label", - "children": [ - { - "role": "link", - "name": "Break out and explore castles and palaces. Find out more . Link target opens in a new window.", - "value": "https://www.sbb.ch/" - } - ] + "value": "https://www.sbb.ch/" } ] } diff --git a/src/components/teaser-hero/readme.md b/src/components/teaser-hero/readme.md index 753674b3a8..5c2d4b8545 100644 --- a/src/components/teaser-hero/readme.md +++ b/src/components/teaser-hero/readme.md @@ -34,15 +34,16 @@ Avoid slotting block elements (e.g. `div`) as this violates semantic rules and c ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| ------------- | -------------- | ------- | --------------------------------------- | ------- | ----------------------------------------------------------------- | -| `linkContent` | `link-content` | public | `string \| undefined` | | Panel link text. | -| `imageSrc` | `image-src` | public | `string \| undefined` | | Image src will be passed to `sbb-image`. | -| `imageAlt` | `image-alt` | public | `string \| undefined` | | Image alt text will be passed to `sbb-image`. | -| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | -| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | -| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | -| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| Name | Attribute | Privacy | Type | Default | Description | +| -------------------- | --------------------- | ------- | --------------------------------------- | ------- | ----------------------------------------------------------------- | +| `linkContent` | `link-content` | public | `string \| undefined` | | Panel link text. | +| `imageSrc` | `image-src` | public | `string \| undefined` | | Image src will be passed to `sbb-image`. | +| `imageAlt` | `image-alt` | public | `string \| undefined` | | Image alt text will be passed to `sbb-image`. | +| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | +| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | +| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | +| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the inner anchor element. | ## Slots diff --git a/src/components/teaser-hero/teaser-hero.scss b/src/components/teaser-hero/teaser-hero.scss index c4cd136527..8e89806e94 100644 --- a/src/components/teaser-hero/teaser-hero.scss +++ b/src/components/teaser-hero/teaser-hero.scss @@ -28,7 +28,7 @@ min-height: var(--sbb-panel-height); // Hide focus outline when focus origin is mouse or touch. This is being used as a workaround in various components. - :host(:focus-visible:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) & { + :host(:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) &:focus-visible { @include sbb.focus-outline; } } diff --git a/src/components/teaser-hero/teaser-hero.spec.ts b/src/components/teaser-hero/teaser-hero.spec.ts index fd8113d68c..e283860c18 100644 --- a/src/components/teaser-hero/teaser-hero.spec.ts +++ b/src/components/teaser-hero/teaser-hero.spec.ts @@ -16,7 +16,7 @@ describe(`sbb-teaser-hero`, () => { beforeEach(async () => { element = await fixture( html` { it('should render with slots', async () => { const root = await fixture( - html`Break out and explore castles and palaces.Find out more { expect(root).dom.to.be.equal( ` - + Break out and explore castles and palaces. Find out more diff --git a/src/components/teaser-hero/teaser-hero.stories.ts b/src/components/teaser-hero/teaser-hero.stories.ts index 285cc33249..b811935d00 100644 --- a/src/components/teaser-hero/teaser-hero.stories.ts +++ b/src/components/teaser-hero/teaser-hero.stories.ts @@ -10,7 +10,7 @@ import sampleImages from '../core/images.js'; import readme from './readme.md?raw'; import './teaser-hero.js'; -const ariaLabel: InputType = { +const accessibilityLabel: InputType = { control: { type: 'text', }, @@ -81,7 +81,7 @@ const imageAlt: InputType = { }; const defaultArgTypes: ArgTypes = { - 'aria-label': ariaLabel, + 'accessibility-label': accessibilityLabel, href, rel, target, @@ -92,7 +92,7 @@ const defaultArgTypes: ArgTypes = { }; const defaultArgs: Args = { - 'aria-label': undefined, + 'accessibility-label': undefined, href: href.options![0], rel: undefined, target: undefined, diff --git a/src/components/teaser-paid/__snapshots__/teaser-paid.spec.snap.js b/src/components/teaser-paid/__snapshots__/teaser-paid.spec.snap.js index 7d9e9400b4..e5add280c0 100644 --- a/src/components/teaser-paid/__snapshots__/teaser-paid.spec.snap.js +++ b/src/components/teaser-paid/__snapshots__/teaser-paid.spec.snap.js @@ -3,14 +3,12 @@ export const snapshots = {}; snapshots["sbb-teaser-paid Dom"] = ` @@ -19,11 +17,10 @@ snapshots["sbb-teaser-paid Dom"] = snapshots["sbb-teaser-paid ShadowDom"] = ` @@ -45,13 +42,7 @@ snapshots["sbb-teaser-paid A11y tree Chrome"] = "children": [ { "role": "link", - "name": "label", - "children": [ - { - "role": "link", - "name": ". Link target opens in a new window." - } - ] + "name": "label" } ] } @@ -68,13 +59,7 @@ snapshots["sbb-teaser-paid A11y tree Firefox"] = { "role": "link", "name": "label", - "children": [ - { - "role": "link", - "name": ". Link target opens in a new window.", - "value": "https://www.sbb.ch/" - } - ] + "value": "https://www.sbb.ch/" } ] } diff --git a/src/components/teaser-paid/readme.md b/src/components/teaser-paid/readme.md index 498974dc59..234a87ca2a 100644 --- a/src/components/teaser-paid/readme.md +++ b/src/components/teaser-paid/readme.md @@ -15,12 +15,13 @@ The `sbb-teaser-paid` component has two slots: the `image` slot, used to slot an ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| ---------- | ---------- | ------- | --------------------------------------- | ------- | ----------------------------------------------------------------- | -| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | -| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | -| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | -| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| Name | Attribute | Privacy | Type | Default | Description | +| -------------------- | --------------------- | ------- | --------------------------------------- | ------- | ----------------------------------------------------------------- | +| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | +| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | +| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | +| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the inner anchor element. | ## Slots diff --git a/src/components/teaser-paid/teaser-paid.scss b/src/components/teaser-paid/teaser-paid.scss index e3eaf09fab..bc6a6a5203 100644 --- a/src/components/teaser-paid/teaser-paid.scss +++ b/src/components/teaser-paid/teaser-paid.scss @@ -31,7 +31,7 @@ display: block; // Hide focus outline when focus origin is mouse or touch. This is being used as a workaround in various components. - :host(:focus-visible:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) & { + :host(:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) &:focus-visible { @include sbb.focus-outline; } } diff --git a/src/components/teaser-paid/teaser-paid.spec.ts b/src/components/teaser-paid/teaser-paid.spec.ts index 4b110c95bd..8ac3487cf7 100644 --- a/src/components/teaser-paid/teaser-paid.spec.ts +++ b/src/components/teaser-paid/teaser-paid.spec.ts @@ -13,7 +13,7 @@ describe(`sbb-teaser-paid`, () => { beforeEach(async () => { element = await fixture( html` `; @@ -18,10 +16,9 @@ snapshots["sbb-teaser renders after centered DOM"] = snapshots["sbb-teaser renders after centered Shadow DOM"] = ` @@ -66,13 +63,7 @@ snapshots["sbb-teaser renders after centered A11y tree Firefox"] = { "role": "link", "name": "SBB teaser", - "children": [ - { - "role": "link", - "name": "​", - "value": "https://github.com/lyne-design-system/lyne-components" - } - ] + "value": "https://github.com/lyne-design-system/lyne-components" } ] } @@ -82,14 +73,12 @@ snapshots["sbb-teaser renders after centered A11y tree Firefox"] = snapshots["sbb-teaser renders after with title level set DOM"] = ` @@ -98,10 +87,9 @@ snapshots["sbb-teaser renders after with title level set DOM"] = snapshots["sbb-teaser renders after with title level set Shadow DOM"] = ` @@ -139,15 +127,13 @@ snapshots["sbb-teaser renders after with title level set Shadow DOM"] = snapshots["sbb-teaser renders below with projected content DOM"] = ` 400x300 @@ -214,13 +199,7 @@ snapshots["sbb-teaser renders after centered A11y tree Chrome"] = "children": [ { "role": "link", - "name": "SBB teaser", - "children": [ - { - "role": "link", - "name": "​" - } - ] + "name": "SBB teaser" } ] } diff --git a/src/components/teaser/readme.md b/src/components/teaser/readme.md index 21ecb3cb4f..ffb874dcdd 100644 --- a/src/components/teaser/readme.md +++ b/src/components/teaser/readme.md @@ -52,16 +52,17 @@ Avoid slotting block elements (e.g. `

`) as this violates semantic rules and ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| -------------- | --------------- | ------- | ---------------------------------------- | ------------------ | ------------------------------------------------------------------------- | -| `alignment` | `alignment` | public | `'after-centered' \| 'after' \| 'below'` | `'after-centered'` | Teaser variant - define the position and the alignment of the text block. | -| `titleLevel` | `title-level` | public | `SbbTitleLevel` | `'5'` | Heading level of the sbb-title element (e.g. h1-h6). | -| `titleContent` | `title-content` | public | `string \| undefined` | | Content of title. | -| `chipContent` | `chip-content` | public | `string \| undefined` | | Content of chip. | -| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | -| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | -| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | -| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| Name | Attribute | Privacy | Type | Default | Description | +| -------------------- | --------------------- | ------- | ---------------------------------------- | ------------------ | ------------------------------------------------------------------------- | +| `alignment` | `alignment` | public | `'after-centered' \| 'after' \| 'below'` | `'after-centered'` | Teaser variant - define the position and the alignment of the text block. | +| `titleLevel` | `title-level` | public | `SbbTitleLevel` | `'5'` | Heading level of the sbb-title element (e.g. h1-h6). | +| `titleContent` | `title-content` | public | `string \| undefined` | | Content of title. | +| `chipContent` | `chip-content` | public | `string \| undefined` | | Content of chip. | +| `href` | `href` | public | `string \| undefined` | | The href value you want to link to. | +| `target` | `target` | public | `LinkTargetType \| string \| undefined` | | Where to display the linked URL. | +| `rel` | `rel` | public | `string \| undefined` | | The relationship of the linked URL as space-separated link types. | +| `download` | `download` | public | `boolean \| undefined` | | Whether the browser will show the download dialog on click. | +| `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the inner anchor element. | ## Slots diff --git a/src/components/teaser/teaser.scss b/src/components/teaser/teaser.scss index ea91fd4790..4aeed1837b 100644 --- a/src/components/teaser/teaser.scss +++ b/src/components/teaser/teaser.scss @@ -42,7 +42,7 @@ text-decoration: none; // Hide focus outline when focus origin is mouse or touch. This is being used as a workaround in various components. - :host(:focus-visible:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) & { + :host(:not([data-focus-origin='mouse'], [data-focus-origin='touch'])) &:focus-visible { @include sbb.focus-outline; border-radius: var(--sbb-teaser-border-radius); diff --git a/src/components/teaser/teaser.spec.ts b/src/components/teaser/teaser.spec.ts index cb83218b1c..81805e3253 100644 --- a/src/components/teaser/teaser.spec.ts +++ b/src/components/teaser/teaser.spec.ts @@ -17,7 +17,7 @@ describe(`sbb-teaser`, () => { html``, ); }); @@ -39,7 +39,7 @@ describe(`sbb-teaser`, () => { html``, ); @@ -59,7 +59,7 @@ describe(`sbb-teaser`, () => { element = await fixture( html` 400x300 diff --git a/src/components/teaser/teaser.stories.ts b/src/components/teaser/teaser.stories.ts index 7dba0ac814..6ba46f66f4 100644 --- a/src/components/teaser/teaser.stories.ts +++ b/src/components/teaser/teaser.stories.ts @@ -59,7 +59,7 @@ const description: InputType = { }, }; -const ariaLabel: InputType = { +const accessibilityLabel: InputType = { control: { type: 'text', }, @@ -71,7 +71,7 @@ const defaultArgTypes: ArgTypes = { alignment, href, description, - 'aria-label': ariaLabel, + 'accessibility-label': accessibilityLabel, }; const defaultArgs: Args = { @@ -80,7 +80,7 @@ const defaultArgs: Args = { alignment: 'after-centered', href: href.options![1], description: 'This is a paragraph', - 'aria-label': + 'accessibility-label': 'The text which gets exposed to screen reader users. The text should reflect all the information which gets passed into the components slots and which is visible in the Teaser, either through text or iconography', };