From c21d102de17c311421884ddc17065f81e2c0f44a Mon Sep 17 00:00:00 2001 From: Sangeetha Babu Date: Sat, 26 Oct 2024 00:57:01 +0530 Subject: [PATCH] feat(Tearsheet): Move Tearsheet into @carbon/ibm-products-web-components (#6204) * feat(Tearsheet): web components create component * feat(Tearsheet): add test case * feat(Tearsheet): web components wtorybook update * feat(Tearsheet): style include issue * feat(Tearsheet): style path update * fix: remove sb theme package * fix(ibm products tearsheet): theme integation issue * fix(ibm products tearsheet): theme integation issue * chore(storybook): rename preview.ts * chore(storybook): rename preview.ts * chore(storybook): update configs * chore(styles): fix web component style import paths * chore(deps): re-add @carbon/ibm-products-styles resolution * chore(deps): update yarn.lock * chore(styles): revert style import change * chore(deps): remove styles resolution * chore(deploy): update netlify.toml * chore(package): update build command * chore(package): update storybook build script * chore(netlify): test config removal --------- Co-authored-by: Matt Gallo Co-authored-by: kennylam <909118+kennylam@users.noreply.github.com> --- cspell.json | 1 + packages/core/.storybook/preview.js | 1 + .../.storybook/main.ts | 16 +- .../.storybook/{Preview.ts => preview.ts} | 0 .../.storybook/{theme.ts => theme.js} | 4 +- .../ibm-products-web-components/netlify.toml | 9 - .../ibm-products-web-components/package.json | 20 +- .../src/components/tearsheet/defs.ts | 30 + .../src/components/tearsheet/index.ts | 10 + .../components/tearsheet/story-styles.scss | 23 + .../src/components/tearsheet/tearsheet.mdx | 101 +++ .../src/components/tearsheet/tearsheet.scss | 318 +++++++ .../components/tearsheet/tearsheet.stories.ts | 703 ++++++++++++++++ .../components/tearsheet/tearsheet.test.ts | 118 +++ .../src/components/tearsheet/tearsheet.ts | 792 ++++++++++++++++++ .../ibm-products-web-components/src/index.ts | 1 + yarn.lock | 306 ++++--- 17 files changed, 2269 insertions(+), 184 deletions(-) rename packages/ibm-products-web-components/.storybook/{Preview.ts => preview.ts} (100%) rename packages/ibm-products-web-components/.storybook/{theme.ts => theme.js} (83%) delete mode 100644 packages/ibm-products-web-components/netlify.toml create mode 100644 packages/ibm-products-web-components/src/components/tearsheet/defs.ts create mode 100644 packages/ibm-products-web-components/src/components/tearsheet/index.ts create mode 100644 packages/ibm-products-web-components/src/components/tearsheet/story-styles.scss create mode 100644 packages/ibm-products-web-components/src/components/tearsheet/tearsheet.mdx create mode 100644 packages/ibm-products-web-components/src/components/tearsheet/tearsheet.scss create mode 100644 packages/ibm-products-web-components/src/components/tearsheet/tearsheet.stories.ts create mode 100644 packages/ibm-products-web-components/src/components/tearsheet/tearsheet.test.ts create mode 100644 packages/ibm-products-web-components/src/components/tearsheet/tearsheet.ts diff --git a/cspell.json b/cspell.json index f5352896a1..97e60cb238 100644 --- a/cspell.json +++ b/cspell.json @@ -122,6 +122,7 @@ "httperror", "httperrorother", "importmodal", + "influencers", "inlineedit", "inlinetip", "interstitialscreenview", diff --git a/packages/core/.storybook/preview.js b/packages/core/.storybook/preview.js index fa6aa508b7..39886913cd 100644 --- a/packages/core/.storybook/preview.js +++ b/packages/core/.storybook/preview.js @@ -158,6 +158,7 @@ const globalTypes = { defaultValue: 'g10', toolbar: { icon: 'paintbrush', + title: 'Theme', items: ['white', 'g10', 'g90', 'g100'], }, }, diff --git a/packages/ibm-products-web-components/.storybook/main.ts b/packages/ibm-products-web-components/.storybook/main.ts index 85310a86e7..651209d990 100644 --- a/packages/ibm-products-web-components/.storybook/main.ts +++ b/packages/ibm-products-web-components/.storybook/main.ts @@ -4,7 +4,21 @@ import viteSVGResultCarbonIconLoader from '../tools/vite-svg-result-carbon-icon- const config = { stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'], - addons: ['@storybook/addon-links', '@storybook/addon-essentials'], + addons: [ + '@storybook/addon-links', + '@storybook/addon-toolbars', + { + name: '@storybook/addon-essentials', + options: { + actions: true, + backgrounds: false, + controls: true, + docs: true, + toolbars: true, + viewport: true, + }, + }, + ], framework: { name: '@storybook/web-components-vite', options: {}, diff --git a/packages/ibm-products-web-components/.storybook/Preview.ts b/packages/ibm-products-web-components/.storybook/preview.ts similarity index 100% rename from packages/ibm-products-web-components/.storybook/Preview.ts rename to packages/ibm-products-web-components/.storybook/preview.ts diff --git a/packages/ibm-products-web-components/.storybook/theme.ts b/packages/ibm-products-web-components/.storybook/theme.js similarity index 83% rename from packages/ibm-products-web-components/.storybook/theme.ts rename to packages/ibm-products-web-components/.storybook/theme.js index eaa2117efb..ad23afeec7 100644 --- a/packages/ibm-products-web-components/.storybook/theme.ts +++ b/packages/ibm-products-web-components/.storybook/theme.js @@ -1,10 +1,10 @@ -import { create } from '@storybook/theming'; +import { create } from '@storybook/theming/create'; import packageInfo from '../package.json'; const { description, version } = packageInfo; export default create({ - base: 'light', + base: 'white', brandTitle: `${description} v${version}`, fontBase: "'IBM Plex Sans', 'Helvetica Neue', Arial, sans-serif", fontCode: "'IBM Plex Mono', Menlo, 'DejaVu Sans Mono', Courier, monospace", diff --git a/packages/ibm-products-web-components/netlify.toml b/packages/ibm-products-web-components/netlify.toml deleted file mode 100644 index 9bb231a757..0000000000 --- a/packages/ibm-products-web-components/netlify.toml +++ /dev/null @@ -1,9 +0,0 @@ -# Netlify build and deploy settings - https://docs.netlify.com/configure-builds/file-based-configuration/#sample-file -[build] -command = "yarn build:storybook" -publish = "storybook-static" -ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF packages/ibm-products-web-components" - -[build.environment] -YARN_ENABLE_GLOBAL_CACHE = "true" -YARN_GLOBAL_FOLDER = "/opt/buildhome/.yarn_cache" diff --git a/packages/ibm-products-web-components/package.json b/packages/ibm-products-web-components/package.json index 9d6af6d73e..3371fc6611 100644 --- a/packages/ibm-products-web-components/package.json +++ b/packages/ibm-products-web-components/package.json @@ -39,8 +39,8 @@ "web components" ], "scripts": { - "build": "yarn clean && node tasks/build.js && yarn wca", - "build:storybook": "yarn wca && storybook build", + "build": "node tasks/build.js && yarn wca", + "build:storybook": "storybook build", "clean": "rimraf es lib scss dist storybook-static", "preview": "vite preview", "storybook": "storybook dev -p 3000", @@ -62,14 +62,14 @@ "@rollup/plugin-commonjs": "^28.0.0", "@rollup/plugin-node-resolve": "^15.3.0", "@rollup/plugin-typescript": "^12.1.0", - "@storybook/addon-essentials": "^8.2.8", - "@storybook/addon-links": "^8.2.8", - "@storybook/addon-storysource": "^8.2.8", - "@storybook/blocks": "^8.2.8", - "@storybook/manager-api": "^8.3.4", - "@storybook/theming": "^8.3.4", - "@storybook/web-components": "^8.2.8", - "@storybook/web-components-vite": "^8.2.8", + "@storybook/addon-essentials": "^8.3.6", + "@storybook/addon-links": "^8.3.6", + "@storybook/addon-storysource": "^8.3.6", + "@storybook/addon-toolbars": "^8.3.6", + "@storybook/blocks": "^8.3.6", + "@storybook/theming": "^8.3.6", + "@storybook/web-components": "^8.3.6", + "@storybook/web-components-vite": "^8.3.6", "@types/jest": "^29.5.13", "@vitest/browser": "latest", "@vitest/ui": "latest", diff --git a/packages/ibm-products-web-components/src/components/tearsheet/defs.ts b/packages/ibm-products-web-components/src/components/tearsheet/defs.ts new file mode 100644 index 0000000000..1c9a216b63 --- /dev/null +++ b/packages/ibm-products-web-components/src/components/tearsheet/defs.ts @@ -0,0 +1,30 @@ +/** + * @license + * + * Copyright IBM Corp. 2023, 2024 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +export enum TEARSHEET_INFLUENCER_PLACEMENT { + /** right / default */ + RIGHT = 'right', + + /** left */ + LEFT = 'left', +} + +export enum TEARSHEET_INFLUENCER_WIDTH { + /** narrow /default */ + NARROW = 'narrow', + /** wide */ + WIDE = 'wide', +} + +export enum TEARSHEET_WIDTH { + /** narrow */ + NARROW = 'narrow', + /** wide */ + WIDE = 'wide', +} diff --git a/packages/ibm-products-web-components/src/components/tearsheet/index.ts b/packages/ibm-products-web-components/src/components/tearsheet/index.ts new file mode 100644 index 0000000000..ccfe427053 --- /dev/null +++ b/packages/ibm-products-web-components/src/components/tearsheet/index.ts @@ -0,0 +1,10 @@ +/** + * @license + * + * Copyright IBM Corp. 2024, 2024 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import './tearsheet'; diff --git a/packages/ibm-products-web-components/src/components/tearsheet/story-styles.scss b/packages/ibm-products-web-components/src/components/tearsheet/story-styles.scss new file mode 100644 index 0000000000..0384a18609 --- /dev/null +++ b/packages/ibm-products-web-components/src/components/tearsheet/story-styles.scss @@ -0,0 +1,23 @@ +/* +* Copyright IBM Corp. 2023, 2024 +* +* This source code is licensed under the Apache-2.0 license found in the +* LICENSE file in the root directory of this source tree. +*/ +@use '@carbon/styles/scss/spacing' as *; +@use '@carbon/styles/scss/config' as *; + +$story-prefix: 'tearsheet-stories'; + +@use '@carbon/styles/scss/spacing' as *; + +#page-content-selector { + position: absolute; + z-index: 9999; + inset-block-start: 0; + inset-inline-start: 0; +} + +.#{$story-prefix}__tabs .#{$prefix}--tab-content { + display: none; +} diff --git a/packages/ibm-products-web-components/src/components/tearsheet/tearsheet.mdx b/packages/ibm-products-web-components/src/components/tearsheet/tearsheet.mdx new file mode 100644 index 0000000000..8c44b87303 --- /dev/null +++ b/packages/ibm-products-web-components/src/components/tearsheet/tearsheet.mdx @@ -0,0 +1,101 @@ +import { ArgTypes, Markdown, Meta } from '@storybook/blocks'; +import { cdnJs, cdnCss } from '../../globals/internal/storybook-cdn'; +import * as TearsheetStories from './tearsheet.stories'; + + + +# Tearsheet + +> 💡 Check our +> [Stackblitz](https://stackblitz.com/github/carbon-design-system/carbon/tree/main/packages/web-components/examples/components/tearsheet) +> example implementation. + +[![Edit carbon-web-components](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/carbon-design-system/carbon/tree/main/packages/web-components/examples/components/tearsheet) + +Tearsheets keep users in-context of a page while performing tasks like +navigating, editing, viewing details, or configuring something new. + +## Getting started + +Here's a quick example to get you started. + +### JS (via import) + +```javascript +import '@carbon/web-components/es/components/tearsheet/index.js'; +// The following are used for slotted fields +import '@carbon/web-components/es/components/text-input/index.js'; +import '@carbon/web-components/es/components/textarea/index.js'; +import '@carbon/web-components/es/components/button/index.js'; +``` + +{`${cdnJs({ components: ['tearsheet'] })}`} +{`${cdnCss()}`} + +### HTML + +```html + + +
Tearsheet content
+
+ + +
+
+ + +
+
+ + + +
+ + +
+ Subtitle text which can provide more detail on the content being displayed. +
+ + + Copy + + ${Settings({ slot: 'icon' })} + + + ${Trashcan({ slot: 'icon' })} + + + + Ghost + +
+``` + +## `` attributes, properties and events + +Note: For `boolean` attributes, `true` means simply setting the attribute (e.g. +``) and `false` means not setting the attribute (e.g. +`` without `open` attribute). + + diff --git a/packages/ibm-products-web-components/src/components/tearsheet/tearsheet.scss b/packages/ibm-products-web-components/src/components/tearsheet/tearsheet.scss new file mode 100644 index 0000000000..4a407acbe0 --- /dev/null +++ b/packages/ibm-products-web-components/src/components/tearsheet/tearsheet.scss @@ -0,0 +1,318 @@ +/* +* Copyright IBM Corp. 2023, 2024 +* +* This source code is licensed under the Apache-2.0 license found in the +* LICENSE file in the root directory of this source tree. +*/ + +$css--plex: true !default; + +/* Other Carbon settings. */ +@use '@carbon/styles/scss/reset'; +@use '@carbon/styles/scss/breakpoint' as *; +@use '@carbon/styles/scss/config' as *; +@use '@carbon/styles/scss/motion' as *; +@use '@carbon/styles/scss/spacing' as *; +@use '@carbon/styles/scss/theme' as *; +@use '@carbon/styles/scss/type'; +@use '@carbon/styles/scss/utilities'; +@use '@carbon/styles/scss/utilities/ai-gradient' as *; +@use '@carbon/styles/scss/components/modal' as *; +@use '@carbon/styles/scss/utilities/convert' as *; +@use 'sass:map'; + +$prefix: 'c4p'; +$carbon-prefix: 'cds'; + +@use '@carbon/ibm-products-styles/scss/components/Tearsheet/index' as *; +@use '@carbon/ibm-products-styles/scss/components/ActionSet/index' as *; + +$block-class: #{$prefix}--tearsheet; +$block-class-action-set: #{$prefix}--action-set; +$motion-duration: $duration-moderate-02; + +:host(#{$prefix}-tearsheet) { + --content-padding: #{$spacing-05}; + + @extend .#{$carbon-prefix}--modal; + @extend .#{$prefix}--tearsheet; + + .#{$block-class}__header, + .#{$block-class}__content, + .#{$block-class}__influencer { + padding: var(--content-padding); + } + + .#{$block-class}__container { + /* lower prop is deprecated but the default in ibm products */ + @extend .#{$block-class}__container--lower; + + max-block-size: calc(100% - (#{$spacing-09} + #{$spacing-08})); + } + + .#{$block-class}__container[stack-position='1'][stack-depth='2'], + .#{$block-class}__container[stack-position='2'][stack-depth='3'] { + max-block-size: calc( + 100% - (#{$spacing-09} + #{$spacing-08}) + #{$spacing-05} + ); + transform: scale(var(--#{$block-class}--stacking-scale-factor-single)); + } + + .#{$block-class}__container[stack-position='1'][stack-depth='3'] { + max-block-size: calc( + 100% - (#{$spacing-09} + #{$spacing-08}) + (2 * #{$spacing-05}) + ); + transform: scale(var(--#{$block-class}--stacking-scale-factor-double)); + } + + .#{$block-class}__buttons { + @extend .#{$block-class}__button-container; + @extend .#{$block-class-action-set}; + + display: flex; + background: $background; + inline-size: 100%; + } + + .#{$block-class}__buttons[hidden] { + @extend .#{$carbon-prefix}--visually-hidden; + + display: none; + } + + .#{$block-class}__buttons ::slotted(#{$carbon-prefix}-button) { + @extend .#{$block-class-action-set}__action-button; + + flex: 0 1 25%; + block-size: $spacing-11; + max-inline-size: to-rem(232px); + } + + .#{$block-class}__buttons ::slotted(#{$carbon-prefix}-button[kind='ghost']) { + flex: 1 1 25%; + max-inline-size: none; + } + + .#{$block-class}__buttons ::slotted(#{$carbon-prefix}-button[hidden]) { + @extend .#{$carbon-prefix}--visually-hidden; + + display: none; + } + + .#{$block-class}__influencer[wide] { + @extend .#{$block-class}__influencer--wide; + } +} + +:host(#{$prefix}-tearsheet[open]) { + --overlay-color: #{$overlay}; + --overlay-opacity: 1; + + z-index: utilities.z('modal'); + align-items: flex-end; + background: initial; + opacity: 1; + + transition: visibility 0s linear; + visibility: inherit; + + .#{$prefix}--tearsheet__container { + transform: translate3d(0, 0, 0); + transition: transform $duration-moderate-02 motion(entrance, expressive); + } + + @media (prefers-reduced-motion: reduce) { + transition: none; + } + + &::before { + position: absolute; + display: block; + background: var(--overlay-color); + content: ''; + inset: 0; + opacity: var(--overlay-opacity); + + transition: background-color $motion-duration motion(exit, expressive), + opacity $motion-duration motion(exit, expressive); + + @media (prefers-reduced-motion: reduce) { + transition: none; + } + + &[stack-position='1'][stack-depth='2'] { + --overlay-opacity: 0.67; + } + + &[stack-position='1'][stack-depth='3'] { + --overlay-opacity: 0.22; + } + + &[stack-position='2'][stack-depth='3'] { + --overlay-opacity: 0.5; + } + + &[stack-position='2'][stack-depth='2'], + &[stack-position='3'][stack-depth='3'] { + --overlay-opacity: 0.5; + } + } +} + +:host(#{$prefix}-tearsheet[hidden]) { + @extend .#{$carbon-prefix}--visually-hidden; +} + +:host(#{$prefix}-tearsheet[slug]) { + --overlay-color: #{$ai-overlay}; + + .#{$block-class}__container { + border: 1px solid transparent; + + /* override carbon ai removing background gradient */ + background: linear-gradient(to top, var(--cds-layer), var(--cds-layer)) + padding-box, + linear-gradient( + to bottom, + var(--cds-ai-border-start, #78a9ff), + var(--cds-ai-border-end, #d0e2ff) + ) + border-box, + linear-gradient(to top, var(--cds-layer), var(--cds-layer)) border-box; + border-block-end: 0; + box-shadow: 0 24px 40px -24px $ai-drop-shadow; + } + + .#{$block-class}__content { + @include utilities.ai-popover-gradient('default', 0); + + box-shadow: inset 0 -80px 70px -65px $ai-inner-shadow; + } +} + +:host(#{$prefix}-tearsheet[slug]) + .#{$prefix}--tearsheet__header[has-actions] + ::slotted(#{$prefix}-slug) { + inset-inline-end: 0; +} + +:host(#{$prefix}-tearsheet[stack-position='1'][stack-depth='2']), +:host(#{$prefix}-tearsheet[stack-position='2'][stack-depth='3']) { + z-index: utilities.z('modal') - 1; +} + +:host(#{$prefix}-tearsheet[stack-position='1'][stack-depth='3']) { + z-index: utilities.z('modal') - 2; +} + +:host(#{$prefix}-tearsheet[width='narrow']) { + .#{$block-class}__header { + margin: 0; + background-color: $layer; + border-block-end: 1px solid $border-subtle-01; + } + + .#{$block-class}__header-description { + margin-block-start: $spacing-03; + max-inline-size: 80%; + } + + .#{$block-class}__main { + background-color: $layer; + } +} + +:host(#{$prefix}-tearsheet[width='wide']) { + --content-padding: #{$spacing-06 $spacing-07}; + + .#{$block-class}__header { + margin: 0; + background-color: $layer; + border-block-end: 1px solid $border-subtle-01; + } + + .#{$block-class}__header[has-navigation] { + padding-block-end: 0; + } + + .#{$block-class}__container { + inline-size: 100%; + + @include breakpoint(md) { + inline-size: calc(100% - (2 * #{$spacing-10})); + } + } + + .#{$carbon-prefix}--modal-header__heading.#{$block-class}__heading { + @include type.type-style('heading-04'); + } + + .#{$block-class}__header[has-close-icon], + .#{$block-class}__header[has-slug] { + padding-inline-end: $spacing-11; + } + + .#{$block-class}__header[has-close-icon][has-slug] { + padding-inline-end: calc(#{$spacing-11 + $spacing-09}); + } + + .#{$block-class}__header-navigation { + margin-inline-start: calc(-1 * #{$spacing-05}); + max-block-size: $spacing-08; /* #{$prefix}-tabs too tall */ + } + + .#{$block-class}__content { + // Revert background color overridden by Carbon's modal - https://github.com/carbon-design-system/carbon/blob/main/packages/styles/scss/components/modal/_modal.scss#L54 + .#{$carbon-prefix}--pagination, + .#{$carbon-prefix}--pagination__control-buttons, + .#{$carbon-prefix}--text-input, + .#{$carbon-prefix}--text-area, + .#{$carbon-prefix}--search-input, + .#{$carbon-prefix}--select-input, + .#{$carbon-prefix}--dropdown, + .#{$carbon-prefix}--dropdown-list, + .#{$carbon-prefix}--number input[type='number'], + .#{$carbon-prefix}--date-picker__input { + background-color: $field; + } + + .#{$carbon-prefix}--select--inline .#{$carbon-prefix}--select-input { + background-color: transparent; + } + + // and restore the 'light' prop in case light fields are wanted + .#{$carbon-prefix}--text-input--light, + .#{$carbon-prefix}--text-area--light, + .#{$carbon-prefix}--search--light .#{$carbon-prefix}--search-input, + .#{$carbon-prefix}--select--light .#{$carbon-prefix}--select-input, + .#{$carbon-prefix}--dropdown--light, + .#{$carbon-prefix}--dropdown--light .#{$carbon-prefix}--dropdown-list, + /* stylelint-disable-next-line prettier/prettier */ + .#{$carbon-prefix}--number--light input[type='number'], + .#{$carbon-prefix}--date-picker--light + .#{$carbon-prefix}--date-picker__input { + background-color: $field-02; + } + } + .#{$prefix}--action-set + .#{$prefix}--action-set__action-button.#{$prefix}--action-set__action-button--expressive { + block-size: $spacing-11; + } +} + +:host(#{$prefix}-tearsheet[width='narrow']) + .#{$block-class}__buttons[actions-multiple='single'] + ::slotted(#{$carbon-prefix}-button), +:host(#{$prefix}-tearsheet[width='narrow']) + .#{$block-class}__buttons[actions-multiple='double'] + ::slotted(#{$carbon-prefix}-button) { + // double and single on lg use 50% + flex: 0 1 50%; + max-inline-size: none; +} + +:host(#{$prefix}-tearsheet[width='narrow']) + .#{$block-class}__buttons + ::slotted(#{$carbon-prefix}-button) { + block-size: $spacing-10; +} diff --git a/packages/ibm-products-web-components/src/components/tearsheet/tearsheet.stories.ts b/packages/ibm-products-web-components/src/components/tearsheet/tearsheet.stories.ts new file mode 100644 index 0000000000..407bbbac55 --- /dev/null +++ b/packages/ibm-products-web-components/src/components/tearsheet/tearsheet.stories.ts @@ -0,0 +1,703 @@ +/** + * @license + * + * Copyright IBM Corp. 2024, 2024 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { html } from 'lit'; +import { + TEARSHEET_INFLUENCER_PLACEMENT, + TEARSHEET_INFLUENCER_WIDTH, + TEARSHEET_WIDTH, +} from './tearsheet'; +import './index'; +import '@carbon/web-components/es/components/tabs/index.js'; +import '@carbon/web-components/es/components/slug/index.js'; +import '@carbon/web-components/es/components/progress-indicator/index.js'; +import '@carbon/web-components/es/components/progress-bar/index.js'; +import '@carbon/web-components/es/components/button/index.js'; +import '@carbon/web-components/es/components/text-input/index.js'; +import '@carbon/web-components/es/components/textarea/index.js'; +import { prefix } from '../../globals/settings'; + +import styles from './story-styles.scss?lit'; +import { BUTTON_KIND } from '@carbon/web-components/es/components/button/defs.js'; +const toggleButton = () => { + document.querySelector(`${prefix}-tearsheet`)?.toggleAttribute('open'); +}; + +const widths = { + // 'default (narrow)': null, + [`Narrow (${TEARSHEET_WIDTH.NARROW})`]: TEARSHEET_WIDTH.NARROW, + [`Wide (${TEARSHEET_WIDTH.WIDE})`]: TEARSHEET_WIDTH.WIDE, +}; + +const influencerWidths = { + // 'default (narrow)': null, + [`Narrow (${TEARSHEET_INFLUENCER_WIDTH.NARROW})`]: + TEARSHEET_INFLUENCER_WIDTH.NARROW, + [`Wide (${TEARSHEET_INFLUENCER_WIDTH.WIDE})`]: + TEARSHEET_INFLUENCER_WIDTH.WIDE, +}; + +const influencerPlacements = { + // 'default (right)': null, + [`Left (${TEARSHEET_INFLUENCER_PLACEMENT.LEFT})`]: + TEARSHEET_INFLUENCER_PLACEMENT.LEFT, + [`right (${TEARSHEET_INFLUENCER_PLACEMENT.RIGHT})`]: + TEARSHEET_INFLUENCER_PLACEMENT.RIGHT, +}; + +const influencers = { + 'No influencer': 0, + 'Simple influencer': 1, + 'Progress influencer': 2, +}; + +const getInfluencer = (index) => { + switch (index) { + case 1: + return html`
+ Influencer +
`; + case 2: + return html` + + + + + + `; + default: + return null; + } +}; + +const contents = { + Empty: 0, + 'Brief content': 1, + 'Longer content': 2, +}; + +const storyPrefix = 'tearsheet-stories'; + +const getContent = (index) => { + switch (index) { + case 1: + return html` + +
+
Section
+ + +
+ `; + case 2: + return html` +
+
Section
+
+ + +
+
+ + +
+
+ + + +
+
`; + default: + return null; + } +}; + +const labels = { + 'No label': 0, + 'Shorter label': 1, + 'Longer label': 2, +}; + +const getLabel = (index) => { + switch (index) { + case 1: + return html`Optional label for context`; + case 2: + return html`A longer label giving a bit more context + `; + default: + return null; + } +}; + +const headerActions = { + 'No header actions': 0, + 'Drop down': 1, + Buttons: 2, +}; + +const getActionToolbarItems = (index) => { + switch (index) { + case 1: + return html` + ${['option 1', 'option 2', 'option 3', 'option 4'].map( + (option) => html` ${option}` + )} + `; + case 2: + return html` + + Secondary + + + Primary + + `; + default: + return null; + } +}; + +const actionItems = { + 'No actions': 0, + 'One button': 1, + 'Two buttons with ghost': 2, + 'Two buttons with danger': 3, + 'Three buttons with ghost': 4, + 'Three buttons with danger': 5, + 'Four buttons with ghost': 6, + 'Four buttons with danger': 7, + 'Too many buttons': 8, +}; + +const toActions = (kinds: BUTTON_KIND[]) => { + return kinds?.map((kind) => { + return html` + ${kind.charAt(0).toUpperCase() + kind.slice(1)} + `; + }); +}; + +// TODO: There are problems switching this +const getActionItems = (index) => { + switch (index) { + case 1: + return toActions([BUTTON_KIND.PRIMARY]); + case 2: + return toActions([BUTTON_KIND.GHOST, BUTTON_KIND.PRIMARY]); + case 3: + return toActions([BUTTON_KIND.DANGER, BUTTON_KIND.PRIMARY]); + case 4: + return toActions([ + BUTTON_KIND.GHOST, + BUTTON_KIND.SECONDARY, + BUTTON_KIND.PRIMARY, + ]); + case 5: + return toActions([ + BUTTON_KIND.DANGER, + BUTTON_KIND.SECONDARY, + BUTTON_KIND.PRIMARY, + ]); + case 6: + return toActions([ + BUTTON_KIND.GHOST, + BUTTON_KIND.TERTIARY, + BUTTON_KIND.SECONDARY, + BUTTON_KIND.PRIMARY, + ]); + case 7: + return toActions([ + BUTTON_KIND.DANGER, + BUTTON_KIND.TERTIARY, + BUTTON_KIND.SECONDARY, + BUTTON_KIND.PRIMARY, + ]); + case 8: + return toActions([ + BUTTON_KIND.GHOST, + BUTTON_KIND.DANGER, + BUTTON_KIND.TERTIARY, + BUTTON_KIND.SECONDARY, + BUTTON_KIND.PRIMARY, + ]); + default: + return null; + } +}; + +const navigation = { + 'No navigation': 0, + 'With navigation': 1, +}; + +const getNavigation = (index) => { + switch (index) { + case 1: + return html`
+ + Tab 1 + Tab 2 + Tab 3 + Tab 4 + +
`; + default: + return null; + } +}; + +const slugs = { + 'No Slug': 0, + 'With Slug': 1, +}; + +const getSlug = (index) => { + switch (index) { + case 1: + return html` +
+

AI Explained

+

84%

+

Confidence score

+ +

+ Lorem ipsum dolor sit amet, di os consectetur adipiscing elit, sed + do eiusmod tempor incididunt ut fsil labore et dolore magna aliqua. +

+ +
+

Model type

+

Foundation model

+
+
`; + default: + return null; + } +}; + +export const Default = { + args: { + actionItems: getActionItems(4), + headerActions: getActionToolbarItems(0), + content: getContent(2), + label: getLabel(1), + open: false, + influencerWidth: TEARSHEET_INFLUENCER_WIDTH.NARROW, + influencerPlacement: TEARSHEET_INFLUENCER_PLACEMENT.LEFT, + influencer: getInfluencer(0), + preventCloseOnClickOutside: false, + selectorInitialFocus: '', + width: TEARSHEET_WIDTH.WIDE, + slug: getSlug(0), + description: 'Description used to describe the flow if need be.', + title: 'Title used to designate the overarching flow of the tearsheet.', + headerNavigation: getNavigation(0), + }, + argTypes: { + actionItems: { + control: 'select', + description: 'Slot (actions)', + options: actionItems, + }, + headerActions: { + control: 'select', + description: 'Slot (header-toolbar)', + options: headerActions, + }, + content: { + control: 'select', + description: 'Slot (default), panel contents', + options: contents, + }, + label: { + control: 'select', + description: 'label', + options: labels, + }, + open: { + control: 'boolean', + description: 'open', + }, + influencerWidth: { + control: 'select', + description: 'influencer-width', + options: influencerWidths, + }, + influencerPlacement: { + control: 'select', + description: 'influencer-placement', + options: influencerPlacements, + }, + influencer: { + control: 'select', + description: 'influencer (slot)', + options: influencers, + }, + preventCloseOnClickOutside: { + control: 'boolean', + description: 'prevent-close-on-click-outside', + }, + selectorInitialFocus: { + control: 'text', + description: 'selector-initial-focus', + }, + width: { + control: 'select', + description: 'width', + options: widths, + }, + slug: { + control: 'select', + description: 'slug (AI slug)', + options: slugs, + }, + description: { + control: 'text', + description: 'description', + }, + title: { + control: 'text', + description: 'title', + }, + headerNavigation: { + control: 'select', + description: 'header-navigation', + options: navigation, + }, + }, + render: (args) => { + return html` +
+
+
+ Toggle tearsheet +
+
+ + + ${args.content} + + + ${args.label} + + + ${args.title ? html`${args.title}` : ''} + + + ${args.description + ? html`${args.description}` + : ''} + + + ${args.headerActions} + + + ${args.actionItems} + + + ${args.slug} + + + ${args.headerNavigation} + + + ${args.influencer} + + `; + }, +}; + +export const WithNavigation = { + ...Default, + args: { + ...Default.args, + headerNavigation: getNavigation(1), + }, +}; + +export const WithInfluencer = { + ...Default, + args: { + ...Default.args, + influencer: getInfluencer(2), + }, +}; + +export const WithAllHeaderItemsAndInfluencer = { + ...Default, + args: { + ...Default.args, + headerActions: getActionToolbarItems(2), + influencer: getInfluencer(2), + }, +}; + +export const Narrow = { + ...Default, + args: { + ...Default.args, + label: getLabel(0), + width: TEARSHEET_WIDTH.NARROW, + }, +}; + +export const NarrowWithAllHeaderItems = { + ...Default, + args: { + ...Default.args, + width: TEARSHEET_WIDTH.NARROW, + }, +}; + +export const StackingTemplate = { + ...Default, + args: { + ...Default.args, + }, + render: (args) => { + const toggleButton = (index) => { + const tearsheet = document.querySelector(`[data-index="${index}"]`); + tearsheet?.toggleAttribute('open'); + }; + + return html` +
+
+
+ + Toggle tearsheet one + Toggle tearsheet two + Toggle tearsheet three + +
+
+ + + Toggle tearsheet two + ${args.content} + + + ${args.label} + + + ${args.title ? html`One ${args.title}` : ''} + + + ${args.description + ? html`${args.description}` + : ''} + + + ${args.headerActions} + + + ${args.actionItems} + + + ${args.slug} + + + ${args.headerNavigation} + + + ${args.influencer} + + + + Toggle tearsheet three + ${args.content} + + + ${args.label} + + + ${args.title ? html`Two ${args.title}` : ''} + + + ${args.description + ? html`${args.description}` + : ''} + + + ${args.headerActions} + + + ${args.actionItems} + + + ${args.slug} + + + ${args.headerNavigation} + + + ${args.influencer} + + + + ${args.content} + + + ${args.label} + + + ${args.title ? html`Three ${args.title}` : ''} + + + ${args.description + ? html`${args.description}` + : ''} + + + ${args.headerActions} + + + ${args.actionItems} + + + ${args.slug} + + + ${args.headerNavigation} + + + ${args.influencer} + + `; + }, +}; + +const meta = { + title: 'Experimental/Tearsheet', +}; + +export default meta; diff --git a/packages/ibm-products-web-components/src/components/tearsheet/tearsheet.test.ts b/packages/ibm-products-web-components/src/components/tearsheet/tearsheet.test.ts new file mode 100644 index 0000000000..b0e3c90b4a --- /dev/null +++ b/packages/ibm-products-web-components/src/components/tearsheet/tearsheet.test.ts @@ -0,0 +1,118 @@ +/** + * Copyright IBM Corp. 2024, 2024 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +vi.mock('@carbon/icons/lib/close/20', () => vi.fn().mockReturnValue({})); +import { describe, expect, it, vi } from 'vitest'; +import { render, html } from 'lit'; + +const defaultProps = { + actionItems: ` + Ghost + Secondary + Primary + `, + headerActions: '', + content: `
+
Section
+
+ + +
+ + + +
+
+ + + +
+ `, + label: `Optional label for context`, + open: false, + influencerWidth: 'narrow', + influencerPlacement: 'left', + influencer: '', + preventCloseOnClickOutside: false, + selectorInitialFocus: '', + width: 'wide', + slug: '', + description: 'Description used to describe the flow if need be.', + title: 'Title used to designate the overarching flow of the tearsheet.', + headerNavigation: '', +}; + +const template = (props = defaultProps) => + html` + + + ${props.content} + + + ${props.label} + + + ${props.title ? html`${props.title}` : ''} + + + ${props.description + ? html`${props.description}` + : ''} + + + ${props.headerActions} + + + ${props.actionItems} + + + ${props.slug} + + + ${props.headerNavigation} + + + ${props.influencer} + + `; + +describe('c4p-tearsheet', () => { + it('should render a tearsheet', async () => { + render(template(), document.body); + await Promise.resolve(); + const elem = document.body.querySelector('c4p-tearsheet' as any); + expect(elem).toBeDefined(); + }); +}); diff --git a/packages/ibm-products-web-components/src/components/tearsheet/tearsheet.ts b/packages/ibm-products-web-components/src/components/tearsheet/tearsheet.ts new file mode 100644 index 0000000000..f44045374e --- /dev/null +++ b/packages/ibm-products-web-components/src/components/tearsheet/tearsheet.ts @@ -0,0 +1,792 @@ +/** + * @license + * + * Copyright IBM Corp. 2023, 2024 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { LitElement, html } from 'lit'; +import { + property, + query, + queryAssignedElements, + state, +} from 'lit/decorators.js'; +import { prefix, carbonPrefix } from '../../globals/settings'; +import HostListener from '@carbon/web-components/es/globals/decorators/host-listener.js'; +import HostListenerMixin from '@carbon/web-components/es/globals/mixins/host-listener.js'; +import styles from './tearsheet.scss?lit'; +import { selectorTabbable } from '../../globals/settings'; +import { carbonElement as customElement } from '@carbon/web-components/es/globals/decorators/carbon-element.js'; +import '@carbon/web-components/es/components/button/index.js'; +import '@carbon/web-components/es/components/layer/index.js'; +import '@carbon/web-components/es/components/button/button-set-base.js'; +import '@carbon/web-components/es/components/modal/index.js'; +import { + TEARSHEET_INFLUENCER_PLACEMENT, + TEARSHEET_INFLUENCER_WIDTH, + TEARSHEET_WIDTH, +} from './defs'; + +export { + TEARSHEET_INFLUENCER_PLACEMENT, + TEARSHEET_INFLUENCER_WIDTH, + TEARSHEET_WIDTH, +}; + +const maxStackDepth = 3; +type StackHandler = (newDepth: number, newPosition: number) => void; +interface StackState { + open: StackHandler[]; + all: StackHandler[]; +} + +// eslint-disable-next-line no-bitwise +const PRECEDING = + Node.DOCUMENT_POSITION_PRECEDING | Node.DOCUMENT_POSITION_CONTAINS; +// eslint-disable-next-line no-bitwise +const FOLLOWING = + Node.DOCUMENT_POSITION_FOLLOWING | Node.DOCUMENT_POSITION_CONTAINED_BY; + +const blockClass = `${prefix}--tearsheet`; +const blockClassModalHeader = `${carbonPrefix}--modal-header`; +const blockClassActionSet = `${prefix}--action-set`; + +/** + * Tries to focus on the given elements and bails out if one of them is successful. + * + * @param elements The elements. + * @param reverse `true` to go through the list in reverse order. + * @returns `true` if one of the attempts is successful, `false` otherwise. + */ +function tryFocusElements(elements: NodeListOf, reverse: boolean) { + if (!reverse) { + for (let i = 0; i < elements.length; ++i) { + const elem = elements[i]; + elem.focus(); + if (elem.ownerDocument!.activeElement === elem) { + return true; + } + } + } else { + for (let i = elements.length - 1; i >= 0; --i) { + const elem = elements[i]; + elem.focus(); + if (elem.ownerDocument!.activeElement === elem) { + return true; + } + } + } + return false; +} + +/** + * Tearsheet. + * + * @element c4p-tearsheet + * @csspart dialog The dialog. + * @fires c4p-tearsheet-beingclosed + * The custom event fired before this tearsheet is being closed upon a user gesture. + * Cancellation of this event stops the user-initiated action of closing this tearsheet. + * @fires c4p-tearsheet-closed - The custom event fired after this tearsheet is closed upon a user gesture. + */ +@customElement(`${prefix}-tearsheet`) +class CDSTearsheet extends HostListenerMixin(LitElement) { + /** + * The element that had focus before this tearsheet gets open. + */ + private _launcher: Element | null = null; + + /** + * Node to track focus going outside of tearsheet content. + */ + @query('#start-sentinel') + private _startSentinelNode!: HTMLAnchorElement; + + /** + * Node to track focus going outside of tearsheet content. + */ + @query('#end-sentinel') + private _endSentinelNode!: HTMLAnchorElement; + + /** + * Node to track tearsheet. + */ + @query(`.${blockClass}__container`) + private _tearsheet!: HTMLDivElement; + + @queryAssignedElements({ + slot: 'actions', + selector: `${carbonPrefix}-button`, + }) + private _actions!: Array; + + @state() + _actionsCount = 0; + + @state() + _hasHeaderActions = false; + + @state() + _hasLabel = false; + + @state() + _hasSlug = false; + + @state() + _hasTitle = false; + + @state() + _hasDescription = false; + + @state() + _hasInfluencerLeft = false; + + @state() + _hasInfluencerRight = false; + + @state() + _isOpen = false; + + @state() + _hasHeaderNavigation = false; + + /** + * Handles `click` event on this element. + * + * @param event The event. + */ + @HostListener('click') + // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to + private _handleClick = (event: MouseEvent) => { + if ( + event.composedPath().indexOf(this.shadowRoot!) < 0 && + !this.preventCloseOnClickOutside + ) { + this._handleUserInitiatedClose(event.target); + } + }; + + /** + * Handles `blur` event on this element. + * + * @param event The event. + * @param event.target The event target. + * @param event.relatedTarget The event relatedTarget. + */ + @HostListener('shadowRoot:focusout') + // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to + private _handleBlur = async ({ target, relatedTarget }: FocusEvent) => { + if (!this._topOfStack()) { + return; + } + + const { + // condensedActions, + open, + _startSentinelNode: startSentinelNode, + _endSentinelNode: endSentinelNode, + } = this; + + const oldContains = target !== this && this.contains(target as Node); + const currentContains = + relatedTarget !== this && + (this.contains(relatedTarget as Node) || + (this.shadowRoot?.contains(relatedTarget as Node) && + relatedTarget !== (startSentinelNode as Node) && + relatedTarget !== (endSentinelNode as Node))); + + // Performs focus wrapping if _all_ of the following is met: + // * This tearsheet is open + // * The viewport still has focus + // * Tearsheet body used to have focus but no longer has focus + const { selectorTabbable: selectorTabbableForTearsheet } = this + .constructor as typeof CDSTearsheet; + + if (open && relatedTarget && oldContains && !currentContains) { + const comparisonResult = (target as Node).compareDocumentPosition( + relatedTarget as Node + ); + // eslint-disable-next-line no-bitwise + if (relatedTarget === startSentinelNode || comparisonResult & PRECEDING) { + await (this.constructor as typeof CDSTearsheet)._delay(); + if ( + !tryFocusElements( + this.querySelectorAll(selectorTabbableForTearsheet), + true + ) && + relatedTarget !== this + ) { + this.focus(); + } + } + // eslint-disable-next-line no-bitwise + else if ( + relatedTarget === endSentinelNode || + comparisonResult & FOLLOWING + ) { + await (this.constructor as typeof CDSTearsheet)._delay(); + if ( + !tryFocusElements( + this.querySelectorAll(selectorTabbableForTearsheet), + true + ) + ) { + this.focus(); + } + } + } + }; + + @HostListener('document:keydown') + // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to + private _handleKeydown = ({ key, target }: KeyboardEvent) => { + if ((key === 'Esc' || key === 'Escape') && this._topOfStack()) { + this._handleUserInitiatedClose(target); + } + }; + + private _checkSetHasSlot(e: Event) { + const t = e.target as HTMLSlotElement; + const dataPostfix = t.getAttribute('data-postfix'); + const postfix = dataPostfix ? `-${dataPostfix}` : ''; + + // snake `ab-cd-ef` to _has camel case _hasAbCdEf + const hasName = `_has-${t.name}${postfix}`.replace(/-./g, (c) => + c[1].toUpperCase() + ); + this[hasName] = (t?.assignedElements()?.length ?? 0) > 0; + } + + /** + * Handles `click` event on the modal container. + * + * @param event The event. + */ + private _handleClickContainer(event: MouseEvent) { + if ( + (event.target as Element).matches( + (this.constructor as typeof CDSTearsheet).selectorCloseButton + ) + ) { + this._handleUserInitiatedClose(event.target); + } + } + + /** + * Handles user-initiated close request of this tearsheet. + * + * @param triggeredBy The element that triggered this close request. + */ + private _handleUserInitiatedClose(triggeredBy: EventTarget | null) { + if (this.open) { + const init = { + bubbles: true, + cancelable: true, + composed: true, + detail: { + triggeredBy, + }, + }; + if ( + this.dispatchEvent( + new CustomEvent( + (this.constructor as typeof CDSTearsheet).eventBeforeClose, + init + ) + ) + ) { + this.open = false; + this.dispatchEvent( + new CustomEvent( + (this.constructor as typeof CDSTearsheet).eventClose, + init + ) + ); + } + } + } + + private _handleSlugChange(e: Event) { + const childItems = (e.target as HTMLSlotElement).assignedElements(); + + this._hasSlug = childItems.length > 0; + if (this._hasSlug) { + childItems[0].setAttribute('size', 'lg'); + this.setAttribute('slug', ''); + } else { + this.removeAttribute('slug'); + } + } + + /** + * Optional aria label for the tearsheet + */ + @property({ reflect: true, attribute: 'aria-label' }) + ariaLabel = ''; + + /** + * Sets the close button icon description + */ + @property({ reflect: true, attribute: 'close-icon-description' }) + closeIconDescription = 'Close'; + + /** + * Enable a close icon ('x') in the header area of the tearsheet. By default, + * (when this prop is omitted, or undefined or null) a tearsheet does not + * display a close icon if there are navigation actions ("transactional + * tearsheet") and displays one if there are no navigation actions ("passive + * tearsheet"), and that behavior can be overridden if required by setting + * this prop to either true or false. + */ + + @property({ reflect: true, type: Boolean, attribute: 'has-close-icon' }) + hasCloseIcon = false; + + /** + * The placement of the influencer section, 'left' or 'right'. + */ + @property({ reflect: true, attribute: 'influencer-placement' }) + influencerPlacement = TEARSHEET_INFLUENCER_PLACEMENT.RIGHT; + + /** + * The width of the influencer section, 'narrow' or 'wide'. + */ + @property({ reflect: true, attribute: 'influencer-width' }) + influencerWidth = TEARSHEET_INFLUENCER_WIDTH.NARROW; + + /** + * `true` if the tearsheet should be open. + */ + @property({ type: Boolean, reflect: true }) + open = false; + + /** + * Prevent closing on click outside of tearsheet + */ + @property({ type: Boolean, attribute: 'prevent-close-on-click-outside' }) + preventCloseOnClickOutside = false; + + /** + * The initial location of focus in the side panel + */ + @property({ + reflect: true, + attribute: 'selector-initial-focus', + type: String, + }) + selectorInitialFocus; + + /** + * The width of the influencer section, 'narrow' or 'wide'. + */ + @property({ reflect: true, attribute: 'width' }) + width = TEARSHEET_WIDTH.NARROW; + + private _checkUpdateActionSizes = () => { + if (this._actions) { + for (let i = 0; i < this._actions.length; i++) { + this._actions[i].setAttribute( + 'size', + this.width === 'wide' ? '2xl' : 'xl' + ); + } + } + }; + + private _maxActions = 4; + private _handleActionsChange(e: Event) { + const target = e.target as HTMLSlotElement; + const actions = target?.assignedElements(); + const actionsCount = actions?.length ?? 0; + + if (actionsCount > this._maxActions) { + this._actionsCount = this._maxActions; + console.error(`Too many tearsheet actions, max ${this._maxActions}.`); + } else { + this._actionsCount = actionsCount; + } + + for (let i = 0; i < actions?.length; i++) { + if (i + 1 > this._maxActions) { + // hide excessive tearsheet actions + actions[i].setAttribute('hidden', 'true'); + actions[i].setAttribute( + `data-actions-limit-${this._maxActions}-exceeded`, + `${actions.length}` + ); + } else { + actions[i].classList.add(`${blockClassActionSet}__action-button`); + } + } + this._checkUpdateActionSizes(); + } + + // Data structure to communicate the state of tearsheet stacking + // (i.e. when more than one tearsheet is open). Each tearsheet supplies a + // handler to be called whenever the stacking of the tearsheets changes, which + // happens when a tearsheet opens or closes. The 'open' array contains one + // handler per OPEN tearsheet ordered from lowest to highest in visual z-order. + // The 'all' array contains all the handlers for open and closed tearsheets. + + @state() + _stackDepth = -1; + + @state() + _stackPosition = -1; + + private _topOfStack = () => { + return this._stackDepth === this._stackPosition; + }; + + private static _stack: StackState = { + open: [], + all: [], + }; + private _notifyStack = () => { + CDSTearsheet._stack.all.forEach( + (handler: (stackSize: number, position: number) => void) => { + handler( + Math.min(CDSTearsheet._stack.open.length, maxStackDepth), + CDSTearsheet._stack.open.indexOf(handler) + 1 + ); + } + ); + }; + + private _handleStackChange: StackHandler = (newDepth, newPosition) => { + this._stackDepth = newDepth; + this._stackPosition = newPosition; + if (this._stackDepth > 1 && this._stackPosition > 0) { + this.setAttribute('stack-position', `${newPosition}`); + this.setAttribute('stack-depth', `${this._stackDepth}`); + } else { + this.removeAttribute('stack-position'); + this.removeAttribute('stack-depth'); + } + }; + + private _updateStack = () => { + if (this.open) { + CDSTearsheet._stack.open.push(this._handleStackChange); + } else { + const indexOpen = CDSTearsheet._stack.open.indexOf( + this._handleStackChange + ); + if (indexOpen >= 0) { + CDSTearsheet._stack.open.splice(indexOpen, 1); + } + } + this._notifyStack(); + }; + + actionsMultiple = ['', 'single', 'double', 'triple'][this._actionsCount]; + + connectedCallback() { + super.connectedCallback(); + + CDSTearsheet._stack.all.push(this._handleStackChange); + } + + disconnectedCallback() { + super.disconnectedCallback(); + + const indexAll = CDSTearsheet._stack.all.indexOf(this._handleStackChange); + CDSTearsheet._stack.all.splice(indexAll, 1); + const indexOpen = CDSTearsheet._stack.all.indexOf(this._handleStackChange); + CDSTearsheet._stack.open.splice(indexOpen, 1); + } + + render() { + const { + closeIconDescription, + influencerPlacement, + influencerWidth, + open, + width, + } = this; + + const actionsMultiple = ['', 'single', 'double', 'triple'][ + this._actionsCount + ]; + const headerFieldsTemplate = html`
+

+ +

+

+ +

+
+ +
+
`; + + const headerActionsTemplate = html`
+ +
`; + + const headerTemplate = html` 0} + ?has-slug=${this?._hasSlug} + width=${width} + > + ${this.width === TEARSHEET_WIDTH.WIDE + ? html`${headerFieldsTemplate}${headerActionsTemplate}` + : html`
${headerFieldsTemplate}${headerActionsTemplate}
`} + +
+ +
+ + ${this.hasCloseIcon || this?._actionsCount === 0 + ? html`` + : ''} +
`; + + return html` + + + + `; + } + + _checkSetOpen = () => { + const { _tearsheet: tearsheet } = this; + if (tearsheet && this._isOpen) { + // wait until the tearsheet has transitioned off the screen to remove + tearsheet.addEventListener('transitionend', () => { + this._isOpen = false; + }); + } else { + // allow the html to render before animating in the tearsheet + window.requestAnimationFrame(() => { + this._isOpen = this.open; + }); + } + }; + + async updated(changedProperties) { + if (changedProperties.has('width')) { + this._checkUpdateActionSizes(); + } + + if ( + process.env.NODE_ENV === 'development' && + (changedProperties.has('width') || + changedProperties.has('_hasHeaderNavigation') || + changedProperties.has('_hasInfluencerLeft') || + changedProperties.has('_hasInfluencerRight') || + changedProperties.has('_hasHeaderActions')) + ) { + if (this.width === 'narrow') { + if (this._hasHeaderNavigation) { + console.error( + `Header navigation is not permitted in narrow Tearsheet.` + ); + } + if (this._hasInfluencerLeft || this._hasInfluencerRight) { + console.error(`Influencer is not permitted in narrow Tearsheet.`); + } + if (this._hasHeaderActions) { + console.error( + `Header actions are not permitted in narrow Tearsheet.` + ); + } + } + } + + if (changedProperties.has('open')) { + this._updateStack(); + + this._checkSetOpen(); + if (this.open) { + this._launcher = this.ownerDocument!.activeElement; + const focusNode = + this.selectorInitialFocus && + this.querySelector(this.selectorInitialFocus); + + await (this.constructor as typeof CDSTearsheet)._delay(); + if (focusNode) { + // For cases where a `carbon-web-components` component (e.g. ``) being `primaryFocusNode`, + // where its first update/render cycle that makes it focusable happens after ``'s first update/render cycle + (focusNode as HTMLElement).focus(); + } else if ( + !tryFocusElements( + this.querySelectorAll( + (this.constructor as typeof CDSTearsheet).selectorTabbable + ), + true + ) + ) { + this.focus(); + } + } else if ( + this._launcher && + typeof (this._launcher as HTMLElement).focus === 'function' + ) { + (this._launcher as HTMLElement).focus(); + this._launcher = null; + } + } + } + + /** + * @param ms The number of milliseconds. + * @returns A promise that is resolves after the given milliseconds. + */ + private static _delay(ms = 0) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); + } + + /** + * A selector selecting buttons that should close this modal. + */ + static get selectorCloseButton() { + return `[data-modal-close],${carbonPrefix}-modal-close-button`; + } + + /** + * A selector selecting tabbable nodes. + */ + static get selectorTabbable() { + return selectorTabbable; + } + + /** + * The name of the custom event fired before this tearsheet is being closed upon a user gesture. + * Cancellation of this event stops the user-initiated action of closing this tearsheet. + */ + static get eventBeforeClose() { + return `${prefix}-tearsheet-beingclosed`; + } + + /** + * The name of the custom event fired after this tearsheet is closed upon a user gesture. + */ + static get eventClose() { + return `${prefix}-tearsheet-closed`; + } + + /** + * The name of the custom event fired on clicking the navigate back button + */ + static get eventNavigateBack() { + return `${prefix}-tearsheet-header-navigate-back`; + } + + static styles = styles; // `styles` here is a `CSSResult` generated by custom WebPack loader +} + +export default CDSTearsheet; diff --git a/packages/ibm-products-web-components/src/index.ts b/packages/ibm-products-web-components/src/index.ts index 16e4db0fae..5f266a65c0 100644 --- a/packages/ibm-products-web-components/src/index.ts +++ b/packages/ibm-products-web-components/src/index.ts @@ -8,3 +8,4 @@ */ export { default as CDSSidePanel } from './components/side-panel/side-panel'; +export { default as CDSTearsheet } from './components/tearsheet/tearsheet'; diff --git a/yarn.lock b/yarn.lock index 1daeb2f189..4c1b54e5f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1869,14 +1869,14 @@ __metadata: "@rollup/plugin-commonjs": "npm:^28.0.0" "@rollup/plugin-node-resolve": "npm:^15.3.0" "@rollup/plugin-typescript": "npm:^12.1.0" - "@storybook/addon-essentials": "npm:^8.2.8" - "@storybook/addon-links": "npm:^8.2.8" - "@storybook/addon-storysource": "npm:^8.2.8" - "@storybook/blocks": "npm:^8.2.8" - "@storybook/manager-api": "npm:^8.3.4" - "@storybook/theming": "npm:^8.3.4" - "@storybook/web-components": "npm:^8.2.8" - "@storybook/web-components-vite": "npm:^8.2.8" + "@storybook/addon-essentials": "npm:^8.3.6" + "@storybook/addon-links": "npm:^8.3.6" + "@storybook/addon-storysource": "npm:^8.3.6" + "@storybook/addon-toolbars": "npm:^8.3.6" + "@storybook/blocks": "npm:^8.3.6" + "@storybook/theming": "npm:^8.3.6" + "@storybook/web-components": "npm:^8.3.6" + "@storybook/web-components-vite": "npm:^8.3.6" "@types/jest": "npm:^29.5.13" "@vitest/browser": "npm:latest" "@vitest/ui": "npm:latest" @@ -6051,9 +6051,9 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-actions@npm:8.3.3": - version: 8.3.3 - resolution: "@storybook/addon-actions@npm:8.3.3" +"@storybook/addon-actions@npm:8.3.6": + version: 8.3.6 + resolution: "@storybook/addon-actions@npm:8.3.6" dependencies: "@storybook/global": "npm:^5.0.0" "@types/uuid": "npm:^9.0.1" @@ -6061,8 +6061,8 @@ __metadata: polished: "npm:^4.2.2" uuid: "npm:^9.0.0" peerDependencies: - storybook: ^8.3.3 - checksum: d09b0a9138a05cd0a4ead309cff6a4cfab21f27c7d0232092d2c44ef7eca244dbba9bbbf411110477f191df7eea617a70543c9589c88bf5de4758428d614514e + storybook: ^8.3.6 + checksum: 94b5832dfab5494570ee06b39ccf4d0ae119332c8db479b0ef73aa9e06c853808aca642e14aca26c713ddfbaccccfcc56b8d38429d4d54d9d96fb4223474e406 languageName: node linkType: hard @@ -6077,16 +6077,16 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-backgrounds@npm:8.3.3": - version: 8.3.3 - resolution: "@storybook/addon-backgrounds@npm:8.3.3" +"@storybook/addon-backgrounds@npm:8.3.6": + version: 8.3.6 + resolution: "@storybook/addon-backgrounds@npm:8.3.6" dependencies: "@storybook/global": "npm:^5.0.0" memoizerific: "npm:^1.11.3" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^8.3.3 - checksum: 245b33a529ec88dfce2838ad1a9415555b73535fa8d1920cce92298507ccf8194e90e37fe2c82de2e6448ab0dd789cd1f24ea63bf28e55103d07c7ef99898a52 + storybook: ^8.3.6 + checksum: a8eaf489d7a6accbc838e7949b17540a0377526a45ca1babba44128c3a6d5a7e691b39ce41885ac2e191662bb7db9c912b833832a035dffe8a251ded0431a792 languageName: node linkType: hard @@ -6101,17 +6101,17 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-controls@npm:8.3.3": - version: 8.3.3 - resolution: "@storybook/addon-controls@npm:8.3.3" +"@storybook/addon-controls@npm:8.3.6": + version: 8.3.6 + resolution: "@storybook/addon-controls@npm:8.3.6" dependencies: "@storybook/global": "npm:^5.0.0" dequal: "npm:^2.0.2" lodash: "npm:^4.17.21" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^8.3.3 - checksum: 2028392103e866d891a484079dce3e98e1daa2970727d288c35825a767ee49c4983752d398deef18008b83d249ad7e78309c6023b7a66db6be336f5232e1d8a1 + storybook: ^8.3.6 + checksum: 0ea4df71f2a12252c559d8b9beeefad17358dea5cc675e03a76a9effba9e48b3fa42d2f194c62e9accd8bb86bbf64078bd9d03f134d5bfa903adfdacbcd638ea languageName: node linkType: hard @@ -6143,15 +6143,15 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-docs@npm:8.3.3": - version: 8.3.3 - resolution: "@storybook/addon-docs@npm:8.3.3" +"@storybook/addon-docs@npm:8.3.6": + version: 8.3.6 + resolution: "@storybook/addon-docs@npm:8.3.6" dependencies: "@mdx-js/react": "npm:^3.0.0" - "@storybook/blocks": "npm:8.3.3" - "@storybook/csf-plugin": "npm:8.3.3" + "@storybook/blocks": "npm:8.3.6" + "@storybook/csf-plugin": "npm:8.3.6" "@storybook/global": "npm:^5.0.0" - "@storybook/react-dom-shim": "npm:8.3.3" + "@storybook/react-dom-shim": "npm:8.3.6" "@types/react": "npm:^16.8.0 || ^17.0.0 || ^18.0.0" fs-extra: "npm:^11.1.0" react: "npm:^16.8.0 || ^17.0.0 || ^18.0.0" @@ -6160,8 +6160,8 @@ __metadata: rehype-slug: "npm:^6.0.0" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^8.3.3 - checksum: 91d5e71bff5b60be5bc1444cb7d678202aa63222d5e811202b11c45cb425303196a75226cd06acfc9a589eeb90420d4778d85ba800bf07c7dae4a7c9f10f1f7d + storybook: ^8.3.6 + checksum: 8c8ae44903ddd8e100c0da3952d7c83dace3b1fd7a87531263b487dca378122d2273a57afd8b18006ddf3ae1c3473f5a197c44742843e92dd724734b9c5adeb3 languageName: node linkType: hard @@ -6187,23 +6187,23 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-essentials@npm:^8.2.8": - version: 8.3.3 - resolution: "@storybook/addon-essentials@npm:8.3.3" - dependencies: - "@storybook/addon-actions": "npm:8.3.3" - "@storybook/addon-backgrounds": "npm:8.3.3" - "@storybook/addon-controls": "npm:8.3.3" - "@storybook/addon-docs": "npm:8.3.3" - "@storybook/addon-highlight": "npm:8.3.3" - "@storybook/addon-measure": "npm:8.3.3" - "@storybook/addon-outline": "npm:8.3.3" - "@storybook/addon-toolbars": "npm:8.3.3" - "@storybook/addon-viewport": "npm:8.3.3" +"@storybook/addon-essentials@npm:^8.3.6": + version: 8.3.6 + resolution: "@storybook/addon-essentials@npm:8.3.6" + dependencies: + "@storybook/addon-actions": "npm:8.3.6" + "@storybook/addon-backgrounds": "npm:8.3.6" + "@storybook/addon-controls": "npm:8.3.6" + "@storybook/addon-docs": "npm:8.3.6" + "@storybook/addon-highlight": "npm:8.3.6" + "@storybook/addon-measure": "npm:8.3.6" + "@storybook/addon-outline": "npm:8.3.6" + "@storybook/addon-toolbars": "npm:8.3.6" + "@storybook/addon-viewport": "npm:8.3.6" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^8.3.3 - checksum: 18a4a8ab4456f43719ab95ff23875e454e45fdbd768c3069a9db9e9140138404c176c34aa909676eb447abbf2bf71bfa6a775024c0b4cfb759a3cda63bd31f63 + storybook: ^8.3.6 + checksum: 4b15ece66548adfefe41bcfed67aa60526253a47b36245fa63b523986e106f7399b6a1720f59a260ae89a7629b4ebefafca3a19853c5ccecd0b2f259f8dd0c3a languageName: node linkType: hard @@ -6236,14 +6236,14 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-highlight@npm:8.3.3": - version: 8.3.3 - resolution: "@storybook/addon-highlight@npm:8.3.3" +"@storybook/addon-highlight@npm:8.3.6": + version: 8.3.6 + resolution: "@storybook/addon-highlight@npm:8.3.6" dependencies: "@storybook/global": "npm:^5.0.0" peerDependencies: - storybook: ^8.3.3 - checksum: 0bb4c2d62ae3addcc7e4685bf802a410734b2286d36779db3a416752dda5041bdadefc9f32968029ac2788e1e070353dafa63f881ebb434caa163f7b00adee06 + storybook: ^8.3.6 + checksum: 948f295e0196d7a4669095859286dbfce463be220d1d8adcca578ba40c888f1879816ba3343db1a286cfe52bc79ae554314def76d6ec11e12493192c13eea2e5 languageName: node linkType: hard @@ -6263,20 +6263,20 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-links@npm:^8.2.8": - version: 8.3.3 - resolution: "@storybook/addon-links@npm:8.3.3" +"@storybook/addon-links@npm:^8.3.6": + version: 8.3.6 + resolution: "@storybook/addon-links@npm:8.3.6" dependencies: "@storybook/csf": "npm:^0.1.11" "@storybook/global": "npm:^5.0.0" ts-dedent: "npm:^2.0.0" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.3.3 + storybook: ^8.3.6 peerDependenciesMeta: react: optional: true - checksum: 874798d37335b5af90f9825f568d31988d2e27320f5c46fa47bd865d7ac8a491e896ce467105ff8f46990e3178db5f8681f1bfc1d3a3adf7f8498f5cb5af7120 + checksum: 1c6a1483098e5f1292824cb038d7a0efae78a8aee86e940092200b646501a6e4713a24e422fd3dd25781f17e08276dc5156cf3f4838b92a029fff2b4f3eb4e84 languageName: node linkType: hard @@ -6290,15 +6290,15 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-measure@npm:8.3.3": - version: 8.3.3 - resolution: "@storybook/addon-measure@npm:8.3.3" +"@storybook/addon-measure@npm:8.3.6": + version: 8.3.6 + resolution: "@storybook/addon-measure@npm:8.3.6" dependencies: "@storybook/global": "npm:^5.0.0" tiny-invariant: "npm:^1.3.1" peerDependencies: - storybook: ^8.3.3 - checksum: 23296c9bca5c543475afc2d7e01ad7081de4c6ac4a946a4d9e66befc04b46c730a3ee51b959c517d1596c0d62219e5692bfef43ee1451bd794ffdbd9b48d5008 + storybook: ^8.3.6 + checksum: cec0d3517ccdbe61e09906154929381cdd9ded1ac50391bf5530144e4ef40e8f46f1c0f8cadf41c382a98209df1c9c9c156c0b1acf3038526f24a92cc9082260 languageName: node linkType: hard @@ -6312,15 +6312,15 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-outline@npm:8.3.3": - version: 8.3.3 - resolution: "@storybook/addon-outline@npm:8.3.3" +"@storybook/addon-outline@npm:8.3.6": + version: 8.3.6 + resolution: "@storybook/addon-outline@npm:8.3.6" dependencies: "@storybook/global": "npm:^5.0.0" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^8.3.3 - checksum: 07135357bb0e3962ac94d452fa23a159614437c5d932d1020c687adb92186f7969109bff8d42083dbbed4b00d27cb59bfcf002701b12c6c7e647c1fbe982b09c + storybook: ^8.3.6 + checksum: 8b63ca2a5a6c5cd30fc4359f05f911825ff54946508a458aa8803b46cbde8badf470ffd9521480eb0be52eb00c064ccb1181170dcd0b621db091f52b04177b40 languageName: node linkType: hard @@ -6335,16 +6335,16 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-storysource@npm:^8.2.8": - version: 8.3.3 - resolution: "@storybook/addon-storysource@npm:8.3.3" +"@storybook/addon-storysource@npm:^8.3.6": + version: 8.3.6 + resolution: "@storybook/addon-storysource@npm:8.3.6" dependencies: - "@storybook/source-loader": "npm:8.3.3" + "@storybook/source-loader": "npm:8.3.6" estraverse: "npm:^5.2.0" tiny-invariant: "npm:^1.3.1" peerDependencies: - storybook: ^8.3.3 - checksum: 9ebc5f6e65b5e4229e2d19d9fa95941af7fcf264470878669c61cd33ab76451ec5c6909f2d6ac6d7d071fd4809160d6c3031040f5425a0dfc791e6a7b2a40e8a + storybook: ^8.3.6 + checksum: 6693afc4a79e0a75c3a58c091fadfa0d96d36bc511c7e960167028b06f1b31de857b0df80f6831893513e7ea0451c0cdc05a94f4b3957f6e5cdd4c6e7ab224f6 languageName: node linkType: hard @@ -6355,12 +6355,12 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-toolbars@npm:8.3.3": - version: 8.3.3 - resolution: "@storybook/addon-toolbars@npm:8.3.3" +"@storybook/addon-toolbars@npm:8.3.6, @storybook/addon-toolbars@npm:^8.3.6": + version: 8.3.6 + resolution: "@storybook/addon-toolbars@npm:8.3.6" peerDependencies: - storybook: ^8.3.3 - checksum: 51a7f460a9e158a8be8948797f3fbba71a6442fb73cc0eace143c32331bf6e39a9387f6b89ad5a2948868eba26f3edd6679a5c57c6883e08113c2517a16d1934 + storybook: ^8.3.6 + checksum: 09ae00f53e92735735eee645b35fca80e3dce29d39a0821f675e0f0bf6aae1ffe94b6545eb132da4026a86a77b352b2709956f742b5906d8caccabc1f72f948a languageName: node linkType: hard @@ -6373,14 +6373,14 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-viewport@npm:8.3.3": - version: 8.3.3 - resolution: "@storybook/addon-viewport@npm:8.3.3" +"@storybook/addon-viewport@npm:8.3.6": + version: 8.3.6 + resolution: "@storybook/addon-viewport@npm:8.3.6" dependencies: memoizerific: "npm:^1.11.3" peerDependencies: - storybook: ^8.3.3 - checksum: a29068e6aabd1470066c2fde6c033528216897af4586039344c9dfb0bae434d2b8f29ac122c21416b8a1a4da1652240ed2249ada3187dbeb8d88768ecd8b184c + storybook: ^8.3.6 + checksum: b9e09004b742dabbd115cec74a75656b01aef44c3476508cdfbdee24efbcb50b90470e66bd6bd735e804139e36aa65fea4d96c8c8f92b08153e6dde764f55943 languageName: node linkType: hard @@ -6445,9 +6445,9 @@ __metadata: languageName: node linkType: hard -"@storybook/blocks@npm:8.3.3, @storybook/blocks@npm:^8.2.8": - version: 8.3.3 - resolution: "@storybook/blocks@npm:8.3.3" +"@storybook/blocks@npm:8.3.6, @storybook/blocks@npm:^8.3.6": + version: 8.3.6 + resolution: "@storybook/blocks@npm:8.3.6" dependencies: "@storybook/csf": "npm:^0.1.11" "@storybook/global": "npm:^5.0.0" @@ -6466,13 +6466,13 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.3.3 + storybook: ^8.3.6 peerDependenciesMeta: react: optional: true react-dom: optional: true - checksum: fea00d9f369a365bcb9724ed4d0d163621724f1d6424a436190c4865bb4da0a19ae015d3b0b26fe913fdd527a6599a276ce1106cbaddd9534dbacca1ef72ef9f + checksum: db3b4676602c52af32de06032ed15a7b7723138675be0f27d6ac0f806806dab58b8ba0d97a69ff33e01c169e47bccb6b2f25df3e8b472287c76ae9da40be5a6b languageName: node linkType: hard @@ -6535,11 +6535,11 @@ __metadata: languageName: node linkType: hard -"@storybook/builder-vite@npm:8.3.3": - version: 8.3.3 - resolution: "@storybook/builder-vite@npm:8.3.3" +"@storybook/builder-vite@npm:8.3.6": + version: 8.3.6 + resolution: "@storybook/builder-vite@npm:8.3.6" dependencies: - "@storybook/csf-plugin": "npm:8.3.3" + "@storybook/csf-plugin": "npm:8.3.6" "@types/find-cache-dir": "npm:^3.2.1" browser-assert: "npm:^1.2.1" es-module-lexer: "npm:^1.5.0" @@ -6550,7 +6550,7 @@ __metadata: ts-dedent: "npm:^2.0.0" peerDependencies: "@preact/preset-vite": "*" - storybook: ^8.3.3 + storybook: ^8.3.6 typescript: ">= 4.3.x" vite: ^4.0.0 || ^5.0.0 vite-plugin-glimmerx: "*" @@ -6561,7 +6561,7 @@ __metadata: optional: true vite-plugin-glimmerx: optional: true - checksum: 28500d516ffb7466bef24e4abe5ac8fafe51c9212ce27c44d54e2f35d76a203a0f01a9d4f9d9ea3fee81cee43fdc0ba24799658a8a224f25276ea6912e4f6d10 + checksum: 8a9b6563eef30333d310e996a0b29333c9871a0468006f68e7b38b274c6957f53a167869d459ce6878d3b8c4a4c63092179aa8fdf41b241dff82d989aa1d7205 languageName: node linkType: hard @@ -6775,12 +6775,12 @@ __metadata: languageName: node linkType: hard -"@storybook/components@npm:^8.3.3": - version: 8.3.3 - resolution: "@storybook/components@npm:8.3.3" +"@storybook/components@npm:^8.3.6": + version: 8.3.6 + resolution: "@storybook/components@npm:8.3.6" peerDependencies: - storybook: ^8.3.3 - checksum: 716a6f92689b0b173ae59c3bb4fc243ee07523f9db7723d90bb1f7f7c3d4895811125c15cb24b67876b4e5effa8963aa176b3a949cd7643cef39860473e2fa31 + storybook: ^8.3.6 + checksum: 778a114ddba6bd71ce18a8659c4c8b9b4cbcfa5665a68e3bae9467c492a218c2d95ded0825cc920984a6017f12ce811f48517812eb0de2183623cd06be677d7a languageName: node linkType: hard @@ -6929,14 +6929,14 @@ __metadata: languageName: node linkType: hard -"@storybook/csf-plugin@npm:8.3.3": - version: 8.3.3 - resolution: "@storybook/csf-plugin@npm:8.3.3" +"@storybook/csf-plugin@npm:8.3.6": + version: 8.3.6 + resolution: "@storybook/csf-plugin@npm:8.3.6" dependencies: unplugin: "npm:^1.3.1" peerDependencies: - storybook: ^8.3.3 - checksum: 1e87e0b1576eb1fde976bd660a60f21a3092439ddd2685434684ab9de72ed9588f8ac347d03b9b97e5a2d3aa2bf1f22e4595751b1e28965c244ca3593bbcf732 + storybook: ^8.3.6 + checksum: 08cd3f8563808889877484f112bca7bca496f3ea195e18660941f11644422282062135a40e0f5f732fee9b5d7a214baddf2a9e58eb10d5ff54bd30b61ee0923d languageName: node linkType: hard @@ -7105,21 +7105,12 @@ __metadata: languageName: node linkType: hard -"@storybook/manager-api@npm:^8.3.3": - version: 8.3.3 - resolution: "@storybook/manager-api@npm:8.3.3" - peerDependencies: - storybook: ^8.3.3 - checksum: ced92a96ec1fdd72a596ccfb9724b2a6520888f3b9819e5855c5374c70a42ebc7d48259ca2d2e78eec45728fb371395a72be2440ca4422b5ab92176054bc5b97 - languageName: node - linkType: hard - -"@storybook/manager-api@npm:^8.3.4": - version: 8.3.4 - resolution: "@storybook/manager-api@npm:8.3.4" +"@storybook/manager-api@npm:^8.3.6": + version: 8.3.6 + resolution: "@storybook/manager-api@npm:8.3.6" peerDependencies: - storybook: ^8.3.4 - checksum: e2d15619047c72d67c6a1e20cd4cd5d273fc0cfbdd4bf55663e76272eafeb08dee78ffe8524cf18f30630e5e7de7cac43d3b08f950dd775faf0af46a8057cdfa + storybook: ^8.3.6 + checksum: 94faf62726b948c2f8e52e15ac99f4ca2f5884a874fbecda6a81e58a03ff316d17ed36c8c146bd388bb58dad2a2bd09cd61c6d7fc1ff2cddfc774c2aed34e37c languageName: node linkType: hard @@ -7203,12 +7194,12 @@ __metadata: languageName: node linkType: hard -"@storybook/preview-api@npm:^8.3.3": - version: 8.3.3 - resolution: "@storybook/preview-api@npm:8.3.3" +"@storybook/preview-api@npm:^8.3.6": + version: 8.3.6 + resolution: "@storybook/preview-api@npm:8.3.6" peerDependencies: - storybook: ^8.3.3 - checksum: a5fb29845b8ed1ecc8bcd8d3f2c18bdc8f8dab2be6dce42f1b92df256a22885ef5918d6a401642808abe6ceedcc218225f28983d5c95b53819dab9fda34a33c6 + storybook: ^8.3.6 + checksum: 5a645dc19308b69020da2ee282860666c6f7ebe45525802cb4da3d4e496f410a30915e570ae4a07b23d429a5d7f0c7254d9f76e6bc236ff2a85b0cc0e56b0ad0 languageName: node linkType: hard @@ -7229,14 +7220,14 @@ __metadata: languageName: node linkType: hard -"@storybook/react-dom-shim@npm:8.3.3": - version: 8.3.3 - resolution: "@storybook/react-dom-shim@npm:8.3.3" +"@storybook/react-dom-shim@npm:8.3.6": + version: 8.3.6 + resolution: "@storybook/react-dom-shim@npm:8.3.6" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.3.3 - checksum: ca9500737d12b3b0822f1f26be37789bcb6b1ea606d0c94a96f3b9fd19236b53cf8dfba7a5ca939ac4e8a385ae532dca52fc8261ed483b1cabf30d658d715a68 + storybook: ^8.3.6 + checksum: 229001fd8138821d71c7c53ececc152858e92a49cdc50ddb401fecbbe5e4ca3b92082a15b15e1d074ebbb22f294aa5310f54356c7a372479439e985af62cd262 languageName: node linkType: hard @@ -7347,17 +7338,17 @@ __metadata: languageName: node linkType: hard -"@storybook/source-loader@npm:8.3.3": - version: 8.3.3 - resolution: "@storybook/source-loader@npm:8.3.3" +"@storybook/source-loader@npm:8.3.6": + version: 8.3.6 + resolution: "@storybook/source-loader@npm:8.3.6" dependencies: "@storybook/csf": "npm:^0.1.11" estraverse: "npm:^5.2.0" lodash: "npm:^4.17.21" prettier: "npm:^3.1.1" peerDependencies: - storybook: ^8.3.3 - checksum: 8fc42e05724489368a9e384c59af8964f11ba146a624efdbaa7d4c40f51483fd00f5dc7577d5f76ffd47796c5a916ce1722354f1d371155938162ad66395fd3d + storybook: ^8.3.6 + checksum: 810728e1c38ccf3deaee8b5fe195dbf7a8b16dca4c64d448b44f3134526f266bfaba3faf042c889c5c6125ff1fea11ac21defd28222181f68874ae1a9e5609cf languageName: node linkType: hard @@ -7427,21 +7418,12 @@ __metadata: languageName: node linkType: hard -"@storybook/theming@npm:^8.3.3": - version: 8.3.3 - resolution: "@storybook/theming@npm:8.3.3" - peerDependencies: - storybook: ^8.3.3 - checksum: e0560e617158e57cb3efb61a1405d9a5351b353e1a685f5647a9bf8b491e481297316c6748ccff3eaae65324608802bb394ca7d6a809d33e9671c49d80a331ab - languageName: node - linkType: hard - -"@storybook/theming@npm:^8.3.4": - version: 8.3.4 - resolution: "@storybook/theming@npm:8.3.4" +"@storybook/theming@npm:^8.3.6": + version: 8.3.6 + resolution: "@storybook/theming@npm:8.3.6" peerDependencies: - storybook: ^8.3.4 - checksum: eb38c1621a0ec4c25c086f3d9a3f78e10e7fc906e40114a4527848a88c2c7dd77e8478f8d894cfb8df632e62beb4bfa1d9c8fd0b978a74176e4f7fa244b8057f + storybook: ^8.3.6 + checksum: 7b40b35069225d37f71f43e35174717ba6ec8d273eef671a2d37d9745a300c5b3887fa145aee6fdbe4759b2140ddf25e8cbd569074ae1c7d44b0ccc99c1a3451 languageName: node linkType: hard @@ -7480,34 +7462,34 @@ __metadata: languageName: node linkType: hard -"@storybook/web-components-vite@npm:^8.2.8": - version: 8.3.3 - resolution: "@storybook/web-components-vite@npm:8.3.3" +"@storybook/web-components-vite@npm:^8.3.6": + version: 8.3.6 + resolution: "@storybook/web-components-vite@npm:8.3.6" dependencies: - "@storybook/builder-vite": "npm:8.3.3" - "@storybook/web-components": "npm:8.3.3" + "@storybook/builder-vite": "npm:8.3.6" + "@storybook/web-components": "npm:8.3.6" magic-string: "npm:^0.30.0" peerDependencies: - storybook: ^8.3.3 - checksum: 4c59c279bf5f24a63e14bf99acf00fb91dee4d1bb32e465e62eba5feea3b277fa880aae2d1502442e9d26fbc440db5c32c005322cf5ea0d7cec8c61a445c5059 + storybook: ^8.3.6 + checksum: e0b8969aeb5d4744570498ca54a2bc49822fa60aa90fe3be3d01dbe0d491c2a48a77578d58df593b869f3a459193647facc59c340585739ee92505b2397f54cc languageName: node linkType: hard -"@storybook/web-components@npm:8.3.3, @storybook/web-components@npm:^8.2.8": - version: 8.3.3 - resolution: "@storybook/web-components@npm:8.3.3" +"@storybook/web-components@npm:8.3.6, @storybook/web-components@npm:^8.3.6": + version: 8.3.6 + resolution: "@storybook/web-components@npm:8.3.6" dependencies: - "@storybook/components": "npm:^8.3.3" + "@storybook/components": "npm:^8.3.6" "@storybook/global": "npm:^5.0.0" - "@storybook/manager-api": "npm:^8.3.3" - "@storybook/preview-api": "npm:^8.3.3" - "@storybook/theming": "npm:^8.3.3" + "@storybook/manager-api": "npm:^8.3.6" + "@storybook/preview-api": "npm:^8.3.6" + "@storybook/theming": "npm:^8.3.6" tiny-invariant: "npm:^1.3.1" ts-dedent: "npm:^2.0.0" peerDependencies: lit: ^2.0.0 || ^3.0.0 - storybook: ^8.3.3 - checksum: 9b69b2fbd9e1d604244413d91a9ee8ffb52139a353d5d7f0ff96c605635e41e65e2fd39852c63c7b30cc8eaaa106604038c850bc98a580c43542ae7f44028858 + storybook: ^8.3.6 + checksum: fb652fcb25bf4be6c9eaf2e198c1b02e288061787c6ea9ea7fbb595433e824897bfe623847345a51a4aff4ed6ff91c5d9ec394eb7813091179a270632b39befc languageName: node linkType: hard