From 7ff035889e0d3cb68a98d9e025c5997efaeacdd8 Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Fri, 24 Nov 2023 20:57:11 +0100 Subject: [PATCH 01/65] feat: add show/hide animation for dialog header --- src/components/dialog/dialog.scss | 45 ++++++++++-- src/components/dialog/dialog.ts | 112 ++++++++++++++++++++++++------ 2 files changed, 133 insertions(+), 24 deletions(-) diff --git a/src/components/dialog/dialog.scss b/src/components/dialog/dialog.scss index db56e3c232..553bd2e4ab 100644 --- a/src/components/dialog/dialog.scss +++ b/src/components/dialog/dialog.scss @@ -25,6 +25,8 @@ --sbb-dialog-backdrop-pointer-events: none; --sbb-dialog-backdrop-color: transparent; --sbb-dialog-header-padding-block: var(--sbb-spacing-responsive-s) 0; + --sbb-dialog-header-transition-duration: var(--sbb-animation-duration-6x); + --sbb-dialog-header-margin-block-start: 0; --sbb-dialog-footer-border: var(--sbb-border-width-1x) solid var(--sbb-color-cloud); position: fixed; @@ -59,6 +61,30 @@ } } +:host([data-hide-header]) { + --sbb-dialog-header-margin-block-start: calc(var(--sbb-dialog-header-height) * -1); + + // Hide transition + --sbb-dialog-header-transition-timing: cubic-bezier(0.4, 0, 1, 1); +} + +:host(:not([data-hide-header])) { + // Show transition + --sbb-dialog-header-transition-timing: cubic-bezier(0, 0, 0.2, 1); +} + +:host([data-hide-header]) { + --sbb-dialog-header-margin-block-start: calc(var(--sbb-dialog-header-height) * -1); + + // Hide transition + --sbb-dialog-header-transition-timing: cubic-bezier(0.4, 0, 1, 1); +} + +:host(:not([data-hide-header])) { + // Show transition + --sbb-dialog-header-transition-timing: cubic-bezier(0, 0, 0.2, 1); +} + :host([data-fullscreen]) { --sbb-dialog-backdrop-color: transparent; --sbb-dialog-max-width: 100%; @@ -198,6 +224,16 @@ pointer-events: all; } + // Apply show/hide animation unless it has a visible focus within. + &:not([data-has-visible-focus]:focus-within) { + margin-block-start: var(--sbb-dialog-header-margin-block-start); + transition: { + property: margin, box-shadow; + duration: var(--sbb-dialog-header-transition-duration); + timing-function: var(--sbb-dialog-header-transition-timing); + } + } + :host([data-fullscreen]) & { position: fixed; width: var(--sbb-dialog-width); @@ -206,6 +242,11 @@ padding-block-start: var(--sbb-spacing-responsive-xs); } + &[data-has-visible-focus]:focus-within, + :host([data-overflows]:not([data-fullscreen], [negative], [data-hide-header])) & { + @include sbb.shadow-level-9-soft; + } + @include sbb.mq($from: medium) { border-radius: var(--sbb-dialog-border-radius) var(--sbb-dialog-border-radius) 0 0; } @@ -253,15 +294,11 @@ :host(:not([data-slot-names~='action-group'])) & { display: none; } -} -// stylelint-disable selector-not-notation -:is(.sbb-dialog__header, .sbb-dialog__footer) { :host([data-overflows]:not([data-fullscreen], [negative])) & { @include sbb.shadow-level-9-soft; } } -// stylelint-enable selector-not-notation // It is necessary to use animations with keyframes instead of transitions in order not to alter // the default `display: block` of the modal otherwise it causes several problems, diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts index 84f946c608..a44d876552 100644 --- a/src/components/dialog/dialog.ts +++ b/src/components/dialog/dialog.ts @@ -4,7 +4,12 @@ import { customElement, property } from 'lit/decorators.js'; import { ref } from 'lit/directives/ref.js'; import { html, unsafeStatic } from 'lit/static-html.js'; -import { SbbFocusHandler, IS_FOCUSABLE_QUERY, setModalityOnNextFocus } from '../core/a11y'; +import { + SbbFocusHandler, + IS_FOCUSABLE_QUERY, + setModalityOnNextFocus, + sbbInputModalityDetector, +} from '../core/a11y'; import { SbbLanguageController, SbbSlotStateController } from '../core/controllers'; import { hostContext, isValidAttribute, SbbScrollHandler, setAttribute } from '../core/dom'; import { EventEmitter } from '../core/eventing'; @@ -137,15 +142,17 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { ); private _dialog!: HTMLDivElement; - private _dialogWrapperElement!: HTMLElement; + private _dialogHeaderElement!: HTMLElement; private _dialogContentElement!: HTMLElement; private _dialogCloseElement?: HTMLElement; private _dialogController!: AbortController; - private _windowEventsController!: AbortController; + private _openDialogController!: AbortController; private _focusHandler = new SbbFocusHandler(); private _scrollHandler = new SbbScrollHandler(); private _returnValue: any; private _isPointerDownEventOnDialog: boolean = false; + private _overflows: boolean; + private _lastScroll = 0; private _dialogId = `sbb-dialog-${nextId++}`; // Last element which had focus before the dialog was opened. @@ -212,6 +219,42 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { } } + private _onContentScroll(): void { + const hasVisibleHeader = this.dataset.hideHeader === undefined; + const dialogHeaderHeight = this._dialogHeaderElement.clientHeight; + + // Check whether hiding the header would make the scrollbar disappear + // and prevent the hiding animation if so. + if ( + hasVisibleHeader && + this._dialogContentElement.clientHeight + dialogHeaderHeight >= + this._dialogContentElement.scrollHeight + ) { + return; + } + + const currentScroll = this._dialogContentElement.scrollTop; + if ( + Math.round(currentScroll + this._dialogContentElement.clientHeight) >= + this._dialogContentElement.scrollHeight + ) { + return; + } + // Check whether is scrolling down or up. + if (currentScroll > 0 && this._lastScroll < currentScroll) { + // Scrolling down + if (hasVisibleHeader) { + this.style.setProperty('--sbb-dialog-header-height', `${dialogHeaderHeight}px`); + } + toggleDatasetEntry(this, 'hideHeader', true); + } else { + // Scrolling up + toggleDatasetEntry(this, 'hideHeader', false); + } + // `currentScroll` can be negative, e.g. on mobile; this is not allowed. + this._lastScroll = currentScroll <= 0 ? 0 : currentScroll; + } + public override connectedCallback(): void { super.connectedCallback(); this._state = this._state || 'closed'; @@ -234,7 +277,7 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { public override disconnectedCallback(): void { super.disconnectedCallback(); this._dialogController?.abort(); - this._windowEventsController?.abort(); + this._openDialogController?.abort(); this._focusHandler.disconnect(); this._dialogContentResizeObserver.disconnect(); this._removeInstanceFromGlobalCollection(); @@ -245,8 +288,8 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { dialogRefs.splice(dialogRefs.indexOf(this as SbbDialogElement), 1); } - private _attachWindowEvents(): void { - this._windowEventsController = new AbortController(); + private _attachOpenDialogEvents(): void { + this._openDialogController = new AbortController(); // Remove dialog label as soon as it is not needed anymore to prevent accessing it with browse mode. window.addEventListener( 'keydown', @@ -255,12 +298,39 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { await this._onKeydownEvent(event); }, { - signal: this._windowEventsController.signal, + signal: this._openDialogController.signal, }, ); window.addEventListener('click', () => this._removeAriaLiveRefContent(), { - signal: this._windowEventsController.signal, + signal: this._openDialogController.signal, }); + // If the content overflows, apply the header animation on scroll. + if (this._overflows) { + this._dialogContentElement?.addEventListener('scroll', () => this._onContentScroll(), { + passive: true, + signal: this._openDialogController.signal, + }); + Array.from(this._dialogHeaderElement.querySelectorAll('sbb-button'))?.forEach((el) => { + el.addEventListener( + 'focusin', + () => { + toggleDatasetEntry( + this._dialogHeaderElement, + 'hasVisibleFocus', + sbbInputModalityDetector.mostRecentModality === 'keyboard', + ); + }, + { signal: this._openDialogController.signal }, + ); + el.addEventListener( + 'blur', + () => { + toggleDatasetEntry(this._dialogHeaderElement, 'hasVisibleFocus', false); + }, + { signal: this._openDialogController.signal }, + ); + }); + } } // Check if the pointerdown event target is triggered on the dialog. @@ -319,10 +389,11 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { setTimeout(() => this._setAriaLiveRefContent()); this._focusHandler.trap(this); this._dialogContentResizeObserver.observe(this._dialogContentElement); - this._attachWindowEvents(); + this._attachOpenDialogEvents(); } else if (event.animationName === 'close' && this._state === 'closing') { + toggleDatasetEntry(this, 'hideHeader', false); + this._dialogContentElement.scrollTo(0, 0); this._state = 'closed'; - this._dialogWrapperElement.querySelector('.sbb-dialog__content')?.scrollTo(0, 0); removeInertMechanism(); setModalityOnNextFocus(this._lastFocusedElement); // Manually focus last focused element @@ -331,7 +402,7 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { returnValue: this._returnValue, closeTarget: this._dialogCloseElement, }); - this._windowEventsController?.abort(); + this._openDialogController?.abort(); this._focusHandler.disconnect(); this._dialogContentResizeObserver.disconnect(); this._removeInstanceFromGlobalCollection(); @@ -367,10 +438,12 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { } private _setOverflowAttribute(): void { - this.toggleAttribute( - 'data-overflows', - this._dialogContentElement.scrollHeight > this._dialogContentElement.clientHeight, - ); + this._overflows = + this._dialogContentElement.scrollHeight > this._dialogContentElement.clientHeight; + if (!this._overflows && this.dataset.hideHeader === '') { + this.toggleAttribute('data-hide-header', false); + } + this.toggleAttribute('data-overflows', this._overflows); } protected override render(): TemplateResult { @@ -403,7 +476,10 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { /* eslint-enable lit/binding-positions */ const dialogHeader = html` -
+
(this._dialogHeaderElement = dialogHeaderrRef as HTMLElement))} + > ${this.titleBackButton ? backButton : nothing} this._closeOnSbbDialogCloseClick(event)} class="sbb-dialog__wrapper" - ${ref( - (dialogWrapperRef?: Element) => - (this._dialogWrapperElement = dialogWrapperRef as HTMLElement), - )} > ${dialogHeader}
Date: Sat, 25 Nov 2023 21:41:36 +0100 Subject: [PATCH 02/65] feat: make mobile animation smoother --- src/components/dialog/dialog.scss | 39 +++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/components/dialog/dialog.scss b/src/components/dialog/dialog.scss index 553bd2e4ab..88024534fe 100644 --- a/src/components/dialog/dialog.scss +++ b/src/components/dialog/dialog.scss @@ -27,6 +27,8 @@ --sbb-dialog-header-padding-block: var(--sbb-spacing-responsive-s) 0; --sbb-dialog-header-transition-duration: var(--sbb-animation-duration-6x); --sbb-dialog-header-margin-block-start: 0; + --sbb-dialog-content-transition: transform var(--sbb-dialog-header-transition-duration) + var(--sbb-dialog-animation-easing); --sbb-dialog-footer-border: var(--sbb-border-width-1x) solid var(--sbb-color-cloud); position: fixed; @@ -74,15 +76,31 @@ } :host([data-hide-header]) { + // Hide transition + --sbb-dialog-content-transition: transform var(--sbb-dialog-header-transition-duration) + var(--sbb-dialog-animation-easing); --sbb-dialog-header-margin-block-start: calc(var(--sbb-dialog-header-height) * -1); +} +:host(:not([data-hide-header])) { + // Show transition + --sbb-dialog-content-transition: transform var(--sbb-dialog-header-transition-duration) + var(--sbb-dialog-animation-easing), + margin 0s var(--sbb-dialog-header-transition-duration); +} + +:host([data-hide-header]) { // Hide transition - --sbb-dialog-header-transition-timing: cubic-bezier(0.4, 0, 1, 1); + --sbb-dialog-content-transition: transform var(--sbb-dialog-header-transition-duration) + var(--sbb-dialog-animation-easing); + --sbb-dialog-header-margin-block-start: calc(var(--sbb-dialog-header-height) * -1); } :host(:not([data-hide-header])) { // Show transition - --sbb-dialog-header-transition-timing: cubic-bezier(0, 0, 0.2, 1); + --sbb-dialog-content-transition: transform var(--sbb-dialog-header-transition-duration) + var(--sbb-dialog-animation-easing), + margin 0s var(--sbb-dialog-header-transition-duration); } :host([data-fullscreen]) { @@ -226,11 +244,11 @@ // Apply show/hide animation unless it has a visible focus within. &:not([data-has-visible-focus]:focus-within) { - margin-block-start: var(--sbb-dialog-header-margin-block-start); + transform: translateY(var(--sbb-dialog-header-margin-block-start)); transition: { - property: margin, box-shadow; + property: box-shadow, transform; duration: var(--sbb-dialog-header-transition-duration); - timing-function: var(--sbb-dialog-header-transition-timing); + timing-function: var(--sbb-dialog-animation-easing); } } @@ -275,12 +293,23 @@ padding-inline: var(--sbb-dialog-padding-inline); padding-block: var(--sbb-dialog-padding-block); overflow: auto; + transform: translateY(var(--sbb-dialog-header-margin-block-start)); + margin-block: 0 var(--sbb-dialog-header-margin-block-start); + transition: var(--sbb-dialog-content-transition); + z-index: -1; :host([data-fullscreen]) & { padding-block-start: var(--sbb-spacing-fixed-20x); padding-inline: var(--sbb-layout-base-offset-responsive); height: 100vh; } + + @include sbb.mq($from: medium) { + transform: unset; + margin-block: var(--sbb-dialog-header-margin-block-start) 0; + transition: margin var(--sbb-dialog-header-transition-duration) + var(--sbb-dialog-animation-easing); + } } .sbb-dialog__footer { From a1c4e28ff7f068910e350cb4ec56082eedd49e87 Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Sat, 25 Nov 2023 22:51:51 +0100 Subject: [PATCH 03/65] fix: stabilize dialog header animation --- src/components/dialog/dialog.ts | 57 +++++++++++++++++---------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts index a44d876552..b8b43cad07 100644 --- a/src/components/dialog/dialog.ts +++ b/src/components/dialog/dialog.ts @@ -304,33 +304,11 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { window.addEventListener('click', () => this._removeAriaLiveRefContent(), { signal: this._openDialogController.signal, }); - // If the content overflows, apply the header animation on scroll. - if (this._overflows) { - this._dialogContentElement?.addEventListener('scroll', () => this._onContentScroll(), { - passive: true, - signal: this._openDialogController.signal, - }); - Array.from(this._dialogHeaderElement.querySelectorAll('sbb-button'))?.forEach((el) => { - el.addEventListener( - 'focusin', - () => { - toggleDatasetEntry( - this._dialogHeaderElement, - 'hasVisibleFocus', - sbbInputModalityDetector.mostRecentModality === 'keyboard', - ); - }, - { signal: this._openDialogController.signal }, - ); - el.addEventListener( - 'blur', - () => { - toggleDatasetEntry(this._dialogHeaderElement, 'hasVisibleFocus', false); - }, - { signal: this._openDialogController.signal }, - ); - }); - } + // If the content overflows, show/hide the dialog header on scroll. + this._dialogContentElement?.addEventListener('scroll', () => this._onContentScroll(), { + passive: true, + signal: this._openDialogController.signal, + }); } // Check if the pointerdown event target is triggered on the dialog. @@ -384,12 +362,12 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { this._state = 'opened'; this._didOpen.emit(); applyInertMechanism(this); + this._attachOpenDialogEvents(); this._setDialogFocus(); // Use timeout to read label after focused element setTimeout(() => this._setAriaLiveRefContent()); this._focusHandler.trap(this); this._dialogContentResizeObserver.observe(this._dialogContentElement); - this._attachOpenDialogEvents(); } else if (event.animationName === 'close' && this._state === 'closing') { toggleDatasetEntry(this, 'hideHeader', false); this._dialogContentElement.scrollTo(0, 0); @@ -432,6 +410,29 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { // Set focus on the first focusable element. private _setDialogFocus(): void { + // Determine whether the dialog header has a visible focus within. + Array.from(this._dialogHeaderElement.querySelectorAll('sbb-button'))?.forEach((el) => { + el.addEventListener( + 'focusin', + () => { + if (this._overflows) { + toggleDatasetEntry( + this._dialogHeaderElement, + 'hasVisibleFocus', + sbbInputModalityDetector.mostRecentModality === 'keyboard', + ); + } + }, + { signal: this._openDialogController.signal }, + ); + el.addEventListener( + 'blur', + () => { + toggleDatasetEntry(this._dialogHeaderElement, 'hasVisibleFocus', false); + }, + { signal: this._openDialogController.signal }, + ); + }); const firstFocusable = this.shadowRoot!.querySelector(IS_FOCUSABLE_QUERY) as HTMLElement; setModalityOnNextFocus(firstFocusable); firstFocusable.focus(); From 206249b403fe346b06a2fada37ad8324d8ae97ff Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Mon, 27 Nov 2023 12:39:34 +0100 Subject: [PATCH 04/65] feat: improve header animation --- src/components/dialog/dialog.scss | 22 +++++++--------------- src/components/dialog/dialog.ts | 29 +++++++++++++++++------------ 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/components/dialog/dialog.scss b/src/components/dialog/dialog.scss index 88024534fe..5463af0c83 100644 --- a/src/components/dialog/dialog.scss +++ b/src/components/dialog/dialog.scss @@ -25,9 +25,8 @@ --sbb-dialog-backdrop-pointer-events: none; --sbb-dialog-backdrop-color: transparent; --sbb-dialog-header-padding-block: var(--sbb-spacing-responsive-s) 0; - --sbb-dialog-header-transition-duration: var(--sbb-animation-duration-6x); --sbb-dialog-header-margin-block-start: 0; - --sbb-dialog-content-transition: transform var(--sbb-dialog-header-transition-duration) + --sbb-dialog-content-transition: transform var(--sbb-dialog-animation-duration) var(--sbb-dialog-animation-easing); --sbb-dialog-footer-border: var(--sbb-border-width-1x) solid var(--sbb-color-cloud); @@ -91,18 +90,9 @@ :host([data-hide-header]) { // Hide transition - --sbb-dialog-content-transition: transform var(--sbb-dialog-header-transition-duration) - var(--sbb-dialog-animation-easing); --sbb-dialog-header-margin-block-start: calc(var(--sbb-dialog-header-height) * -1); } -:host(:not([data-hide-header])) { - // Show transition - --sbb-dialog-content-transition: transform var(--sbb-dialog-header-transition-duration) - var(--sbb-dialog-animation-easing), - margin 0s var(--sbb-dialog-header-transition-duration); -} - :host([data-fullscreen]) { --sbb-dialog-backdrop-color: transparent; --sbb-dialog-max-width: 100%; @@ -247,7 +237,7 @@ transform: translateY(var(--sbb-dialog-header-margin-block-start)); transition: { property: box-shadow, transform; - duration: var(--sbb-dialog-header-transition-duration); + duration: var(--sbb-dialog-animation-duration); timing-function: var(--sbb-dialog-animation-easing); } } @@ -294,7 +284,7 @@ padding-block: var(--sbb-dialog-padding-block); overflow: auto; transform: translateY(var(--sbb-dialog-header-margin-block-start)); - margin-block: 0 var(--sbb-dialog-header-margin-block-start); + margin-block: 0 calc(var(--sbb-dialog-header-height) * -1); transition: var(--sbb-dialog-content-transition); z-index: -1; @@ -304,11 +294,13 @@ height: 100vh; } + // In order to improve the header transition on mobile (especially iOS) we use + // a combination of the transform and margin properties on the smaller breakpoints, + // while on desktop we use just the margin-block for a better transition of the visible scrollbar. @include sbb.mq($from: medium) { transform: unset; margin-block: var(--sbb-dialog-header-margin-block-start) 0; - transition: margin var(--sbb-dialog-header-transition-duration) - var(--sbb-dialog-animation-easing); + transition: margin var(--sbb-dialog-animation-duration) var(--sbb-dialog-animation-easing); } } diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts index b8b43cad07..75438313a7 100644 --- a/src/components/dialog/dialog.ts +++ b/src/components/dialog/dialog.ts @@ -111,15 +111,12 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { private get _state(): SbbOverlayState { return this.dataset?.state as SbbOverlayState; } - + private get _hasTitle(): boolean { return !!this.titleContent || this._namedSlots.slots.has('title'); } - private _dialogContentResizeObserver = new AgnosticResizeObserver(() => - this._setOverflowAttribute(), - ); - + private _dialogContentResizeObserver = new AgnosticResizeObserver(() => this._onContentResize()); private _ariaLiveRef!: HTMLElement; private _ariaLiveRefToggle = false; @@ -143,6 +140,7 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { private _dialog!: HTMLDivElement; private _dialogHeaderElement!: HTMLElement; + private _dialogHeaderHeight!: number; private _dialogContentElement!: HTMLElement; private _dialogCloseElement?: HTMLElement; private _dialogController!: AbortController; @@ -179,7 +177,7 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { // Add this dialog to the global collection dialogRefs.push(this as SbbDialogElement); - this._setOverflowAttribute(); + this._onContentResize(); // Disable scrolling for content below the dialog this._scrollHandler.disableScroll(); @@ -221,13 +219,12 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { private _onContentScroll(): void { const hasVisibleHeader = this.dataset.hideHeader === undefined; - const dialogHeaderHeight = this._dialogHeaderElement.clientHeight; // Check whether hiding the header would make the scrollbar disappear // and prevent the hiding animation if so. if ( hasVisibleHeader && - this._dialogContentElement.clientHeight + dialogHeaderHeight >= + this._dialogContentElement.clientHeight + this._dialogHeaderHeight >= this._dialogContentElement.scrollHeight ) { return; @@ -243,9 +240,6 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { // Check whether is scrolling down or up. if (currentScroll > 0 && this._lastScroll < currentScroll) { // Scrolling down - if (hasVisibleHeader) { - this.style.setProperty('--sbb-dialog-header-height', `${dialogHeaderHeight}px`); - } toggleDatasetEntry(this, 'hideHeader', true); } else { // Scrolling up @@ -438,9 +432,20 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { firstFocusable.focus(); } - private _setOverflowAttribute(): void { + private _setDialogHeaderHeight(): void { + this._dialogHeaderHeight = this._dialogHeaderElement.clientHeight; + this.style.setProperty( + '--sbb-dialog-header-height', + `${this._dialogHeaderElement.clientHeight}px`, + ); + } + + private _onContentResize(): void { + this._setDialogHeaderHeight(); + // Check whether the content overflows and set the `overflows` attribute. this._overflows = this._dialogContentElement.scrollHeight > this._dialogContentElement.clientHeight; + // If the content doesn't overflow anymore after resizing and the header is currently hidden, shows the header again. if (!this._overflows && this.dataset.hideHeader === '') { this.toggleAttribute('data-hide-header', false); } From bfa914b88a7caf0d39ea6c46a612cc4b0c8d5a0c Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Mon, 27 Nov 2023 12:45:10 +0100 Subject: [PATCH 05/65] test: add e2e tests --- src/components/dialog/dialog.e2e.ts | 56 +++++++++++++++++++++++++ src/components/dialog/dialog.stories.ts | 40 +++++++++++++++++- 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/components/dialog/dialog.e2e.ts b/src/components/dialog/dialog.e2e.ts index 4052e253d9..30e924f423 100644 --- a/src/components/dialog/dialog.e2e.ts +++ b/src/components/dialog/dialog.e2e.ts @@ -429,3 +429,59 @@ describe(`sbb-dialog with ${fixture.name}`, () => { expect(ariaLiveRef.textContent!.trim()).to.be.equal(`${i18nDialog.en}, Special Dialog`); }); }); + +describe('sbb-dialog with long content', () => { + let element: SbbDialog; + + beforeEach(async () => { + await setViewport({ width: 900, height: 300 }); + element = await fixture(html` + + Frodo halted for a moment, looking back. Elrond was in his chair and the fire was on his + face like summer-light upon the trees. Near him sat the Lady Arwen. To his surprise Frodo + saw that Aragorn stood beside her; his dark cloak was thrown back, and he seemed to be clad + in elven-mail, and a star shone on his breast. They spoke together, and then suddenly it + seemed to Frodo that Arwen turned towards him, and the light of her eyes fell on him from + afar and pierced his heart. He stood still enchanted, while the sweet syllables of the + elvish song fell like clear jewels of blended word and melody. 'It is a song to Elbereth,'' + said Bilbo. 'They will sing that, and other songs of the Blessed Realm, many times tonight. + Come on!’ —J.R.R. Tolkien, The Lord of the Rings: The Fellowship of the Ring, “Many + Meetings” J.R.R. Tolkien, the mastermind behind Middle-earth's enchanting world, was born on + January 3, 1892. With "The Hobbit" and "The Lord of the Rings", he pioneered fantasy + literature. Tolkien's linguistic brilliance and mythic passion converge in a literary legacy + that continues to transport readers to magical realms. +
Action group
+
+ `); + }); + + it('renders', () => { + assert.instanceOf(element, SbbDialog); + }); + + it('sets the data-overflows attribute', async () => { + await openDialog(element); + + expect(element).to.have.attribute('data-state', 'opened'); + expect(element).to.have.attribute('data-overflows', ''); + }); + + it('shows/hides the dialog header on scroll', async () => { + await openDialog(element); + expect(element).not.to.have.attribute('data-hide-header'); + + const content = element.shadowRoot.querySelector('.sbb-dialog__content'); + + // Scroll down. + content.scrollTo(0, 50); + await waitForCondition(() => element.hasAttribute('data-hide-header')); + + expect(element).to.have.attribute('data-hide-header'); + + // Scroll up. + content.scrollTo(0, 0); + await waitForCondition(() => !element.hasAttribute('data-hide-header')); + + expect(element).not.to.have.attribute('data-hide-header'); + }); +}); diff --git a/src/components/dialog/dialog.stories.ts b/src/components/dialog/dialog.stories.ts index 49a66c2fda..fa3e1f3839 100644 --- a/src/components/dialog/dialog.stories.ts +++ b/src/components/dialog/dialog.stories.ts @@ -190,6 +190,44 @@ const formStyle: Args = { gap: 'var(--sbb-spacing-fixed-4x)', }; +const textBlockStyle: Args = { + position: 'relative', + marginBlockStart: '1rem', + padding: '1rem', + backgroundColor: 'var(--sbb-color-milk-default)', + border: 'var(--sbb-border-width-1x) solid var(--sbb-color-cloud-default)', + borderRadius: 'var(--sbb-border-radius-4x)', + zIndex: '100', +}; + +const textBlock = (): TemplateResult => html` +
+ J.R.R. Tolkien, the mastermind behind Middle-earth's enchanting world, was born on January 3, + 1892. With "The Hobbit" and "The Lord of the Rings", he pioneered fantasy literature. Tolkien's + linguistic brilliance and mythic passion converge in a literary legacy that continues to + transport readers to magical realms. +
+`; + +const textBlockStyle: Args = { + position: 'relative', + marginBlockStart: '1rem', + padding: '1rem', + backgroundColor: 'var(--sbb-color-milk-default)', + border: 'var(--sbb-border-width-1x) solid var(--sbb-color-cloud-default)', + borderRadius: 'var(--sbb-border-radius-4x)', + zIndex: '100', +}; + +const textBlock = (): TemplateResult => html` +
+ J.R.R. Tolkien, the mastermind behind Middle-earth's enchanting world, was born on January 3, + 1892. With "The Hobbit" and "The Lord of the Rings", he pioneered fantasy literature. Tolkien's + linguistic brilliance and mythic passion converge in a literary legacy that continues to + transport readers to magical realms. +
+`; + const DefaultTemplate = (args: Args): TemplateResult => html` ${triggerButton('my-dialog-1')} @@ -233,7 +271,7 @@ const LongContentTemplate = (args: Args): TemplateResult => html` He stood still enchanted, while the sweet syllables of the elvish song fell like clear jewels of blended word and melody. 'It is a song to Elbereth,'' said Bilbo. 'They will sing that, and other songs of the Blessed Realm, many times tonight. Come on!’ —J.R.R. Tolkien, The Lord of the - Rings: The Fellowship of the Ring, “Many Meetings” ${actionGroup(args.negative)} + Rings: The Fellowship of the Ring, “Many Meetings” ${actionGroup(args.negative)} ${textBlock()} `; From 497209fc23486f0468baa0083402d8ebcdc6328d Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Tue, 28 Nov 2023 12:56:40 +0100 Subject: [PATCH 06/65] refactor: rename data attr --- src/components/dialog/dialog.scss | 4 ++-- src/components/dialog/dialog.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/dialog/dialog.scss b/src/components/dialog/dialog.scss index 5463af0c83..5f4fe4434b 100644 --- a/src/components/dialog/dialog.scss +++ b/src/components/dialog/dialog.scss @@ -233,7 +233,7 @@ } // Apply show/hide animation unless it has a visible focus within. - &:not([data-has-visible-focus]:focus-within) { + &:not([data-has-visible-focus-within]:focus-within) { transform: translateY(var(--sbb-dialog-header-margin-block-start)); transition: { property: box-shadow, transform; @@ -250,7 +250,7 @@ padding-block-start: var(--sbb-spacing-responsive-xs); } - &[data-has-visible-focus]:focus-within, + &[data-has-visible-focus-within]:focus-within, :host([data-overflows]:not([data-fullscreen], [negative], [data-hide-header])) & { @include sbb.shadow-level-9-soft; } diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts index 75438313a7..5ee015d844 100644 --- a/src/components/dialog/dialog.ts +++ b/src/components/dialog/dialog.ts @@ -412,7 +412,7 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { if (this._overflows) { toggleDatasetEntry( this._dialogHeaderElement, - 'hasVisibleFocus', + 'hasVisibleFocusWithin', sbbInputModalityDetector.mostRecentModality === 'keyboard', ); } @@ -422,7 +422,7 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { el.addEventListener( 'blur', () => { - toggleDatasetEntry(this._dialogHeaderElement, 'hasVisibleFocus', false); + toggleDatasetEntry(this._dialogHeaderElement, 'hasVisibleFocusWithin', false); }, { signal: this._openDialogController.signal }, ); From bfa410775e60d78bc8fc68a2e45e2e7fb6e2e99c Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Tue, 12 Dec 2023 16:47:45 +0100 Subject: [PATCH 07/65] fix: review --- src/components/dialog/dialog.e2e.ts | 4 ++-- src/components/dialog/dialog.scss | 4 ++-- src/components/dialog/dialog.stories.ts | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/dialog/dialog.e2e.ts b/src/components/dialog/dialog.e2e.ts index 30e924f423..8bd860819b 100644 --- a/src/components/dialog/dialog.e2e.ts +++ b/src/components/dialog/dialog.e2e.ts @@ -431,7 +431,7 @@ describe(`sbb-dialog with ${fixture.name}`, () => { }); describe('sbb-dialog with long content', () => { - let element: SbbDialog; + let element: SbbDialogElement; beforeEach(async () => { await setViewport({ width: 900, height: 300 }); @@ -456,7 +456,7 @@ describe('sbb-dialog with long content', () => { }); it('renders', () => { - assert.instanceOf(element, SbbDialog); + assert.instanceOf(element, SbbDialogElement); }); it('sets the data-overflows attribute', async () => { diff --git a/src/components/dialog/dialog.scss b/src/components/dialog/dialog.scss index 5f4fe4434b..c29bb6ecff 100644 --- a/src/components/dialog/dialog.scss +++ b/src/components/dialog/dialog.scss @@ -295,9 +295,9 @@ } // In order to improve the header transition on mobile (especially iOS) we use - // a combination of the transform and margin properties on the smaller breakpoints, + // a combination of the transform and margin properties on touch devices, // while on desktop we use just the margin-block for a better transition of the visible scrollbar. - @include sbb.mq($from: medium) { + @include sbb.hover-mq($hover: true) { transform: unset; margin-block: var(--sbb-dialog-header-margin-block-start) 0; transition: margin var(--sbb-dialog-animation-duration) var(--sbb-dialog-animation-easing); diff --git a/src/components/dialog/dialog.stories.ts b/src/components/dialog/dialog.stories.ts index fa3e1f3839..3a898e6722 100644 --- a/src/components/dialog/dialog.stories.ts +++ b/src/components/dialog/dialog.stories.ts @@ -197,7 +197,6 @@ const textBlockStyle: Args = { backgroundColor: 'var(--sbb-color-milk-default)', border: 'var(--sbb-border-width-1x) solid var(--sbb-color-cloud-default)', borderRadius: 'var(--sbb-border-radius-4x)', - zIndex: '100', }; const textBlock = (): TemplateResult => html` From 31098627d3bc3fd220d3d8bb8495e86fa42c0203 Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Wed, 13 Dec 2023 13:30:51 +0100 Subject: [PATCH 08/65] fix: attr ref remove --- src/components/dialog/dialog.ts | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts index 5ee015d844..6361a5f952 100644 --- a/src/components/dialog/dialog.ts +++ b/src/components/dialog/dialog.ts @@ -1,7 +1,6 @@ import type { CSSResultGroup, TemplateResult } from 'lit'; import { LitElement, nothing } from 'lit'; import { customElement, property } from 'lit/decorators.js'; -import { ref } from 'lit/directives/ref.js'; import { html, unsafeStatic } from 'lit/static-html.js'; import { @@ -138,7 +137,6 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { SbbDialogElement.events.backClick, ); - private _dialog!: HTMLDivElement; private _dialogHeaderElement!: HTMLElement; private _dialogHeaderHeight!: number; private _dialogContentElement!: HTMLElement; @@ -165,11 +163,15 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { * Opens the dialog element. */ public open(): void { - if (this._state !== 'closed' || !this._dialog) { + if (this._state !== 'closed') { return; } this._lastFocusedElement = document.activeElement as HTMLElement; + // Initialize dialog elements + this._dialogHeaderElement = this.shadowRoot.querySelector('.sbb-dialog__header'); + this._dialogContentElement = this.shadowRoot.querySelector('.sbb-dialog__content'); + if (!this._willOpen.emit()) { return; } @@ -482,10 +484,7 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { /* eslint-enable lit/binding-positions */ const dialogHeader = html` -
(this._dialogHeaderElement = dialogHeaderrRef as HTMLElement))} - > +
${this.titleBackButton ? backButton : nothing} this._onDialogAnimationEnd(event)} class="sbb-dialog" id=${this._dialogId} - ${ref((dialogRef?: Element) => (this._dialog = dialogRef as HTMLDivElement))} >
this._closeOnSbbDialogCloseClick(event)} class="sbb-dialog__wrapper" > ${dialogHeader} -
- (this._dialogContentElement = dialogContent as HTMLElement), - )} - > +
`; +const dialogHeader = (args): TemplateResult => html` + Title +`; + +const dialogHeader = (args: Args): TemplateResult => html` + Title +`; + const textBlockStyle: Args = { position: 'relative', marginBlockStart: '1rem', @@ -230,7 +265,10 @@ const textBlock = (): TemplateResult => html` const DefaultTemplate = (args: Args): TemplateResult => html` ${triggerButton('my-dialog-1')} -

Dialog content

+ ${dialogHeader(args)} + +

Dialog content

+
${actionGroup(args.negative)}
`; @@ -255,22 +293,26 @@ const SlottedTitleTemplate = (args: Args): TemplateResult => html` const LongContentTemplate = (args: Args): TemplateResult => html` ${triggerButton('my-dialog-3')} - Frodo halted for a moment, looking back. Elrond was in his chair and the fire was on his face - like summer-light upon the trees. Near him sat the Lady Arwen. To his surprise Frodo saw that - Aragorn stood beside her; his dark cloak was thrown back, and he seemed to be clad in - elven-mail, and a star shone on his breast. They spoke together, and then suddenly it seemed to - Frodo that Arwen turned towards him, and the light of her eyes fell on him from afar and pierced - his heart. - - He stood still enchanted, while the sweet syllables of the elvish song fell like clear jewels of - blended word and melody. 'It is a song to Elbereth,'' said Bilbo. 'They will sing that, and - other songs of the Blessed Realm, many times tonight. Come on!’ —J.R.R. Tolkien, The Lord of the - Rings: The Fellowship of the Ring, “Many Meetings” ${actionGroup(args.negative)} ${textBlock()} + ${dialogHeader(args)} + + Frodo halted for a moment, looking back. Elrond was in his chair and the fire was on his face + like summer-light upon the trees. Near him sat the Lady Arwen. To his surprise Frodo saw that + Aragorn stood beside her; his dark cloak was thrown back, and he seemed to be clad in + elven-mail, and a star shone on his breast. They spoke together, and then suddenly it seemed + to Frodo that Arwen turned towards him, and the light of her eyes fell on him from afar and + pierced his heart. + + He stood still enchanted, while the sweet syllables of the elvish song fell like clear jewels + of blended word and melody. 'It is a song to Elbereth,'' said Bilbo. 'They will sing that, and + other songs of the Blessed Realm, many times tonight. Come on!’ —J.R.R. Tolkien, The Lord of + the Rings: The Fellowship of the Ring, “Many Meetings” ${textBlock()} + + ${actionGroup(args.negative)} `; @@ -454,7 +496,7 @@ const meta: Meta = { }, layout: 'fullscreen', }, - title: 'components/sbb-dialog', + title: 'components/sbb-dialog/sbb-dialog', }; export default meta; diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts index d2062ba9a5..b8dfdb28c6 100644 --- a/src/components/dialog/dialog.ts +++ b/src/components/dialog/dialog.ts @@ -1,23 +1,18 @@ import type { CSSResultGroup, TemplateResult } from 'lit'; -import { LitElement, nothing } from 'lit'; +import { LitElement } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { html, unsafeStatic } from 'lit/static-html.js'; -import { - SbbFocusHandler, - IS_FOCUSABLE_QUERY, - setModalityOnNextFocus, - sbbInputModalityDetector, -} from '../core/a11y'; +import { SbbFocusHandler, getFirstFocusableElement, setModalityOnNextFocus } from '../core/a11y'; import { SbbLanguageController, SbbSlotStateController } from '../core/controllers'; -import { hostContext, isValidAttribute, SbbScrollHandler, setAttribute } from '../core/dom'; +import { hostContext, isValidAttribute, SbbScrollHandler, isBreakpoint } from '../core/dom'; import { EventEmitter } from '../core/eventing'; -import { i18nCloseDialog, i18nDialog, i18nGoBack } from '../core/i18n'; +import { i18nDialog } from '../core/i18n'; import { SbbNegativeMixin } from '../core/mixins'; import { AgnosticResizeObserver } from '../core/observers'; import type { SbbOverlayState } from '../core/overlay'; import { applyInertMechanism, removeInertMechanism } from '../core/overlay'; -import type { SbbTitleLevel } from '../title'; +import type { SbbSbbDialogTitleElement } from '../dialog-title'; import style from './dialog.scss?lit&inline'; @@ -56,21 +51,6 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { backClick: 'requestBackAction', } as const; - /** - * Dialog title. - */ - @property({ attribute: 'title-content', reflect: true }) public titleContent?: string; - - /** - * Level of title, will be rendered as heading tag (e.g. h1). Defaults to level 1. - */ - @property({ attribute: 'title-level' }) public titleLevel: SbbTitleLevel = '1'; - - /** - * Whether a back button is displayed next to the title. - */ - @property({ attribute: 'title-back-button', type: Boolean }) public titleBackButton = false; - /** * Backdrop click action. */ @@ -81,20 +61,6 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { */ @property({ attribute: 'accessibility-label' }) public accessibilityLabel: string | undefined; - /** - * This will be forwarded as aria-label to the close button element. - */ - @property({ attribute: 'accessibility-close-label' }) public accessibilityCloseLabel: - | string - | undefined; - - /** - * This will be forwarded as aria-label to the back button element. - */ - @property({ attribute: 'accessibility-back-label' }) public accessibilityBackLabel: - | string - | undefined; - /** * Whether the animation is enabled. */ @@ -111,10 +77,6 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { return this.dataset?.state as SbbOverlayState; } - private get _hasTitle(): boolean { - return !!this.titleContent || this._namedSlots.slots.has('title'); - } - // We use a timeout as a workaround to the "ResizeObserver loop completed with undelivered notifications" error. // For more details: // - https://github.com/WICG/resize-observer/issues/38#issuecomment-422126006 @@ -137,14 +99,8 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { /** Emits whenever the `sbb-dialog` is closed. */ private _didClose: EventEmitter = new EventEmitter(this, SbbDialogElement.events.didClose); - /** Emits whenever the back button is clicked. */ - private _backClick: EventEmitter = new EventEmitter( - this, - SbbDialogElement.events.backClick, - ); - - private _dialogHeaderElement!: HTMLElement; - private _dialogHeaderHeight!: number; + private _dialogTitleElement!: SbbDialogTitleElement; + private _dialogTitleHeight!: number; private _dialogContentElement!: HTMLElement; private _dialogCloseElement?: HTMLElement; private _dialogController!: AbortController; @@ -153,17 +109,14 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { private _scrollHandler = new SbbScrollHandler(); private _returnValue: any; private _isPointerDownEventOnDialog: boolean = false; - private _overflows: boolean; + private _overflows: boolean = false; private _lastScroll = 0; private _dialogId = `sbb-dialog-${nextId++}`; // Last element which had focus before the dialog was opened. private _lastFocusedElement?: HTMLElement; - private _language = new SbbLanguageController(this); - private _namedSlots = new SbbSlotStateController(this, () => - setAttribute(this, 'data-fullscreen', !this._hasTitle), - ); + private _language = new LanguageController(this); /** * Opens the dialog element. @@ -175,8 +128,9 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { this._lastFocusedElement = document.activeElement as HTMLElement; // Initialize dialog elements - this._dialogHeaderElement = this.shadowRoot.querySelector('.sbb-dialog__header'); - this._dialogContentElement = this.shadowRoot.querySelector('.sbb-dialog__content'); + this._dialogTitleElement = this.querySelector('sbb-dialog-title')!; + this._dialogContentElement = this.querySelector('sbb-dialog-content')!.shadowRoot! + .firstElementChild as HTMLElement; if (!this._willOpen.emit()) { return; @@ -232,7 +186,7 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { // and prevent the hiding animation if so. if ( hasVisibleHeader && - this._dialogContentElement.clientHeight + this._dialogHeaderHeight >= + this._dialogContentElement.clientHeight + this._dialogTitleHeight >= this._dialogContentElement.scrollHeight ) { return; @@ -248,10 +202,10 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { // Check whether is scrolling down or up. if (currentScroll > 0 && this._lastScroll < currentScroll) { // Scrolling down - toggleDatasetEntry(this, 'hideHeader', true); + this._setHideHeaderDataAttribute(true); } else { // Scrolling up - toggleDatasetEntry(this, 'hideHeader', false); + this._setHideHeaderDataAttribute(false); } // `currentScroll` can be negative, e.g. on mobile; this is not allowed. this._lastScroll = currentScroll <= 0 ? 0 : currentScroll; @@ -259,6 +213,8 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { public override connectedCallback(): void { super.connectedCallback(); + new NamedSlotStateController(this); + this._state = this._state || 'closed'; this._dialogController?.abort(); this._dialogController = new AbortController(); @@ -311,6 +267,9 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { passive: true, signal: this._openDialogController.signal, }); + window.addEventListener('resize', () => this._setHideHeaderDataAttribute(false), { + signal: this._openDialogController.signal, + }); } // Check if the pointerdown event target is triggered on the dialog. @@ -344,16 +303,24 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { // Close the dialog on click of any element that has the 'sbb-dialog-close' attribute. private _closeOnSbbDialogCloseClick(event: Event): void { - const target = event.target as HTMLElement; - - if (target.hasAttribute('sbb-dialog-close') && !isValidAttribute(target, 'disabled')) { - // Check if the target is a submission element within a form and return the form, if present - const closestForm = - target.getAttribute('type') === 'submit' - ? (hostContext('form', target) as HTMLFormElement) - : undefined; - this.close(closestForm, target); + const dialogCloseElement = event + .composedPath() + .filter((e): e is HTMLElement => e instanceof window.HTMLElement) + .find( + (target) => + target.hasAttribute('sbb-dialog-close') && !isValidAttribute(target, 'disabled'), + ); + + if (!dialogCloseElement) { + return; } + + // Check if the target is a submission element within a form and return the form, if present + const closestForm = + dialogCloseElement.getAttribute('type') === 'submit' + ? (hostContext('form', dialogCloseElement) as HTMLFormElement) + : undefined; + this.close(closestForm, dialogCloseElement); } // Wait for dialog transition to complete. @@ -370,7 +337,7 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { setTimeout(() => this._setAriaLiveRefContent()); this._focusHandler.trap(this); } else if (event.animationName === 'close' && this._state === 'closing') { - toggleDatasetEntry(this, 'hideHeader', false); + this._setHideHeaderDataAttribute(false); this._dialogContentElement.scrollTo(0, 0); this._state = 'closed'; removeInertMechanism(); @@ -411,40 +378,16 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { // Set focus on the first focusable element. private _setDialogFocus(): void { - // Determine whether the dialog header has a visible focus within. - Array.from(this._dialogHeaderElement.querySelectorAll('sbb-button'))?.forEach((el) => { - el.addEventListener( - 'focusin', - () => { - if (this._overflows) { - toggleDatasetEntry( - this._dialogHeaderElement, - 'hasVisibleFocusWithin', - sbbInputModalityDetector.mostRecentModality === 'keyboard', - ); - } - }, - { signal: this._openDialogController.signal }, - ); - el.addEventListener( - 'blur', - () => { - toggleDatasetEntry(this._dialogHeaderElement, 'hasVisibleFocusWithin', false); - }, - { signal: this._openDialogController.signal }, - ); - }); - const firstFocusable = this.shadowRoot!.querySelector(IS_FOCUSABLE_QUERY) as HTMLElement; + const firstFocusable = getFirstFocusableElement( + Array.from(this.children).filter((e): e is HTMLElement => e instanceof window.HTMLElement), + ); setModalityOnNextFocus(firstFocusable); - firstFocusable.focus(); + firstFocusable?.focus(); } private _setDialogHeaderHeight(): void { - this._dialogHeaderHeight = this._dialogHeaderElement.clientHeight; - this.style.setProperty( - '--sbb-dialog-header-height', - `${this._dialogHeaderElement.clientHeight}px`, - ); + this._dialogTitleHeight = this._dialogTitleElement.shadowRoot!.firstElementChild!.clientHeight; + this.style.setProperty('--sbb-dialog-header-height', `${this._dialogTitleHeight}px`); } private _onContentResize(): void { @@ -452,60 +395,23 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { // Check whether the content overflows and set the `overflows` attribute. this._overflows = this._dialogContentElement.scrollHeight > this._dialogContentElement.clientHeight; - // If the content doesn't overflow anymore after resizing and the header is currently hidden, shows the header again. - if (!this._overflows && this.dataset.hideHeader === '') { - this.toggleAttribute('data-hide-header', false); - } - this.toggleAttribute('data-overflows', this._overflows); + this._setOverflowsDataAttribute(); } - protected override render(): TemplateResult { - const TAG_NAME = this.negative ? 'sbb-transparent-button' : 'sbb-secondary-button'; - - /* eslint-disable lit/binding-positions */ - const closeButton = html` - <${unsafeStatic(TAG_NAME)} - class="sbb-dialog__close" - aria-label=${this.accessibilityCloseLabel || i18nCloseDialog[this._language.current]} - ?negative=${this.negative} - size="m" - type="button" - icon-name="cross-small" - sbb-dialog-close - > - `; - - const backButton = html` - <${unsafeStatic(TAG_NAME)} - class="sbb-dialog__back" - aria-label=${this.accessibilityBackLabel || i18nGoBack[this._language.current]} - ?negative=${this.negative} - size="m" - type="button" - icon-name="chevron-small-left-small" - @click=${() => this._backClick.emit()} - > - `; - /* eslint-enable lit/binding-positions */ - - const dialogHeader = html` -
- ${this.titleBackButton ? backButton : nothing} - - ${this.titleContent} - - ${closeButton} -
- `; + private _setHideHeaderDataAttribute(value: boolean): void { + const hideOnScroll = this._dialogTitleElement.hideOnScroll; + const hideHeader = + !!hideOnScroll && isBreakpoint('zero', hideOnScroll, { includeMaxBreakpoint: true }); + this.toggleAtribute('data-hide-header', !hideHeader ? false : value); + this._dialogTitleElement.toggleAtribute('data-hide-header', !hideHeader ? false : value); + } - setAttribute(this, 'data-fullscreen', !this._hasTitle); + private _setOverflowsDataAttribute(): void { + this.toggleAtribute('data-overflows', this._overflows); + this._dialogTitleElement.toggleAtribute('data-overflows', this._overflows); + } + protected override render(): TemplateResult { return html`
this._closeOnSbbDialogCloseClick(event)} class="sbb-dialog__wrapper" > - ${dialogHeader} -
- -
+ + diff --git a/src/components/dialog/readme.md b/src/components/dialog/readme.md index 98e4732711..eb123c4064 100644 --- a/src/components/dialog/readme.md +++ b/src/components/dialog/readme.md @@ -78,17 +78,13 @@ It's possible to display the component in `negative` variant using the self-name ## Properties -| Name | Attribute | Privacy | Type | Default | Description | -| ------------------------- | --------------------------- | ------- | ---------------------------- | --------- | ------------------------------------------------------------------------------- | -| `titleContent` | `title-content` | public | `string \| undefined` | | Dialog title. | -| `titleLevel` | `title-level` | public | `SbbTitleLevel` | `'1'` | Level of title, will be rendered as heading tag (e.g. h1). Defaults to level 1. | -| `titleBackButton` | `title-back-button` | public | `boolean` | `false` | Whether a back button is displayed next to the title. | -| `backdropAction` | `backdrop-action` | public | `'close' \| 'none'` | `'close'` | Backdrop click action. | -| `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the relevant nested element. | -| `accessibilityCloseLabel` | `accessibility-close-label` | public | `\| string \| undefined` | | This will be forwarded as aria-label to the close button element. | -| `accessibilityBackLabel` | `accessibility-back-label` | public | `\| string \| undefined` | | This will be forwarded as aria-label to the back button element. | -| `disableAnimation` | `disable-animation` | public | `boolean` | `false` | Whether the animation is enabled. | -| `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. | +| Name | Attribute | Privacy | Type | Default | Description | +| -------------------- | --------------------- | ------- | --------------------- | --------- | -------------------------------------------------------------------- | +| `titleContent` | `title-content` | public | `string` | | Dialog title. | +| `backdropAction` | `backdrop-action` | public | `'close' \| 'none'` | `'close'` | Backdrop click action. | +| `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. | +| `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the relevant nested element. | +| `disableAnimation` | `disable-animation` | public | `boolean` | `false` | Whether the animation is enabled. | ## Methods From 2b230eb31994b5b4e00fce57e08281fd714009b2 Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Fri, 2 Feb 2024 11:06:02 +0100 Subject: [PATCH 14/65] feat: add dialog actions --- .../dialog-actions/dialog-actions.e2e.ts | 25 +++++++++ .../dialog-actions/dialog-actions.scss | 21 +++++++ .../dialog-actions/dialog-actions.spec.ts | 17 ++++++ .../dialog-actions/dialog-actions.stories.ts | 55 +++++++++++++++++++ .../dialog-actions/dialog-actions.ts | 40 ++++++++++++++ src/components/dialog-actions/index.ts | 1 + src/components/dialog-actions/readme.md | 44 +++++++++++++++ src/components/dialog/dialog.scss | 22 +------- src/components/dialog/dialog.stories.ts | 16 ++---- src/components/dialog/dialog.ts | 18 +++--- 10 files changed, 222 insertions(+), 37 deletions(-) create mode 100644 src/components/dialog-actions/dialog-actions.e2e.ts create mode 100644 src/components/dialog-actions/dialog-actions.scss create mode 100644 src/components/dialog-actions/dialog-actions.spec.ts create mode 100644 src/components/dialog-actions/dialog-actions.stories.ts create mode 100644 src/components/dialog-actions/dialog-actions.ts create mode 100644 src/components/dialog-actions/index.ts create mode 100644 src/components/dialog-actions/readme.md diff --git a/src/components/dialog-actions/dialog-actions.e2e.ts b/src/components/dialog-actions/dialog-actions.e2e.ts new file mode 100644 index 0000000000..cc7294bbc7 --- /dev/null +++ b/src/components/dialog-actions/dialog-actions.e2e.ts @@ -0,0 +1,25 @@ +import { assert, expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; + +import { EventSpy, waitForLitRender } from '../core/testing'; + +import { SbbDialogActionsElement } from './dialog-actions'; + +describe('sbb-dialog-actions', () => { + let element: SbbDialogActionsElement; + + beforeEach(async () => { + element = await fixture(html``); + }); + + it('renders', async () => { + assert.instanceOf(element, SbbDialogActionsElement); + }); + + it('emits on click', async () => { + const myEventNameSpy = new EventSpy(SbbDialogActionsElement.events.myEventName); + element.click(); + await waitForLitRender(element); + expect(myEventNameSpy.count).to.be.equal(1); + }); +}); diff --git a/src/components/dialog-actions/dialog-actions.scss b/src/components/dialog-actions/dialog-actions.scss new file mode 100644 index 0000000000..7fbac4492e --- /dev/null +++ b/src/components/dialog-actions/dialog-actions.scss @@ -0,0 +1,21 @@ +@use '../core/styles' as sbb; + +// Default component properties, defined for :host. Properties which can not +// travel the shadow boundary are defined through this mixin +@include sbb.host-component-properties; + +:host { + display: contents; +} + +.sbb-dialog-actions { + padding-inline: var(--sbb-dialog-padding-inline); + padding-block: var(--sbb-spacing-responsive-s); + margin-block-start: auto; + background-color: var(--sbb-dialog-background-color); + border-block-start: var(--sbb-dialog-actions-border); + + :host([data-overflows]:not([negative])) & { + @include sbb.shadow-level-9-soft; + } +} diff --git a/src/components/dialog-actions/dialog-actions.spec.ts b/src/components/dialog-actions/dialog-actions.spec.ts new file mode 100644 index 0000000000..ce5a424d76 --- /dev/null +++ b/src/components/dialog-actions/dialog-actions.spec.ts @@ -0,0 +1,17 @@ +import { expect, fixture } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; +import './dialog-actions'; + +describe('sbb-dialog-actions', () => { + it('renders', async () => { + const root = await fixture(html``); + + expect(root).dom.to.be.equal(``); + + expect(root).shadowDom.to.be.equal(` +
+ Label +
+ `); + }); +}); diff --git a/src/components/dialog-actions/dialog-actions.stories.ts b/src/components/dialog-actions/dialog-actions.stories.ts new file mode 100644 index 0000000000..d0f6a3cbf1 --- /dev/null +++ b/src/components/dialog-actions/dialog-actions.stories.ts @@ -0,0 +1,55 @@ +import { withActions } from '@storybook/addon-actions/decorator'; +import type { InputType } from '@storybook/types'; +import type { Args, ArgTypes, Decorator, Meta, StoryObj } from '@storybook/web-components'; +import { html, TemplateResult } from 'lit'; + +import { sbbSpread } from '../core/dom'; + +import type { SbbDialogActionsElement } from './dialog-actions'; +import readme from './readme.md?raw'; + +import './dialog-actions'; + +const myProp: InputType = { + control: { + type: 'text', + }, +}; + +const defaultArgTypes: ArgTypes = { + 'my-prop': myProp, +}; + +const defaultArgs: Args = { + 'my-prop': 'Label', +}; + +const Template = (args: Args): TemplateResult => + html``; + +export const Default: StoryObj = { + render: Template, + argTypes: defaultArgTypes, + args: { ...defaultArgs }, +}; + +const meta: Meta = { + decorators: [ + (story) => html`
${story()}
`, + withActions as Decorator, + ], + parameters: { + actions: { + handles: [SbbDialogActionsElement.events.myEventName], + }, + backgrounds: { + disable: true, + }, + docs: { + extractComponentDescription: () => readme, + }, + }, + title: 'components/sbb-dialog-actions', +}; + +export default meta; diff --git a/src/components/dialog-actions/dialog-actions.ts b/src/components/dialog-actions/dialog-actions.ts new file mode 100644 index 0000000000..a69e17cec6 --- /dev/null +++ b/src/components/dialog-actions/dialog-actions.ts @@ -0,0 +1,40 @@ +import { CSSResultGroup, html, TemplateResult } from 'lit'; +import { customElement } from 'lit/decorators.js'; + +import { SbbActionGroupElement } from '../action-group'; +import { setAttribute } from '../core/dom'; + +import style from './dialog-actions.scss?lit&inline'; + +/** + * Describe the purpose of the component with a single short sentence. + * + * @slot - Use the unnamed slot to add `sbb-TODO` elements. + * @event {CustomEvent} myEventName - TODO: Document this event + */ +@customElement('sbb-dialog-actions') +export class SbbDialogActionsElement extends SbbActionGroupElement { + public static override styles: CSSResultGroup = [SbbActionGroupElement.styles, style]; + + public override connectedCallback(): void { + super.connectedCallback(); + } + + public override disconnectedCallback(): void { + super.disconnectedCallback(); + // do stuff + } + + protected override render(): TemplateResult { + setAttribute(this, 'slot', 'actions'); + + return html`
${super.render()}
`; + } +} + +declare global { + interface HTMLElementTagNameMap { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'sbb-dialog-actions': SbbDialogActionsElement; + } +} diff --git a/src/components/dialog-actions/index.ts b/src/components/dialog-actions/index.ts new file mode 100644 index 0000000000..39621636d6 --- /dev/null +++ b/src/components/dialog-actions/index.ts @@ -0,0 +1 @@ +export * from './dialog-actions'; diff --git a/src/components/dialog-actions/readme.md b/src/components/dialog-actions/readme.md new file mode 100644 index 0000000000..0a4bd342cc --- /dev/null +++ b/src/components/dialog-actions/readme.md @@ -0,0 +1,44 @@ +> Explain the use and the purpose of the component; add minor details if needed and provide a basic example.
+> If you reference other components, link their documentation at least once (the path must start from _/docs/..._ ).
+> For the examples, use triple backticks with file extension (` ```html ``` `).
+> The following list of paragraphs is only suggested; remove, create and adapt as needed. + +The `sbb-dialog-actions` is a component . . . + +```html + +``` + +## Slots + +> Describe slot naming and usage and provide an example of slotted content. + +## States + +> Describe the component states (`disabled`, `readonly`, etc.) and provide examples. + +## Style + +> Describe the properties which change the component visualization (`size`, `negative`, etc.) and provide examples. + +## Interactions + +> Describe how it's possible to interact with the component (open and close a `sbb-dialog`, dismiss a `sbb-alert`, etc.) and provide examples. + +## Events + +> Describe events triggered by the component and possibly how to get information from the payload. + +## Keyboard interaction + +> If the component has logic for keyboard navigation (as the `sbb-calendar` or the `sbb-select`) describe it. + +| Keyboard | Action | +| -------------- | ------------- | +| Key | What it does. | + +## Accessibility + +> Describe how accessibility is implemented and if there are issues or suggested best-practice for the consumers. + + diff --git a/src/components/dialog/dialog.scss b/src/components/dialog/dialog.scss index fd4e76874e..92e521f2a1 100644 --- a/src/components/dialog/dialog.scss +++ b/src/components/dialog/dialog.scss @@ -26,7 +26,7 @@ --sbb-dialog-backdrop-color: transparent; --sbb-dialog-content-transition: transform var(--sbb-dialog-animation-duration) var(--sbb-dialog-animation-easing); - --sbb-dialog-footer-border: var(--sbb-border-width-1x) solid var(--sbb-color-cloud); + --sbb-dialog-actions-border: var(--sbb-border-width-1x) solid var(--sbb-color-cloud); position: fixed; inset: var(--sbb-dialog-inset); @@ -103,7 +103,7 @@ --sbb-focus-outline-color: var(--sbb-focus-outline-color-dark); --sbb-dialog-color: var(--sbb-color-white); --sbb-dialog-background-color: var(--sbb-color-midnight); - --sbb-dialog-footer-border: none; + --sbb-dialog-actions-border: none; } :host([disable-animation]) { @@ -115,7 +115,7 @@ } :host([data-overflows]:not([data-fullscreen], [negative])) { - --sbb-dialog-footer-border: none; + --sbb-dialog-actions-border: none; } :host(:not([data-state='closed'])) { @@ -211,22 +211,6 @@ } } -.sbb-dialog__footer { - padding-inline: var(--sbb-dialog-padding-inline); - padding-block: var(--sbb-spacing-responsive-s); - margin-block-start: auto; - background-color: var(--sbb-dialog-background-color); - border-block-start: var(--sbb-dialog-footer-border); - - :host(:not([data-slot-names~='action-group'])) & { - display: none; - } - - :host([data-overflows]:not([data-fullscreen], [negative])) & { - @include sbb.shadow-level-9-soft; - } -} - // It is necessary to use animations with keyframes instead of transitions in order not to alter // the default `display: block` of the modal otherwise it causes several problems, // especially for accessibility. diff --git a/src/components/dialog/dialog.stories.ts b/src/components/dialog/dialog.stories.ts index 30e11fbdcc..9c29ece973 100644 --- a/src/components/dialog/dialog.stories.ts +++ b/src/components/dialog/dialog.stories.ts @@ -31,6 +31,7 @@ import '../image'; import '../action-group'; import '../dialog-title'; import '../dialog-content'; +import '../dialog-actions'; // Story interaction executed after the story renders const playStory = async ({ canvasElement }: StoryContext): Promise => { @@ -160,13 +161,8 @@ const triggerButton = (dialogId: string): TemplateResult => html` `; -const actionGroup = (negative: boolean): TemplateResult => html` - +const actionGroup = (negative: unknown): TemplateResult => html` + html` Cancel Confirm - + `; const codeStyle: Args = { @@ -226,7 +222,7 @@ const dialogHeader = (args): TemplateResult => html` accessibility-close-label=${args.accessibilityCloseLabel} accessibility-back-label=${args.accessibilityBackLabel} ?negative=${args.negative} - hide-on-scroll="small" + hide-on-scroll >Title `; @@ -238,7 +234,7 @@ const dialogHeader = (args: Args): TemplateResult => html` accessibility-close-label=${args.accessibilityCloseLabel} accessibility-back-label=${args.accessibilityBackLabel} ?negative=${args.negative} - hide-on-scroll="small" + hide-on-scroll >Title `; diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts index b8dfdb28c6..d95f4b3f56 100644 --- a/src/components/dialog/dialog.ts +++ b/src/components/dialog/dialog.ts @@ -12,7 +12,8 @@ import { SbbNegativeMixin } from '../core/mixins'; import { AgnosticResizeObserver } from '../core/observers'; import type { SbbOverlayState } from '../core/overlay'; import { applyInertMechanism, removeInertMechanism } from '../core/overlay'; -import type { SbbSbbDialogTitleElement } from '../dialog-title'; +import type { SbbDialogActionsElement } from '../dialog-actions'; +import type { SbbDialogTitleElement } from '../dialog-title'; import style from './dialog.scss?lit&inline'; @@ -102,6 +103,7 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { private _dialogTitleElement!: SbbDialogTitleElement; private _dialogTitleHeight!: number; private _dialogContentElement!: HTMLElement; + private _dialogActionsElement?: SbbDialogActionsElement; private _dialogCloseElement?: HTMLElement; private _dialogController!: AbortController; private _openDialogController!: AbortController; @@ -131,6 +133,7 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { this._dialogTitleElement = this.querySelector('sbb-dialog-title')!; this._dialogContentElement = this.querySelector('sbb-dialog-content')!.shadowRoot! .firstElementChild as HTMLElement; + this._dialogActionsElement = this.querySelector('sbb-dialog-actions') || undefined; if (!this._willOpen.emit()) { return; @@ -402,13 +405,14 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { const hideOnScroll = this._dialogTitleElement.hideOnScroll; const hideHeader = !!hideOnScroll && isBreakpoint('zero', hideOnScroll, { includeMaxBreakpoint: true }); - this.toggleAtribute('data-hide-header', !hideHeader ? false : value); - this._dialogTitleElement.toggleAtribute('data-hide-header', !hideHeader ? false : value); + this.toggleAttribute('data-hide-header', !hideHeader ? false : value); + this._dialogTitleElement.toggleAttribute('data-hide-header', !hideHeader ? false : value); } private _setOverflowsDataAttribute(): void { - this.toggleAtribute('data-overflows', this._overflows); - this._dialogTitleElement.toggleAtribute('data-overflows', this._overflows); + this.toggleAttribute('data-overflows', this._overflows); + this._dialogTitleElement.toggleAttribute('data-overflows', this._overflows); + this._dialogActionsElement ?? this._dialogActionsElement.toggleAttribute('data-overflows', this._overflows); } protected override render(): TemplateResult { @@ -425,9 +429,7 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { > - +
From 6de6568854472550b7c26d630b29676924385deb Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Fri, 2 Feb 2024 11:41:59 +0100 Subject: [PATCH 15/65] fix: rebase --- .../dialog-actions/dialog-actions.ts | 3 +- .../dialog-content/dialog-content.ts | 3 +- src/components/dialog-title/dialog-title.ts | 9 +++-- src/components/dialog/dialog.stories.ts | 35 ++----------------- 4 files changed, 12 insertions(+), 38 deletions(-) diff --git a/src/components/dialog-actions/dialog-actions.ts b/src/components/dialog-actions/dialog-actions.ts index a69e17cec6..e1e6c4d1bc 100644 --- a/src/components/dialog-actions/dialog-actions.ts +++ b/src/components/dialog-actions/dialog-actions.ts @@ -1,4 +1,5 @@ -import { CSSResultGroup, html, TemplateResult } from 'lit'; +import type { CSSResultGroup, TemplateResult } from 'lit'; +import { html } from 'lit'; import { customElement } from 'lit/decorators.js'; import { SbbActionGroupElement } from '../action-group'; diff --git a/src/components/dialog-content/dialog-content.ts b/src/components/dialog-content/dialog-content.ts index 447830eb87..4b785b1fd8 100644 --- a/src/components/dialog-content/dialog-content.ts +++ b/src/components/dialog-content/dialog-content.ts @@ -1,4 +1,5 @@ -import { CSSResultGroup, html, LitElement, TemplateResult } from 'lit'; +import type { CSSResultGroup, TemplateResult } from 'lit'; +import { html, LitElement } from 'lit'; import { customElement } from 'lit/decorators.js'; import { setAttribute } from '../core/dom'; diff --git a/src/components/dialog-title/dialog-title.ts b/src/components/dialog-title/dialog-title.ts index eab82b061a..01f301716a 100644 --- a/src/components/dialog-title/dialog-title.ts +++ b/src/components/dialog-title/dialog-title.ts @@ -1,11 +1,14 @@ -import { CSSResultGroup, html, nothing, TemplateResult } from 'lit'; +import type { CSSResultGroup, TemplateResult } from 'lit'; +import { html, nothing } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { LanguageController } from '../core/common-behaviors'; -import { Breakpoint, setAttribute } from '../core/dom'; +import type { Breakpoint } from '../core/dom'; +import { setAttribute } from '../core/dom'; import { ConnectedAbortController, EventEmitter } from '../core/eventing'; import { i18nCloseDialog, i18nGoBack } from '../core/i18n'; -import { SbbTitleElement, TitleLevel } from '../title'; +import type { TitleLevel } from '../title'; +import { SbbTitleElement } from '../title'; import '../button'; import style from './dialog-title.scss?lit&inline'; diff --git a/src/components/dialog/dialog.stories.ts b/src/components/dialog/dialog.stories.ts index 9c29ece973..05ce54ca90 100644 --- a/src/components/dialog/dialog.stories.ts +++ b/src/components/dialog/dialog.stories.ts @@ -161,7 +161,7 @@ const triggerButton = (dialogId: string): TemplateResult => html` `; -const actionGroup = (negative: unknown): TemplateResult => html` +const actionGroup = (negative: boolean): TemplateResult => html` html` -
- J.R.R. Tolkien, the mastermind behind Middle-earth's enchanting world, was born on January 3, - 1892. With "The Hobbit" and "The Lord of the Rings", he pioneered fantasy literature. Tolkien's - linguistic brilliance and mythic passion converge in a literary legacy that continues to - transport readers to magical realms. -
-`; - -const dialogHeader = (args): TemplateResult => html` - Title -`; - const dialogHeader = (args: Args): TemplateResult => html` html` accessibility-close-label=${args.accessibilityCloseLabel} accessibility-back-label=${args.accessibilityBackLabel} ?negative=${args.negative} - hide-on-scroll + hide-on-scroll="" >Title `; -const textBlockStyle: Args = { - position: 'relative', - marginBlockStart: '1rem', - padding: '1rem', - backgroundColor: 'var(--sbb-color-milk-default)', - border: 'var(--sbb-border-width-1x) solid var(--sbb-color-cloud-default)', - borderRadius: 'var(--sbb-border-radius-4x)', - zIndex: '100', -}; - const textBlock = (): TemplateResult => html`
J.R.R. Tolkien, the mastermind behind Middle-earth's enchanting world, was born on January 3, From ac2e2ad4a9079476202029e0918672b003bed771 Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Mon, 5 Feb 2024 09:36:46 +0100 Subject: [PATCH 16/65] fix: integrity --- src/components/dialog-actions/readme.md | 22 ++++++++++++++++++++++ src/components/dialog-content/readme.md | 12 ++++++++++++ src/components/dialog-title/readme.md | 7 +------ src/components/dialog/dialog.ts | 3 ++- src/components/dialog/readme.md | 1 - 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/components/dialog-actions/readme.md b/src/components/dialog-actions/readme.md index 0a4bd342cc..6244ffc55f 100644 --- a/src/components/dialog-actions/readme.md +++ b/src/components/dialog-actions/readme.md @@ -42,3 +42,25 @@ The `sbb-dialog-actions` is a component . . . > Describe how accessibility is implemented and if there are issues or suggested best-practice for the consumers. + +## Properties + +| 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` | `'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` | `'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. | + +## Events + +| Name | Type | Description | Inherited From | +| ------------- | ------------------ | ------------------------- | -------------- | +| `myEventName` | `CustomEvent` | TODO: Document this event | | + +## Slots + +| Name | Description | +| ---- | ------------------------------------------------ | +| | Use the unnamed slot to add `sbb-TODO` elements. | diff --git a/src/components/dialog-content/readme.md b/src/components/dialog-content/readme.md index 6af6578481..7993f094b5 100644 --- a/src/components/dialog-content/readme.md +++ b/src/components/dialog-content/readme.md @@ -42,3 +42,15 @@ The `sbb-dialog-content` is a component . . . > Describe how accessibility is implemented and if there are issues or suggested best-practice for the consumers. + +## Events + +| Name | Type | Description | Inherited From | +| ------------- | ------------------ | ------------------------- | -------------- | +| `myEventName` | `CustomEvent` | TODO: Document this event | | + +## Slots + +| Name | Description | +| ---- | ------------------------------------------------ | +| | Use the unnamed slot to add `sbb-TODO` elements. | diff --git a/src/components/dialog-title/readme.md b/src/components/dialog-title/readme.md index bf7a2d7e0e..5cfe4f12cd 100644 --- a/src/components/dialog-title/readme.md +++ b/src/components/dialog-title/readme.md @@ -51,16 +51,11 @@ The `sbb-dialog-title` is a component . . . | `titleBackButton` | `title-back-button` | public | `boolean` | `false` | Whether a back button is displayed next to the title. | | `accessibilityCloseLabel` | `accessibility-close-label` | public | `\| string \| undefined` | | This will be forwarded as aria-label to the close button element. | | `accessibilityBackLabel` | `accessibility-back-label` | public | `\| string \| undefined` | | This will be forwarded as aria-label to the back button element. | +| `hideOnScroll` | `hide-on-scroll` | public | `false \| '' \| Breakpoint` | `false` | Whether to hide the title up to a certain breakpoint. | | `level` | `level` | public | `TitleLevel \| undefined` | `'1'` | Title level | | `visuallyHidden` | `visually-hidden` | public | `boolean \| undefined` | | Sometimes we need a title in the markup to present a proper hierarchy to the screen readers while we do not want to let that title appear visually. In this case we set visuallyHidden to true | | `negative` | `negative` | public | `boolean \| undefined` | `false` | Choose negative variant | -## Methods - -| Name | Privacy | Description | Parameters | Return | Inherited From | -| ----------------------- | ------- | ----------- | ---------------- | ------ | -------------- | -| `setOverflowsAttribute` | public | | `value: boolean` | `void` | | - ## Events | Name | Type | Description | Inherited From | diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts index d95f4b3f56..fcf4d1a9b2 100644 --- a/src/components/dialog/dialog.ts +++ b/src/components/dialog/dialog.ts @@ -412,7 +412,8 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { private _setOverflowsDataAttribute(): void { this.toggleAttribute('data-overflows', this._overflows); this._dialogTitleElement.toggleAttribute('data-overflows', this._overflows); - this._dialogActionsElement ?? this._dialogActionsElement.toggleAttribute('data-overflows', this._overflows); + this._dialogActionsElement ?? + this._dialogActionsElement.toggleAttribute('data-overflows', this._overflows); } protected override render(): TemplateResult { diff --git a/src/components/dialog/readme.md b/src/components/dialog/readme.md index eb123c4064..5b3127dd5e 100644 --- a/src/components/dialog/readme.md +++ b/src/components/dialog/readme.md @@ -80,7 +80,6 @@ It's possible to display the component in `negative` variant using the self-name | Name | Attribute | Privacy | Type | Default | Description | | -------------------- | --------------------- | ------- | --------------------- | --------- | -------------------------------------------------------------------- | -| `titleContent` | `title-content` | public | `string` | | Dialog title. | | `backdropAction` | `backdrop-action` | public | `'close' \| 'none'` | `'close'` | Backdrop click action. | | `negative` | `negative` | public | `boolean` | `false` | Negative coloring variant flag. | | `accessibilityLabel` | `accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the relevant nested element. | From bb721ade394d6b34bbeda56f4b6a520635baf9c5 Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Mon, 5 Feb 2024 09:43:46 +0100 Subject: [PATCH 17/65] fix: lint --- src/components/dialog-actions/dialog-actions.e2e.ts | 11 +---------- .../dialog-actions/dialog-actions.stories.ts | 8 ++------ .../dialog-content/dialog-content.stories.ts | 3 ++- src/components/dialog-title/dialog-title.stories.ts | 3 ++- src/components/dialog/dialog.e2e.ts | 2 +- 5 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/components/dialog-actions/dialog-actions.e2e.ts b/src/components/dialog-actions/dialog-actions.e2e.ts index cc7294bbc7..b7ed049612 100644 --- a/src/components/dialog-actions/dialog-actions.e2e.ts +++ b/src/components/dialog-actions/dialog-actions.e2e.ts @@ -1,8 +1,6 @@ -import { assert, expect, fixture } from '@open-wc/testing'; +import { assert, fixture } from '@open-wc/testing'; import { html } from 'lit/static-html.js'; -import { EventSpy, waitForLitRender } from '../core/testing'; - import { SbbDialogActionsElement } from './dialog-actions'; describe('sbb-dialog-actions', () => { @@ -15,11 +13,4 @@ describe('sbb-dialog-actions', () => { it('renders', async () => { assert.instanceOf(element, SbbDialogActionsElement); }); - - it('emits on click', async () => { - const myEventNameSpy = new EventSpy(SbbDialogActionsElement.events.myEventName); - element.click(); - await waitForLitRender(element); - expect(myEventNameSpy.count).to.be.equal(1); - }); }); diff --git a/src/components/dialog-actions/dialog-actions.stories.ts b/src/components/dialog-actions/dialog-actions.stories.ts index d0f6a3cbf1..88e36fe4bc 100644 --- a/src/components/dialog-actions/dialog-actions.stories.ts +++ b/src/components/dialog-actions/dialog-actions.stories.ts @@ -1,13 +1,12 @@ import { withActions } from '@storybook/addon-actions/decorator'; import type { InputType } from '@storybook/types'; import type { Args, ArgTypes, Decorator, Meta, StoryObj } from '@storybook/web-components'; -import { html, TemplateResult } from 'lit'; +import type { TemplateResult } from 'lit'; +import { html } from 'lit'; import { sbbSpread } from '../core/dom'; -import type { SbbDialogActionsElement } from './dialog-actions'; import readme from './readme.md?raw'; - import './dialog-actions'; const myProp: InputType = { @@ -39,9 +38,6 @@ const meta: Meta = { withActions as Decorator, ], parameters: { - actions: { - handles: [SbbDialogActionsElement.events.myEventName], - }, backgrounds: { disable: true, }, diff --git a/src/components/dialog-content/dialog-content.stories.ts b/src/components/dialog-content/dialog-content.stories.ts index 4a1b06e9bd..8dd9cbae0c 100644 --- a/src/components/dialog-content/dialog-content.stories.ts +++ b/src/components/dialog-content/dialog-content.stories.ts @@ -1,7 +1,8 @@ import { withActions } from '@storybook/addon-actions/decorator'; import type { InputType } from '@storybook/types'; import type { Args, ArgTypes, Decorator, Meta, StoryObj } from '@storybook/web-components'; -import { html, TemplateResult } from 'lit'; +import type { TemplateResult } from 'lit'; +import { html } from 'lit'; import { sbbSpread } from '../core/dom'; diff --git a/src/components/dialog-title/dialog-title.stories.ts b/src/components/dialog-title/dialog-title.stories.ts index 92cb5f3965..d4065dc1c8 100644 --- a/src/components/dialog-title/dialog-title.stories.ts +++ b/src/components/dialog-title/dialog-title.stories.ts @@ -1,7 +1,8 @@ import { withActions } from '@storybook/addon-actions/decorator'; import type { InputType } from '@storybook/types'; import type { Args, ArgTypes, Decorator, Meta, StoryObj } from '@storybook/web-components'; -import { html, TemplateResult } from 'lit'; +import type { TemplateResult } from 'lit'; +import { html } from 'lit'; import { sbbSpread } from '../core/dom'; diff --git a/src/components/dialog/dialog.e2e.ts b/src/components/dialog/dialog.e2e.ts index 8bd860819b..fa6dcabfec 100644 --- a/src/components/dialog/dialog.e2e.ts +++ b/src/components/dialog/dialog.e2e.ts @@ -470,7 +470,7 @@ describe('sbb-dialog with long content', () => { await openDialog(element); expect(element).not.to.have.attribute('data-hide-header'); - const content = element.shadowRoot.querySelector('.sbb-dialog__content'); + const content = element.shadowRoot!.querySelector('.sbb-dialog__content')!; // Scroll down. content.scrollTo(0, 50); From 3901c356ca147dca745862476b4534834c0589dc Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Wed, 7 Feb 2024 10:23:21 +0100 Subject: [PATCH 18/65] feat: handle negative state --- .../dialog-actions/dialog-actions.scss | 2 +- src/components/dialog-title/dialog-title.ts | 21 +--- src/components/dialog/dialog.stories.ts | 97 ++++++++++++------- src/components/dialog/dialog.ts | 25 ++++- 4 files changed, 88 insertions(+), 57 deletions(-) diff --git a/src/components/dialog-actions/dialog-actions.scss b/src/components/dialog-actions/dialog-actions.scss index 7fbac4492e..e80846c081 100644 --- a/src/components/dialog-actions/dialog-actions.scss +++ b/src/components/dialog-actions/dialog-actions.scss @@ -15,7 +15,7 @@ background-color: var(--sbb-dialog-background-color); border-block-start: var(--sbb-dialog-actions-border); - :host([data-overflows]:not([negative])) & { + :host([data-overflows]:not([data-negative])) & { @include sbb.shadow-level-9-soft; } } diff --git a/src/components/dialog-title/dialog-title.ts b/src/components/dialog-title/dialog-title.ts index 01f301716a..9472400dd1 100644 --- a/src/components/dialog-title/dialog-title.ts +++ b/src/components/dialog-title/dialog-title.ts @@ -5,7 +5,7 @@ import { customElement, property } from 'lit/decorators.js'; import { LanguageController } from '../core/common-behaviors'; import type { Breakpoint } from '../core/dom'; import { setAttribute } from '../core/dom'; -import { ConnectedAbortController, EventEmitter } from '../core/eventing'; +import { EventEmitter } from '../core/eventing'; import { i18nCloseDialog, i18nGoBack } from '../core/i18n'; import type { TitleLevel } from '../title'; import { SbbTitleElement } from '../title'; @@ -15,7 +15,7 @@ import style from './dialog-title.scss?lit&inline'; /** * It displays a title inside a dialog header. - * @event {CustomEvent} requestBackAction - TODO: Document this event + * @event {CustomEvent} requestBackAction - Emits whenever the back button is clicked. */ @customElement('sbb-dialog-title') export class SbbDialogTitleElement extends SbbTitleElement { @@ -61,29 +61,12 @@ export class SbbDialogTitleElement extends SbbTitleElement { } private _hideOnScroll: false | '' | Breakpoint = false; - private _abort = new ConnectedAbortController(this); private _backClick: EventEmitter = new EventEmitter( this, SbbDialogTitleElement.events.backClick, ); private _language = new LanguageController(this); - private _onClickFn(): void { - this._backClick.emit(); - } - - public override connectedCallback(): void { - super.connectedCallback(); - const signal = this._abort.signal; - this.addEventListener('click', () => this._onClickFn(), { signal }); - // do stuff - } - - public override disconnectedCallback(): void { - super.disconnectedCallback(); - // do stuff - } - protected override render(): TemplateResult { setAttribute(this, 'slot', 'title'); diff --git a/src/components/dialog/dialog.stories.ts b/src/components/dialog/dialog.stories.ts index 05ce54ca90..1fe826a29b 100644 --- a/src/components/dialog/dialog.stories.ts +++ b/src/components/dialog/dialog.stories.ts @@ -18,6 +18,7 @@ import { sbbSpread } from '../../storybook/helpers/spread'; import { waitForComponentsReady } from '../../storybook/testing/wait-for-components-ready'; import { waitForStablePosition } from '../../storybook/testing/wait-for-stable-position'; import sampleImages from '../core/images'; +import { SbbDialogTitleElement } from '../dialog-title'; import { SbbDialogElement } from './dialog'; import readme from './readme.md?raw'; @@ -29,7 +30,6 @@ import '../title'; import '../form-field'; import '../image'; import '../action-group'; -import '../dialog-title'; import '../dialog-content'; import '../dialog-actions'; @@ -47,29 +47,30 @@ const playStory = async ({ canvasElement }: StoryContext): Promise => { await userEvent.click(button); }; -const titleContent: InputType = { +const titleLevel: InputType = { control: { - type: 'text', + type: 'inline-radio', }, + options: [1, 2, 3, 4, 5, 6], table: { category: 'Header', }, }; -const titleLevel: InputType = { +const titleBackButton: InputType = { control: { - type: 'inline-radio', + type: 'boolean', }, - options: [1, 2, 3, 4, 5, 6], table: { category: 'Header', }, }; -const titleBackButton: InputType = { +const hideOnScroll: InputType = { control: { - type: 'boolean', + type: 'select', }, + options: [false, '', 'zero', 'micro', 'small', 'medium', 'large', 'wide', 'ultra'], table: { category: 'Header', }, @@ -122,25 +123,25 @@ const backdropAction: InputType = { }; const basicArgTypes: ArgTypes = { - 'title-content': titleContent, - 'title-level': titleLevel, - 'title-back-button': titleBackButton, + titleLevel, + titleBackButton, + hideOnScroll, + accessibilityCloseLabel, + accessibilityBackLabel, negative, 'accessibility-label': accessibilityLabel, - 'accessibility-close-label': accessibilityCloseLabel, - 'accessibility-back-label': accessibilityBackLabel, 'disable-animation': disableAnimation, 'backdrop-action': backdropAction, }; const basicArgs: Args = { - 'title-content': 'A describing title of the dialog', - 'title-level': undefined, - 'title-back-button': true, + titleLevel: undefined, + titleBackButton: true, + hideOnScroll: hideOnScroll.options[1], + accessibilityCloseLabel: 'Close dialog', + accessibilityBackLabel: 'Go back', negative: false, - 'accessibility-label': undefined, - 'accessibility-close-label': undefined, - 'accessibility-back-label': undefined, + 'accessibility-label': 'Dialog aria-label', 'disable-animation': isChromatic(), 'backdrop-action': backdropAction.options[0], }; @@ -206,15 +207,20 @@ const textBlockStyle: Args = { borderRadius: 'var(--sbb-border-radius-4x)', }; -const dialogHeader = (args: Args): TemplateResult => html` +const dialogHeader = ( + titleLevel: number, + titleBackButton: boolean, + hideOnScroll: any, + accessibilityCloseLabel: string, + accessibilityBackLabel: string, +): TemplateResult => html` TitleA describing title of the dialog `; @@ -227,10 +233,23 @@ const textBlock = (): TemplateResult => html`
`; -const DefaultTemplate = (args: Args): TemplateResult => html` +const DefaultTemplate = ({ + titleLevel, + titleBackButton, + hideOnScroll, + accessibilityCloseLabel, + accessibilityBackLabel, + ...args +}: Args): TemplateResult => html` ${triggerButton('my-dialog-1')} - ${dialogHeader(args)} + ${dialogHeader( + titleLevel, + titleBackButton, + hideOnScroll, + accessibilityCloseLabel, + accessibilityBackLabel, + )}

Dialog content

@@ -255,10 +274,23 @@ const SlottedTitleTemplate = (args: Args): TemplateResult => html`
`; -const LongContentTemplate = (args: Args): TemplateResult => html` +const LongContentTemplate = ({ + titleLevel, + titleBackButton, + hideOnScroll, + accessibilityCloseLabel, + accessibilityBackLabel, + ...args +}: Args): TemplateResult => html` ${triggerButton('my-dialog-3')} - ${dialogHeader(args)} + ${dialogHeader( + titleLevel, + titleBackButton, + hideOnScroll, + accessibilityCloseLabel, + accessibilityBackLabel, + )} Frodo halted for a moment, looking back. Elrond was in his chair and the fire was on his face like summer-light upon the trees. Near him sat the Lady Arwen. To his surprise Frodo saw that @@ -396,7 +428,6 @@ export const SlottedTitle: StoryObj = { argTypes: basicArgTypes, args: { ...basicArgs, - 'title-content': undefined, 'title-back-button': false, }, play: isChromatic() ? playStory : undefined, @@ -449,7 +480,7 @@ const meta: Meta = { SbbDialogElement.events.didOpen, SbbDialogElement.events.willClose, SbbDialogElement.events.didClose, - SbbDialogElement.events.backClick, + SbbDialogTitleElement.events.backClick, ], }, backgrounds: { diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts index fcf4d1a9b2..465306da7e 100644 --- a/src/components/dialog/dialog.ts +++ b/src/components/dialog/dialog.ts @@ -1,4 +1,4 @@ -import type { CSSResultGroup, TemplateResult } from 'lit'; +import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit'; import { LitElement } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { html, unsafeStatic } from 'lit/static-html.js'; @@ -36,7 +36,6 @@ let nextId = 0; * @event {CustomEvent} didOpen - Emits whenever the `sbb-dialog` is opened. * @event {CustomEvent} willClose - Emits whenever the `sbb-dialog` begins the closing transition. Can be canceled. * @event {CustomEvent} didClose - Emits whenever the `sbb-dialog` is closed. - * @event {CustomEvent} requestBackAction - Emits whenever the back button is clicked. * @cssprop [--sbb-dialog-z-index=var(--sbb-overlay-default-z-index)] - To specify a custom stack order, * the `z-index` can be overridden by defining this CSS variable. The default `z-index` of the * component is set to `var(--sbb-overlay-default-z-index)` with a value of `1000`. @@ -49,7 +48,6 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { didOpen: 'didOpen', willClose: 'willClose', didClose: 'didClose', - backClick: 'requestBackAction', } as const; /** @@ -135,6 +133,8 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { .firstElementChild as HTMLElement; this._dialogActionsElement = this.querySelector('sbb-dialog-actions') || undefined; + this._syncNegative(); + if (!this._willOpen.emit()) { return; } @@ -235,6 +235,22 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { } } + protected override willUpdate(changedProperties: PropertyValues): void { + if (changedProperties.has('negative')) { + this._syncNegative(); + } + } + + private _syncNegative(): void { + if (this._dialogTitleElement) { + this._dialogTitleElement.negative = this.negative; + } + + if (this._dialogActionsElement) { + toggleDatasetEntry(this._dialogActionsElement!, 'negative', this.negative); + } + } + public override disconnectedCallback(): void { super.disconnectedCallback(); this._dialogController?.abort(); @@ -412,8 +428,9 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { private _setOverflowsDataAttribute(): void { this.toggleAttribute('data-overflows', this._overflows); this._dialogTitleElement.toggleAttribute('data-overflows', this._overflows); - this._dialogActionsElement ?? + if (this._dialogActionsElement) { this._dialogActionsElement.toggleAttribute('data-overflows', this._overflows); + } } protected override render(): TemplateResult { From 4f78ca97012a1d8c1bc57340284a7434b574eb9b Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Wed, 7 Feb 2024 15:20:15 +0100 Subject: [PATCH 19/65] fix: dialog stories --- src/components/dialog/dialog.stories.ts | 175 ++++++++++-------------- 1 file changed, 72 insertions(+), 103 deletions(-) diff --git a/src/components/dialog/dialog.stories.ts b/src/components/dialog/dialog.stories.ts index 1fe826a29b..f03b156aae 100644 --- a/src/components/dialog/dialog.stories.ts +++ b/src/components/dialog/dialog.stories.ts @@ -47,13 +47,13 @@ const playStory = async ({ canvasElement }: StoryContext): Promise => { await userEvent.click(button); }; -const titleLevel: InputType = { +const level: InputType = { control: { type: 'inline-radio', }, options: [1, 2, 3, 4, 5, 6], table: { - category: 'Header', + category: 'Title', }, }; @@ -62,7 +62,7 @@ const titleBackButton: InputType = { type: 'boolean', }, table: { - category: 'Header', + category: 'Title', }, }; @@ -72,7 +72,7 @@ const hideOnScroll: InputType = { }, options: [false, '', 'zero', 'micro', 'small', 'medium', 'large', 'wide', 'ultra'], table: { - category: 'Header', + category: 'Title', }, }; @@ -123,7 +123,7 @@ const backdropAction: InputType = { }; const basicArgTypes: ArgTypes = { - titleLevel, + level, titleBackButton, hideOnScroll, accessibilityCloseLabel, @@ -135,7 +135,7 @@ const basicArgTypes: ArgTypes = { }; const basicArgs: Args = { - titleLevel: undefined, + level: level.options[0], titleBackButton: true, hideOnScroll: hideOnScroll.options[1], accessibilityCloseLabel: 'Close dialog', @@ -208,14 +208,14 @@ const textBlockStyle: Args = { }; const dialogHeader = ( - titleLevel: number, + level: number, titleBackButton: boolean, hideOnScroll: any, accessibilityCloseLabel: string, accessibilityBackLabel: string, ): TemplateResult => html` html` `; const DefaultTemplate = ({ - titleLevel, + level, titleBackButton, hideOnScroll, accessibilityCloseLabel, @@ -244,7 +244,7 @@ const DefaultTemplate = ({ ${triggerButton('my-dialog-1')} ${dialogHeader( - titleLevel, + level, titleBackButton, hideOnScroll, accessibilityCloseLabel, @@ -257,25 +257,8 @@ const DefaultTemplate = ({ `; -const SlottedTitleTemplate = (args: Args): TemplateResult => html` - ${triggerButton('my-dialog-2')} - - - - The Catcher in the Rye - -

- “What really knocks me out is a book that, when you're all done reading it, you wish the - author that wrote it was a terrific friend of yours and you could call him up on the phone - whenever you felt like it. That doesn't happen much, though.” ― J.D. Salinger, The Catcher in - the Rye -

- ${actionGroup(args.negative)} -
-`; - const LongContentTemplate = ({ - titleLevel, + level, titleBackButton, hideOnScroll, accessibilityCloseLabel, @@ -285,7 +268,7 @@ const LongContentTemplate = ({ ${triggerButton('my-dialog-3')} ${dialogHeader( - titleLevel, + level, titleBackButton, hideOnScroll, accessibilityCloseLabel, @@ -313,7 +296,14 @@ const LongContentTemplate = ({ `; -const FormTemplate = (args: Args): TemplateResult => html` +const FormTemplate = ({ + level, + titleBackButton, + hideOnScroll, + accessibilityCloseLabel, + accessibilityBackLabel, + ...args +}: Args): TemplateResult => html` ${triggerButton('my-dialog-4')}
@@ -325,7 +315,7 @@ const FormTemplate = (args: Args): TemplateResult => html` data-testid="dialog" id="my-dialog-4" @willClose=${(event: CustomEvent) => { - if (event.detail) { + if (event.detail.returnValue) { document.getElementById('returned-value-message')!.innerHTML = `${event.detail.returnValue.message?.value}`; document.getElementById('returned-value-animal')!.innerHTML = @@ -334,68 +324,64 @@ const FormTemplate = (args: Args): TemplateResult => html` }} ${sbbSpread(args)} > -
- Submit the form below to close the dialog box using the - close(result?: any, target?: HTMLElement) - method and returning the form values to update the details. -
-
e.preventDefault()}> - - + ${dialogHeader( + level, + titleBackButton, + hideOnScroll, + accessibilityCloseLabel, + accessibilityBackLabel, + )} + +
+ Submit the form below to close the dialog box using the + close(result?: any, target?: HTMLElement) + method and returning the form values to update the details. +
+ e.preventDefault()}> + + - - + + - - - Update details - + +
+ Update details + + `; -const NoFooterTemplate = (args: Args): TemplateResult => html` +const NoFooterTemplate = ({ + level, + titleBackButton, + hideOnScroll, + accessibilityCloseLabel, + accessibilityBackLabel, + ...args +}: Args): TemplateResult => html` ${triggerButton('my-dialog-5')} -

- “What really knocks me out is a book that, when you're all done reading it, you wish the - author that wrote it was a terrific friend of yours and you could call him up on the phone - whenever you felt like it. That doesn't happen much, though.” ― J.D. Salinger, The Catcher in - the Rye -

-
-`; - -const FullScreenTemplate = (args: Args): TemplateResult => html` - ${triggerButton('my-dialog-6')} - - - Many Meetings - - Frodo halted for a moment, looking back. Elrond was in his chair and the fire was on his face - like summer-light upon the trees. Near him sat the Lady Arwen. To his surprise Frodo saw that - Aragorn stood beside her; his dark cloak was thrown back, and he seemed to be clad in - elven-mail, and a star shone on his breast. They spoke together, and then suddenly it seemed to - Frodo that Arwen turned towards him, and the light of her eyes fell on him from afar and pierced - his heart. - - He stood still enchanted, while the sweet syllables of the elvish song fell like clear jewels of - blended word and melody. 'It is a song to Elbereth,'' said Bilbo. 'They will sing that, and - other songs of the Blessed Realm, many times tonight. Come on!’ —J.R.R. Tolkien, The Lord of the - Rings: The Fellowship of the Ring, “Many Meetings” ${actionGroup(args.negative)} + ${dialogHeader( + level, + titleBackButton, + hideOnScroll, + accessibilityCloseLabel, + accessibilityBackLabel, + )} + +

+ “What really knocks me out is a book that, when you're all done reading it, you wish the + author that wrote it was a terrific friend of yours and you could call him up on the phone + whenever you felt like it. That doesn't happen much, though.” ― J.D. Salinger, The Catcher + in the Rye +

+
`; @@ -423,16 +409,6 @@ export const AllowBackdropClick: StoryObj = { play: isChromatic() ? playStory : undefined, }; -export const SlottedTitle: StoryObj = { - render: SlottedTitleTemplate, - argTypes: basicArgTypes, - args: { - ...basicArgs, - 'title-back-button': false, - }, - play: isChromatic() ? playStory : undefined, -}; - export const LongContent: StoryObj = { render: LongContentTemplate, argTypes: basicArgTypes, @@ -454,13 +430,6 @@ export const NoFooter: StoryObj = { play: isChromatic() ? playStory : undefined, }; -export const FullScreen: StoryObj = { - render: FullScreenTemplate, - argTypes: basicArgTypes, - args: { ...basicArgs, 'title-content': undefined }, - play: isChromatic() ? playStory : undefined, -}; - const meta: Meta = { decorators: [ (story) => html` From e05e98c51ae523f84d341d7f26c361c05ee740cb Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Wed, 7 Feb 2024 15:40:12 +0100 Subject: [PATCH 20/65] fix: dialog title stories --- src/components/dialog-title/dialog-title.scss | 7 --- .../dialog-title/dialog-title.stories.ts | 50 +++++++++++++++++-- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/components/dialog-title/dialog-title.scss b/src/components/dialog-title/dialog-title.scss index cbdd2be7ed..0c6d9f8cbd 100644 --- a/src/components/dialog-title/dialog-title.scss +++ b/src/components/dialog-title/dialog-title.scss @@ -7,16 +7,9 @@ :host { --sbb-dialog-header-padding-block: var(--sbb-spacing-responsive-s) 0; - // --sbb-dialog-header-margin-block-start: 0; - display: contents; } -// :host([data-hide-header]) { -// // Hide transition -// --sbb-dialog-header-margin-block-start: calc(var(--sbb-dialog-header-height) * -1); -// } - :host([data-overflows]:not([data-fullscreen])) { --sbb-dialog-header-padding-block: var(--sbb-spacing-responsive-s); } diff --git a/src/components/dialog-title/dialog-title.stories.ts b/src/components/dialog-title/dialog-title.stories.ts index d4065dc1c8..c61ed91d58 100644 --- a/src/components/dialog-title/dialog-title.stories.ts +++ b/src/components/dialog-title/dialog-title.stories.ts @@ -6,22 +6,61 @@ import { html } from 'lit'; import { sbbSpread } from '../core/dom'; +import { SbbDialogTitleElement } from './dialog-title'; import readme from './readme.md?raw'; -import './dialog-title'; +const level: InputType = { + control: { + type: 'inline-radio', + }, + options: [1, 2, 3, 4, 5, 6], +}; + +const titleBackButton: InputType = { + control: { + type: 'boolean', + }, +}; -const titleContent: InputType = { +const hideOnScroll: InputType = { + control: { + type: 'select', + }, + options: [false, '', 'zero', 'micro', 'small', 'medium', 'large', 'wide', 'ultra'], +}; + +const accessibilityCloseLabel: InputType = { control: { type: 'text', }, + table: { + category: 'Accessibility', + }, +}; + +const accessibilityBackLabel: InputType = { + control: { + type: 'text', + }, + table: { + category: 'Accessibility', + }, }; const defaultArgTypes: ArgTypes = { - 'title-content': titleContent, + level, + 'title-back-button': titleBackButton, + 'hide-on-scroll': hideOnScroll, + 'accessibility-close-label': accessibilityCloseLabel, + 'accessibility-back-label': accessibilityBackLabel, }; const defaultArgs: Args = { - 'title-content': 'Title content', + level: level.options[0], + 'title-back-button': true, + 'hide-on-scroll': hideOnScroll.options[0], + 'accessibility-close-label': 'Close dialog', + 'accessibility-back-label': 'Go back', }; const Template = (args: Args): TemplateResult => @@ -39,6 +78,9 @@ const meta: Meta = { withActions as Decorator, ], parameters: { + actions: { + handles: [SbbDialogTitleElement.events.backClick], + }, backgrounds: { disable: true, }, From 1972457cb72b325bba695a99c2d35f934479054f Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Wed, 7 Feb 2024 15:47:31 +0100 Subject: [PATCH 21/65] fix: dialog content stories --- .../dialog-content/dialog-content.stories.ts | 34 +++---------------- .../dialog-content/dialog-content.ts | 31 ++--------------- src/components/dialog-title/dialog-title.ts | 1 + src/components/dialog/dialog.ts | 5 +-- 4 files changed, 11 insertions(+), 60 deletions(-) diff --git a/src/components/dialog-content/dialog-content.stories.ts b/src/components/dialog-content/dialog-content.stories.ts index 8dd9cbae0c..411760755f 100644 --- a/src/components/dialog-content/dialog-content.stories.ts +++ b/src/components/dialog-content/dialog-content.stories.ts @@ -1,36 +1,15 @@ import { withActions } from '@storybook/addon-actions/decorator'; -import type { InputType } from '@storybook/types'; -import type { Args, ArgTypes, Decorator, Meta, StoryObj } from '@storybook/web-components'; +import type { Decorator, Meta, StoryObj } from '@storybook/web-components'; import type { TemplateResult } from 'lit'; import { html } from 'lit'; -import { sbbSpread } from '../core/dom'; - -import { SbbDialogContentElement } from './dialog-content'; +import './dialog-content'; import readme from './readme.md?raw'; -const myProp: InputType = { - control: { - type: 'text', - }, -}; - -const defaultArgTypes: ArgTypes = { - 'my-prop': myProp, -}; - -const defaultArgs: Args = { - 'my-prop': 'Label', -}; +const Template = (): TemplateResult => + html`This is a dialog content.`; -const Template = (args: Args): TemplateResult => - html``; - -export const Default: StoryObj = { - render: Template, - argTypes: defaultArgTypes, - args: { ...defaultArgs }, -}; +export const Default: StoryObj = { render: Template }; const meta: Meta = { decorators: [ @@ -38,9 +17,6 @@ const meta: Meta = { withActions as Decorator, ], parameters: { - actions: { - handles: [SbbDialogContentElement.events.myEventName], - }, backgrounds: { disable: true, }, diff --git a/src/components/dialog-content/dialog-content.ts b/src/components/dialog-content/dialog-content.ts index 4b785b1fd8..a6ddf12ac3 100644 --- a/src/components/dialog-content/dialog-content.ts +++ b/src/components/dialog-content/dialog-content.ts @@ -3,44 +3,17 @@ import { html, LitElement } from 'lit'; import { customElement } from 'lit/decorators.js'; import { setAttribute } from '../core/dom'; -import { ConnectedAbortController, EventEmitter } from '../core/eventing'; import style from './dialog-content.scss?lit&inline'; /** - * Describe the purpose of the component with a single short sentence. + * Use this component to provide a content for an `sbb-dialog`. * - * @slot - Use the unnamed slot to add `sbb-TODO` elements. - * @event {CustomEvent} myEventName - TODO: Document this event + * @slot - Use the unnamed slot to provide a dialog content. */ @customElement('sbb-dialog-content') export class SbbDialogContentElement extends LitElement { public static override styles: CSSResultGroup = style; - public static readonly events: Record = { - myEventName: 'myEventName', - } as const; - - private _abort = new ConnectedAbortController(this); - private _myEvent: EventEmitter = new EventEmitter( - this, - SbbDialogContentElement.events.myEventName, - ); - - private _onClickFn(): void { - this._myEvent.emit(); - } - - public override connectedCallback(): void { - super.connectedCallback(); - const signal = this._abort.signal; - this.addEventListener('click', () => this._onClickFn(), { signal }); - // do stuff - } - - public override disconnectedCallback(): void { - super.disconnectedCallback(); - // do stuff - } protected override render(): TemplateResult { setAttribute(this, 'slot', 'content'); diff --git a/src/components/dialog-title/dialog-title.ts b/src/components/dialog-title/dialog-title.ts index 9472400dd1..56176a0aed 100644 --- a/src/components/dialog-title/dialog-title.ts +++ b/src/components/dialog-title/dialog-title.ts @@ -15,6 +15,7 @@ import style from './dialog-title.scss?lit&inline'; /** * It displays a title inside a dialog header. + * * @event {CustomEvent} requestBackAction - Emits whenever the back button is clicked. */ @customElement('sbb-dialog-title') diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts index 465306da7e..82c0b839de 100644 --- a/src/components/dialog/dialog.ts +++ b/src/components/dialog/dialog.ts @@ -30,8 +30,9 @@ let nextId = 0; * It displays an interactive overlay element. * * @slot - Use the unnamed slot to add content to the `sbb-dialog`. - * @slot title - Use this slot to provide a title. - * @slot action-group - Use this slot to display a `sbb-action-group` in the footer. + * @slot title - Use this slot to provide a `sbb-dialog-title`. + * @slot title - Use this slot to provide a `sbb-dialog-content`. + * @slot actions - Use this slot to provide a `sbb-dialog-actions`. * @event {CustomEvent} willOpen - Emits whenever the `sbb-dialog` starts the opening transition. Can be canceled. * @event {CustomEvent} didOpen - Emits whenever the `sbb-dialog` is opened. * @event {CustomEvent} willClose - Emits whenever the `sbb-dialog` begins the closing transition. Can be canceled. From ccb52847587c66ff4e980c2f32aead332e8e5e0b Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Wed, 7 Feb 2024 15:53:30 +0100 Subject: [PATCH 22/65] fix: dialog actions stories --- .../dialog-actions/dialog-actions.stories.ts | 49 ++++++++----------- .../dialog-actions/dialog-actions.ts | 14 +----- 2 files changed, 22 insertions(+), 41 deletions(-) diff --git a/src/components/dialog-actions/dialog-actions.stories.ts b/src/components/dialog-actions/dialog-actions.stories.ts index 88e36fe4bc..b9ad481de5 100644 --- a/src/components/dialog-actions/dialog-actions.stories.ts +++ b/src/components/dialog-actions/dialog-actions.stories.ts @@ -1,36 +1,29 @@ import { withActions } from '@storybook/addon-actions/decorator'; -import type { InputType } from '@storybook/types'; -import type { Args, ArgTypes, Decorator, Meta, StoryObj } from '@storybook/web-components'; +import type { Decorator, Meta, StoryObj } from '@storybook/web-components'; import type { TemplateResult } from 'lit'; import { html } from 'lit'; -import { sbbSpread } from '../core/dom'; - -import readme from './readme.md?raw'; import './dialog-actions'; +import readme from './readme.md?raw'; -const myProp: InputType = { - control: { - type: 'text', - }, -}; - -const defaultArgTypes: ArgTypes = { - 'my-prop': myProp, -}; - -const defaultArgs: Args = { - 'my-prop': 'Label', -}; - -const Template = (args: Args): TemplateResult => - html``; - -export const Default: StoryObj = { - render: Template, - argTypes: defaultArgTypes, - args: { ...defaultArgs }, -}; +import '../button'; +import '../link'; + +const Template = (): TemplateResult => + html` + + Link + + Cancel + Confirm + `; + +export const Default: StoryObj = { render: Template }; const meta: Meta = { decorators: [ @@ -45,7 +38,7 @@ const meta: Meta = { extractComponentDescription: () => readme, }, }, - title: 'components/sbb-dialog-actions', + title: 'components/sbb-dialog/sbb-dialog-actions', }; export default meta; diff --git a/src/components/dialog-actions/dialog-actions.ts b/src/components/dialog-actions/dialog-actions.ts index e1e6c4d1bc..bd73e53698 100644 --- a/src/components/dialog-actions/dialog-actions.ts +++ b/src/components/dialog-actions/dialog-actions.ts @@ -8,24 +8,12 @@ import { setAttribute } from '../core/dom'; import style from './dialog-actions.scss?lit&inline'; /** - * Describe the purpose of the component with a single short sentence. - * - * @slot - Use the unnamed slot to add `sbb-TODO` elements. - * @event {CustomEvent} myEventName - TODO: Document this event + * Use this component to display a footer into an `sbb-dialog` with an action group. */ @customElement('sbb-dialog-actions') export class SbbDialogActionsElement extends SbbActionGroupElement { public static override styles: CSSResultGroup = [SbbActionGroupElement.styles, style]; - public override connectedCallback(): void { - super.connectedCallback(); - } - - public override disconnectedCallback(): void { - super.disconnectedCallback(); - // do stuff - } - protected override render(): TemplateResult { setAttribute(this, 'slot', 'actions'); From 6da8a9dfea571cb309faad422b4a6b9b28685139 Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Wed, 7 Feb 2024 15:54:33 +0100 Subject: [PATCH 23/65] fix: integrity --- src/components/dialog-actions/readme.md | 12 ------------ src/components/dialog-content/readme.md | 12 +++--------- src/components/dialog-title/readme.md | 6 +++--- src/components/dialog/readme.md | 24 ++++++++++++------------ 4 files changed, 18 insertions(+), 36 deletions(-) diff --git a/src/components/dialog-actions/readme.md b/src/components/dialog-actions/readme.md index 6244ffc55f..2b78fce9ef 100644 --- a/src/components/dialog-actions/readme.md +++ b/src/components/dialog-actions/readme.md @@ -52,15 +52,3 @@ The `sbb-dialog-actions` is a component . . . | `orientation` | `orientation` | public | `SbbOrientation` | `'horizontal'` | Indicates the orientation of the components inside the ``. | | `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. | - -## Events - -| Name | Type | Description | Inherited From | -| ------------- | ------------------ | ------------------------- | -------------- | -| `myEventName` | `CustomEvent` | TODO: Document this event | | - -## Slots - -| Name | Description | -| ---- | ------------------------------------------------ | -| | Use the unnamed slot to add `sbb-TODO` elements. | diff --git a/src/components/dialog-content/readme.md b/src/components/dialog-content/readme.md index 7993f094b5..e962207d0e 100644 --- a/src/components/dialog-content/readme.md +++ b/src/components/dialog-content/readme.md @@ -43,14 +43,8 @@ The `sbb-dialog-content` is a component . . . -## Events - -| Name | Type | Description | Inherited From | -| ------------- | ------------------ | ------------------------- | -------------- | -| `myEventName` | `CustomEvent` | TODO: Document this event | | - ## Slots -| Name | Description | -| ---- | ------------------------------------------------ | -| | Use the unnamed slot to add `sbb-TODO` elements. | +| Name | Description | +| ---- | ------------------------------------------------- | +| | Use the unnamed slot to provide a dialog content. | diff --git a/src/components/dialog-title/readme.md b/src/components/dialog-title/readme.md index 5cfe4f12cd..78a41f64a6 100644 --- a/src/components/dialog-title/readme.md +++ b/src/components/dialog-title/readme.md @@ -58,6 +58,6 @@ The `sbb-dialog-title` is a component . . . ## Events -| Name | Type | Description | Inherited From | -| ------------------- | ------------------ | ------------------------- | -------------- | -| `requestBackAction` | `CustomEvent` | TODO: Document this event | | +| Name | Type | Description | Inherited From | +| ------------------- | ------------------- | ------------------------------------------ | -------------- | +| `requestBackAction` | `CustomEvent` | Emits whenever the back button is clicked. | | diff --git a/src/components/dialog/readme.md b/src/components/dialog/readme.md index 5b3127dd5e..6e20da6873 100644 --- a/src/components/dialog/readme.md +++ b/src/components/dialog/readme.md @@ -94,13 +94,12 @@ It's possible to display the component in `negative` variant using the self-name ## Events -| Name | Type | Description | Inherited From | -| ------------------- | ------------------- | ------------------------------------------------------------------------------- | -------------- | -| `willOpen` | `CustomEvent` | Emits whenever the `sbb-dialog` starts the opening transition. Can be canceled. | | -| `didOpen` | `CustomEvent` | Emits whenever the `sbb-dialog` is opened. | | -| `willClose` | `CustomEvent` | Emits whenever the `sbb-dialog` begins the closing transition. Can be canceled. | | -| `didClose` | `CustomEvent` | Emits whenever the `sbb-dialog` is closed. | | -| `requestBackAction` | `CustomEvent` | Emits whenever the back button is clicked. | | +| Name | Type | Description | Inherited From | +| ----------- | ------------------- | ------------------------------------------------------------------------------- | -------------- | +| `willOpen` | `CustomEvent` | Emits whenever the `sbb-dialog` starts the opening transition. Can be canceled. | | +| `didOpen` | `CustomEvent` | Emits whenever the `sbb-dialog` is opened. | | +| `willClose` | `CustomEvent` | Emits whenever the `sbb-dialog` begins the closing transition. Can be canceled. | | +| `didClose` | `CustomEvent` | Emits whenever the `sbb-dialog` is closed. | | ## CSS Properties @@ -110,8 +109,9 @@ It's possible to display the component in `negative` variant using the self-name ## Slots -| Name | Description | -| -------------- | ------------------------------------------------------------ | -| | Use the unnamed slot to add content to the `sbb-dialog`. | -| `title` | Use this slot to provide a title. | -| `action-group` | Use this slot to display a `sbb-action-group` in the footer. | +| Name | Description | +| --------- | -------------------------------------------------------- | +| | Use the unnamed slot to add content to the `sbb-dialog`. | +| `title` | Use this slot to provide a `sbb-dialog-title`. | +| `title` | Use this slot to provide a `sbb-dialog-content`. | +| `actions` | Use this slot to provide a `sbb-dialog-actions`. | From 0adc70be5062c242339c1b4ea6993ac7d1d8404c Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Wed, 7 Feb 2024 16:00:06 +0100 Subject: [PATCH 24/65] fix: remove fullscreen mode --- .../dialog-content/dialog-content.scss | 6 ----- src/components/dialog-title/dialog-title.scss | 12 ++------- src/components/dialog/dialog.e2e.ts | 25 ------------------- src/components/dialog/dialog.scss | 21 ++-------------- src/components/dialog/dialog.spec.ts | 5 ++-- 5 files changed, 6 insertions(+), 63 deletions(-) diff --git a/src/components/dialog-content/dialog-content.scss b/src/components/dialog-content/dialog-content.scss index 2ce126ca72..1f7db3e8f0 100644 --- a/src/components/dialog-content/dialog-content.scss +++ b/src/components/dialog-content/dialog-content.scss @@ -19,12 +19,6 @@ transition: var(--sbb-dialog-content-transition); z-index: -1; - :host([data-fullscreen]) & { - padding-block-start: var(--sbb-spacing-fixed-20x); - padding-inline: var(--sbb-layout-base-offset-responsive); - height: 100vh; - } - // In order to improve the header transition on mobile (especially iOS) we use // a combination of the transform and margin properties on touch devices, // while on desktop we use just the margin-block for a better transition of the visible scrollbar. diff --git a/src/components/dialog-title/dialog-title.scss b/src/components/dialog-title/dialog-title.scss index 0c6d9f8cbd..13ef930935 100644 --- a/src/components/dialog-title/dialog-title.scss +++ b/src/components/dialog-title/dialog-title.scss @@ -10,7 +10,7 @@ display: contents; } -:host([data-overflows]:not([data-fullscreen])) { +:host([data-overflows]) { --sbb-dialog-header-padding-block: var(--sbb-spacing-responsive-s); } @@ -48,16 +48,8 @@ } } - :host([data-fullscreen]) & { - position: fixed; - width: var(--sbb-dialog-width); - background-color: transparent; - padding-inline: var(--sbb-spacing-responsive-xs); - padding-block-start: var(--sbb-spacing-responsive-xs); - } - &[data-has-visible-focus-within]:focus-within, - :host([data-overflows]:not([data-fullscreen], [negative], [data-hide-header])) & { + :host([data-overflows]:not([negative], [data-hide-header])) & { @include sbb.shadow-level-9-soft; } diff --git a/src/components/dialog/dialog.e2e.ts b/src/components/dialog/dialog.e2e.ts index fa6dcabfec..743e972244 100644 --- a/src/components/dialog/dialog.e2e.ts +++ b/src/components/dialog/dialog.e2e.ts @@ -244,31 +244,6 @@ describe(`sbb-dialog with ${fixture.name}`, () => { expect(element).to.have.attribute('data-state', 'closed'); }); - it('does not have the fullscreen attribute', async () => { - await openDialog(element); - - expect(element).not.to.have.attribute('data-fullscreen'); - }); - - it('renders in fullscreen mode if no title is provided', async () => { - element = await fixture( - html` - - Dialog content. -
Action group
-
- `, - { modules: ['./dialog.ts'] }, - ); - ariaLiveRef = element.shadowRoot!.querySelector('sbb-screen-reader-only')!; - - await openDialog(element); - - await waitForCondition(() => ariaLiveRef.textContent!.trim() === `${i18nDialog.en}`); - - expect(element).to.have.attribute('data-fullscreen'); - }); - it('closes stacked dialogs one by one on ESC key pressed', async () => { element = await fixture( html` diff --git a/src/components/dialog/dialog.scss b/src/components/dialog/dialog.scss index 92e521f2a1..d0932845f1 100644 --- a/src/components/dialog/dialog.scss +++ b/src/components/dialog/dialog.scss @@ -91,12 +91,6 @@ --sbb-dialog-header-margin-block-start: calc(var(--sbb-dialog-header-height) * -1); } -:host([data-fullscreen]) { - --sbb-dialog-backdrop-color: transparent; - --sbb-dialog-max-width: 100%; - --sbb-dialog-max-height: 100%; -} - :host([negative]) { @include sbb.scrollbar-variables--color-negative; @@ -110,11 +104,7 @@ --sbb-dialog-animation-duration: 0.1ms; } -:host([data-fullscreen]:not([negative])) { - --sbb-dialog-background-color: var(--sbb-color-milk); -} - -:host([data-overflows]:not([data-fullscreen], [negative])) { +:host([data-overflows]:not([negative])) { --sbb-dialog-actions-border: none; } @@ -160,10 +150,6 @@ color: var(--sbb-dialog-color); background-color: var(--sbb-dialog-background-color); - :host([data-fullscreen]) & { - border-radius: 0; - } - :host([data-state]:not([data-state='closed'])) & { display: block; @@ -186,10 +172,7 @@ @include sbb.mq($from: medium) { border-radius: var(--sbb-dialog-border-radius); overflow: hidden; - - :host(:not([data-fullscreen])) & { - height: fit-content; - } + height: fit-content; } } diff --git a/src/components/dialog/dialog.spec.ts b/src/components/dialog/dialog.spec.ts index b3895e5293..058c56364d 100644 --- a/src/components/dialog/dialog.spec.ts +++ b/src/components/dialog/dialog.spec.ts @@ -9,9 +9,8 @@ describe(`sbb-dialog`, () => { it('renders', async () => { const root = await fixture(html``); - expect(root).dom.to.be.equal(``); - - await expect(root).shadowDom.to.be.equalSnapshot(); + expect(root).dom.to.be.equal(``); + expect(root).shadowDom.to.be.equalSnapshot(); }); testA11yTreeSnapshot(html``); From fd7b95661ffa0c6b58bc75d5c6f8c2dd5a82b687 Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Thu, 8 Feb 2024 11:06:10 +0100 Subject: [PATCH 25/65] fix: tests --- .../dialog-actions/dialog-actions.spec.ts | 18 +++- .../dialog-content/dialog-content.e2e.ts | 11 +-- .../dialog-content/dialog-content.scss | 2 +- .../dialog-content/dialog-content.spec.ts | 6 +- .../dialog-content/dialog-content.ts | 2 +- .../dialog-title/dialog-title.e2e.ts | 8 +- .../dialog-title/dialog-title.spec.ts | 29 ++++++- src/components/dialog/dialog.e2e.ts | 85 +++++++++++-------- src/components/dialog/dialog.stories.ts | 55 ++++++++++-- src/components/dialog/dialog.ts | 8 +- src/components/dialog/readme.md | 11 ++- 11 files changed, 156 insertions(+), 79 deletions(-) diff --git a/src/components/dialog-actions/dialog-actions.spec.ts b/src/components/dialog-actions/dialog-actions.spec.ts index ce5a424d76..bd2a5261b3 100644 --- a/src/components/dialog-actions/dialog-actions.spec.ts +++ b/src/components/dialog-actions/dialog-actions.spec.ts @@ -4,13 +4,25 @@ import './dialog-actions'; describe('sbb-dialog-actions', () => { it('renders', async () => { - const root = await fixture(html``); + const root = await fixture(html``); - expect(root).dom.to.be.equal(``); + expect(root).dom.to.be.equal(` + + `); expect(root).shadowDom.to.be.equal(`
- Label +
+ + +
`); }); diff --git a/src/components/dialog-content/dialog-content.e2e.ts b/src/components/dialog-content/dialog-content.e2e.ts index d140c69f6b..e138e117b9 100644 --- a/src/components/dialog-content/dialog-content.e2e.ts +++ b/src/components/dialog-content/dialog-content.e2e.ts @@ -1,8 +1,6 @@ -import { assert, expect, fixture } from '@open-wc/testing'; +import { assert, fixture } from '@open-wc/testing'; import { html } from 'lit/static-html.js'; -import { EventSpy, waitForLitRender } from '../core/testing'; - import { SbbDialogContentElement } from './dialog-content'; describe('sbb-dialog-content', () => { @@ -15,11 +13,4 @@ describe('sbb-dialog-content', () => { it('renders', async () => { assert.instanceOf(element, SbbDialogContentElement); }); - - it('emits on click', async () => { - const myEventNameSpy = new EventSpy(SbbDialogContentElement.events.myEventName); - element.click(); - await waitForLitRender(element); - expect(myEventNameSpy.count).to.be.equal(1); - }); }); diff --git a/src/components/dialog-content/dialog-content.scss b/src/components/dialog-content/dialog-content.scss index 1f7db3e8f0..9e9e70b57e 100644 --- a/src/components/dialog-content/dialog-content.scss +++ b/src/components/dialog-content/dialog-content.scss @@ -8,7 +8,7 @@ display: contents; } -.sbb-dialog__content { +.sbb-dialog-content { @include sbb.scrollbar-rules; padding-inline: var(--sbb-dialog-padding-inline); diff --git a/src/components/dialog-content/dialog-content.spec.ts b/src/components/dialog-content/dialog-content.spec.ts index 4fbab390af..8d8684cacb 100644 --- a/src/components/dialog-content/dialog-content.spec.ts +++ b/src/components/dialog-content/dialog-content.spec.ts @@ -4,13 +4,13 @@ import './dialog-content'; describe('sbb-dialog-content', () => { it('renders', async () => { - const root = await fixture(html``); + const root = await fixture(html`Content`); - expect(root).dom.to.be.equal(``); + expect(root).dom.to.be.equal(`Content`); expect(root).shadowDom.to.be.equal(`
- Label +
`); }); diff --git a/src/components/dialog-content/dialog-content.ts b/src/components/dialog-content/dialog-content.ts index a6ddf12ac3..580ecf7c83 100644 --- a/src/components/dialog-content/dialog-content.ts +++ b/src/components/dialog-content/dialog-content.ts @@ -19,7 +19,7 @@ export class SbbDialogContentElement extends LitElement { setAttribute(this, 'slot', 'content'); return html` -
+
`; diff --git a/src/components/dialog-title/dialog-title.e2e.ts b/src/components/dialog-title/dialog-title.e2e.ts index b53d94ce11..44144785e8 100644 --- a/src/components/dialog-title/dialog-title.e2e.ts +++ b/src/components/dialog-title/dialog-title.e2e.ts @@ -9,16 +9,16 @@ describe('sbb-dialog-title', () => { let element: SbbDialogTitleElement; beforeEach(async () => { - element = await fixture(html``); + element = await fixture(html`Title`); }); it('renders', async () => { assert.instanceOf(element, SbbDialogTitleElement); }); - it('emits on click', async () => { - const myEventNameSpy = new EventSpy(SbbDialogTitleElement.events.myEventName); - element.click(); + it('emits requestBackAction on back button click', async () => { + const myEventNameSpy = new EventSpy(SbbDialogTitleElement.events.backClick); + (element.shadowRoot!.querySelector('.sbb-dialog__back')! as HTMLElement).click(); await waitForLitRender(element); expect(myEventNameSpy.count).to.be.equal(1); }); diff --git a/src/components/dialog-title/dialog-title.spec.ts b/src/components/dialog-title/dialog-title.spec.ts index 5ceba501ce..0517d6af6f 100644 --- a/src/components/dialog-title/dialog-title.spec.ts +++ b/src/components/dialog-title/dialog-title.spec.ts @@ -4,13 +4,34 @@ import './dialog-title'; describe('sbb-dialog-title', () => { it('renders', async () => { - const root = await fixture(html``); + const root = await fixture(html`Title`); - expect(root).dom.to.be.equal(``); + expect(root).dom.to.be.equal(` + Title + `); expect(root).shadowDom.to.be.equal(` -
- Label +
+

+ +

+
`); }); diff --git a/src/components/dialog/dialog.e2e.ts b/src/components/dialog/dialog.e2e.ts index 743e972244..27bfa85330 100644 --- a/src/components/dialog/dialog.e2e.ts +++ b/src/components/dialog/dialog.e2e.ts @@ -7,7 +7,9 @@ import { EventSpy, waitForCondition, waitForLitRender } from '../core/testing'; import { fixture } from '../core/testing/private'; import { SbbDialogElement } from './dialog'; -import '../title'; +import '../dialog-title'; +import '../dialog-content'; +import '../dialog-actions'; async function openDialog(element: SbbDialogElement): Promise { const willOpen = new EventSpy(SbbDialogElement.events.willOpen); @@ -34,9 +36,10 @@ describe(`sbb-dialog with ${fixture.name}`, () => { await setViewport({ width: 900, height: 600 }); element = await fixture( html` - - Dialog content. -
Action group
+ + Title + Dialog content + Action group `, { modules: ['./dialog.ts'] }, @@ -201,7 +204,9 @@ describe(`sbb-dialog with ${fixture.name}`, () => { }); it('closes the dialog on close button click', async () => { - const closeButton = element.shadowRoot!.querySelector('[sbb-dialog-close]') as HTMLElement; + const closeButton = element + .querySelector('sbb-dialog-title')! + .shadowRoot!.querySelector('[sbb-dialog-close]') as HTMLElement; const willClose = new EventSpy(SbbDialogElement.events.willClose); const didClose = new EventSpy(SbbDialogElement.events.didClose); @@ -247,13 +252,16 @@ describe(`sbb-dialog with ${fixture.name}`, () => { it('closes stacked dialogs one by one on ESC key pressed', async () => { element = await fixture( html` - - Dialog content. -
Action group
+ + Title + Dialog content + Action group - - Stacked dialog. + + Stacked title + Dialog content + Action group `, { modules: ['./dialog.ts'] }, @@ -319,18 +327,18 @@ describe(`sbb-dialog with ${fixture.name}`, () => { it('does not close the dialog on other overlay click', async () => { await setViewport({ width: 900, height: 600 }); - element = await fixture( - html` - - Dialog content. -
Action group
- - Dialog content. -
Action group
-
+ element = await fixture(html` + + Title + Dialog content + + + Inner Dialog title + Dialog content - `, - { modules: ['./dialog.ts'] }, + + `, + { modules: ['./dialog.ts'] }, ); const willOpen = new EventSpy(SbbDialogElement.events.willOpen); const didOpen = new EventSpy(SbbDialogElement.events.didOpen); @@ -411,21 +419,24 @@ describe('sbb-dialog with long content', () => { beforeEach(async () => { await setViewport({ width: 900, height: 300 }); element = await fixture(html` - - Frodo halted for a moment, looking back. Elrond was in his chair and the fire was on his - face like summer-light upon the trees. Near him sat the Lady Arwen. To his surprise Frodo - saw that Aragorn stood beside her; his dark cloak was thrown back, and he seemed to be clad - in elven-mail, and a star shone on his breast. They spoke together, and then suddenly it - seemed to Frodo that Arwen turned towards him, and the light of her eyes fell on him from - afar and pierced his heart. He stood still enchanted, while the sweet syllables of the - elvish song fell like clear jewels of blended word and melody. 'It is a song to Elbereth,'' - said Bilbo. 'They will sing that, and other songs of the Blessed Realm, many times tonight. - Come on!’ —J.R.R. Tolkien, The Lord of the Rings: The Fellowship of the Ring, “Many - Meetings” J.R.R. Tolkien, the mastermind behind Middle-earth's enchanting world, was born on - January 3, 1892. With "The Hobbit" and "The Lord of the Rings", he pioneered fantasy - literature. Tolkien's linguistic brilliance and mythic passion converge in a literary legacy - that continues to transport readers to magical realms. -
Action group
+ + Title + + Frodo halted for a moment, looking back. Elrond was in his chair and the fire was on his + face like summer-light upon the trees. Near him sat the Lady Arwen. To his surprise Frodo + saw that Aragorn stood beside her; his dark cloak was thrown back, and he seemed to be + clad in elven-mail, and a star shone on his breast. They spoke together, and then suddenly + it seemed to Frodo that Arwen turned towards him, and the light of her eyes fell on him + from afar and pierced his heart. He stood still enchanted, while the sweet syllables of + the elvish song fell like clear jewels of blended word and melody. 'It is a song to + Elbereth,'' said Bilbo. 'They will sing that, and other songs of the Blessed Realm, many + times tonight. Come on!’ —J.R.R. Tolkien, The Lord of the Rings: The Fellowship of the + Ring, “Many Meetings” J.R.R. Tolkien, the mastermind behind Middle-earth's enchanting + world, was born on January 3, 1892. With "The Hobbit" and "The Lord of the Rings", he + pioneered fantasy literature. Tolkien's linguistic brilliance and mythic passion converge + in a literary legacy that continues to transport readers to magical realms. + + Action group `); }); @@ -445,7 +456,7 @@ describe('sbb-dialog with long content', () => { await openDialog(element); expect(element).not.to.have.attribute('data-hide-header'); - const content = element.shadowRoot!.querySelector('.sbb-dialog__content')!; + const content = element.querySelector('sbb-dialog-content')!.shadowRoot!.firstElementChild!; // Scroll down. content.scrollTo(0, 50); diff --git a/src/components/dialog/dialog.stories.ts b/src/components/dialog/dialog.stories.ts index f03b156aae..51111cc2d3 100644 --- a/src/components/dialog/dialog.stories.ts +++ b/src/components/dialog/dialog.stories.ts @@ -141,7 +141,7 @@ const basicArgs: Args = { accessibilityCloseLabel: 'Close dialog', accessibilityBackLabel: 'Go back', negative: false, - 'accessibility-label': 'Dialog aria-label', + 'accessibility-label': undefined, 'disable-animation': isChromatic(), 'backdrop-action': backdropAction.options[0], }; @@ -265,8 +265,8 @@ const LongContentTemplate = ({ accessibilityBackLabel, ...args }: Args): TemplateResult => html` - ${triggerButton('my-dialog-3')} - + ${triggerButton('my-dialog-2')} + ${dialogHeader( level, titleBackButton, @@ -304,7 +304,7 @@ const FormTemplate = ({ accessibilityBackLabel, ...args }: Args): TemplateResult => html` - ${triggerButton('my-dialog-4')} + ${triggerButton('my-dialog-3')}
Your message: Hello 👋
@@ -313,7 +313,7 @@ const FormTemplate = ({
{ if (event.detail.returnValue) { document.getElementById('returned-value-message')!.innerHTML = @@ -365,8 +365,8 @@ const NoFooterTemplate = ({ accessibilityBackLabel, ...args }: Args): TemplateResult => html` - ${triggerButton('my-dialog-5')} - + ${triggerButton('my-dialog-4')} + ${dialogHeader( level, titleBackButton, @@ -385,6 +385,40 @@ const NoFooterTemplate = ({ `; +const NestedTemplate = ({ + level, + titleBackButton, + hideOnScroll, + accessibilityCloseLabel, + accessibilityBackLabel, + ...args +}: Args): TemplateResult => html` + ${triggerButton('my-dialog-5')} + + ${dialogHeader( + level, + titleBackButton, + hideOnScroll, + accessibilityCloseLabel, + accessibilityBackLabel, + )} + Click the button to open a nested + dialog. ${triggerButton('my-dialog-6')} + + ${dialogHeader( + level, + titleBackButton, + hideOnScroll, + accessibilityCloseLabel, + accessibilityBackLabel, + )} + Nested dialog content. + + +`; + export const Default: StoryObj = { render: DefaultTemplate, argTypes: basicArgTypes, @@ -430,6 +464,13 @@ export const NoFooter: StoryObj = { play: isChromatic() ? playStory : undefined, }; +export const Nested: StoryObj = { + render: NestedTemplate, + argTypes: basicArgTypes, + args: { ...basicArgs }, + play: isChromatic() ? playStory : undefined, +}; + const meta: Meta = { decorators: [ (story) => html` diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts index 82c0b839de..a7d850a489 100644 --- a/src/components/dialog/dialog.ts +++ b/src/components/dialog/dialog.ts @@ -29,9 +29,8 @@ let nextId = 0; /** * It displays an interactive overlay element. * - * @slot - Use the unnamed slot to add content to the `sbb-dialog`. * @slot title - Use this slot to provide a `sbb-dialog-title`. - * @slot title - Use this slot to provide a `sbb-dialog-content`. + * @slot content - Use this slot to provide a `sbb-dialog-content`. * @slot actions - Use this slot to provide a `sbb-dialog-actions`. * @event {CustomEvent} willOpen - Emits whenever the `sbb-dialog` starts the opening transition. Can be canceled. * @event {CustomEvent} didOpen - Emits whenever the `sbb-dialog` is opened. @@ -347,6 +346,8 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { // In rare cases it can be that the animationEnd event is triggered twice. // To avoid entering a corrupt state, exit when state is not expected. private _onDialogAnimationEnd(event: AnimationEvent): void { + console.log('Animation'); + if (event.animationName === 'open' && this._state === 'opening') { this._state = 'opened'; this._didOpen.emit(); @@ -383,7 +384,7 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { // Take accessibility label or current string in title section const label = this.accessibilityLabel || - (this.shadowRoot!.querySelector('.sbb-dialog__title') as HTMLElement)?.innerText.trim(); + (this.querySelector('sbb-dialog-title') as HTMLElement)!.innerText.trim(); // If the text content remains the same, on VoiceOver the aria-live region is not announced a second time. // In order to support reading on every opening, we toggle an invisible space. @@ -449,6 +450,7 @@ export class SbbDialogElement extends SbbNegativeMixin(LitElement) { +
diff --git a/src/components/dialog/readme.md b/src/components/dialog/readme.md index 6e20da6873..83e3329432 100644 --- a/src/components/dialog/readme.md +++ b/src/components/dialog/readme.md @@ -109,9 +109,8 @@ It's possible to display the component in `negative` variant using the self-name ## Slots -| Name | Description | -| --------- | -------------------------------------------------------- | -| | Use the unnamed slot to add content to the `sbb-dialog`. | -| `title` | Use this slot to provide a `sbb-dialog-title`. | -| `title` | Use this slot to provide a `sbb-dialog-content`. | -| `actions` | Use this slot to provide a `sbb-dialog-actions`. | +| Name | Description | +| --------- | ------------------------------------------------ | +| `title` | Use this slot to provide a `sbb-dialog-title`. | +| `content` | Use this slot to provide a `sbb-dialog-content`. | +| `actions` | Use this slot to provide a `sbb-dialog-actions`. | From 29b32853f1e77ba5a5c5049bdcc09b0a39cc53be Mon Sep 17 00:00:00 2001 From: Marco D'Auria Date: Thu, 8 Feb 2024 11:27:33 +0100 Subject: [PATCH 26/65] refactor: re-organize folders --- .../dialog-actions/dialog-actions.e2e.ts | 0 .../dialog-actions/dialog-actions.scss | 2 +- .../dialog-actions/dialog-actions.spec.ts | 0 .../dialog-actions/dialog-actions.stories.ts | 4 +- .../dialog-actions/dialog-actions.ts | 4 +- .../{ => dialog}/dialog-actions/index.ts | 0 .../{ => dialog}/dialog-actions/readme.md | 0 .../dialog-content/dialog-content.e2e.ts | 0 .../dialog-content/dialog-content.scss | 2 +- .../dialog-content/dialog-content.spec.ts | 0 .../dialog-content/dialog-content.stories.ts | 0 .../dialog-content/dialog-content.ts | 2 +- .../{ => dialog}/dialog-content/index.ts | 0 .../{ => dialog}/dialog-content/readme.md | 0 .../dialog-title/dialog-title.e2e.ts | 2 +- .../dialog-title/dialog-title.scss | 2 +- .../dialog-title/dialog-title.spec.ts | 0 .../dialog-title/dialog-title.stories.ts | 2 +- .../{ => dialog}/dialog-title/dialog-title.ts | 16 +++--- .../{ => dialog}/dialog-title/index.ts | 0 .../{ => dialog}/dialog-title/readme.md | 0 .../__snapshots__/dialog.spec.snap.js | 0 .../dialog/{ => dialog}/dialog.e2e.ts | 56 ++++++++----------- .../dialog/{ => dialog}/dialog.scss | 2 +- .../dialog/{ => dialog}/dialog.spec.ts | 0 .../dialog/{ => dialog}/dialog.stories.ts | 39 ++++++------- src/components/dialog/{ => dialog}/dialog.ts | 54 ++++++++++-------- src/components/dialog/dialog/index.ts | 1 + src/components/dialog/{ => dialog}/readme.md | 0 src/components/dialog/index.ts | 3 + 30 files changed, 97 insertions(+), 94 deletions(-) rename src/components/{ => dialog}/dialog-actions/dialog-actions.e2e.ts (100%) rename src/components/{ => dialog}/dialog-actions/dialog-actions.scss (94%) rename src/components/{ => dialog}/dialog-actions/dialog-actions.spec.ts (100%) rename src/components/{ => dialog}/dialog-actions/dialog-actions.stories.ts (96%) rename src/components/{ => dialog}/dialog-actions/dialog-actions.ts (88%) rename src/components/{ => dialog}/dialog-actions/index.ts (100%) rename src/components/{ => dialog}/dialog-actions/readme.md (100%) rename src/components/{ => dialog}/dialog-content/dialog-content.e2e.ts (100%) rename src/components/{ => dialog}/dialog-content/dialog-content.scss (97%) rename src/components/{ => dialog}/dialog-content/dialog-content.spec.ts (100%) rename src/components/{ => dialog}/dialog-content/dialog-content.stories.ts (100%) rename src/components/{ => dialog}/dialog-content/dialog-content.ts (94%) rename src/components/{ => dialog}/dialog-content/index.ts (100%) rename src/components/{ => dialog}/dialog-content/readme.md (100%) rename src/components/{ => dialog}/dialog-title/dialog-title.e2e.ts (92%) rename src/components/{ => dialog}/dialog-title/dialog-title.scss (98%) rename src/components/{ => dialog}/dialog-title/dialog-title.spec.ts (100%) rename src/components/{ => dialog}/dialog-title/dialog-title.stories.ts (97%) rename src/components/{ => dialog}/dialog-title/dialog-title.ts (89%) rename src/components/{ => dialog}/dialog-title/index.ts (100%) rename src/components/{ => dialog}/dialog-title/readme.md (100%) rename src/components/dialog/{ => dialog}/__snapshots__/dialog.spec.snap.js (100%) rename src/components/dialog/{ => dialog}/dialog.e2e.ts (93%) rename src/components/dialog/{ => dialog}/dialog.scss (99%) rename src/components/dialog/{ => dialog}/dialog.spec.ts (100%) rename src/components/dialog/{ => dialog}/dialog.stories.ts (93%) rename src/components/dialog/{ => dialog}/dialog.ts (91%) create mode 100644 src/components/dialog/dialog/index.ts rename src/components/dialog/{ => dialog}/readme.md (100%) diff --git a/src/components/dialog-actions/dialog-actions.e2e.ts b/src/components/dialog/dialog-actions/dialog-actions.e2e.ts similarity index 100% rename from src/components/dialog-actions/dialog-actions.e2e.ts rename to src/components/dialog/dialog-actions/dialog-actions.e2e.ts diff --git a/src/components/dialog-actions/dialog-actions.scss b/src/components/dialog/dialog-actions/dialog-actions.scss similarity index 94% rename from src/components/dialog-actions/dialog-actions.scss rename to src/components/dialog/dialog-actions/dialog-actions.scss index e80846c081..fad2fe671b 100644 --- a/src/components/dialog-actions/dialog-actions.scss +++ b/src/components/dialog/dialog-actions/dialog-actions.scss @@ -1,4 +1,4 @@ -@use '../core/styles' as sbb; +@use '../../core/styles' as sbb; // Default component properties, defined for :host. Properties which can not // travel the shadow boundary are defined through this mixin diff --git a/src/components/dialog-actions/dialog-actions.spec.ts b/src/components/dialog/dialog-actions/dialog-actions.spec.ts similarity index 100% rename from src/components/dialog-actions/dialog-actions.spec.ts rename to src/components/dialog/dialog-actions/dialog-actions.spec.ts diff --git a/src/components/dialog-actions/dialog-actions.stories.ts b/src/components/dialog/dialog-actions/dialog-actions.stories.ts similarity index 96% rename from src/components/dialog-actions/dialog-actions.stories.ts rename to src/components/dialog/dialog-actions/dialog-actions.stories.ts index b9ad481de5..b8aa87f065 100644 --- a/src/components/dialog-actions/dialog-actions.stories.ts +++ b/src/components/dialog/dialog-actions/dialog-actions.stories.ts @@ -6,8 +6,8 @@ import { html } from 'lit'; import './dialog-actions'; import readme from './readme.md?raw'; -import '../button'; -import '../link'; +import '../../button'; +import '../../link'; const Template = (): TemplateResult => html` diff --git a/src/components/dialog-actions/dialog-actions.ts b/src/components/dialog/dialog-actions/dialog-actions.ts similarity index 88% rename from src/components/dialog-actions/dialog-actions.ts rename to src/components/dialog/dialog-actions/dialog-actions.ts index bd73e53698..6289650fe0 100644 --- a/src/components/dialog-actions/dialog-actions.ts +++ b/src/components/dialog/dialog-actions/dialog-actions.ts @@ -2,8 +2,8 @@ import type { CSSResultGroup, TemplateResult } from 'lit'; import { html } from 'lit'; import { customElement } from 'lit/decorators.js'; -import { SbbActionGroupElement } from '../action-group'; -import { setAttribute } from '../core/dom'; +import { SbbActionGroupElement } from '../../action-group'; +import { setAttribute } from '../../core/dom'; import style from './dialog-actions.scss?lit&inline'; diff --git a/src/components/dialog-actions/index.ts b/src/components/dialog/dialog-actions/index.ts similarity index 100% rename from src/components/dialog-actions/index.ts rename to src/components/dialog/dialog-actions/index.ts diff --git a/src/components/dialog-actions/readme.md b/src/components/dialog/dialog-actions/readme.md similarity index 100% rename from src/components/dialog-actions/readme.md rename to src/components/dialog/dialog-actions/readme.md diff --git a/src/components/dialog-content/dialog-content.e2e.ts b/src/components/dialog/dialog-content/dialog-content.e2e.ts similarity index 100% rename from src/components/dialog-content/dialog-content.e2e.ts rename to src/components/dialog/dialog-content/dialog-content.e2e.ts diff --git a/src/components/dialog-content/dialog-content.scss b/src/components/dialog/dialog-content/dialog-content.scss similarity index 97% rename from src/components/dialog-content/dialog-content.scss rename to src/components/dialog/dialog-content/dialog-content.scss index 9e9e70b57e..2b49cb5c0a 100644 --- a/src/components/dialog-content/dialog-content.scss +++ b/src/components/dialog/dialog-content/dialog-content.scss @@ -1,4 +1,4 @@ -@use '../core/styles' as sbb; +@use '../../core/styles' as sbb; // Default component properties, defined for :host. Properties which can not // travel the shadow boundary are defined through this mixin diff --git a/src/components/dialog-content/dialog-content.spec.ts b/src/components/dialog/dialog-content/dialog-content.spec.ts similarity index 100% rename from src/components/dialog-content/dialog-content.spec.ts rename to src/components/dialog/dialog-content/dialog-content.spec.ts diff --git a/src/components/dialog-content/dialog-content.stories.ts b/src/components/dialog/dialog-content/dialog-content.stories.ts similarity index 100% rename from src/components/dialog-content/dialog-content.stories.ts rename to src/components/dialog/dialog-content/dialog-content.stories.ts diff --git a/src/components/dialog-content/dialog-content.ts b/src/components/dialog/dialog-content/dialog-content.ts similarity index 94% rename from src/components/dialog-content/dialog-content.ts rename to src/components/dialog/dialog-content/dialog-content.ts index 580ecf7c83..6325b2ef10 100644 --- a/src/components/dialog-content/dialog-content.ts +++ b/src/components/dialog/dialog-content/dialog-content.ts @@ -2,7 +2,7 @@ import type { CSSResultGroup, TemplateResult } from 'lit'; import { html, LitElement } from 'lit'; import { customElement } from 'lit/decorators.js'; -import { setAttribute } from '../core/dom'; +import { setAttribute } from '../../core/dom'; import style from './dialog-content.scss?lit&inline'; diff --git a/src/components/dialog-content/index.ts b/src/components/dialog/dialog-content/index.ts similarity index 100% rename from src/components/dialog-content/index.ts rename to src/components/dialog/dialog-content/index.ts diff --git a/src/components/dialog-content/readme.md b/src/components/dialog/dialog-content/readme.md similarity index 100% rename from src/components/dialog-content/readme.md rename to src/components/dialog/dialog-content/readme.md diff --git a/src/components/dialog-title/dialog-title.e2e.ts b/src/components/dialog/dialog-title/dialog-title.e2e.ts similarity index 92% rename from src/components/dialog-title/dialog-title.e2e.ts rename to src/components/dialog/dialog-title/dialog-title.e2e.ts index 44144785e8..b2945f4610 100644 --- a/src/components/dialog-title/dialog-title.e2e.ts +++ b/src/components/dialog/dialog-title/dialog-title.e2e.ts @@ -1,7 +1,7 @@ import { assert, expect, fixture } from '@open-wc/testing'; import { html } from 'lit/static-html.js'; -import { EventSpy, waitForLitRender } from '../core/testing'; +import { EventSpy, waitForLitRender } from '../../core/testing'; import { SbbDialogTitleElement } from './dialog-title'; diff --git a/src/components/dialog-title/dialog-title.scss b/src/components/dialog/dialog-title/dialog-title.scss similarity index 98% rename from src/components/dialog-title/dialog-title.scss rename to src/components/dialog/dialog-title/dialog-title.scss index 13ef930935..318671873d 100644 --- a/src/components/dialog-title/dialog-title.scss +++ b/src/components/dialog/dialog-title/dialog-title.scss @@ -1,4 +1,4 @@ -@use '../core/styles' as sbb; +@use '../../core/styles' as sbb; // Default component properties, defined for :host. Properties which can not // travel the shadow boundary are defined through this mixin diff --git a/src/components/dialog-title/dialog-title.spec.ts b/src/components/dialog/dialog-title/dialog-title.spec.ts similarity index 100% rename from src/components/dialog-title/dialog-title.spec.ts rename to src/components/dialog/dialog-title/dialog-title.spec.ts diff --git a/src/components/dialog-title/dialog-title.stories.ts b/src/components/dialog/dialog-title/dialog-title.stories.ts similarity index 97% rename from src/components/dialog-title/dialog-title.stories.ts rename to src/components/dialog/dialog-title/dialog-title.stories.ts index c61ed91d58..794df0760a 100644 --- a/src/components/dialog-title/dialog-title.stories.ts +++ b/src/components/dialog/dialog-title/dialog-title.stories.ts @@ -4,7 +4,7 @@ import type { Args, ArgTypes, Decorator, Meta, StoryObj } from '@storybook/web-c import type { TemplateResult } from 'lit'; import { html } from 'lit'; -import { sbbSpread } from '../core/dom'; +import { sbbSpread } from '../../core/dom'; import { SbbDialogTitleElement } from './dialog-title'; import readme from './readme.md?raw'; diff --git a/src/components/dialog-title/dialog-title.ts b/src/components/dialog/dialog-title/dialog-title.ts similarity index 89% rename from src/components/dialog-title/dialog-title.ts rename to src/components/dialog/dialog-title/dialog-title.ts index 56176a0aed..3be9998341 100644 --- a/src/components/dialog-title/dialog-title.ts +++ b/src/components/dialog/dialog-title/dialog-title.ts @@ -2,14 +2,14 @@ import type { CSSResultGroup, TemplateResult } from 'lit'; import { html, nothing } from 'lit'; import { customElement, property } from 'lit/decorators.js'; -import { LanguageController } from '../core/common-behaviors'; -import type { Breakpoint } from '../core/dom'; -import { setAttribute } from '../core/dom'; -import { EventEmitter } from '../core/eventing'; -import { i18nCloseDialog, i18nGoBack } from '../core/i18n'; -import type { TitleLevel } from '../title'; -import { SbbTitleElement } from '../title'; -import '../button'; +import { LanguageController } from '../../core/common-behaviors'; +import type { Breakpoint } from '../../core/dom'; +import { setAttribute } from '../../core/dom'; +import { EventEmitter } from '../../core/eventing'; +import { i18nCloseDialog, i18nGoBack } from '../../core/i18n'; +import type { TitleLevel } from '../../title'; +import { SbbTitleElement } from '../../title'; +import '../../button'; import style from './dialog-title.scss?lit&inline'; diff --git a/src/components/dialog-title/index.ts b/src/components/dialog/dialog-title/index.ts similarity index 100% rename from src/components/dialog-title/index.ts rename to src/components/dialog/dialog-title/index.ts diff --git a/src/components/dialog-title/readme.md b/src/components/dialog/dialog-title/readme.md similarity index 100% rename from src/components/dialog-title/readme.md rename to src/components/dialog/dialog-title/readme.md diff --git a/src/components/dialog/__snapshots__/dialog.spec.snap.js b/src/components/dialog/dialog/__snapshots__/dialog.spec.snap.js similarity index 100% rename from src/components/dialog/__snapshots__/dialog.spec.snap.js rename to src/components/dialog/dialog/__snapshots__/dialog.spec.snap.js diff --git a/src/components/dialog/dialog.e2e.ts b/src/components/dialog/dialog/dialog.e2e.ts similarity index 93% rename from src/components/dialog/dialog.e2e.ts rename to src/components/dialog/dialog/dialog.e2e.ts index 27bfa85330..a509905d6f 100644 --- a/src/components/dialog/dialog.e2e.ts +++ b/src/components/dialog/dialog/dialog.e2e.ts @@ -1,12 +1,13 @@ -import { assert, expect } from '@open-wc/testing'; +import { assert, expect, fixture } from '@open-wc/testing'; import { sendKeys, setViewport } from '@web/test-runner-commands'; import { html } from 'lit/static-html.js'; -import { i18nDialog } from '../core/i18n'; -import { EventSpy, waitForCondition, waitForLitRender } from '../core/testing'; -import { fixture } from '../core/testing/private'; +import { i18nDialog } from '../../core/i18n'; +import { EventSpy, waitForCondition, waitForLitRender } from '../../core/testing'; import { SbbDialogElement } from './dialog'; +import '../../button'; +import '../../icon'; import '../dialog-title'; import '../dialog-content'; import '../dialog-actions'; @@ -29,22 +30,19 @@ async function openDialog(element: SbbDialogElement): Promise { expect(element).to.have.attribute('data-state', 'opened'); } -describe(`sbb-dialog with ${fixture.name}`, () => { +describe('sbb-dialog', () => { let element: SbbDialogElement, ariaLiveRef: HTMLElement; beforeEach(async () => { await setViewport({ width: 900, height: 600 }); - element = await fixture( - html` - - Title + element = await fixture(html` + + Title Dialog content - Action group - - `, - { modules: ['./dialog.ts'] }, - ); - ariaLiveRef = element.shadowRoot!.querySelector('sbb-screen-reader-only')!; + Action group + + `); + ariaLiveRef = element.shadowRoot!.querySelector('sbb-screenreader-only')!; }); it('renders', () => { @@ -250,22 +248,19 @@ describe(`sbb-dialog with ${fixture.name}`, () => { }); it('closes stacked dialogs one by one on ESC key pressed', async () => { - element = await fixture( - html` - - Title + element = await fixture(html` + + Title Dialog content - Action group - + Action group + - + Stacked title - Dialog content + Dialog content Action group - - `, - { modules: ['./dialog.ts'] }, - ); + + `); const willOpen = new EventSpy(SbbDialogElement.events.willOpen); const didOpen = new EventSpy(SbbDialogElement.events.didOpen); @@ -274,8 +269,7 @@ describe(`sbb-dialog with ${fixture.name}`, () => { await openDialog(element); - const stackedDialog = - element.parentElement!.querySelector('#stacked-dialog')!; + const stackedDialog = document.querySelector('#stacked-dialog') as SbbDialogElement; stackedDialog.open(); await waitForLitRender(element); @@ -337,9 +331,7 @@ describe(`sbb-dialog with ${fixture.name}`, () => { Dialog content - `, - { modules: ['./dialog.ts'] }, - ); + `); const willOpen = new EventSpy(SbbDialogElement.events.willOpen); const didOpen = new EventSpy(SbbDialogElement.events.didOpen); const willClose = new EventSpy(SbbDialogElement.events.willClose); diff --git a/src/components/dialog/dialog.scss b/src/components/dialog/dialog/dialog.scss similarity index 99% rename from src/components/dialog/dialog.scss rename to src/components/dialog/dialog/dialog.scss index d0932845f1..3f0cc2668c 100644 --- a/src/components/dialog/dialog.scss +++ b/src/components/dialog/dialog/dialog.scss @@ -1,4 +1,4 @@ -@use '../core/styles' as sbb; +@use '../../core/styles' as sbb; // Default component properties, defined for :host. Properties which can not // travel the shadow boundary are defined through this mixin diff --git a/src/components/dialog/dialog.spec.ts b/src/components/dialog/dialog/dialog.spec.ts similarity index 100% rename from src/components/dialog/dialog.spec.ts rename to src/components/dialog/dialog/dialog.spec.ts diff --git a/src/components/dialog/dialog.stories.ts b/src/components/dialog/dialog/dialog.stories.ts similarity index 93% rename from src/components/dialog/dialog.stories.ts rename to src/components/dialog/dialog/dialog.stories.ts index 51111cc2d3..5a6e4e4138 100644 --- a/src/components/dialog/dialog.stories.ts +++ b/src/components/dialog/dialog/dialog.stories.ts @@ -14,22 +14,21 @@ import type { TemplateResult } from 'lit'; import { html } from 'lit'; import { styleMap } from 'lit/directives/style-map.js'; -import { sbbSpread } from '../../storybook/helpers/spread'; -import { waitForComponentsReady } from '../../storybook/testing/wait-for-components-ready'; -import { waitForStablePosition } from '../../storybook/testing/wait-for-stable-position'; -import sampleImages from '../core/images'; +import { waitForComponentsReady } from '../../../storybook/testing/wait-for-components-ready'; +import { waitForStablePosition } from '../../../storybook/testing/wait-for-stable-position'; +import { sbbSpread } from '../../core/dom'; +import sampleImages from '../../core/images'; import { SbbDialogTitleElement } from '../dialog-title'; import { SbbDialogElement } from './dialog'; import readme from './readme.md?raw'; -import '../button/secondary-button'; -import '../button/button'; -import '../link/block-link'; -import '../title'; -import '../form-field'; -import '../image'; -import '../action-group'; +import '../../button'; +import '../../link'; +import '../../title'; +import '../../form-field'; +import '../../image'; +import '../../action-group'; import '../dialog-content'; import '../dialog-actions'; @@ -164,7 +163,7 @@ const triggerButton = (dialogId: string): TemplateResult => html` const actionGroup = (negative: boolean): TemplateResult => html` - html` sbb-dialog-close > Link - - Cancel - Confirm + + Cancel + Confirm `; @@ -188,7 +187,7 @@ const formDetailsStyle: Args = { marginTop: 'var(--sbb-spacing-fixed-4x)', padding: 'var(--sbb-spacing-fixed-4x)', borderRadius: 'var(--sbb-border-radius-8x)', - backgroundColor: 'var(--sbb-color-milk)', + backgroundColor: 'var(--sbb-color-milk-default)', }; const formStyle: Args = { @@ -338,12 +337,10 @@ const FormTemplate = ({ method and returning the form values to update the details.
e.preventDefault()}> - - - + + - - +