From 410d7300889e556c799c9e73872565a782d37d66 Mon Sep 17 00:00:00 2001 From: Davide Mininni Date: Wed, 10 Jan 2024 18:29:43 +0100 Subject: [PATCH] feat: strict mode first impl --- src/components/accordion/accordion.e2e.ts | 67 +++--- src/components/accordion/accordion.ts | 4 +- .../action-group/action-group.stories.ts | 56 ++--- src/components/action-group/action-group.ts | 16 +- src/components/action-group/readme.md | 6 +- .../alert/alert-group/alert-group.e2e.ts | 39 ++-- .../alert/alert-group/alert-group.stories.ts | 2 +- .../alert/alert-group/alert-group.ts | 6 +- src/components/alert/alert-group/readme.md | 2 +- src/components/alert/alert/alert.spec.ts | 2 +- src/components/alert/alert/alert.stories.ts | 2 +- src/components/alert/alert/alert.ts | 12 +- .../autocomplete/autocomplete.e2e.ts | 6 +- .../autocomplete/autocomplete.stories.ts | 12 +- src/components/autocomplete/autocomplete.ts | 59 ++--- src/components/autocomplete/readme.md | 18 +- .../breadcrumb-group/breadcrumb-group.e2e.ts | 61 ++--- .../breadcrumb-group.stories.ts | 15 +- .../breadcrumb-group/breadcrumb-group.ts | 4 +- .../breadcrumb/breadcrumb/breadcrumb.e2e.ts | 2 +- .../breadcrumb/breadcrumb/breadcrumb.ts | 11 +- .../breadcrumb/breadcrumb/readme.md | 14 +- src/components/button/button.e2e.ts | 2 +- src/components/button/button.ts | 12 +- src/components/button/readme.md | 6 +- src/components/calendar/calendar.e2e.ts | 211 +++++++++--------- src/components/calendar/calendar.spec.ts | 14 +- src/components/calendar/calendar.stories.ts | 10 +- src/components/calendar/calendar.ts | 130 ++++++----- src/components/calendar/readme.md | 6 +- .../card/card-action/card-action.e2e.ts | 16 +- .../card/card-action/card-action.ts | 4 +- .../card/card-badge/card-badge.stories.ts | 2 +- src/components/card/card-badge/card-badge.ts | 6 +- src/components/card/card/card.e2e.ts | 4 +- src/components/card/card/card.spec.ts | 2 +- src/components/card/card/card.stories.ts | 2 +- src/components/card/card/card.ts | 2 +- src/components/card/card/readme.md | 2 +- .../checkbox-group/checkbox-group.e2e.ts | 12 +- .../checkbox-group/checkbox-group.stories.ts | 35 +-- .../checkbox/checkbox-group/checkbox-group.ts | 2 +- .../checkbox/checkbox/checkbox.e2e.ts | 9 +- .../checkbox/checkbox/checkbox.spec.ts | 2 +- src/components/checkbox/checkbox/checkbox.ts | 30 +-- src/components/checkbox/checkbox/readme.md | 2 +- src/components/clock/clock.stories.ts | 4 +- src/components/clock/clock.ts | 30 +-- .../core/a11y/arrow-navigation.spec.ts | 12 +- src/components/core/a11y/arrow-navigation.ts | 1 + src/components/core/a11y/focus.ts | 4 +- .../core/a11y/input-modality-detector.ts | 2 +- .../common-behaviors/language-controller.ts | 2 +- .../common-behaviors/slot-child-observer.ts | 6 +- src/components/core/config/config.ts | 4 +- src/components/core/datetime/date-adapter.ts | 12 +- src/components/core/datetime/date-helper.ts | 5 +- .../core/datetime/native-date-adapter.spec.ts | 18 +- .../core/datetime/native-date-adapter.ts | 19 +- src/components/core/dom/input-element.ts | 6 +- src/components/core/dom/is-valid-attribute.ts | 4 +- src/components/core/dom/platform.ts | 2 +- src/components/core/dom/scroll.ts | 12 +- src/components/core/dom/spread.spec.ts | 9 +- src/components/core/dom/spread.ts | 2 +- .../core/eventing/action-element-handlers.ts | 2 +- .../eventing/composed-path-has-attribute.ts | 4 +- .../eventing/connected-abort-controller.ts | 8 +- src/components/core/eventing/custom-events.ts | 76 +++++++ src/components/core/eventing/index.ts | 1 + .../eventing/named-slot-change-handler.ts | 13 +- src/components/core/i18n/i18n.ts | 2 +- .../interfaces/link-button-properties.spec.ts | 6 +- .../core/interfaces/link-button-properties.ts | 14 +- src/components/core/interfaces/types.ts | 2 +- .../core/observers/intersection-observer.ts | 6 +- .../overlay/overlay-trigger-attributes.ts | 2 +- src/components/core/overlay/overlay.ts | 20 +- src/components/core/overlay/position.ts | 8 +- src/components/core/testing/event-spy.ts | 10 +- .../core/timetable/timetable-helper.ts | 2 +- .../core/timetable/timetable-properties.ts | 19 ++ .../datepicker-next-day.e2e.ts | 21 +- .../datepicker-next-day.spec.ts | 9 +- .../datepicker-next-day.stories.ts | 10 +- .../datepicker-next-day.ts | 26 +-- .../datepicker-previous-day.e2e.ts | 24 +- .../datepicker-previous-day.spec.ts | 15 +- .../datepicker-previous-day.stories.ts | 10 +- .../datepicker-previous-day.ts | 26 +-- .../datepicker-toggle.e2e.ts | 43 ++-- .../datepicker-toggle.spec.ts | 9 +- .../datepicker-toggle.stories.ts | 17 +- .../datepicker-toggle/datepicker-toggle.ts | 36 +-- .../datepicker/datepicker/datepicker.e2e.ts | 45 ++-- .../datepicker/datepicker/datepicker.spec.ts | 22 +- .../datepicker/datepicker.stories.ts | 35 +-- .../datepicker/datepicker/datepicker.ts | 108 ++++----- .../datepicker/datepicker/readme.md | 8 +- src/components/dialog/dialog.e2e.ts | 28 +-- src/components/dialog/dialog.stories.ts | 33 +-- src/components/dialog/dialog.ts | 40 ++-- src/components/dialog/readme.md | 2 +- src/components/divider/divider.stories.ts | 2 +- .../expansion-panel-header.ts | 4 +- .../expansion-panel-header/readme.md | 2 +- .../expansion-panel/expansion-panel.e2e.ts | 17 +- .../expansion-panel/expansion-panel.ts | 14 +- .../expansion-panel/expansion-panel/readme.md | 16 +- .../file-selector/file-selector.e2e.ts | 32 +-- .../file-selector/file-selector.stories.ts | 6 +- src/components/file-selector/file-selector.ts | 48 ++-- src/components/file-selector/readme.md | 8 +- src/components/footer/footer.stories.ts | 2 +- src/components/form-error/form-error.e2e.ts | 2 +- .../form-field-clear/form-field-clear.e2e.ts | 6 +- .../form-field-clear/form-field-clear.ts | 6 +- .../form-field/form-field/form-field.e2e.ts | 22 +- .../form-field/form-field.stories.ts | 10 +- .../form-field/form-field/form-field.ts | 35 +-- .../form-field/form-field/readme.md | 32 +-- .../header/header-action/header-action.e2e.ts | 2 +- .../header-action/header-action.stories.ts | 4 +- src/components/header/header/header.e2e.ts | 13 +- .../header/header/header.stories.ts | 15 +- src/components/header/header/header.ts | 10 +- src/components/icon/icon-base.ts | 4 +- src/components/icon/icon-request.ts | 2 + src/components/icon/icon.spec.ts | 4 +- src/components/icon/icon.stories.ts | 4 +- src/components/icon/icon.ts | 2 +- src/components/image/image.e2e.ts | 6 +- src/components/image/image.stories.ts | 2 +- src/components/image/image.ts | 13 +- .../journey-header/journey-header.stories.ts | 2 +- .../journey-summary.stories.ts | 4 +- .../journey-summary/journey-summary.ts | 25 ++- src/components/link-list/link-list.e2e.ts | 5 +- src/components/link-list/link-list.stories.ts | 4 +- src/components/link-list/link-list.ts | 2 +- src/components/link-list/readme.md | 2 +- src/components/link/link.e2e.ts | 2 +- src/components/link/link.stories.ts | 2 +- .../loading-indicator.stories.ts | 16 +- src/components/logo/logo.stories.ts | 2 +- .../map-container/map-container.e2e.ts | 8 +- .../map-container/map-container.stories.ts | 2 +- src/components/map-container/map-container.ts | 11 +- .../menu/menu-action/menu-action.e2e.ts | 2 +- .../menu/menu-action/menu-action.stories.ts | 10 +- src/components/menu/menu/menu.e2e.ts | 13 +- src/components/menu/menu/menu.stories.ts | 23 +- src/components/menu/menu/menu.ts | 32 +-- src/components/menu/menu/readme.md | 10 +- src/components/message/message.stories.ts | 8 +- .../navigation-action.e2e.ts | 7 +- .../navigation-action/navigation-action.ts | 2 +- .../navigation-list/navigation-list.e2e.ts | 6 +- .../navigation-marker.e2e.ts | 6 +- .../navigation-marker.stories.ts | 2 +- .../navigation-marker/navigation-marker.ts | 10 +- .../navigation-section.e2e.ts | 2 +- .../navigation-section/navigation-section.ts | 42 ++-- .../navigation/navigation-section/readme.md | 14 +- .../navigation/navigation/navigation.e2e.ts | 67 +++--- .../navigation/navigation.stories.ts | 15 +- .../navigation/navigation/navigation.ts | 38 ++-- .../navigation/navigation/readme.md | 10 +- .../notification/notification.e2e.ts | 6 +- .../notification/notification.stories.ts | 2 +- src/components/notification/notification.ts | 10 +- src/components/notification/readme.md | 14 +- .../option/optgroup/optgroup.e2e.ts | 12 +- src/components/option/optgroup/optgroup.ts | 6 +- src/components/option/option/option.e2e.ts | 13 +- src/components/option/option/option.ts | 29 +-- .../pearl-chain-time/pearl-chain-time.ts | 2 +- .../pearl-chain-vertical-item.ts | 4 +- src/components/pearl-chain/pearl-chain.ts | 63 +++--- src/components/pearl-chain/readme.md | 8 +- .../radio-button-group.stories.ts | 2 +- .../radio-button-group/radio-button-group.ts | 13 +- .../radio-button/radio-button.e2e.ts | 2 +- .../radio-button/radio-button/radio-button.ts | 17 +- .../radio-button/radio-button/readme.md | 4 +- src/components/select/readme.md | 20 +- src/components/select/select.e2e.ts | 28 +-- src/components/select/select.spec.ts | 12 +- src/components/select/select.stories.ts | 24 +- src/components/select/select.ts | 72 +++--- .../selection-panel/selection-panel.e2e.ts | 168 ++++++++------ .../selection-panel.stories.ts | 16 +- .../selection-panel/selection-panel.ts | 13 +- .../skiplink-list/skiplink-list.e2e.ts | 10 +- .../skiplink-list/skiplink-list.stories.ts | 9 +- src/components/slider/slider.stories.ts | 15 +- src/components/slider/slider.ts | 8 +- .../tabs/tab-group/tab-group.e2e.ts | 8 +- src/components/tabs/tab-group/tab-group.ts | 47 ++-- src/components/tag/tag-group/tag-group.e2e.ts | 14 +- .../tag/tag-group/tag-group.stories.ts | 8 +- src/components/tag/tag-group/tag-group.ts | 12 +- src/components/tag/tag/tag.ts | 4 +- src/components/teaser-hero/teaser-hero.e2e.ts | 2 +- .../teaser-hero/teaser-hero.stories.ts | 2 +- src/components/teaser/readme.md | 2 +- src/components/teaser/teaser.e2e.ts | 2 +- src/components/teaser/teaser.ts | 3 +- src/components/time-input/readme.md | 6 +- src/components/time-input/time-input.e2e.ts | 37 +-- .../time-input/time-input.stories.ts | 23 +- src/components/time-input/time-input.ts | 34 +-- .../timetable-barrier-free.ts | 3 +- .../timetable-occupancy-icon.e2e.ts | 2 +- .../timetable-occupancy-icon.ts | 4 +- src/components/timetable-occupancy/readme.md | 10 +- .../timetable-occupancy.ts | 6 +- .../timetable-row-column-headers.ts | 2 +- .../timetable-row/timetable-row.e2e.ts | 5 +- .../timetable-transportation-number.ts | 3 +- .../timetable-travel-hints.ts | 3 +- src/components/title/title.e2e.ts | 2 +- src/components/title/title.stories.ts | 2 +- src/components/toast/toast.e2e.ts | 7 +- src/components/toast/toast.stories.ts | 19 +- src/components/toast/toast.ts | 28 ++- .../toggle-check/toggle-check.e2e.ts | 8 +- src/components/toggle-check/toggle-check.ts | 8 +- .../toggle/toggle-option/toggle-option.ts | 12 +- src/components/toggle/toggle/readme.md | 2 +- src/components/toggle/toggle/toggle.ts | 7 +- .../tooltip/tooltip-trigger/readme.md | 2 +- .../tooltip-trigger/tooltip-trigger.e2e.ts | 4 +- .../tooltip-trigger/tooltip-trigger.ts | 2 +- src/components/tooltip/tooltip/readme.md | 18 +- src/components/tooltip/tooltip/tooltip.e2e.ts | 55 ++--- .../tooltip/tooltip/tooltip.stories.ts | 13 +- src/components/tooltip/tooltip/tooltip.ts | 40 ++-- .../train-formation/train-formation.e2e.ts | 22 +- .../train/train-formation/train-formation.ts | 14 +- src/components/train/train-wagon/readme.md | 2 +- .../train/train-wagon/train-wagon.e2e.ts | 7 +- .../train/train-wagon/train-wagon.spec.ts | 26 ++- .../train/train-wagon/train-wagon.ts | 10 +- src/components/train/train/train.e2e.ts | 3 +- src/components/train/train/train.spec.ts | 16 +- src/components/visual-checkbox/readme.md | 4 +- .../visual-checkbox/visual-checkbox.ts | 4 +- src/components/vite.config.ts | 1 + src/storybook/pages/home/home.common.ts | 9 +- src/storybook/testing/chromatic.ts | 19 +- tsconfig.json | 1 + 252 files changed, 2107 insertions(+), 1710 deletions(-) create mode 100644 src/components/core/eventing/custom-events.ts diff --git a/src/components/accordion/accordion.e2e.ts b/src/components/accordion/accordion.e2e.ts index c27e27880ec..91d7e911f28 100644 --- a/src/components/accordion/accordion.e2e.ts +++ b/src/components/accordion/accordion.e2e.ts @@ -46,14 +46,14 @@ describe('sbb-accordion', () => { it('should set accordion context on expansion panel when removing and adding expansion-panels', async () => { let panels: SbbExpansionPanelElement[]; - element.querySelector('sbb-expansion-panel').remove(); + element.querySelector('sbb-expansion-panel')?.remove(); await waitForLitRender(element); panels = Array.from(element.querySelectorAll('sbb-expansion-panel')); expect(panels[0]).to.have.attribute('data-accordion-first'); expect(panels[1]).to.have.attribute('data-accordion-last'); - element.querySelector('sbb-expansion-panel').remove(); + element.querySelector('sbb-expansion-panel')?.remove(); await waitForLitRender(element); const lastRemainingPanel = element.querySelector('sbb-expansion-panel'); @@ -74,13 +74,13 @@ describe('sbb-accordion', () => { const panels = Array.from(element.querySelectorAll('sbb-expansion-panel')); expect(panels.length).to.be.equal(3); expect( - panels[0].shadowRoot.querySelector('.sbb-expansion-panel').firstElementChild.tagName, + panels[0].shadowRoot?.querySelector('.sbb-expansion-panel')?.firstElementChild?.tagName, ).to.be.equal('H4'); expect( - panels[1].shadowRoot.querySelector('.sbb-expansion-panel').firstElementChild.tagName, + panels[1].shadowRoot?.querySelector('.sbb-expansion-panel')?.firstElementChild?.tagName, ).to.be.equal('H4'); expect( - panels[2].shadowRoot.querySelector('.sbb-expansion-panel').firstElementChild.tagName, + panels[2].shadowRoot?.querySelector('.sbb-expansion-panel')?.firstElementChild?.tagName, ).to.be.equal('H4'); }); @@ -90,24 +90,30 @@ describe('sbb-accordion', () => { const panels = Array.from(element.querySelectorAll('sbb-expansion-panel')); expect(panels.length).to.be.equal(3); expect( - panels[0].shadowRoot.querySelector('.sbb-expansion-panel').firstElementChild.tagName, + panels[0].shadowRoot?.querySelector('.sbb-expansion-panel')?.firstElementChild?.tagName, ).to.be.equal('H6'); expect( - panels[1].shadowRoot.querySelector('.sbb-expansion-panel').firstElementChild.tagName, + panels[1].shadowRoot?.querySelector('.sbb-expansion-panel')?.firstElementChild?.tagName, ).to.be.equal('H6'); expect( - panels[2].shadowRoot.querySelector('.sbb-expansion-panel').firstElementChild.tagName, + panels[2].shadowRoot?.querySelector('.sbb-expansion-panel')?.firstElementChild?.tagName, ).to.be.equal('H6'); }); it('should close others when expanding and multi = false', async () => { const willOpenEventSpy = new EventSpy(SbbExpansionPanelElement.events.willOpen); - const panelOne: SbbExpansionPanelElement = element.querySelector('#panel-1'); - const headerOne: SbbExpansionPanelHeaderElement = element.querySelector('#header-1'); - const panelTwo: SbbExpansionPanelElement = element.querySelector('#panel-2'); - const headerTwo: SbbExpansionPanelHeaderElement = element.querySelector('#header-2'); - const panelThree: SbbExpansionPanelElement = element.querySelector('#panel-3'); - const headerThree: SbbExpansionPanelHeaderElement = element.querySelector('#header-3'); + const panelOne: SbbExpansionPanelElement = + element.querySelector('#panel-1')!; + const headerOne: SbbExpansionPanelHeaderElement = + element.querySelector('#header-1')!; + const panelTwo: SbbExpansionPanelElement = + element.querySelector('#panel-2')!; + const headerTwo: SbbExpansionPanelHeaderElement = + element.querySelector('#header-2')!; + const panelThree: SbbExpansionPanelElement = + element.querySelector('#panel-3')!; + const headerThree: SbbExpansionPanelHeaderElement = + element.querySelector('#header-3')!; for (const panel of [panelOne, panelTwo, panelThree]) { expect(panel.expanded).to.be.equal(false); @@ -139,12 +145,18 @@ describe('sbb-accordion', () => { element.multi = true; await waitForLitRender(element); const willOpenEventSpy = new EventSpy(SbbExpansionPanelElement.events.willOpen); - const panelOne: SbbExpansionPanelElement = element.querySelector('#panel-1'); - const headerOne: SbbExpansionPanelHeaderElement = element.querySelector('#header-1'); - const panelTwo: SbbExpansionPanelElement = element.querySelector('#panel-2'); - const headerTwo: SbbExpansionPanelHeaderElement = element.querySelector('#header-2'); - const panelThree: SbbExpansionPanelElement = element.querySelector('#panel-3'); - const headerThree: SbbExpansionPanelHeaderElement = element.querySelector('#header-3'); + const panelOne: SbbExpansionPanelElement = + element.querySelector('#panel-1')!; + const headerOne: SbbExpansionPanelHeaderElement = + element.querySelector('#header-1')!; + const panelTwo: SbbExpansionPanelElement = + element.querySelector('#panel-2')!; + const headerTwo: SbbExpansionPanelHeaderElement = + element.querySelector('#header-2')!; + const panelThree: SbbExpansionPanelElement = + element.querySelector('#panel-3')!; + const headerThree: SbbExpansionPanelHeaderElement = + element.querySelector('#header-3')!; for (const panel of [panelOne, panelTwo, panelThree]) { expect(panel.expanded).to.be.equal(false); @@ -175,11 +187,16 @@ describe('sbb-accordion', () => { it('should close all panels except the first when multi changes from true to false', async () => { element.multi = true; await waitForLitRender(element); - const panelOne: SbbExpansionPanelElement = element.querySelector('#panel-1'); - const panelTwo: SbbExpansionPanelElement = element.querySelector('#panel-2'); - const headerTwo: SbbExpansionPanelHeaderElement = element.querySelector('#header-2'); - const panelThree: SbbExpansionPanelElement = element.querySelector('#panel-3'); - const headerThree: SbbExpansionPanelHeaderElement = element.querySelector('#header-3'); + const panelOne: SbbExpansionPanelElement = + element.querySelector('#panel-1')!; + const panelTwo: SbbExpansionPanelElement = + element.querySelector('#panel-2')!; + const headerTwo: SbbExpansionPanelHeaderElement = + element.querySelector('#header-2')!; + const panelThree: SbbExpansionPanelElement = + element.querySelector('#panel-3')!; + const headerThree: SbbExpansionPanelHeaderElement = + element.querySelector('#header-3')!; for (const panel of [panelOne, panelTwo, panelThree]) { expect(panel.expanded).to.be.equal(false); diff --git a/src/components/accordion/accordion.ts b/src/components/accordion/accordion.ts index 800f6e743e0..9dc5e619ff4 100644 --- a/src/components/accordion/accordion.ts +++ b/src/components/accordion/accordion.ts @@ -68,7 +68,9 @@ export class SbbAccordionElement extends LitElement { } private _setTitleLevelOnChildren(): void { - this._expansionPanels.forEach((panel) => (panel.titleLevel = this.titleLevel)); + this._expansionPanels.forEach( + (panel: SbbExpansionPanelElement) => (panel.titleLevel = this.titleLevel), + ); } private get _expansionPanels(): SbbExpansionPanelElement[] { diff --git a/src/components/action-group/action-group.stories.ts b/src/components/action-group/action-group.stories.ts index 7cecd98882c..e62cb4b1154 100644 --- a/src/components/action-group/action-group.stories.ts +++ b/src/components/action-group/action-group.stories.ts @@ -2,6 +2,7 @@ import { withActions } from '@storybook/addon-actions/decorator'; import type { InputType } from '@storybook/types'; import type { Meta, StoryObj, ArgTypes, Args, Decorator } from '@storybook/web-components'; import { html, TemplateResult } from 'lit'; +import { ifDefined } from 'lit/directives/if-defined.js'; import { styleMap } from 'lit/directives/style-map.js'; import { sbbSpread } from '../core/dom'; @@ -11,17 +12,17 @@ import './action-group'; import '../link'; import '../button'; -const secondaryButtonTemplate = (alignSelf): TemplateResult => html` - Button 1 +const secondaryButtonTemplate = (alignSelf?: string): TemplateResult => html` + Button 1 `; -const buttonTemplate = (alignSelf): TemplateResult => html` - Button 2 +const buttonTemplate = (alignSelf?: string): TemplateResult => html` + Button 2 `; -const linkTemplate = (alignSelf): TemplateResult => html` +const linkTemplate = (alignSelf?: string): TemplateResult => html` @@ -29,59 +30,64 @@ const linkTemplate = (alignSelf): TemplateResult => html` `; -const TemplateTwoElements = (alignSelfFirst?, alignSelfSecond?): TemplateResult => html` +const TemplateTwoElements = ( + alignSelfFirst?: string, + alignSelfSecond?: string, +): TemplateResult => html` ${secondaryButtonTemplate(alignSelfFirst)} ${buttonTemplate(alignSelfSecond)} `; const TemplateThreeElements = ( - alignSelfFirst?, - alignSelfSecond?, - alignSelfThird?, + alignSelfFirst?: string, + alignSelfSecond?: string, + alignSelfThird?: string, ): TemplateResult => html` ${TemplateTwoElements(alignSelfFirst, alignSelfSecond)} ${linkTemplate(alignSelfThird)} `; -const CommonTemplateThreeElementsAllocation = ({ ...args }): TemplateResult => html` +const CommonTemplateThreeElementsAllocation = (args: Args): TemplateResult => html` ${TemplateThreeElements()} `; -const CommonTemplateTwoElementsAllocation = ({ ...args }): TemplateResult => html` +const CommonTemplateTwoElementsAllocation = (args: Args): TemplateResult => html` ${TemplateTwoElements()} `; -const TemplateHorizontalAllocation111 = ({ ...args }): TemplateResult => html` - ${TemplateThreeElements(null, 'center')} +const TemplateHorizontalAllocation111 = (args: Args): TemplateResult => html` + ${TemplateThreeElements(undefined, 'center')} `; -const TemplateHorizontalAllocation201 = ({ ...args }): TemplateResult => html` +const TemplateHorizontalAllocation201 = (args: Args): TemplateResult => html` ${TemplateThreeElements(null, null, 'end')}${TemplateThreeElements(undefined, undefined, 'end')} `; -const TemplateHorizontalAllocation102 = ({ ...args }): TemplateResult => html` +const TemplateHorizontalAllocation102 = (args: Args): TemplateResult => html` ${TemplateThreeElements('start')} `; -const TemplateHorizontalAllocation101 = ({ ...args }): TemplateResult => html` - ${TemplateTwoElements(null, 'end')} +const TemplateHorizontalAllocation101 = (args: Args): TemplateResult => html` + ${TemplateTwoElements(undefined, 'end')} `; -const TemplateVerticalAllocation300FullWidth = ({ ...args }): TemplateResult => html` +const TemplateVerticalAllocation300FullWidth = (args: Args): TemplateResult => html` ${TemplateThreeElements(null, null, 'start')}${TemplateThreeElements(undefined, undefined, 'start')} `; -const TemplateVerticalAllocation030FullWidth = ({ ...args }): TemplateResult => html` +const TemplateVerticalAllocation030FullWidth = (args: Args): TemplateResult => html` ${TemplateThreeElements(null, null, 'center')}${TemplateThreeElements(undefined, undefined, 'center')} `; -const TemplateVerticalAllocation003FullWidth = ({ ...args }): TemplateResult => html` +const TemplateVerticalAllocation003FullWidth = (args: Args): TemplateResult => html` ${TemplateThreeElements(null, null, 'end')}${TemplateThreeElements(undefined, undefined, 'end')} `; diff --git a/src/components/action-group/action-group.ts b/src/components/action-group/action-group.ts index a0f6a25f4cf..d38e3e84fea 100644 --- a/src/components/action-group/action-group.ts +++ b/src/components/action-group/action-group.ts @@ -1,9 +1,9 @@ import { CSSResultGroup, html, LitElement, TemplateResult, PropertyValues } from 'lit'; import { customElement, property } from 'lit/decorators.js'; -import { SbbButtonSize } from '../button'; +import { SbbButtonElement, SbbButtonSize } from '../button'; import { SbbHorizontalFrom, SbbOrientation } from '../core/interfaces'; -import { SbbLinkSize } from '../link'; +import { SbbLinkElement, SbbLinkSize } from '../link'; import style from './action-group.scss?lit&inline'; @@ -26,7 +26,7 @@ export class SbbActionGroupElement extends LitElement { * Overrides the behaviour of `orientation` property. */ @property({ attribute: 'horizontal-from', reflect: true }) - public horizontalFrom?: SbbHorizontalFrom = 'medium'; + public horizontalFrom: SbbHorizontalFrom = 'medium'; /** * Indicates the orientation of the components inside the ``. @@ -39,17 +39,19 @@ export class SbbActionGroupElement extends LitElement { * sbb-button instances. */ @property({ attribute: 'button-size', reflect: true }) - public buttonSize?: SbbButtonSize = 'l'; + public buttonSize: SbbButtonSize = 'l'; /** * Size of the nested sbb-link instances. This will overwrite the size attribute of nested * sbb-link instances. */ @property({ attribute: 'link-size', reflect: true }) - public linkSize?: SbbLinkSize = 'm'; + public linkSize: SbbLinkSize = 'm'; private _syncButtons(): void { - this.querySelectorAll?.('sbb-button').forEach((b) => (b.size = this.buttonSize)); + this.querySelectorAll?.('sbb-button').forEach( + (b: SbbButtonElement) => (b.size = this.buttonSize), + ); } protected override willUpdate(changedProperties: PropertyValues): void { @@ -62,7 +64,7 @@ export class SbbActionGroupElement extends LitElement { } private _syncLinks(): void { - this.querySelectorAll?.('sbb-link').forEach((link) => { + this.querySelectorAll?.('sbb-link').forEach((link: SbbLinkElement) => { link.variant = 'block'; link.size = this.linkSize; }); diff --git a/src/components/action-group/readme.md b/src/components/action-group/readme.md index fef95bcac04..9c4c6314e46 100644 --- a/src/components/action-group/readme.md +++ b/src/components/action-group/readme.md @@ -123,10 +123,10 @@ The values for `align-group` and `align-self` for the various allocations are as | Name | Attribute | Privacy | Type | Default | Description | | ---------------- | ----------------- | ------- | ------------------------------------------- | -------------- | --------------------------------------------------------------------------------------------------------------- | | `alignGroup` | `align-group` | public | `'start' \| 'center' \| 'stretch' \| 'end'` | `'start'` | Set the slotted `` children's alignment. | -| `horizontalFrom` | `horizontal-from` | public | `SbbHorizontalFrom \| undefined` | `'medium'` | Overrides the behaviour of `orientation` property. | +| `horizontalFrom` | `horizontal-from` | public | `SbbHorizontalFrom` | `'medium'` | Overrides the behaviour of `orientation` property. | | `orientation` | `orientation` | public | `SbbOrientation` | `'horizontal'` | Indicates the orientation of the components inside the ``. | -| `buttonSize` | `button-size` | public | `SbbButtonSize \| undefined` | `'l'` | Size of the nested sbb-button instances. This will overwrite the size attribute of nested sbb-button instances. | -| `linkSize` | `link-size` | public | `SbbLinkSize \| undefined` | `'m'` | Size of the nested sbb-link instances. This will overwrite the size attribute of nested sbb-link instances. | +| `buttonSize` | `button-size` | public | `SbbButtonSize` | `'l'` | Size of the nested sbb-button instances. This will overwrite the size attribute of nested sbb-button instances. | +| `linkSize` | `link-size` | public | `SbbLinkSize` | `'m'` | Size of the nested sbb-link instances. This will overwrite the size attribute of nested sbb-link instances. | ## Slots diff --git a/src/components/alert/alert-group/alert-group.e2e.ts b/src/components/alert/alert-group/alert-group.e2e.ts index 68502f5cde2..3802e4f6833 100644 --- a/src/components/alert/alert-group/alert-group.e2e.ts +++ b/src/components/alert/alert-group/alert-group.e2e.ts @@ -3,10 +3,10 @@ import { html } from 'lit/static-html.js'; import { SbbButtonElement } from '../../button'; import { waitForCondition, EventSpy, waitForLitRender } from '../../core/testing'; +import type { SbbAlertElement } from '../alert'; import { SbbAlertGroupElement } from './alert-group'; -import '.'; import '../alert'; describe('sbb-alert-group', () => { @@ -36,17 +36,17 @@ describe('sbb-alert-group', () => { // Then two alerts should be rendered and accessibility title should be displayed expect(element.querySelectorAll('sbb-alert').length).to.be.equal(2); - const alertGroupTitle = element.shadowRoot.querySelector('.sbb-alert-group__title'); - expect(alertGroupTitle.textContent.trim()).to.be.equal(accessibilityTitle); - expect(alertGroupTitle.tagName).to.be.equal(`H${accessibilityTitleLevel}`); + const alertGroupTitle = element.shadowRoot!.querySelector('.sbb-alert-group__title'); + expect(alertGroupTitle?.textContent?.trim()).to.be.equal(accessibilityTitle); + expect(alertGroupTitle?.tagName).to.be.equal(`H${accessibilityTitleLevel}`); // When clicking on close button of the first alert const closeButton = element - .querySelector('sbb-alert') - .shadowRoot.querySelector('.sbb-alert__close-button-wrapper sbb-button') as SbbButtonElement; + .querySelector('sbb-alert') + ?.shadowRoot!.querySelector('.sbb-alert__close-button-wrapper sbb-button'); - closeButton.focus(); - closeButton.click(); + closeButton?.focus(); + closeButton?.click(); await waitForLitRender(element); // Then one alert should be removed from sbb-alert-group, tabindex should be set to 0, @@ -56,26 +56,25 @@ describe('sbb-alert-group', () => { expect(didDismissAlertSpy.count).to.be.equal(1); expect(element.querySelectorAll('sbb-alert').length).to.be.equal(1); expect(element.tabIndex).to.be.equal(0); - expect(document.activeElement.id).to.be.equal(alertGroupId); + expect(document.activeElement?.id).to.be.equal(alertGroupId); expect( - element.shadowRoot.querySelector('.sbb-alert-group__title').textContent.trim(), + element.shadowRoot!.querySelector('.sbb-alert-group__title')?.textContent?.trim(), ).to.be.equal(accessibilityTitle); expect(emptySpy.count).not.to.be.greaterThan(0); // When clicking on close button of the second alert - ( - element - .querySelector('sbb-alert') - .shadowRoot.querySelector('.sbb-alert__close-button-wrapper sbb-button') as SbbButtonElement - ).click(); + element + .querySelector('sbb-alert') + ?.shadowRoot!.querySelector('.sbb-alert__close-button-wrapper sbb-button') + ?.click(); await waitForLitRender(element); // Then the alert should be removed from sbb-alert-group, tabindex should be set to 0, // focus should be on sbb-alert-group, accessibility title should be removed and empty event should be fired. expect(element.querySelectorAll('sbb-alert').length).to.be.equal(0); expect(element.tabIndex).to.be.equal(0); - expect(document.activeElement.id).to.be.equal(alertGroupId); - expect(element.shadowRoot.querySelector('.sbb-alert-group__title')).to.be.null; + expect(document.activeElement?.id).to.be.equal(alertGroupId); + expect(element.shadowRoot?.querySelector('.sbb-alert-group__title')).to.be.null; await waitForCondition(() => didDismissAlertSpy.events.length === 2); expect(didDismissAlertSpy.count).to.be.equal(2); expect(emptySpy.count).to.be.greaterThan(0); @@ -85,8 +84,8 @@ describe('sbb-alert-group', () => { await waitForLitRender(element); // Then the active element id should be unset and tabindex should be removed - await waitForCondition(() => document.activeElement.id === ''); - expect(document.activeElement.id).to.be.equal(''); + await waitForCondition(() => document.activeElement?.id === ''); + expect(document.activeElement?.id).to.be.equal(''); expect(element.tabIndex).to.be.equal(-1); }); @@ -98,7 +97,7 @@ describe('sbb-alert-group', () => { const emptySpy = new EventSpy(SbbAlertGroupElement.events.empty); // Then no title should be rendered and no empty event fired - expect(element.shadowRoot.querySelector('.sbb-alert-group__title')).to.be.null; + expect(element.shadowRoot?.querySelector('.sbb-alert-group__title')).to.be.null; expect(emptySpy.count).not.to.be.greaterThan(0); }); }); diff --git a/src/components/alert/alert-group/alert-group.stories.ts b/src/components/alert/alert-group/alert-group.stories.ts index b8a5bdbbfc4..285ab31109a 100644 --- a/src/components/alert/alert-group/alert-group.stories.ts +++ b/src/components/alert/alert-group/alert-group.stories.ts @@ -11,7 +11,7 @@ import readme from './readme.md?raw'; import '../alert'; -const Template = (args): TemplateResult => html` +const Template = (args: Args): TemplateResult => html` = new EventEmitter( @@ -59,7 +59,7 @@ export class SbbAlertGroupElement extends LitElement { const target = event.target as SbbAlertElement; const hasFocusInsideAlertGroup = document.activeElement === target; - target.parentNode.removeChild(target); + target.parentNode?.removeChild(target); this._didDismissAlert.emit(target); // Restore focus diff --git a/src/components/alert/alert-group/readme.md b/src/components/alert/alert-group/readme.md index f8c2373af09..488f0e1a8c9 100644 --- a/src/components/alert/alert-group/readme.md +++ b/src/components/alert/alert-group/readme.md @@ -46,7 +46,7 @@ and therefore interrupts screen reader flow, to immediately read out the alert c | Name | Attribute | Privacy | Type | Default | Description | | ------------------------- | --------------------------- | ------- | ------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `role` | `role` | public | `'alert' \| 'status' \| string` | `'status'` | The role attribute defines how to announce alerts to the user. 'status': sets aria-live to polite and aria-atomic to true. 'alert': sets aria-live to assertive and aria-atomic to true. | -| `accessibilityTitle` | `accessibility-title` | public | `string` | | Title for this alert group which is only visible for screen reader users. | +| `accessibilityTitle` | `accessibility-title` | public | `string \| undefined` | | Title for this alert group which is only visible for screen reader users. | | `accessibilityTitleLevel` | `accessibility-title-level` | public | `TitleLevel` | `'2'` | Level of the accessibility title, will be rendered as heading tag (e.g. h2). Defaults to level 2. | ## Events diff --git a/src/components/alert/alert/alert.spec.ts b/src/components/alert/alert/alert.spec.ts index 7f26f854f8d..a6278f409ae 100644 --- a/src/components/alert/alert/alert.spec.ts +++ b/src/components/alert/alert/alert.spec.ts @@ -115,6 +115,6 @@ describe('sbb-alert', () => { html`Alert content`, ); - expect(element.shadowRoot.querySelector('.sbb-alert__close-button-wrapper')).to.be.null; + expect(element.shadowRoot!.querySelector('.sbb-alert__close-button-wrapper')).to.be.null; }); }); diff --git a/src/components/alert/alert/alert.stories.ts b/src/components/alert/alert/alert.stories.ts index adc0c7da6d2..5d46fc09f2e 100644 --- a/src/components/alert/alert/alert.stories.ts +++ b/src/components/alert/alert/alert.stories.ts @@ -13,7 +13,7 @@ const Default = ({ 'content-slot-text': contentSlotText, ...args }: Args): Templ ${contentSlotText} `; -const DefaultWithOtherContent = (args): TemplateResult => { +const DefaultWithOtherContent = (args: Args): TemplateResult => { return html`
${Default(args)} diff --git a/src/components/alert/alert/alert.ts b/src/components/alert/alert/alert.ts index 03e3cb8bd88..e7c9f2e4edb 100644 --- a/src/components/alert/alert/alert.ts +++ b/src/components/alert/alert/alert.ts @@ -125,7 +125,7 @@ export class SbbAlertElement extends LitElement implements LinkProperties { } /** Present the alert. */ - private _present(): Promise { + private _present(): void { this._willPresent.emit(); if (this.disableAnimation) { @@ -169,7 +169,7 @@ export class SbbAlertElement extends LitElement implements LinkProperties { this._didPresent.emit(); } - private _linkProperties(): Record { + private _linkProperties(): Record { return { ['aria-label']: this.accessibilityLabel, href: this.href, @@ -182,16 +182,16 @@ export class SbbAlertElement extends LitElement implements LinkProperties { return html`
{ - this._transitionWrapperElement = el; + ${ref((el?: Element): void => { + this._transitionWrapperElement = el as HTMLElement; })} >
{ + ${ref((el?: Element): void => { const isFirstInitialization = !this._alertElement; - this._alertElement = el; + this._alertElement = el as HTMLElement; if (isFirstInitialization) { this._initFadeInTransitionStyles(); } diff --git a/src/components/autocomplete/autocomplete.e2e.ts b/src/components/autocomplete/autocomplete.e2e.ts index 4d8800e59ba..285d1213d63 100644 --- a/src/components/autocomplete/autocomplete.e2e.ts +++ b/src/components/autocomplete/autocomplete.e2e.ts @@ -23,8 +23,8 @@ describe('sbb-autocomplete', () => { `); - input = formField.querySelector('input'); - element = formField.querySelector('sbb-autocomplete'); + input = formField.querySelector('input')!; + element = formField.querySelector('sbb-autocomplete')!; }); it('renders and sets the correct attributes', () => { @@ -111,7 +111,7 @@ describe('sbb-autocomplete', () => { await waitForLitRender(element); expect(optionSelectedEventSpy.count).to.be.equal(1); - expect(optionSelectedEventSpy.firstEvent.target).to.have.property('id', 'option-2'); + expect(optionSelectedEventSpy.firstEvent?.target).to.have.property('id', 'option-2'); }); it('opens and select with keyboard', async () => { diff --git a/src/components/autocomplete/autocomplete.stories.ts b/src/components/autocomplete/autocomplete.stories.ts index a4650646db1..2ca21d63172 100644 --- a/src/components/autocomplete/autocomplete.stories.ts +++ b/src/components/autocomplete/autocomplete.stories.ts @@ -193,7 +193,7 @@ const scrollDecorator: Decorator = (story) => html` `; // Story interaction executed after the story renders -const playStory = async ({ canvasElement }): Promise => { +const playStory = async ({ canvasElement }: StoryContext): Promise => { const canvas = within(canvasElement); await waitForComponentsReady(() => @@ -247,7 +247,7 @@ const textBlock = (): TemplateResult => html`
`; -const Template = (args): TemplateResult => html` +const Template = (args: Args): TemplateResult => html`
html`
`; -const OptionGroupTemplate = (args): TemplateResult => html` +const OptionGroupTemplate = (args: Args): TemplateResult => html`
html`
`; -const MixedTemplate = (args): TemplateResult => html` +const MixedTemplate = (args: Args): TemplateResult => html`
html`
`; -const RequiredTemplate = (args): TemplateResult => { +const RequiredTemplate = (args: Args): TemplateResult => { const sbbFormError: SbbFormErrorElement = document.createElement('sbb-form-error'); sbbFormError.setAttribute('slot', 'error'); sbbFormError.textContent = 'This is a required field.'; @@ -364,7 +364,7 @@ const RequiredTemplate = (args): TemplateResult => { placeholder="Placeholder" ?disabled=${args.disabled} ?readonly=${args.readonly} - @change=${(event) => { + @change=${(event: Event) => { if ((event.currentTarget as HTMLInputElement).value !== '') { sbbFormError.remove(); document.getElementById('sbb-autocomplete')!.classList.remove('sbb-invalid'); diff --git a/src/components/autocomplete/autocomplete.ts b/src/components/autocomplete/autocomplete.ts index 75917e7d68d..093a486d7f7 100644 --- a/src/components/autocomplete/autocomplete.ts +++ b/src/components/autocomplete/autocomplete.ts @@ -21,7 +21,7 @@ import { setAriaComboBoxAttributes, setOverlayPosition, } from '../core/overlay'; -import type { SbbOptionElement } from '../option'; +import type { SbbOptionElement, SbbOptGroupElement } from '../option'; import style from './autocomplete.scss?lit&inline'; @@ -50,14 +50,14 @@ export class SbbAutocompleteElement extends LitElement { * The element where the autocomplete will attach; accepts both an element's id or an HTMLElement. * If not set, will search for the first 'sbb-form-field' ancestor. */ - @property() public origin: string | HTMLElement; + @property() public origin?: string | HTMLElement; /** * The input element that will trigger the autocomplete opening; accepts both an element's id or an HTMLElement. * By default, the autocomplete will open on focus, click, input or `ArrowDown` keypress of the 'trigger' element. * If not set, will search for the first 'input' child of a 'sbb-form-field' ancestor. */ - @property() public trigger: string | HTMLInputElement; + @property() public trigger?: string | HTMLInputElement; /** Whether the animation is disabled. */ @property({ attribute: 'disable-animation', reflect: true, type: Boolean }) @@ -65,7 +65,7 @@ export class SbbAutocompleteElement extends LitElement { /** Whether the icon space is preserved when no icon is set. */ @property({ attribute: 'preserve-icon-space', reflect: true, type: Boolean }) - public preserveIconSpace: boolean; + public preserveIconSpace?: boolean; /** Negative coloring variant flag. */ @property({ reflect: true, type: Boolean }) public negative = false; @@ -88,8 +88,8 @@ export class SbbAutocompleteElement extends LitElement { /** Emits whenever the `sbb-autocomplete` is closed. */ private _didClose: EventEmitter = new EventEmitter(this, SbbAutocompleteElement.events.didClose); - private _overlay: HTMLElement; - private _optionContainer: HTMLElement; + private _overlay!: HTMLElement; + private _optionContainer!: HTMLElement; /** Returns the element where autocomplete overlay is attached to. */ public get originElement(): HTMLElement { @@ -98,20 +98,20 @@ export class SbbAutocompleteElement extends LitElement { } return this._originElement; } - private _originElement: HTMLElement; + private _originElement?: HTMLElement; /** Returns the trigger element. */ public get triggerElement(): HTMLInputElement { - return this._triggerElement; + return this._triggerElement as HTMLInputElement; } - private _triggerElement: HTMLInputElement; + private _triggerElement?: HTMLInputElement; - private _triggerEventsController: AbortController; - private _openPanelEventsController: AbortController; + private _triggerEventsController!: AbortController; + private _openPanelEventsController!: AbortController; private _overlayId = `sbb-autocomplete-${++nextId}`; private _activeItemIndex = -1; private _didLoad = false; - private _isPointerDownEventOnMenu: boolean; + private _isPointerDownEventOnMenu: boolean = false; private _abort = new ConnectedAbortController(this); /** @@ -162,8 +162,8 @@ export class SbbAutocompleteElement extends LitElement { /** Removes trigger click listener on trigger change. */ private _resetOriginClickListener( - newValue: string | HTMLElement, - oldValue: string | HTMLElement, + newValue?: string | HTMLElement, + oldValue?: string | HTMLElement, ): void { if (newValue !== oldValue) { this._componentSetup(); @@ -172,8 +172,8 @@ export class SbbAutocompleteElement extends LitElement { /** Removes trigger click listener on trigger change. */ private _resetTriggerClickListener( - newValue: string | HTMLElement, - oldValue: string | HTMLElement, + newValue?: string | HTMLElement, + oldValue?: string | HTMLElement, ): void { if (newValue !== oldValue) { this._componentSetup(); @@ -193,7 +193,7 @@ export class SbbAutocompleteElement extends LitElement { .forEach((option) => (option.selected = false)); // Set the option value - this.triggerElement.value = target.value; + this.triggerElement.value = target.value as string; // Manually trigger the change events this.triggerElement.dispatchEvent(new Event('change', { bubbles: true })); @@ -202,8 +202,11 @@ export class SbbAutocompleteElement extends LitElement { this.close(); } - private _onOptionClick(event): void { - if (event.target?.tagName !== 'SBB-OPTION' || event.target.disabled) { + private _onOptionClick(event: MouseEvent): void { + if ( + (event.target as Element).tagName !== 'SBB-OPTION' || + (event.target as SbbOptionElement).disabled + ) { return; } this.close(); @@ -226,7 +229,7 @@ export class SbbAutocompleteElement extends LitElement { this.addEventListener('optionSelectionChange', (e: CustomEvent) => this._onOptionSelected(e), { signal, }); - this.addEventListener('click', (e) => this._onOptionClick(e), { signal }); + this.addEventListener('click', (e: MouseEvent) => this._onOptionClick(e), { signal }); } protected override willUpdate(changedProperties: PropertyValues): void { @@ -249,9 +252,9 @@ export class SbbAutocompleteElement extends LitElement { private _syncNegative(): void { this.querySelectorAll?.('sbb-divider').forEach((divider) => (divider.negative = this.negative)); - this.querySelectorAll?.('sbb-option, sbb-optgroup').forEach((element: HTMLElement) => - toggleDatasetEntry(element, 'negative', this.negative), - ); + this.querySelectorAll?.( + 'sbb-option, sbb-optgroup', + ).forEach((element) => toggleDatasetEntry(element, 'negative', this.negative)); } public override disconnectedCallback(): void { @@ -271,7 +274,7 @@ export class SbbAutocompleteElement extends LitElement { toggleDatasetEntry( this, 'optionPanelOriginBorderless', - this.closest('sbb-form-field')?.hasAttribute('borderless'), + !!this.closest?.('sbb-form-field')?.hasAttribute('borderless'), ); this._bindTo(this._getTriggerElement()); @@ -282,10 +285,10 @@ export class SbbAutocompleteElement extends LitElement { * @returns 'origin' or the first 'sbb-form-field' ancestor. */ private _findOriginElement(): HTMLElement { - let result: HTMLElement; + let result: HTMLElement | undefined | null; if (!this.origin) { - result = this.closest?.('sbb-form-field')?.shadowRoot.querySelector('#overlay-anchor'); + result = this.closest?.('sbb-form-field')?.shadowRoot?.querySelector?.('#overlay-anchor'); } else { result = findReferencedElement(this.origin); } @@ -364,7 +367,7 @@ export class SbbAutocompleteElement extends LitElement { this._overlay, this.originElement, this._optionContainer, - this.shadowRoot.querySelector('.sbb-autocomplete__container'), + this.shadowRoot!.querySelector('.sbb-autocomplete__container')!, this, ); } @@ -545,7 +548,7 @@ export class SbbAutocompleteElement extends LitElement { @animationend=${this._onAnimationEnd} class="sbb-autocomplete__panel" ?data-open=${this._state === 'opened' || this._state === 'opening'} - ${ref((overlayRef) => (this._overlay = overlayRef as HTMLElement))} + ${ref((overlayRef?: Element) => (this._overlay = overlayRef as HTMLElement))} >
{ }); it('keyboard navigation', async () => { - const first: SbbBreadcrumbElement = document.querySelector('#breadcrumb-0'); - const second: SbbBreadcrumbElement = document.querySelector('#breadcrumb-1'); - const third: SbbBreadcrumbElement = document.querySelector('#breadcrumb-2'); + const first: SbbBreadcrumbElement = + document.querySelector('#breadcrumb-0')!; + const second: SbbBreadcrumbElement = + document.querySelector('#breadcrumb-1')!; + const third: SbbBreadcrumbElement = + document.querySelector('#breadcrumb-2')!; first.focus(); await sendKeys({ down: 'ArrowRight' }); - expect(document.activeElement.id).to.be.equal(second.id); + expect(document.activeElement?.id).to.be.equal(second.id); await sendKeys({ down: 'ArrowRight' }); - expect(document.activeElement.id).to.be.equal(third.id); + expect(document.activeElement?.id).to.be.equal(third.id); }); }); @@ -60,10 +63,12 @@ describe('sbb-breadcrumb-group', () => { `); await waitForLitRender(breadcrumbGroup); - ellipsisListItemElement = breadcrumbGroup.shadowRoot.querySelector( + ellipsisListItemElement = breadcrumbGroup.shadowRoot!.querySelector( '#sbb-breadcrumb-group-ellipsis', - ); - ellipsisButton = breadcrumbGroup.shadowRoot.querySelector('#sbb-breadcrumb-ellipsis'); + )!; + ellipsisButton = breadcrumbGroup.shadowRoot!.querySelector( + '#sbb-breadcrumb-ellipsis', + )!; }); it('renders', async () => { @@ -71,7 +76,7 @@ describe('sbb-breadcrumb-group', () => { expect(ellipsisButton).not.to.be.null; // only three list items are displayed, and the middle one is the ellipsis button - const li = breadcrumbGroup.shadowRoot.querySelectorAll('li'); + const li = breadcrumbGroup.shadowRoot!.querySelectorAll('li'); expect(li).not.to.be.null; expect(li.length).to.be.equal(3); expect(li[1]).dom.to.be.equal(` @@ -84,7 +89,7 @@ describe('sbb-breadcrumb-group', () => { `); // only two slots are displayed, and the second is the last one - const slots = breadcrumbGroup.shadowRoot.querySelectorAll('li > slot'); + const slots = breadcrumbGroup.shadowRoot!.querySelectorAll('li > slot'); expect(slots.length).to.be.equal(2); expect(slots[0]).to.have.attribute('name', 'breadcrumb-0'); expect(slots[1]).to.have.attribute('name', 'breadcrumb-6'); @@ -93,21 +98,23 @@ describe('sbb-breadcrumb-group', () => { it('keyboard navigation with ellipsis', async () => { expect(ellipsisListItemElement).not.to.be.null; expect(ellipsisButton).not.to.be.null; - const first: SbbBreadcrumbElement = document.querySelector('#breadcrumb-0'); - const last: SbbBreadcrumbElement = document.querySelector('#breadcrumb-6'); + const first: SbbBreadcrumbElement = + document.querySelector('#breadcrumb-0')!; + const last: SbbBreadcrumbElement = + document.querySelector('#breadcrumb-6')!; first.focus(); - expect(document.activeElement.id).to.be.equal(first.id); + expect(document.activeElement?.id).to.be.equal(first.id); await sendKeys({ down: 'ArrowRight' }); - expect(document.activeElement.id).to.be.equal(breadcrumbGroup.id); - expect(breadcrumbGroup.shadowRoot.activeElement.id).to.be.equal(ellipsisButton.id); + expect(document.activeElement?.id).to.be.equal(breadcrumbGroup.id); + expect(breadcrumbGroup.shadowRoot?.activeElement?.id).to.be.equal(ellipsisButton.id); await sendKeys({ down: 'ArrowRight' }); - expect(document.activeElement.id).to.be.equal(last.id); + expect(document.activeElement?.id).to.be.equal(last.id); await sendKeys({ down: 'ArrowRight' }); - expect(document.activeElement.id).to.be.equal(first.id); + expect(document.activeElement?.id).to.be.equal(first.id); }); it('expand breadcrumbs with ellipsis', async () => { @@ -118,10 +125,10 @@ describe('sbb-breadcrumb-group', () => { await waitForLitRender(ellipsisListItemElement); await waitForCondition(() => changeSpy.events.length === 1); - ellipsisListItemElement = breadcrumbGroup.shadowRoot.querySelector( + ellipsisListItemElement = breadcrumbGroup.shadowRoot!.querySelector( '#sbb-breadcrumb-group-ellipsis', - ); - ellipsisButton = breadcrumbGroup.shadowRoot.querySelector('#sbb-breadcrumb-ellipsis'); + )!; + ellipsisButton = breadcrumbGroup.shadowRoot!.querySelector('#sbb-breadcrumb-ellipsis')!; expect(ellipsisListItemElement).to.be.null; expect(ellipsisButton).to.be.null; }); @@ -135,20 +142,20 @@ describe('sbb-breadcrumb-group', () => { await waitForLitRender(breadcrumbGroup); // Then focus should be on first breadcrumb - expect(document.activeElement.id).to.be.equal('breadcrumb-1'); + expect(document.activeElement?.id).to.be.equal('breadcrumb-1'); // When blurring the focus (document.activeElement as HTMLElement).blur(); // Then the body should be focused - expect(document.activeElement.tagName).to.be.equal('BODY'); + expect(document.activeElement?.tagName).to.be.equal('BODY'); // When triggering a slotChange by removing a breadcrumb - document.getElementById('breadcrumb-6').remove(); + document.getElementById('breadcrumb-6')?.remove(); await waitForLitRender(breadcrumbGroup); // Then the body should still be focused - expect(document.activeElement.tagName).to.be.equal('BODY'); + expect(document.activeElement?.tagName).to.be.equal('BODY'); }); it('should remove expand button when too less breadcrumbs available', async () => { @@ -162,10 +169,10 @@ describe('sbb-breadcrumb-group', () => { await waitForLitRender(breadcrumbGroup); - ellipsisListItemElement = breadcrumbGroup.shadowRoot.querySelector( + ellipsisListItemElement = breadcrumbGroup.shadowRoot!.querySelector( '#sbb-breadcrumb-group-ellipsis', - ); - ellipsisButton = breadcrumbGroup.shadowRoot.querySelector('#sbb-breadcrumb-ellipsis'); + )!; + ellipsisButton = breadcrumbGroup.shadowRoot!.querySelector('#sbb-breadcrumb-ellipsis')!; expect(ellipsisListItemElement).to.be.null; expect(ellipsisButton).to.be.null; }); diff --git a/src/components/breadcrumb/breadcrumb-group/breadcrumb-group.stories.ts b/src/components/breadcrumb/breadcrumb-group/breadcrumb-group.stories.ts index e9d3872c851..8b8c48b4c35 100644 --- a/src/components/breadcrumb/breadcrumb-group/breadcrumb-group.stories.ts +++ b/src/components/breadcrumb/breadcrumb-group/breadcrumb-group.stories.ts @@ -4,6 +4,7 @@ import { html, TemplateResult } from 'lit'; import { sbbSpread } from '../../core/dom'; +import type { SbbBreadcrumbGroupElement } from './breadcrumb-group'; import readme from './readme.md?raw'; import '../../button'; @@ -12,8 +13,8 @@ import '../breadcrumb'; const addBreadcrumb = (event: Event): void => { const breadcrumbGroup = (event.target as HTMLElement) - .closest('.container') - .querySelector('sbb-breadcrumb-group'); + .closest('.container')! + .querySelector('sbb-breadcrumb-group')!; const breadcrumb = document.createElement('sbb-breadcrumb'); breadcrumb.setAttribute('href', '/'); breadcrumb.textContent = 'Link ' + breadcrumbGroup.children.length; @@ -22,8 +23,8 @@ const addBreadcrumb = (event: Event): void => { const removeBreadcrumb = (event: Event): void => { const breadcrumbGroup = (event.target as HTMLElement) - .closest('.container') - .querySelector('sbb-breadcrumb-group'); + .closest('.container')! + .querySelector('sbb-breadcrumb-group')!; if (breadcrumbGroup.children.length > 1) { breadcrumbGroup.removeChild(breadcrumbGroup.lastElementChild!); } @@ -116,18 +117,18 @@ const defaultArgs: Args = { 'icon-name': undefined, }; -const breadcrumbTemplate = (args, text: string, i: number): TemplateResult => html` +const breadcrumbTemplate = (args: Args, text: string, i: number): TemplateResult => html` ${text} ${i} `; -const createBreadcrumbs = ({ numberOfBreadcrumbs, text, ...args }): TemplateResult => html` +const createBreadcrumbs = ({ numberOfBreadcrumbs, text, ...args }: Args): TemplateResult => html` ${new Array(numberOfBreadcrumbs - 1) .fill(undefined) .map((_, i) => breadcrumbTemplate(args, text, i + 1))} `; -const Template = (args): TemplateResult => html` +const Template = (args: Args): TemplateResult => html`
${createBreadcrumbs(args)} diff --git a/src/components/breadcrumb/breadcrumb-group/breadcrumb-group.ts b/src/components/breadcrumb/breadcrumb-group/breadcrumb-group.ts index dc3b62d4470..48da3876a15 100644 --- a/src/components/breadcrumb/breadcrumb-group/breadcrumb-group.ts +++ b/src/components/breadcrumb/breadcrumb-group/breadcrumb-group.ts @@ -119,7 +119,7 @@ export class SbbBreadcrumbGroupElement extends SlotChildObserver(LitElement) { private _focusNextCollapsed(evt: KeyboardEvent): void { const arrayCollapsed: SbbBreadcrumbElement[] = [ this._breadcrumbs[0], - this.shadowRoot.querySelector('#sbb-breadcrumb-ellipsis') as SbbBreadcrumbElement, + this.shadowRoot!.querySelector('#sbb-breadcrumb-ellipsis') as SbbBreadcrumbElement, this._breadcrumbs[this._breadcrumbs.length - 1], ]; this._focusNext(evt, arrayCollapsed); @@ -130,7 +130,7 @@ export class SbbBreadcrumbGroupElement extends SlotChildObserver(LitElement) { breadcrumbs: SbbBreadcrumbElement[] = this._breadcrumbs, ): void { const current: number = breadcrumbs.findIndex( - (e) => e === document.activeElement || e === this.shadowRoot.activeElement, + (e) => e === document.activeElement || e === this.shadowRoot!.activeElement, ); const nextIndex: number = getNextElementIndex(evt, current, breadcrumbs.length); breadcrumbs[nextIndex]?.focus(); diff --git a/src/components/breadcrumb/breadcrumb/breadcrumb.e2e.ts b/src/components/breadcrumb/breadcrumb/breadcrumb.e2e.ts index 58f34ad42f9..8ee615895f9 100644 --- a/src/components/breadcrumb/breadcrumb/breadcrumb.e2e.ts +++ b/src/components/breadcrumb/breadcrumb/breadcrumb.e2e.ts @@ -28,6 +28,6 @@ describe('sbb-breadcrumb', () => { element.focus(); await waitForLitRender(element); - expect(document.activeElement.id).to.be.equal('focus-id'); + expect(document.activeElement?.id).to.be.equal('focus-id'); }); }); diff --git a/src/components/breadcrumb/breadcrumb/breadcrumb.ts b/src/components/breadcrumb/breadcrumb/breadcrumb.ts index 893dacf578a..d6e22d435d2 100644 --- a/src/components/breadcrumb/breadcrumb/breadcrumb.ts +++ b/src/components/breadcrumb/breadcrumb/breadcrumb.ts @@ -28,13 +28,13 @@ export class SbbBreadcrumbElement extends SlotChildObserver(LitElement) { public static override styles: CSSResultGroup = style; /** The href value you want to link to. */ - @property() public href: string | undefined; + @property() public href?: string; /** Where to display the linked URL. */ - @property() public target?: LinkTargetType | string | undefined; + @property() public target?: LinkTargetType | string; /** The relationship of the linked URL as space-separated link types. */ - @property() public rel?: string | undefined; + @property() public rel?: string; /** Whether the browser will show the download dialog on click. */ @property({ type: Boolean }) public download?: boolean; @@ -65,9 +65,8 @@ export class SbbBreadcrumbElement extends SlotChildObserver(LitElement) { } protected override checkChildren(): void { - this._hasText = this.shadowRoot - .querySelector('slot:not([name])') - .assignedNodes() + this._hasText = !!this.shadowRoot!.querySelector('slot:not([name])') + ?.assignedNodes() .some((n) => !!n.textContent?.trim()); } diff --git a/src/components/breadcrumb/breadcrumb/readme.md b/src/components/breadcrumb/breadcrumb/readme.md index b4796f902e2..7895bc806a1 100644 --- a/src/components/breadcrumb/breadcrumb/readme.md +++ b/src/components/breadcrumb/breadcrumb/readme.md @@ -39,13 +39,13 @@ By default, the `sbb-breadcrumb-group` component sets `aria-current="page"` on t ## 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 \| undefined` | | Where to display the linked URL. | -| `rel` | `rel` | public | `string \| undefined \| 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. | -| `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. | +| 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. | +| `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. | ## Slots diff --git a/src/components/button/button.e2e.ts b/src/components/button/button.e2e.ts index f1c3646c7f0..628ee53ac93 100644 --- a/src/components/button/button.e2e.ts +++ b/src/components/button/button.e2e.ts @@ -97,7 +97,7 @@ describe('sbb-button', () => { element.focus(); await waitForLitRender(element); - expect(document.activeElement.id).to.be.equal('focus-id'); + expect(document.activeElement?.id).to.be.equal('focus-id'); }); }); }); diff --git a/src/components/button/button.ts b/src/components/button/button.ts index 202a1441b6b..72d538a270a 100644 --- a/src/components/button/button.ts +++ b/src/components/button/button.ts @@ -54,7 +54,7 @@ export class SbbButtonElement extends LitElement implements LinkButtonProperties @property({ reflect: true, type: Boolean }) public negative = false; /** Size variant, either l or m. */ - @property({ reflect: true }) public size?: SbbButtonSize = 'l'; + @property({ reflect: true }) public size: SbbButtonSize = 'l'; /** * Set this property to true if you want only a visual representation of a @@ -70,25 +70,25 @@ export class SbbButtonElement extends LitElement implements LinkButtonProperties @property({ attribute: 'icon-name' }) public iconName?: string; /** The href value you want to link to (if it is present, button becomes a link). */ - @property() public href: string | undefined; + @property() public href?: string; /** Where to display the linked URL. */ - @property() public target?: LinkTargetType | string | undefined; + @property() public target?: LinkTargetType | string; /** The relationship of the linked URL as space-separated link types. */ - @property() public rel?: string | undefined; + @property() public rel?: string; /** Whether the browser will show the download dialog on click. */ @property({ type: Boolean }) public download?: boolean; /** The type attribute to use for the button. */ - @property() public type: ButtonType | undefined; + @property() public type?: ButtonType; /** Whether the button is disabled. */ @property({ reflect: true, type: Boolean }) public disabled = false; /** The name attribute to use for the button. */ - @property({ reflect: true }) public name: string | undefined; + @property({ reflect: true }) public name?: string; /** The value attribute to use for the button. */ @property() public value?: string; diff --git a/src/components/button/readme.md b/src/components/button/readme.md index 90485ebfc56..d55793357e4 100644 --- a/src/components/button/readme.md +++ b/src/components/button/readme.md @@ -82,12 +82,12 @@ Use the accessibility properties in case of an icon-only button to describe the | ---------- | ----------- | ------- | ------------------------------------------------------------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `variant` | `variant` | public | `\| 'primary' \| 'secondary' \| 'tertiary' \| 'transparent'` | `'primary'` | Variant of the button, like primary, secondary etc. | | `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. | -| `size` | `size` | public | `SbbButtonSize \| undefined` | `'l'` | Size variant, either l or m. | +| `size` | `size` | public | `SbbButtonSize` | `'l'` | Size variant, either l or m. | | `isStatic` | `is-static` | public | `boolean` | `false` | Set this property to true if you want only a visual representation of a button, but no interaction (a span instead of a link/button will be rendered). | | `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 (if it is present, button becomes a link). | -| `target` | `target` | public | `LinkTargetType \| string \| undefined \| undefined` | | Where to display the linked URL. | -| `rel` | `rel` | public | `string \| undefined \| undefined` | | The relationship of the linked URL as space-separated link types. | +| `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. | | `type` | `type` | public | `ButtonType \| undefined` | | The type attribute to use for the button. | | `disabled` | `disabled` | public | `boolean` | `false` | Whether the button is disabled. | diff --git a/src/components/calendar/calendar.e2e.ts b/src/components/calendar/calendar.e2e.ts index 01e43b9bf5b..51dabfe7309 100644 --- a/src/components/calendar/calendar.e2e.ts +++ b/src/components/calendar/calendar.e2e.ts @@ -23,35 +23,35 @@ describe('sbb-calendar', () => { }); it('highlights current day', async () => { - const currentDayButton = element.shadowRoot.querySelector('button[data-day="10 1 2023"]'); + const currentDayButton = element.shadowRoot!.querySelector('button[data-day="10 1 2023"]'); expect(currentDayButton).to.have.class('sbb-calendar__cell-current'); }); it('renders and navigates to next month', async () => { - let day = element.shadowRoot.querySelector('.sbb-calendar__day'); + let day = element.shadowRoot!.querySelector('.sbb-calendar__day') as HTMLButtonElement; expect(await day.getAttribute('data-day')).to.be.equal('1 1 2023'); - const nextMonthButton: HTMLElement = element.shadowRoot.querySelector( + const nextMonthButton: HTMLElement = element.shadowRoot!.querySelector( '#sbb-calendar__controls-next', - ); + )!; nextMonthButton.click(); await waitForLitRender(element); - day = element.shadowRoot.querySelector('.sbb-calendar__day'); + day = element.shadowRoot!.querySelector('.sbb-calendar__day') as HTMLButtonElement; expect(await day.getAttribute('data-day')).to.be.equal('1 2 2023'); }); it('renders and navigates to previous month', async () => { - let day = element.shadowRoot.querySelector('.sbb-calendar__day'); + let day = element.shadowRoot!.querySelector('.sbb-calendar__day') as HTMLButtonElement; expect(await day.getAttribute('data-day')).to.be.equal('1 1 2023'); - const nextMonthButton: HTMLElement = element.shadowRoot.querySelector( + const nextMonthButton: HTMLElement = element.shadowRoot!.querySelector( '#sbb-calendar__controls-previous', - ); + )!; nextMonthButton.click(); await waitForLitRender(element); - day = element.shadowRoot.querySelector('.sbb-calendar__day'); + day = element.shadowRoot!.querySelector('.sbb-calendar__day') as HTMLButtonElement; expect(await day.getAttribute('data-day')).to.be.equal('1 12 2022'); }); @@ -59,17 +59,17 @@ describe('sbb-calendar', () => { element.max = 1674946800; await waitForLitRender(element); - let day = element.shadowRoot.querySelector('.sbb-calendar__day'); + let day = element.shadowRoot!.querySelector('.sbb-calendar__day') as HTMLButtonElement; expect(await day.getAttribute('data-day')).to.be.equal('1 1 2023'); - const nextMonthButton: HTMLElement = element.shadowRoot.querySelector( + const nextMonthButton: HTMLElement = element.shadowRoot!.querySelector( '#sbb-calendar__controls-next', - ); + )!; expect(nextMonthButton).to.have.attribute('disabled'); nextMonthButton.click(); await waitForLitRender(element); - day = element.shadowRoot.querySelector('.sbb-calendar__day'); + day = element.shadowRoot!.querySelector('.sbb-calendar__day') as HTMLButtonElement; expect(await day.getAttribute('data-day')).to.be.equal('1 1 2023'); }); @@ -77,27 +77,27 @@ describe('sbb-calendar', () => { element.min = 1673737200; await waitForLitRender(element); - let day = element.shadowRoot.querySelector('.sbb-calendar__day'); + let day = element.shadowRoot!.querySelector('.sbb-calendar__day') as HTMLButtonElement; expect(await day.getAttribute('data-day')).to.be.equal('1 1 2023'); - const nextMonthButton = element.shadowRoot.querySelector( + const nextMonthButton = element.shadowRoot!.querySelector( '#sbb-calendar__controls-previous', ) as HTMLElement; expect(nextMonthButton).to.have.attribute('disabled'); nextMonthButton.click(); await waitForLitRender(element); - day = element.shadowRoot.querySelector('.sbb-calendar__day'); + day = element.shadowRoot!.querySelector('.sbb-calendar__day') as HTMLButtonElement; expect(await day.getAttribute('data-day')).to.be.equal('1 1 2023'); }); it('selects a different date', async () => { const selectedSpy = new EventSpy(SbbCalendarElement.events.dateSelected); - const selectedDate = element.shadowRoot.querySelector('button[data-day="15 1 2023"]'); + const selectedDate = element.shadowRoot!.querySelector('button[data-day="15 1 2023"]'); expect(selectedDate).to.have.class('sbb-calendar__selected'); - const newSelectedDate = element.shadowRoot.querySelector( + const newSelectedDate = element.shadowRoot!.querySelector( 'button[data-day="18 1 2023"]', ) as HTMLElement; expect(newSelectedDate).not.to.have.class('sbb-calendar__selected'); @@ -115,7 +115,7 @@ describe('sbb-calendar', () => { element.max = 1674946800; await waitForLitRender(element); - const day = element.shadowRoot.querySelector('button[data-day="30 1 2023"]') as HTMLElement; + const day = element.shadowRoot!.querySelector('button[data-day="30 1 2023"]') as HTMLElement; expect(day).to.have.attribute('disabled'); expect(day).not.to.have.class('sbb-calendar__selected'); day.click(); @@ -126,19 +126,22 @@ describe('sbb-calendar', () => { }); it('changes to year and month selection views', async () => { - const yearSelectionButton: HTMLElement = element.shadowRoot.querySelector( + const yearSelectionButton: HTMLElement = element.shadowRoot!.querySelector( '#sbb-calendar__date-selection', + )!; + let animationSpy = new EventSpy( + 'animationend', + element.shadowRoot!.querySelector('table') as HTMLTableElement, ); - let animationSpy = new EventSpy('animationend', element.shadowRoot.querySelector('table')); expect(yearSelectionButton).not.to.be.null; yearSelectionButton.click(); await waitForLitRender(element); await waitForCondition(() => animationSpy.events.length >= 1); - const yearSelection: HTMLElement = element.shadowRoot.querySelector( + const yearSelection: HTMLElement = element.shadowRoot!.querySelector( '#sbb-calendar__year-selection', - ); + )!; expect(yearSelection).not.to.be.null; expect(yearSelection).dom.to.be.equal(`