diff --git a/2nd-gen/packages/swc/.storybook/helpers/index.ts b/2nd-gen/packages/swc/.storybook/helpers/index.ts index 3f44ab84a86..110515cf442 100644 --- a/2nd-gen/packages/swc/.storybook/helpers/index.ts +++ b/2nd-gen/packages/swc/.storybook/helpers/index.ts @@ -19,6 +19,7 @@ export type { ForcedPseudoState } from './pseudo-state.js'; export { coveredCustomProperties, customPropertyRows, + forceManualPopover, forcedColorsVrtParameters, forcePseudoStates, FORCED_STATES, diff --git a/2nd-gen/packages/swc/.storybook/helpers/vrt.ts b/2nd-gen/packages/swc/.storybook/helpers/vrt.ts index a9952412065..df520786264 100644 --- a/2nd-gen/packages/swc/.storybook/helpers/vrt.ts +++ b/2nd-gen/packages/swc/.storybook/helpers/vrt.ts @@ -60,6 +60,52 @@ export const forcePseudoStates = }); }; +/** + * Forces a group of native `popover="auto"` elements (Tooltip, default-mode + * Popover) to render simultaneously open in a single VRT snapshot. + * + * `popover="auto"` elements share one top-layer dismissal group: opening one + * that is not a DOM/anchor descendant of another open auto popover + * light-dismisses that other popover, and these components' own `toggle` + * listeners sync `open` back to `false` to match. A VRT story that renders + * several open instances side by side (every Tooltip placement, every + * Popover variant) would therefore only ever show the *last* instance + * connected as actually open — every earlier instance silently closes itself + * during initial render, well before Chromatic snapshots it. + * + * `popover="manual"` has no such cross-instance dismissal. This play function + * switches each instance to manual mode after render, then re-toggles its + * `open` property (through the component's own public API, not the native + * Popover API directly) so the component's real open/close lifecycle + * — placement, ARIA wiring, `showPopover()` — runs again under the new, + * non-dismissing mode. This is a VRT-only workaround: production usage + * should keep the default `auto` mode and its native light-dismiss behavior. + * + * @param selector - selects the host elements whose `open` property should + * be re-toggled. + * @param resolvePopoverElement - resolves the element that actually carries + * the `popover` attribute for a given host. Defaults to the host itself + * (e.g. Tooltip, which sets `popover` on its own host). Pass an override for + * components whose popover lives on an internal shadow element instead (e.g. + * Popover's default-mode `.swc-Popover` shadow child). + */ +export const forceManualPopover = + ( + selector: string, + resolvePopoverElement: (host: Element) => Element | null = (host) => host + ) => + async ({ canvasElement }: { canvasElement: HTMLElement }) => { + canvasElement + .querySelectorAll(selector) + .forEach((host) => { + resolvePopoverElement(host)?.setAttribute('popover', 'manual'); + if (typeof host.open === 'boolean') { + host.open = false; + host.open = true; + } + }); + }; + export const vrtParameters = { styles: { display: 'flex', diff --git a/2nd-gen/packages/swc/components/accordion/test/vrt/accordion-custom-properties.vrt.ts b/2nd-gen/packages/swc/components/accordion/test/vrt/accordion-custom-properties.vrt.ts new file mode 100644 index 00000000000..10aa8ac2e81 --- /dev/null +++ b/2nd-gen/packages/swc/components/accordion/test/vrt/accordion-custom-properties.vrt.ts @@ -0,0 +1,141 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '@adobe/spectrum-wc/components/accordion/swc-accordion.js'; +import '@adobe/spectrum-wc/components/accordion/swc-accordion-item.js'; + +import type { CustomPropertyCase } from '../../../../.storybook/helpers/index.js'; +import { + coveredCustomProperties, + customPropertyRows, + forcePseudoStates, + theme, + verifyCustomPropertyCoverage, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; +import customElementsManifest from '../../../../dist/custom-elements.json'; + +const meta: Meta = { + title: 'Accordion/Accordion VRT', + component: 'swc-accordion', + tags: ['dev'], +}; + +export default meta; + +type AccordionItemCustomPropertyCase = + CustomPropertyCase<`--swc-accordion-item-${string}`> & { + state?: 'focus-visible'; + }; + +const ACCORDION_PROPERTY_CASES: readonly CustomPropertyCase<`--swc-accordion-${string}`>[] = + [{ property: '--swc-accordion-min-inline-size', value: '520px' }]; + +const ITEM_PROPERTY_CASES: readonly AccordionItemCustomPropertyCase[] = [ + { + property: '--swc-accordion-item-focus-indicator-corner-radius', + value: '0px', + state: 'focus-visible', + }, + { property: '--swc-accordion-item-header-corner-radius', value: '0px' }, + { property: '--swc-accordion-item-padding-top', value: '24px' }, + { property: '--swc-accordion-item-padding-bottom', value: '24px' }, + { property: '--swc-accordion-item-disclosure-indicator-gap', value: '32px' }, + { property: '--swc-accordion-item-edge-to-content-area', value: '32px' }, + { property: '--swc-accordion-item-header-font-size', value: '24px' }, + { property: '--swc-accordion-item-content-padding-inline', value: '48px' }, + { property: '--swc-accordion-item-divider-color', value: 'magenta' }, +]; + +const forceAccordionItemStates = forcePseudoStates( + 'swc-accordion-item', + 'button' +); + +const accordionExample = (style?: string) => html` + + + Personal information +

Manage contact details.

+
+ + Billing address +

Used for payment verification.

+
+
+`; + +const accordionItemExample = ( + { state }: AccordionItemCustomPropertyCase, + style?: string +) => html` +
+ + + Personal information +

Manage contact details.

+
+
+
+`; + +const coveredAccordionProperties = coveredCustomProperties( + ACCORDION_PROPERTY_CASES +); +const coveredItemProperties = coveredCustomProperties(ITEM_PROPERTY_CASES); + +const verifyAccordionCoverage = async () => + verifyCustomPropertyCoverage({ + customElementsManifest, + modulePath: 'components/accordion/Accordion.ts', + declarationName: 'Accordion', + coveredProperties: coveredAccordionProperties, + }); + +const forceStatesAndVerifyItemCoverage = async ( + context: Parameters>[0] +) => { + await forceAccordionItemStates(context); + await verifyCustomPropertyCoverage({ + customElementsManifest, + modulePath: 'components/accordion/AccordionItem.ts', + declarationName: 'AccordionItem', + coveredProperties: coveredItemProperties, + }); +}; + +export const AccordionCustomProperties: Story = { + render: () => + theme( + customPropertyRows(ACCORDION_PROPERTY_CASES, (_, style) => + accordionExample(style) + ), + 'light', + 'ltr' + ), + parameters: vrtParameters, + play: verifyAccordionCoverage, +}; + +export const AccordionItemCustomProperties: Story = { + render: () => + theme( + customPropertyRows(ITEM_PROPERTY_CASES, accordionItemExample), + 'light', + 'ltr' + ), + parameters: vrtParameters, + play: forceStatesAndVerifyItemCoverage, +}; diff --git a/2nd-gen/packages/swc/components/accordion/test/vrt/accordion.vrt.ts b/2nd-gen/packages/swc/components/accordion/test/vrt/accordion.vrt.ts new file mode 100644 index 00000000000..0997155cd61 --- /dev/null +++ b/2nd-gen/packages/swc/components/accordion/test/vrt/accordion.vrt.ts @@ -0,0 +1,119 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html, nothing } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { + ACCORDION_DENSITIES, + ACCORDION_VALID_SIZES, +} from '@adobe/spectrum-wc-core/components/accordion'; + +import '@adobe/spectrum-wc/components/accordion/swc-accordion.js'; +import '@adobe/spectrum-wc/components/accordion/swc-accordion-item.js'; +import '@adobe/spectrum-wc/components/button/swc-button.js'; + +import { + forcedColorsVrtParameters, + forcePseudoStates, + row, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Accordion/Accordion VRT', + component: 'swc-accordion', + tags: ['dev'], +}; + +export default meta; + +const forceAccordionItemStates = forcePseudoStates( + 'swc-accordion-item', + 'button' +); + +const accordion = ({ + size, + density = 'regular', + quiet = false, + disabled = false, + cjk = false, + state, +}: { + size?: (typeof ACCORDION_VALID_SIZES)[number]; + density?: (typeof ACCORDION_DENSITIES)[number]; + quiet?: boolean; + disabled?: boolean; + cjk?: boolean; + state?: string; +} = {}) => html` + + + ${cjk ? '個人情報' : 'Personal information'} +

${cjk ? '名前と連絡先を管理します。' : 'Manage contact details.'}

+
+ + ${cjk ? '請求先住所' : 'Billing address'} + Edit +

+ ${cjk + ? '支払い方法の確認に使用します。' + : 'Used for payment verification.'} +

+
+ + ${cjk ? '支払い方法' : 'Payment method'} +

+ ${cjk ? '管理者に連絡してください。' : 'Contact your administrator.'} +

+
+
+`; + +const accordionContent = () => html` + ${row( + ACCORDION_VALID_SIZES.map((size) => accordion({ size })), + 'Sizes' + )} + ${row( + ACCORDION_DENSITIES.map((density) => accordion({ density })), + 'Densities' + )} + ${row([accordion({ quiet: true })], 'Quiet')} + ${row([accordion({ disabled: true })], 'Disabled')} + ${row([accordion({ state: 'hover' })], 'Hover')} + ${row([accordion({ state: 'focus-visible' })], 'Focus visible')} + ${row([accordion({ cjk: true })], 'CJK language')} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(accordionContent(), 'light', 'ltr')} + ${theme(accordionContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, + play: forceAccordionItemStates, +}; + +export const ForcedColors: Story = { + render: () => theme(accordionContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, + play: forceAccordionItemStates, +}; diff --git a/2nd-gen/packages/swc/components/action-button/test/vrt/action-button-custom-properties.vrt.ts b/2nd-gen/packages/swc/components/action-button/test/vrt/action-button-custom-properties.vrt.ts new file mode 100644 index 00000000000..56a8f0aa38f --- /dev/null +++ b/2nd-gen/packages/swc/components/action-button/test/vrt/action-button-custom-properties.vrt.ts @@ -0,0 +1,180 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html, nothing } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '@adobe/spectrum-wc/components/action-button/swc-action-button.js'; +import '@adobe/spectrum-wc/components/icon/swc-icon.js'; + +import type { CustomPropertyCase } from '../../../../.storybook/helpers/index.js'; +import { + coveredCustomProperties, + customPropertyRows, + forcePseudoStates, + theme, + verifyCustomPropertyCoverage, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; +import customElementsManifest from '../../../../dist/custom-elements.json'; +import { Arrow100Icon } from '../../../icon/elements/Arrow100Icon.js'; + +const meta: Meta = { + title: 'Action Button/Action Button VRT', + component: 'swc-action-button', + tags: ['dev'], +}; + +export default meta; + +type ActionButtonCustomPropertyCase = + CustomPropertyCase<`--swc-action-button-${string}`> & { + state?: 'hover' | 'focus-visible' | 'active'; + disabled?: boolean; + iconOnly?: boolean; + }; + +const CUSTOM_PROPERTY_CASES: readonly ActionButtonCustomPropertyCase[] = [ + { property: '--swc-action-button-min-block-size', value: '56px' }, + { property: '--swc-action-button-border-radius', value: '0px' }, + { property: '--swc-action-button-font-size', value: '24px' }, + { property: '--swc-action-button-gap', value: '24px' }, + { property: '--swc-action-button-edge-to-text', value: '32px' }, + { property: '--swc-action-button-edge-to-visual', value: '32px' }, + { + property: '--swc-action-button-edge-to-visual-only', + value: '32px', + iconOnly: true, + }, + { property: '--swc-action-button-icon-size', value: '24px' }, + { property: '--swc-action-button-icon-inline-size', value: '32px' }, + { property: '--swc-action-button-icon-block-size', value: '16px' }, + { + property: '--swc-action-button-focus-indicator-color', + value: 'magenta', + state: 'focus-visible', + }, + { + property: '--swc-action-button-background-color-default', + value: 'magenta', + }, + { property: '--swc-action-button-border-color-default', value: 'magenta' }, + { property: '--swc-action-button-content-color-default', value: 'magenta' }, + { + property: '--swc-action-button-background-color-hover', + value: 'magenta', + state: 'hover', + }, + { + property: '--swc-action-button-border-color-hover', + value: 'magenta', + state: 'hover', + }, + { + property: '--swc-action-button-content-color-hover', + value: 'magenta', + state: 'hover', + }, + { + property: '--swc-action-button-background-color-focus', + value: 'magenta', + state: 'focus-visible', + }, + { + property: '--swc-action-button-border-color-focus', + value: 'magenta', + state: 'focus-visible', + }, + { + property: '--swc-action-button-content-color-focus', + value: 'magenta', + state: 'focus-visible', + }, + { + property: '--swc-action-button-background-color-down', + value: 'magenta', + state: 'active', + }, + { + property: '--swc-action-button-border-color-down', + value: 'magenta', + state: 'active', + }, + { + property: '--swc-action-button-content-color-down', + value: 'magenta', + state: 'active', + }, + { + property: '--swc-action-button-background-color-disabled', + value: 'magenta', + disabled: true, + }, + { + property: '--swc-action-button-border-color-disabled', + value: 'magenta', + disabled: true, + }, + { + property: '--swc-action-button-content-color-disabled', + value: 'magenta', + disabled: true, + }, +]; + +const forceActionButtonStates = forcePseudoStates( + 'swc-action-button', + 'button' +); + +const icon = () => html` + +`; + +const renderActionButtonCustomProperty = ( + { state, disabled, iconOnly }: ActionButtonCustomPropertyCase, + style?: string +) => html` + + ${icon()}${iconOnly ? nothing : 'Edit'} + +`; + +const coveredActionButtonCustomProperties = coveredCustomProperties( + CUSTOM_PROPERTY_CASES +); + +const forceStatesAndVerifyCoverage = async ( + context: Parameters>[0] +) => { + await forceActionButtonStates(context); + await verifyCustomPropertyCoverage({ + customElementsManifest, + modulePath: 'components/action-button/ActionButton.ts', + declarationName: 'ActionButton', + coveredProperties: coveredActionButtonCustomProperties, + }); +}; + +const customPropertiesContent = () => + customPropertyRows(CUSTOM_PROPERTY_CASES, renderActionButtonCustomProperty); + +export const CustomProperties: Story = { + render: () => theme(customPropertiesContent(), 'light', 'ltr'), + parameters: vrtParameters, + play: forceStatesAndVerifyCoverage, +}; diff --git a/2nd-gen/packages/swc/components/action-button/test/vrt/action-button-global-styles.vrt.ts b/2nd-gen/packages/swc/components/action-button/test/vrt/action-button-global-styles.vrt.ts new file mode 100644 index 00000000000..9316d9e2753 --- /dev/null +++ b/2nd-gen/packages/swc/components/action-button/test/vrt/action-button-global-styles.vrt.ts @@ -0,0 +1,180 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { + FORCED_STATES, + forcePseudoStates, + row, + staticColorBackground, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Action Button/Action Button VRT', + tags: ['dev'], +}; + +export default meta; + +const globalIconSvg = html` + +`; + +const asLinkAndButton = (classes: string, label: string) => [ + html` + + ${label} (link) + + `, + html` + + `, +]; + +const SIZE_CASES = [ + { classes: 'swc-ActionButton--sizeXs', label: 'Extra-small' }, + { classes: 'swc-ActionButton--sizeS', label: 'Small' }, + { classes: '', label: 'Medium' }, + { classes: 'swc-ActionButton--sizeL', label: 'Large' }, + { classes: 'swc-ActionButton--sizeXl', label: 'Extra-large' }, +]; + +const forceGlobalActionButtonStates = forcePseudoStates('.swc-ActionButton'); + +const globalStylesContent = () => html` + ${row( + SIZE_CASES.flatMap(({ classes, label }) => asLinkAndButton(classes, label)), + 'Sizes' + )} + ${row( + [ + ...asLinkAndButton('swc-ActionButton--quiet', 'Quiet'), + html` + + `, + html` + + `, + ], + 'States' + )} + ${row( + [ + html` + + `, + html` + + `, + html` + + `, + ], + 'Anatomy and CJK' + )} + ${staticColorBackground( + row( + [ + ...asLinkAndButton('swc-ActionButton--staticWhite', 'White'), + ...asLinkAndButton( + 'swc-ActionButton--staticWhite swc-ActionButton--quiet', + 'White quiet' + ), + ], + 'Static white' + ), + 'white' + )} + ${staticColorBackground( + row( + [ + ...asLinkAndButton('swc-ActionButton--staticBlack', 'Black'), + ...asLinkAndButton( + 'swc-ActionButton--staticBlack swc-ActionButton--quiet', + 'Black quiet' + ), + ], + 'Static black' + ), + 'black' + )} + ${row( + FORCED_STATES.flatMap((state) => [ + html` + + ${state} (link) + + `, + html` + + `, + ]), + 'Forced states' + )} +`; + +export const GlobalStyles: Story = { + render: () => html` + ${theme(globalStylesContent(), 'light', 'ltr')} + ${theme(globalStylesContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, + play: forceGlobalActionButtonStates, +}; diff --git a/2nd-gen/packages/swc/components/action-button/test/vrt/action-button.vrt.ts b/2nd-gen/packages/swc/components/action-button/test/vrt/action-button.vrt.ts new file mode 100644 index 00000000000..b554c9e56e5 --- /dev/null +++ b/2nd-gen/packages/swc/components/action-button/test/vrt/action-button.vrt.ts @@ -0,0 +1,148 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html, nothing } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { + ACTION_BUTTON_STATIC_COLORS, + ACTION_BUTTON_VALID_SIZES, +} from '@adobe/spectrum-wc-core/components/action-button'; + +import '@adobe/spectrum-wc/components/action-button/swc-action-button.js'; +import '@adobe/spectrum-wc/components/icon/swc-icon.js'; + +import { + forcedColorsVrtParameters, + forcePseudoStates, + row, + staticColorBackground, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; +import { Arrow100Icon } from '../../../icon/elements/Arrow100Icon.js'; + +const meta: Meta = { + title: 'Action Button/Action Button VRT', + component: 'swc-action-button', + tags: ['dev'], +}; + +export default meta; + +const forceActionButtonStates = forcePseudoStates( + 'swc-action-button', + 'button' +); + +const icon = () => html` + +`; + +const actionButton = ({ + label = 'Edit', + size = 'm', + quiet = false, + disabled = false, + pending = false, + iconOnly = false, + staticColor, + state, +}: { + label?: string; + size?: (typeof ACTION_BUTTON_VALID_SIZES)[number]; + quiet?: boolean; + disabled?: boolean; + pending?: boolean; + iconOnly?: boolean; + staticColor?: (typeof ACTION_BUTTON_STATIC_COLORS)[number]; + state?: string; +} = {}) => html` + + ${icon()}${iconOnly ? nothing : label} + +`; + +const actionButtonContent = () => html` + ${row( + ACTION_BUTTON_VALID_SIZES.map((size) => + actionButton({ size, label: `Size ${size}` }) + ), + 'Sizes' + )} + ${row( + [ + actionButton(), + actionButton({ quiet: true, label: 'Quiet' }), + actionButton({ iconOnly: true }), + ], + 'Anatomy' + )} + ${row( + [ + actionButton({ state: 'hover', label: 'Hover' }), + actionButton({ state: 'focus-visible', label: 'Focus' }), + actionButton({ state: 'active', label: 'Active' }), + actionButton({ disabled: true, label: 'Disabled' }), + actionButton({ pending: true, label: 'Pending' }), + ], + 'States' + )} + ${row( + [ + actionButton({ label: '承認ワークフロー', size: 'm' }), + html` + + ${icon()}승인 워크플로 시작 + + `, + actionButton({ label: '启动审批工作流', quiet: true }), + ], + 'CJK language' + )} + ${ACTION_BUTTON_STATIC_COLORS.map((color) => + staticColorBackground( + row( + [ + actionButton({ staticColor: color, label: 'Default' }), + actionButton({ staticColor: color, quiet: true, label: 'Quiet' }), + actionButton({ staticColor: color, iconOnly: true }), + ], + `Static ${color}` + ), + color + ) + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(actionButtonContent(), 'light', 'ltr')} + ${theme(actionButtonContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, + play: forceActionButtonStates, +}; + +export const ForcedColors: Story = { + render: () => theme(actionButtonContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, + play: forceActionButtonStates, +}; diff --git a/2nd-gen/packages/swc/components/asset/test/vrt/asset.vrt.ts b/2nd-gen/packages/swc/components/asset/test/vrt/asset.vrt.ts new file mode 100644 index 00000000000..3a019dfba9f --- /dev/null +++ b/2nd-gen/packages/swc/components/asset/test/vrt/asset.vrt.ts @@ -0,0 +1,80 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { ASSET_VARIANTS } from '@adobe/spectrum-wc-core/components/asset'; + +import '@adobe/spectrum-wc/components/asset/swc-asset.js'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Asset/Asset VRT', + component: 'swc-asset', + tags: ['dev'], +}; + +export default meta; + +const imageAsset = html` + + Profile photo of Maria Rodriguez + +`; + +const assetContent = () => html` + ${row( + [ + ...ASSET_VARIANTS.map( + (variant) => html` + + ` + ), + imageAsset, + ], + 'Variants' + )} + ${row( + [ + html` +
${imageAsset}
+ `, + html` +
${imageAsset}
+ `, + ], + 'Image fit' + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(assetContent(), 'light', 'ltr')} + ${theme(assetContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(assetContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/components/avatar/test/vrt/avatar-custom-properties.vrt.ts b/2nd-gen/packages/swc/components/avatar/test/vrt/avatar-custom-properties.vrt.ts new file mode 100644 index 00000000000..9a589dda483 --- /dev/null +++ b/2nd-gen/packages/swc/components/avatar/test/vrt/avatar-custom-properties.vrt.ts @@ -0,0 +1,83 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '@adobe/spectrum-wc/components/avatar/swc-avatar.js'; + +import type { CustomPropertyCase } from '../../../../.storybook/helpers/index.js'; +import { + coveredCustomProperties, + customPropertyRows, + theme, + verifyCustomPropertyCoverage, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; +import customElementsManifest from '../../../../dist/custom-elements.json'; + +const meta: Meta = { + title: 'Avatar/Avatar VRT', + component: 'swc-avatar', + tags: ['dev'], +}; + +export default meta; + +type AvatarCustomPropertyCase = CustomPropertyCase<`--swc-avatar-${string}`> & { + outline?: boolean; + disabled?: boolean; +}; + +const PLACEHOLDER_SRC = 'https://picsum.photos/id/64/500/500'; + +const CUSTOM_PROPERTY_CASES: readonly AvatarCustomPropertyCase[] = [ + { property: '--swc-avatar-size', value: '96px' }, + { property: '--swc-avatar-outline-color', value: 'magenta', outline: true }, + { property: '--swc-avatar-outline-width', value: '8px', outline: true }, + { property: '--swc-avatar-opacity-disabled', value: '0.2', disabled: true }, +]; + +const renderAvatarCustomProperty = ( + { outline, disabled }: AvatarCustomPropertyCase, + style?: string +) => html` + +`; + +const coveredAvatarCustomProperties = coveredCustomProperties( + CUSTOM_PROPERTY_CASES +); + +const verifyCoverage = async () => + verifyCustomPropertyCoverage({ + customElementsManifest, + modulePath: 'components/avatar/Avatar.ts', + declarationName: 'Avatar', + coveredProperties: coveredAvatarCustomProperties, + }); + +const customPropertiesContent = () => + customPropertyRows(CUSTOM_PROPERTY_CASES, renderAvatarCustomProperty); + +export const CustomProperties: Story = { + render: () => theme(customPropertiesContent(), 'light', 'ltr'), + parameters: vrtParameters, + play: verifyCoverage, +}; diff --git a/2nd-gen/packages/swc/components/avatar/test/vrt/avatar.vrt.ts b/2nd-gen/packages/swc/components/avatar/test/vrt/avatar.vrt.ts new file mode 100644 index 00000000000..2a480514a9f --- /dev/null +++ b/2nd-gen/packages/swc/components/avatar/test/vrt/avatar.vrt.ts @@ -0,0 +1,98 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { AVATAR_VALID_SIZES } from '@adobe/spectrum-wc-core/components/avatar'; + +import '@adobe/spectrum-wc/components/avatar/swc-avatar.js'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Avatar/Avatar VRT', + component: 'swc-avatar', + tags: ['dev'], +}; + +export default meta; + +const PLACEHOLDER_SRC = 'https://picsum.photos/id/64/500/500'; + +const avatar = ({ + size, + outline = false, + disabled = false, + decorative = false, +}: { + size: (typeof AVATAR_VALID_SIZES)[number]; + outline?: boolean; + disabled?: boolean; + decorative?: boolean; +}) => html` + +`; + +const avatarContent = () => html` + ${row( + AVATAR_VALID_SIZES.map((size) => avatar({ size })), + 'Sizes' + )} + ${row( + [ + html` +
+ ${avatar({ size: 500, outline: true })} + ${avatar({ size: 1000, outline: true })} +
+ `, + ], + 'Outline' + )} + ${row([avatar({ size: 500, disabled: true })], 'Disabled')} + ${row( + [ + html` + ${avatar({ size: 500, decorative: true })}Jane Doe + `, + ], + 'Decorative' + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(avatarContent(), 'light', 'ltr')} + ${theme(avatarContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(avatarContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/components/badge/test/vrt/badge-custom-properties.vrt.ts b/2nd-gen/packages/swc/components/badge/test/vrt/badge-custom-properties.vrt.ts new file mode 100644 index 00000000000..44efd4619ce --- /dev/null +++ b/2nd-gen/packages/swc/components/badge/test/vrt/badge-custom-properties.vrt.ts @@ -0,0 +1,121 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html, nothing } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '@adobe/spectrum-wc/components/badge/swc-badge.js'; +import '@adobe/spectrum-wc/components/icon/swc-icon.js'; + +import type { CustomPropertyCase } from '../../../../.storybook/helpers/index.js'; +import { + coveredCustomProperties, + customPropertyRows, + theme, + verifyCustomPropertyCoverage, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; +import customElementsManifest from '../../../../dist/custom-elements.json'; +import { Arrow100Icon } from '../../../icon/elements/Arrow100Icon.js'; + +const meta: Meta = { + title: 'Badge/Badge VRT', + component: 'swc-badge', + tags: ['dev'], +}; + +export default meta; + +type BadgeCustomPropertyCase = CustomPropertyCase<`--swc-badge-${string}`> & { + outline?: boolean; + icon?: boolean; + iconOnly?: boolean; + label?: string; +}; + +const CUSTOM_PROPERTY_CASES: readonly BadgeCustomPropertyCase[] = [ + { property: '--swc-badge-height', value: '40px' }, + { property: '--swc-badge-corner-radius', value: '0px' }, + { property: '--swc-badge-gap', value: '24px', icon: true }, + { property: '--swc-badge-padding-block', value: '16px' }, + { property: '--swc-badge-padding-inline', value: '32px' }, + { property: '--swc-badge-padding-inline-start', value: '32px' }, + { property: '--swc-badge-font-size', value: '24px' }, + { property: '--swc-badge-line-height', value: '32px' }, + { property: '--swc-badge-icon-size', value: '24px', icon: true }, + { property: '--swc-badge-label-icon-color', value: 'magenta' }, + { property: '--swc-badge-background-color', value: 'magenta' }, + { property: '--swc-badge-border-color', value: 'magenta', outline: true }, + { + property: '--swc-badge-with-icon-padding-inline', + value: '32px', + icon: true, + }, + { + property: '--swc-badge-with-icon-only-padding-inline', + value: '32px', + iconOnly: true, + }, + { + property: '--swc-badge-with-icon-only-padding-block', + value: '16px', + iconOnly: true, + }, + { + property: '--swc-badge-outline-background-color', + value: 'magenta', + outline: true, + }, + { + property: '--swc-badge-outline-label-icon-color', + value: 'magenta', + outline: true, + }, +]; + +const arrowIcon = () => html` + +`; + +const renderBadgeCustomProperty = ( + { outline, icon, iconOnly, label = 'Label' }: BadgeCustomPropertyCase, + style?: string +) => html` + + ${icon || iconOnly ? arrowIcon() : nothing}${iconOnly ? nothing : label} + +`; + +const coveredBadgeCustomProperties = coveredCustomProperties( + CUSTOM_PROPERTY_CASES +); + +const verifyCoverage = async () => + verifyCustomPropertyCoverage({ + customElementsManifest, + modulePath: 'components/badge/Badge.ts', + declarationName: 'Badge', + coveredProperties: coveredBadgeCustomProperties, + }); + +const customPropertiesContent = () => + customPropertyRows(CUSTOM_PROPERTY_CASES, renderBadgeCustomProperty); + +export const CustomProperties: Story = { + render: () => theme(customPropertiesContent(), 'light', 'ltr'), + parameters: vrtParameters, + play: verifyCoverage, +}; diff --git a/2nd-gen/packages/swc/components/badge/test/vrt/badge.vrt.ts b/2nd-gen/packages/swc/components/badge/test/vrt/badge.vrt.ts new file mode 100644 index 00000000000..6a15a12d62d --- /dev/null +++ b/2nd-gen/packages/swc/components/badge/test/vrt/badge.vrt.ts @@ -0,0 +1,182 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { + BADGE_VALID_SIZES, + BADGE_VARIANTS_COLOR, + BADGE_VARIANTS_SEMANTIC, + FIXED_VALUES, +} from '@adobe/spectrum-wc-core/components/badge'; + +import '@adobe/spectrum-wc/components/badge/swc-badge.js'; +import '@adobe/spectrum-wc/components/icon/swc-icon.js'; + +import { + createPermutations, + forcedColorsVrtParameters, + renderStorybookPermutation, + row, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; +import { Arrow100Icon } from '../../../icon/elements/Arrow100Icon.js'; + +const meta: Meta = { + title: 'Badge/Badge VRT', + component: 'swc-badge', + tags: ['dev'], +}; + +export default meta; + +const semanticLabels = { + accent: 'New', + informative: 'Active', + neutral: 'Archived', + positive: 'Approved', + notice: 'Pending approval', + negative: 'Rejected', +} as const; + +const colorLabels = { + fuchsia: 'Marketing', + indigo: 'Engineering', + magenta: 'Design', + purple: 'Product', + seafoam: 'Support', + yellow: 'Busy', + gray: 'Available', + red: 'Sales', + orange: 'Research', + chartreuse: 'Quality', + celery: 'Documentation', + green: 'Legal', + cyan: 'Analytics', + blue: 'Security', + pink: 'Creative', + turquoise: 'Training', + brown: 'Facilities', + cinnamon: 'Compliance', + silver: 'Version 1.2.10', +} as const; + +const renderBadgePermutation = renderStorybookPermutation('swc-badge'); + +const SIZE_PERMUTATIONS = createPermutations([{ size: BADGE_VALID_SIZES }]); +const SEMANTIC_VARIANT_PERMUTATIONS = createPermutations([ + { variant: BADGE_VARIANTS_SEMANTIC }, +]); +const COLOR_VARIANT_PERMUTATIONS = createPermutations([ + { variant: BADGE_VARIANTS_COLOR }, +]); +const STYLE_PERMUTATIONS = createPermutations([ + { variant: BADGE_VARIANTS_SEMANTIC, subtle: [true] }, + { variant: BADGE_VARIANTS_SEMANTIC, outline: [true] }, +]); + +const arrowIcon = () => html` + +`; + +const badgeContent = () => html` + ${row( + SIZE_PERMUTATIONS.map(({ size }) => + renderBadgePermutation({ size, 'default-slot': `Size ${size}` }) + ), + 'Sizes' + )} + ${row( + SEMANTIC_VARIANT_PERMUTATIONS.map(({ variant }) => + renderBadgePermutation({ + variant, + 'default-slot': semanticLabels[variant], + }) + ), + 'Semantic variants' + )} + ${row( + COLOR_VARIANT_PERMUTATIONS.map(({ variant }) => + renderBadgePermutation({ + variant, + 'default-slot': colorLabels[variant], + }) + ), + 'Color variants' + )} + ${row( + STYLE_PERMUTATIONS.map((permutation) => + renderBadgePermutation({ + ...permutation, + 'default-slot': permutation.outline ? 'Outline' : 'Subtle', + }) + ), + 'Subtle and outline' + )} + ${row( + BADGE_VALID_SIZES.map( + (size) => html` + ${arrowIcon()}Icon ${size} + ` + ), + 'Icon and label' + )} + ${row( + BADGE_VALID_SIZES.map( + (size) => html` + + ${arrowIcon()} + + ` + ), + 'Icon only' + )} + ${row( + FIXED_VALUES.map((fixed) => + renderBadgePermutation({ + fixed, + variant: 'accent', + 'default-slot': fixed, + }) + ), + 'Fixed placement' + )} + ${row( + [ + html` + 承認ワークフロー + `, + html` + 승인 워크플로 + `, + html` + ${arrowIcon()}审批工作流 + `, + ], + 'CJK language' + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(badgeContent(), 'light', 'ltr')} + ${theme(badgeContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(badgeContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/components/button-group/test/vrt/button-group-custom-properties.vrt.ts b/2nd-gen/packages/swc/components/button-group/test/vrt/button-group-custom-properties.vrt.ts new file mode 100644 index 00000000000..250149991e1 --- /dev/null +++ b/2nd-gen/packages/swc/components/button-group/test/vrt/button-group-custom-properties.vrt.ts @@ -0,0 +1,73 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '@adobe/spectrum-wc/components/button-group/swc-button-group.js'; +import '@adobe/spectrum-wc/components/button/swc-button.js'; + +import type { CustomPropertyCase } from '../../../../.storybook/helpers/index.js'; +import { + coveredCustomProperties, + customPropertyRows, + theme, + verifyCustomPropertyCoverage, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; +import customElementsManifest from '../../../../dist/custom-elements.json'; + +const meta: Meta = { + title: 'Button Group/Button Group VRT', + component: 'swc-button-group', + tags: ['dev'], +}; + +export default meta; + +const CUSTOM_PROPERTY_CASES: readonly CustomPropertyCase<`--swc-button-group-${string}`>[] = + [ + { property: '--swc-button-group-gap', value: '32px' }, + { property: '--swc-button-group-justify-content', value: 'center' }, + ]; + +const renderButtonGroupCustomProperty = ( + _: CustomPropertyCase, + style?: string +) => html` + + Save + Cancel + Reset + +`; + +const coveredButtonGroupCustomProperties = coveredCustomProperties( + CUSTOM_PROPERTY_CASES +); + +const verifyCoverage = async () => + verifyCustomPropertyCoverage({ + customElementsManifest, + modulePath: 'components/button-group/ButtonGroup.ts', + declarationName: 'ButtonGroup', + coveredProperties: coveredButtonGroupCustomProperties, + }); + +const customPropertiesContent = () => + customPropertyRows(CUSTOM_PROPERTY_CASES, renderButtonGroupCustomProperty); + +export const CustomProperties: Story = { + render: () => theme(customPropertiesContent(), 'light', 'ltr'), + parameters: vrtParameters, + play: verifyCoverage, +}; diff --git a/2nd-gen/packages/swc/components/button-group/test/vrt/button-group.vrt.ts b/2nd-gen/packages/swc/components/button-group/test/vrt/button-group.vrt.ts new file mode 100644 index 00000000000..ec17525cad2 --- /dev/null +++ b/2nd-gen/packages/swc/components/button-group/test/vrt/button-group.vrt.ts @@ -0,0 +1,93 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { + BUTTON_GROUP_ALIGNMENTS, + BUTTON_GROUP_ORIENTATIONS, + BUTTON_GROUP_SIZES, +} from '@adobe/spectrum-wc-core/components/button-group'; + +import '@adobe/spectrum-wc/components/button-group/swc-button-group.js'; +import '@adobe/spectrum-wc/components/button/swc-button.js'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Button Group/Button Group VRT', + component: 'swc-button-group', + tags: ['dev'], +}; + +export default meta; + +const group = ({ + size = 'm', + orientation = 'horizontal', + align = 'start', + disabled = false, + cjk = false, +}: { + size?: (typeof BUTTON_GROUP_SIZES)[number]; + orientation?: (typeof BUTTON_GROUP_ORIENTATIONS)[number]; + align?: (typeof BUTTON_GROUP_ALIGNMENTS)[number]; + disabled?: boolean; + cjk?: boolean; +}) => html` + + ${cjk ? '保存' : 'Save'} + ${cjk ? 'キャンセル' : 'Cancel'} + ${cjk ? 'リセット' : 'Reset'} + +`; + +const buttonGroupContent = () => html` + ${row( + BUTTON_GROUP_SIZES.map((size) => group({ size })), + 'Sizes' + )} + ${row( + BUTTON_GROUP_ORIENTATIONS.map((orientation) => group({ orientation })), + 'Orientations' + )} + ${row( + BUTTON_GROUP_ALIGNMENTS.map((align) => group({ align })), + 'Alignment' + )} + ${row([group({ disabled: true }), group({ cjk: true })], 'States and CJK')} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(buttonGroupContent(), 'light', 'ltr')} + ${theme(buttonGroupContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(buttonGroupContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/components/color-handle/test/vrt/color-handle.vrt.ts b/2nd-gen/packages/swc/components/color-handle/test/vrt/color-handle.vrt.ts new file mode 100644 index 00000000000..4b642472ec3 --- /dev/null +++ b/2nd-gen/packages/swc/components/color-handle/test/vrt/color-handle.vrt.ts @@ -0,0 +1,104 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '@adobe/spectrum-wc/components/color-handle/swc-color-handle.js'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Color Handle/Color Handle VRT', + component: 'swc-color-handle', + tags: ['dev'], +}; + +export default meta; + +const handle = ({ + label, + color, + open = false, + focused = false, + disabled = false, + fill = true, +}: { + label: string; + color: string; + open?: boolean; + focused?: boolean; + disabled?: boolean; + fill?: boolean; +}) => html` +
+
+
+ +
+
+ ${label} +
+`; + +const colorHandleContent = () => html` + ${row( + [ + handle({ label: 'Hex', color: '#1473e6' }), + handle({ label: 'RGBA', color: 'rgba(44, 62, 224, 0.4)' }), + handle({ label: 'HSL', color: 'hsl(111 82% 56%)' }), + handle({ label: 'White', color: 'rgb(255 255 255)' }), + ], + 'Colors' + )} + ${row([handle({ label: 'Open', color: '#1473e6', open: true })], 'Open')} + ${row( + [handle({ label: 'Focused', color: '#1473e6', focused: true })], + 'Focused' + )} + ${row( + [handle({ label: 'Disabled', color: '#1473e6', disabled: true })], + 'Disabled' + )} + ${row( + [handle({ label: 'Outline only', color: '#1473e6', fill: false })], + 'Outline only' + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(colorHandleContent(), 'light', 'ltr')} + ${theme(colorHandleContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(colorHandleContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/components/color-loupe/test/vrt/color-loupe.vrt.ts b/2nd-gen/packages/swc/components/color-loupe/test/vrt/color-loupe.vrt.ts new file mode 100644 index 00000000000..f108f76dbb4 --- /dev/null +++ b/2nd-gen/packages/swc/components/color-loupe/test/vrt/color-loupe.vrt.ts @@ -0,0 +1,69 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '@adobe/spectrum-wc/components/color-loupe/swc-color-loupe.js'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Color Loupe/Color Loupe VRT', + component: 'swc-color-loupe', + tags: ['dev'], +}; + +export default meta; + +const loupe = (label: string, color: string, open = true) => html` +
+
+ +
+ ${label} +
+`; + +const colorLoupeContent = () => html` + ${row( + [ + loupe('Named', 'yellow'), + loupe('Hex', '#ff0000'), + loupe('RGBA', 'rgba(44, 62, 224, 0.81)'), + loupe('HSL', 'hsl(111 82% 56%)'), + ], + 'Colors' + )} + ${row([loupe('Open', '#1473e6')], 'Open')} + ${row([loupe('Closed', '#1473e6', false)], 'Closed')} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(colorLoupeContent(), 'light', 'ltr')} + ${theme(colorLoupeContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(colorLoupeContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/components/divider/test/vrt/divider-custom-properties.vrt.ts b/2nd-gen/packages/swc/components/divider/test/vrt/divider-custom-properties.vrt.ts new file mode 100644 index 00000000000..d1442a0ae5d --- /dev/null +++ b/2nd-gen/packages/swc/components/divider/test/vrt/divider-custom-properties.vrt.ts @@ -0,0 +1,72 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '@adobe/spectrum-wc/components/divider/swc-divider.js'; + +import type { CustomPropertyCase } from '../../../../.storybook/helpers/index.js'; +import { + coveredCustomProperties, + customPropertyRows, + theme, + verifyCustomPropertyCoverage, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; +import customElementsManifest from '../../../../dist/custom-elements.json'; + +const meta: Meta = { + title: 'Divider/Divider VRT', + component: 'swc-divider', + tags: ['dev'], +}; + +export default meta; + +const CUSTOM_PROPERTY_CASES: readonly CustomPropertyCase<`--swc-divider-${string}`>[] = + [ + { property: '--swc-divider-background-color', value: 'magenta' }, + { property: '--swc-divider-thickness', value: '8px' }, + ]; + +const renderDividerCustomProperty = ( + _: CustomPropertyCase, + style?: string +) => html` +
+

Account settings

+ +

Team members

+
+`; + +const coveredDividerCustomProperties = coveredCustomProperties( + CUSTOM_PROPERTY_CASES +); + +const verifyCoverage = async () => + verifyCustomPropertyCoverage({ + customElementsManifest, + modulePath: 'components/divider/Divider.ts', + declarationName: 'Divider', + coveredProperties: coveredDividerCustomProperties, + }); + +const customPropertiesContent = () => + customPropertyRows(CUSTOM_PROPERTY_CASES, renderDividerCustomProperty); + +export const CustomProperties: Story = { + render: () => theme(customPropertiesContent(), 'light', 'ltr'), + parameters: vrtParameters, + play: verifyCoverage, +}; diff --git a/2nd-gen/packages/swc/components/divider/test/vrt/divider.vrt.ts b/2nd-gen/packages/swc/components/divider/test/vrt/divider.vrt.ts new file mode 100644 index 00000000000..dde66f5ab41 --- /dev/null +++ b/2nd-gen/packages/swc/components/divider/test/vrt/divider.vrt.ts @@ -0,0 +1,99 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { + DIVIDER_STATIC_COLORS, + DIVIDER_VALID_SIZES, +} from '@adobe/spectrum-wc-core/components/divider'; + +import '@adobe/spectrum-wc/components/divider/swc-divider.js'; + +import { + forcedColorsVrtParameters, + row, + staticColorBackground, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Divider/Divider VRT', + component: 'swc-divider', + tags: ['dev'], +}; + +export default meta; + +const horizontalDivider = (size: (typeof DIVIDER_VALID_SIZES)[number]) => html` +
+

Account settings

+ +

Team members

+
+`; + +const verticalDivider = (size: (typeof DIVIDER_VALID_SIZES)[number]) => html` +
+ Cut + + Copy + + Paste +
+`; + +const dividerContent = () => html` + ${row(DIVIDER_VALID_SIZES.map(horizontalDivider), 'Horizontal sizes')} + ${row(DIVIDER_VALID_SIZES.map(verticalDivider), 'Vertical sizes')} + ${DIVIDER_STATIC_COLORS.map((color) => + staticColorBackground( + row( + [ + html` +
+

Dashboard settings

+ +

Display options

+
+ `, + html` +
+ Overview + + Files +
+ `, + ], + `Static ${color}` + ), + color + ) + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(dividerContent(), 'light', 'ltr')} + ${theme(dividerContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(dividerContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/components/icon/test/vrt/icon-custom-properties.vrt.ts b/2nd-gen/packages/swc/components/icon/test/vrt/icon-custom-properties.vrt.ts new file mode 100644 index 00000000000..989af13b00b --- /dev/null +++ b/2nd-gen/packages/swc/components/icon/test/vrt/icon-custom-properties.vrt.ts @@ -0,0 +1,72 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '@adobe/spectrum-wc/components/icon/swc-icon.js'; + +import type { CustomPropertyCase } from '../../../../.storybook/helpers/index.js'; +import { + coveredCustomProperties, + customPropertyRows, + theme, + verifyCustomPropertyCoverage, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; +import customElementsManifest from '../../../../dist/custom-elements.json'; +import { Chevron100Icon } from '../../../icon/elements/index.js'; + +const meta: Meta = { + title: 'Icon/Icon VRT', + component: 'swc-icon', + tags: ['dev'], +}; + +export default meta; + +const CUSTOM_PROPERTY_CASES: readonly CustomPropertyCase<`--swc-icon-${string}`>[] = + [ + { property: '--swc-icon-color', value: 'magenta' }, + { property: '--swc-icon-inline-size', value: '32px' }, + { property: '--swc-icon-block-size', value: '16px' }, + ]; + +const renderIconCustomProperty = ( + _: CustomPropertyCase, + style?: string +) => html` + ${Chevron100Icon()} +`; + +const coveredIconCustomProperties = coveredCustomProperties( + CUSTOM_PROPERTY_CASES +); + +const verifyCoverage = async () => + verifyCustomPropertyCoverage({ + customElementsManifest, + modulePath: 'components/icon/Icon.ts', + declarationName: 'Icon', + coveredProperties: coveredIconCustomProperties, + }); + +export const CustomProperties: Story = { + render: () => + theme( + customPropertyRows(CUSTOM_PROPERTY_CASES, renderIconCustomProperty), + 'light', + 'ltr' + ), + parameters: vrtParameters, + play: verifyCoverage, +}; diff --git a/2nd-gen/packages/swc/components/icon/test/vrt/icon.vrt.ts b/2nd-gen/packages/swc/components/icon/test/vrt/icon.vrt.ts new file mode 100644 index 00000000000..b5a7a0ff72f --- /dev/null +++ b/2nd-gen/packages/swc/components/icon/test/vrt/icon.vrt.ts @@ -0,0 +1,74 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { ICON_VALID_SIZES } from '@adobe/spectrum-wc-core/components/icon'; + +import '@adobe/spectrum-wc/components/icon/swc-icon.js'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; +import { + Arrow100Icon, + Chevron100Icon, + Cross100Icon, +} from '../../../icon/elements/index.js'; + +const meta: Meta = { + title: 'Icon/Icon VRT', + component: 'swc-icon', + tags: ['dev'], +}; + +export default meta; + +const icon = ( + label: string, + size: (typeof ICON_VALID_SIZES)[number], + svg = Chevron100Icon() +) => html` + ${svg} +`; + +const iconContent = () => html` + ${row( + ICON_VALID_SIZES.map((size) => icon(`Size ${size}`, size)), + 'Sizes' + )} + ${row( + [ + icon('Chevron', 'm', Chevron100Icon()), + icon('Arrow', 'm', Arrow100Icon()), + icon('Cross', 'm', Cross100Icon()), + ], + 'Sources' + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(iconContent(), 'light', 'ltr')} + ${theme(iconContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(iconContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/components/illustrated-message/test/vrt/illustrated-message.vrt.ts b/2nd-gen/packages/swc/components/illustrated-message/test/vrt/illustrated-message.vrt.ts new file mode 100644 index 00000000000..136d1d7df2b --- /dev/null +++ b/2nd-gen/packages/swc/components/illustrated-message/test/vrt/illustrated-message.vrt.ts @@ -0,0 +1,114 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { + ILLUSTRATED_MESSAGE_VALID_ORIENTATIONS, + ILLUSTRATED_MESSAGE_VALID_SIZES, +} from '@adobe/spectrum-wc-core/components/illustrated-message'; + +import '@adobe/spectrum-wc/components/button/swc-button.js'; +import '@adobe/spectrum-wc/components/button-group/swc-button-group.js'; +import '@adobe/spectrum-wc/components/illustrated-message/swc-illustrated-message.js'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Illustrated Message/Illustrated Message VRT', + component: 'swc-illustrated-message', + tags: ['dev'], +}; + +export default meta; + +const cloud = () => html` + +`; + +const illustratedMessage = ({ + size = 'm', + orientation = 'vertical', + heading = 'No files found', + description = 'Try another search or upload a new file.', + actions = false, +}: { + size?: (typeof ILLUSTRATED_MESSAGE_VALID_SIZES)[number]; + orientation?: (typeof ILLUSTRATED_MESSAGE_VALID_ORIENTATIONS)[number]; + heading?: string; + description?: string; + actions?: boolean; +}) => html` + + ${cloud()} +

${heading}

+ ${description} + ${actions + ? html` + + Upload + Browse + + ` + : ''} +
+`; + +const illustratedMessageContent = () => html` + ${row( + ILLUSTRATED_MESSAGE_VALID_SIZES.map((size) => + illustratedMessage({ size, heading: `Size ${size}` }) + ), + 'Sizes' + )} + ${row( + ILLUSTRATED_MESSAGE_VALID_ORIENTATIONS.map((orientation) => + illustratedMessage({ orientation, heading: orientation }) + ), + 'Orientations' + )} + ${row( + [ + illustratedMessage({ heading: 'Heading only', description: '' }), + illustratedMessage({ heading: 'With actions', actions: true }), + illustratedMessage({ + heading: 'ファイルが見つかりません', + description: '別の検索を試してください。', + }), + ], + 'Anatomy and CJK' + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(illustratedMessageContent(), 'light', 'ltr')} + ${theme(illustratedMessageContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(illustratedMessageContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/components/link/test/vrt/link.vrt.ts b/2nd-gen/packages/swc/components/link/test/vrt/link.vrt.ts new file mode 100644 index 00000000000..c15d9b59575 --- /dev/null +++ b/2nd-gen/packages/swc/components/link/test/vrt/link.vrt.ts @@ -0,0 +1,135 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { + forcedColorsVrtParameters, + forcePseudoStates, + row, + staticColorBackground, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; +import { + LINK_COLOR_VARIANTS, + LINK_STATIC_VARIANTS, + template, +} from '../../stories/link.template.js'; + +const meta: Meta = { + title: 'Link/Link VRT', + tags: ['dev'], +}; + +export default meta; + +const forceLinkStates = forcePseudoStates('a'); + +const linkContent = () => html` + ${row( + LINK_COLOR_VARIANTS.map((variant) => + template({ context: 'explicit', variant, sampleText: variant }) + ), + 'Color variants' + )} + ${row( + [ + template({ + context: 'explicit', + standalone: true, + sampleText: 'Standalone', + }), + template({ + context: 'explicit', + standalone: true, + quiet: true, + sampleText: 'Quiet', + }), + template({ context: 'prose', sampleText: 'inline link' }), + template({ context: 'links', sampleText: 'Privacy policy' }), + ], + 'Contexts' + )} + ${row( + [ + html` + Hover + `, + html` + Focus + `, + html` + Active + `, + ], + 'States' + )} + ${row( + [ + template({ + context: 'explicit', + lang: 'ja', + sampleText: '承認ワークフローを表示', + }), + template({ + context: 'explicit', + lang: 'ko', + sampleText: '승인 워크플로 보기', + }), + template({ + context: 'prose', + lang: 'zh', + sampleText: '查看审批工作流', + }), + ], + 'CJK language' + )} + ${LINK_STATIC_VARIANTS.map((variant) => + staticColorBackground( + row( + [ + template({ + context: 'explicit', + variant, + sampleText: variant, + }), + template({ + context: 'explicit', + variant, + standalone: true, + quiet: true, + sampleText: 'Quiet', + }), + ], + variant + ), + variant === 'staticWhite' ? 'white' : 'black' + ) + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(linkContent(), 'light', 'ltr')} + ${theme(linkContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, + play: forceLinkStates, +}; + +export const ForcedColors: Story = { + render: () => theme(linkContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, + play: forceLinkStates, +}; diff --git a/2nd-gen/packages/swc/components/meter/test/vrt/meter-custom-properties.vrt.ts b/2nd-gen/packages/swc/components/meter/test/vrt/meter-custom-properties.vrt.ts new file mode 100644 index 00000000000..ea10ad1c72c --- /dev/null +++ b/2nd-gen/packages/swc/components/meter/test/vrt/meter-custom-properties.vrt.ts @@ -0,0 +1,77 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '@adobe/spectrum-wc/components/meter/swc-meter.js'; + +import type { CustomPropertyCase } from '../../../../.storybook/helpers/index.js'; +import { + coveredCustomProperties, + customPropertyRows, + theme, + verifyCustomPropertyCoverage, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; +import customElementsManifest from '../../../../dist/custom-elements.json'; + +const meta: Meta = { + title: 'Meter/Meter VRT', + component: 'swc-meter', + tags: ['dev'], +}; + +export default meta; + +const CUSTOM_PROPERTY_CASES: readonly CustomPropertyCase<`--swc-linear-progress-${string}`>[] = + [ + { property: '--swc-linear-progress-fill-color', value: 'magenta' }, + { property: '--swc-linear-progress-track-color', value: 'magenta' }, + { property: '--swc-linear-progress-text-color', value: 'magenta' }, + { property: '--swc-linear-progress-thickness', value: '16px' }, + { property: '--swc-linear-progress-font-size', value: '24px' }, + { property: '--swc-linear-progress-top-to-text', value: '32px' }, + ]; + +const renderMeterCustomProperty = ( + _: CustomPropertyCase, + style?: string +) => html` + + Storage used + Add details to reach 100%. + +`; + +const coveredMeterCustomProperties = coveredCustomProperties( + CUSTOM_PROPERTY_CASES +); + +const verifyCoverage = async () => + verifyCustomPropertyCoverage({ + customElementsManifest, + modulePath: 'components/meter/Meter.ts', + declarationName: 'Meter', + coveredProperties: coveredMeterCustomProperties, + }); + +export const CustomProperties: Story = { + render: () => + theme( + customPropertyRows(CUSTOM_PROPERTY_CASES, renderMeterCustomProperty), + 'light', + 'ltr' + ), + parameters: vrtParameters, + play: verifyCoverage, +}; diff --git a/2nd-gen/packages/swc/components/meter/test/vrt/meter.vrt.ts b/2nd-gen/packages/swc/components/meter/test/vrt/meter.vrt.ts new file mode 100644 index 00000000000..2d9dd943c20 --- /dev/null +++ b/2nd-gen/packages/swc/components/meter/test/vrt/meter.vrt.ts @@ -0,0 +1,121 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { METER_VARIANTS } from '@adobe/spectrum-wc-core/components/meter'; +import { + LINEAR_PROGRESS_LABEL_POSITIONS, + LINEAR_PROGRESS_STATIC_COLORS, + LINEAR_PROGRESS_VALID_SIZES, +} from '@adobe/spectrum-wc-core/mixins/index.js'; + +import '@adobe/spectrum-wc/components/meter/swc-meter.js'; + +import { + forcedColorsVrtParameters, + row, + staticColorBackground, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Meter/Meter VRT', + component: 'swc-meter', + tags: ['dev'], +}; + +export default meta; + +const meter = ({ + variant = 'informative', + size = 'm', + value = 60, + labelPosition = 'top', + staticColor, + label = 'Storage used', +}: { + variant?: (typeof METER_VARIANTS)[number]; + size?: (typeof LINEAR_PROGRESS_VALID_SIZES)[number]; + value?: number; + labelPosition?: (typeof LINEAR_PROGRESS_LABEL_POSITIONS)[number]; + staticColor?: (typeof LINEAR_PROGRESS_STATIC_COLORS)[number]; + label?: string; +}) => html` + + ${label} + Add details to reach 100%. + +`; + +const meterContent = () => html` + ${row( + METER_VARIANTS.map((variant) => meter({ variant })), + 'Variants' + )} + ${row( + LINEAR_PROGRESS_VALID_SIZES.map((size) => meter({ size })), + 'Sizes' + )} + ${row( + [0, 25, 50, 75, 100].map((value) => meter({ value, label: `${value}%` })), + 'Values' + )} + ${row( + LINEAR_PROGRESS_LABEL_POSITIONS.map((labelPosition) => + meter({ labelPosition }) + ), + 'Label positions' + )} + ${row( + [ + meter({ label: '承認ワークフロー' }), + meter({ label: '승인 워크플로' }), + meter({ label: '审批工作流' }), + ], + 'CJK language' + )} + ${LINEAR_PROGRESS_STATIC_COLORS.map((color) => + staticColorBackground( + row( + [ + meter({ staticColor: color }), + meter({ staticColor: color, value: 90 }), + ], + `Static ${color}` + ), + color + ) + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(meterContent(), 'light', 'ltr')} + ${theme(meterContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(meterContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/components/opacity-checkerboard/test/vrt/opacity-checkerboard.vrt.ts b/2nd-gen/packages/swc/components/opacity-checkerboard/test/vrt/opacity-checkerboard.vrt.ts new file mode 100644 index 00000000000..37d00e971d0 --- /dev/null +++ b/2nd-gen/packages/swc/components/opacity-checkerboard/test/vrt/opacity-checkerboard.vrt.ts @@ -0,0 +1,109 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { css, html, LitElement, type TemplateResult } from 'lit'; +import { customElement, property } from 'lit/decorators.js'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { + row, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; + +import opacityCheckerboardStyles from '../../../../stylesheets/_lit-styles/opacity-checkerboard.css'; + +@customElement('demo-opacity-checkerboard-vrt-swatch') +class DemoOpacityCheckerboardVrtSwatch extends LitElement { + static override styles = [ + opacityCheckerboardStyles, + css` + .swatch { + position: relative; + inline-size: 120px; + block-size: 120px; + border: 1px solid var(--swc-gray-300, #ccc); + border-radius: 4px; + overflow: hidden; + } + .swc-OpacityCheckerboard, + .fill { + position: absolute; + inset: 0; + } + .fill { + background: var(--demo-fill, transparent); + } + `, + ]; + + @property() + public color = 'transparent'; + + @property({ reflect: true }) + public size: 'm' | 's' = 'm'; + + protected override render(): TemplateResult { + return html` +
+ + +
+ `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'demo-opacity-checkerboard-vrt-swatch': DemoOpacityCheckerboardVrtSwatch; + } +} + +const meta: Meta = { + title: 'Opacity Checkerboard/Opacity Checkerboard VRT', + component: 'demo-opacity-checkerboard-vrt-swatch', + tags: ['dev'], +}; + +export default meta; + +const swatch = (size: 'm' | 's', color: string) => html` + +`; + +export const Permutations: Story = { + render: () => + theme( + html` + ${row( + [ + swatch('m', 'transparent'), + swatch('m', 'rgba(255 0 0 / 0.4)'), + swatch('s', 'transparent'), + swatch('s', 'rgba(20 115 230 / 0.5)'), + ], + 'Sizes and opacity' + )} + `, + 'light', + 'ltr' + ), + parameters: vrtParameters, +}; diff --git a/2nd-gen/packages/swc/components/popover/stories/popover.stories.ts b/2nd-gen/packages/swc/components/popover/stories/popover.stories.ts index 7f94a7c5489..248ef81acd8 100644 --- a/2nd-gen/packages/swc/components/popover/stories/popover.stories.ts +++ b/2nd-gen/packages/swc/components/popover/stories/popover.stories.ts @@ -12,7 +12,6 @@ import { html } from 'lit'; import { ref } from 'lit/directives/ref.js'; -import { expect, waitFor } from '@storybook/test'; import type { Meta, StoryObj as Story } from '@storybook/web-components'; import { getStorybookHelpers } from '@wc-toolkit/storybook-helpers'; @@ -84,12 +83,6 @@ const meta: Meta = { docs: { subtitle: `An overlay element positioned relative to a trigger`, }, - // Docs stories render the popover closed (just a trigger), which is not - // visually meaningful, so disable Chromatic snapshots by default. The - // VRT-only stories at the bottom of this file re-enable them and open the - // popover so the rendered surface is captured. `delay` lets the entry - // transition settle before the snapshot. - chromatic: { disableSnapshot: true, delay: 500 }, }, tags: ['migrated'], }; @@ -545,134 +538,3 @@ export const Accessibility: Story = { render: (args) => triggered({ ...args }, 'a11y-trigger', 'Open popover'), tags: ['a11y'], }; - -// ──────────────────────────────── -// VRT-ONLY STORIES -// ──────────────────────────────── - -// Chromatic snapshots every story in this file, including those hidden from the -// docs and sidebar by the global `!autodocs` / `!dev` tags. The docs stories -// render the popover closed (just a trigger), so snapshots are disabled for them -// at the meta level and re-enabled per story here. Each of these opens a single -// popover (auto popovers light-dismiss one another, so only one can be open at a -// time) to capture the rendered surface. They stay out of the docs and sidebar -// and are not referenced by the a11y spec, so they do not affect either. - -// Opens the story's single popover and waits for it to anchor and reveal. -const openForVrt: NonNullable = async ({ canvasElement }) => { - const popover = canvasElement.querySelector('swc-popover') as Popover; - await popover.updateComplete; - popover.open = true; - await waitFor(() => - expect(popover.hasAttribute('actual-placement'), 'popover anchored').toBe( - true - ) - ); -}; - -// A centered, padded trigger so any placement has room to render without flipping. -const vrtRender = (args: Record, id: string) => html` -
- ${triggered({ ...args }, id, 'Open popover')} -
-`; - -const vrtStory = (overrides: Record, id: string): Story => ({ - args: { - 'accessible-label': 'Autosave', - 'default-slot': 'Your changes are saved automatically as you edit.', - ...overrides, - }, - render: (args) => vrtRender(args, id), - play: openForVrt, - parameters: { chromatic: { disableSnapshot: false } }, -}); - -// Placements (with the arrow) — `should-flip` off so each renders on the -// requested side and the tip orientation is captured deterministically. -export const VrtPlacementTop = vrtStory( - { placement: 'top', 'should-flip': false }, - 'vrt-placement-top' -); -export const VrtPlacementBottom = vrtStory( - { placement: 'bottom', 'should-flip': false }, - 'vrt-placement-bottom' -); -export const VrtPlacementStart = vrtStory( - { placement: 'start', 'should-flip': false }, - 'vrt-placement-start' -); -export const VrtPlacementEnd = vrtStory( - { placement: 'end', 'should-flip': false }, - 'vrt-placement-end' -); - -// Fixed sizes. -export const VrtSizeSmall = vrtStory({ size: 's' }, 'vrt-size-s'); -export const VrtSizeMedium = vrtStory({ size: 'm' }, 'vrt-size-m'); -export const VrtSizeLarge = vrtStory({ size: 'l' }, 'vrt-size-l'); - -// Arrowless surface (rectangular box-shadow, no tip). -export const VrtHideArrow = vrtStory({ 'hide-arrow': true }, 'vrt-hide-arrow'); - -// Modal surface (native ``, transparent backdrop). -export const VrtModal = vrtStory( - { modal: true, 'accessible-label': 'Account settings' }, - 'vrt-modal' -); - -// Nested popovers with both layers open (the inner opened from inside the outer -// forms an ancestor chain, so the outer stays open beneath it). -export const VrtNested: Story = { - render: () => html` -
- Open outer - -
-

- Outer popover -

- - Open inner - - - Inner popover - -
-
-
- `, - play: async ({ canvasElement }) => { - const outer = canvasElement.querySelector('#vrt-nested-outer') as Popover; - const inner = canvasElement.querySelector('#vrt-nested-inner') as Popover; - await outer.updateComplete; - outer.open = true; - await waitFor(() => - expect(outer.hasAttribute('actual-placement'), 'outer anchored').toBe( - true - ) - ); - inner.open = true; - await waitFor(() => - expect(inner.hasAttribute('actual-placement'), 'inner anchored').toBe( - true - ) - ); - // The ancestor chain keeps the outer open beneath the inner. - expect(outer.open, 'outer stays open under the inner').toBe(true); - }, - parameters: { chromatic: { disableSnapshot: false } }, -}; diff --git a/2nd-gen/packages/swc/components/popover/test/vrt/popover.vrt.ts b/2nd-gen/packages/swc/components/popover/test/vrt/popover.vrt.ts new file mode 100644 index 00000000000..3d535cb487e --- /dev/null +++ b/2nd-gen/packages/swc/components/popover/test/vrt/popover.vrt.ts @@ -0,0 +1,163 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html, nothing } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { + POPOVER_VALID_PLACEMENTS, + POPOVER_VALID_SIZES, +} from '@adobe/spectrum-wc-core/components/popover'; + +import '@adobe/spectrum-wc/components/button/swc-button.js'; +import '@adobe/spectrum-wc/components/popover/swc-popover.js'; + +import { + forcedColorsVrtParameters, + forceManualPopover, + row, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Popover/Popover VRT', + component: 'swc-popover', + tags: ['dev'], +}; + +export default meta; + +// PlacementController positions the popover relative to a real trigger, +// resolved via `for="id"` (same contract as Tooltip). Each call needs a +// globally unique id: `permutationContent()` runs twice per story (once per +// theme() block), so a bare per-row counter would collide between the +// light/ltr and dark/rtl copies sharing one document. The centered, +// padded wrapper gives every placement room to render without flipping. +const popover = ( + id: string, + { + placement = 'bottom', + size, + hideArrow = false, + modal = false, + accessibleLabel = 'Autosave', + content = 'Your changes are saved automatically as you edit.', + }: { + placement?: (typeof POPOVER_VALID_PLACEMENTS)[number]; + size?: (typeof POPOVER_VALID_SIZES)[number]; + hideArrow?: boolean; + modal?: boolean; + accessibleLabel?: string; + content?: string; + } +) => html` +
+ Trigger + + ${content} + +
+`; + +const permutationContent = (idPrefix: string) => html` + ${row( + POPOVER_VALID_SIZES.map((size) => + popover(`${idPrefix}-size-${size}`, { size, content: `Size ${size}` }) + ), + 'Sizes' + )} + ${POPOVER_VALID_PLACEMENTS.map( + (placement) => html` + ${row( + [popover(`${idPrefix}-placement-${placement}`, { placement })], + placement + )} + ` + )} + ${row( + [ + popover(`${idPrefix}-hide-arrow-shown`, { content: 'Arrow shown' }), + popover(`${idPrefix}-hide-arrow-hidden`, { + hideArrow: true, + content: 'Arrow hidden', + }), + ], + 'Hide arrow' + )} +`; + +// Every popover above renders `open`, but its default (non-modal) mode uses +// native `popover="auto"` internally, which only permits one open instance +// page-wide: each one that connects light-dismisses the previously-open +// one. Forcing `popover="manual"` (VRT-only; not how Popover behaves in +// production) on the internal shadow element — not the host, unlike +// Tooltip — lets every size, placement, and arrow variant stay open +// simultaneously. Scoped to `:not([modal])`: a modal popover's internal +// element is a ``, not a `popover="auto"` div, and does not +// participate in this dismissal group. See `forceManualPopover` in +// `.storybook/helpers/vrt.ts`. +const forceOpenPopovers = forceManualPopover( + 'swc-popover:not([modal])', + (host) => host.shadowRoot?.querySelector('.swc-Popover') ?? null +); + +export const Permutations: Story = { + render: () => html` + ${theme(permutationContent('light'), 'light', 'ltr')} + ${theme(permutationContent('dark'), 'dark', 'rtl')} + `, + parameters: vrtParameters, + play: forceOpenPopovers, +}; + +export const ForcedColors: Story = { + render: () => theme(permutationContent('forced'), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, + play: forceOpenPopovers, +}; + +// Modal mode renders a native `` opened via `showModal()`, a +// separate top-layer mechanism from `popover="auto"`/`"manual"`: it applies +// its own `::backdrop` covering the full viewport. Kept in its own story +// (not folded into Permutations) so that backdrop does not dim every other +// row's snapshot; a single modal instance never collides with anything, so +// it needs no `forceManualPopover` play function. +export const Modal: Story = { + render: () => + theme( + row( + [ + popover('modal', { + modal: true, + accessibleLabel: 'Delete file', + content: + 'Are you sure you want to delete this file? This action cannot be undone.', + }), + ], + 'Modal' + ), + 'light', + 'ltr' + ), + parameters: vrtParameters, +}; diff --git a/2nd-gen/packages/swc/components/progress-circle/test/vrt/progress-circle-custom-properties.vrt.ts b/2nd-gen/packages/swc/components/progress-circle/test/vrt/progress-circle-custom-properties.vrt.ts new file mode 100644 index 00000000000..d7c5a5bd04b --- /dev/null +++ b/2nd-gen/packages/swc/components/progress-circle/test/vrt/progress-circle-custom-properties.vrt.ts @@ -0,0 +1,74 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '@adobe/spectrum-wc/components/progress-circle/swc-progress-circle.js'; + +import type { CustomPropertyCase } from '../../../../.storybook/helpers/index.js'; +import { + coveredCustomProperties, + customPropertyRows, + theme, + verifyCustomPropertyCoverage, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; +import customElementsManifest from '../../../../dist/custom-elements.json'; + +const meta: Meta = { + title: 'Progress circle/Progress circle VRT', + component: 'swc-progress-circle', + tags: ['dev'], +}; + +export default meta; + +const CUSTOM_PROPERTY_CASES: readonly CustomPropertyCase<`--swc-progress-circle-${string}`>[] = + [ + { property: '--swc-progress-circle-size', value: '64px' }, + { property: '--swc-progress-circle-track-border-color', value: 'magenta' }, + { property: '--swc-progress-circle-fill-border-color', value: 'magenta' }, + { property: '--swc-progress-circle-thickness', value: '8px' }, + ]; + +const renderProgressCircleCustomProperty = ( + _: CustomPropertyCase, + style?: string +) => html` + +`; + +const coveredProgressCircleCustomProperties = coveredCustomProperties( + CUSTOM_PROPERTY_CASES +); + +const verifyCoverage = async () => + verifyCustomPropertyCoverage({ + customElementsManifest, + modulePath: 'components/progress-circle/ProgressCircle.ts', + declarationName: 'ProgressCircle', + coveredProperties: coveredProgressCircleCustomProperties, + }); + +const customPropertiesContent = () => + customPropertyRows(CUSTOM_PROPERTY_CASES, renderProgressCircleCustomProperty); + +export const CustomProperties: Story = { + render: () => theme(customPropertiesContent(), 'light', 'ltr'), + parameters: vrtParameters, + play: verifyCoverage, +}; diff --git a/2nd-gen/packages/swc/components/progress-circle/test/vrt/progress-circle.vrt.ts b/2nd-gen/packages/swc/components/progress-circle/test/vrt/progress-circle.vrt.ts new file mode 100644 index 00000000000..ed0f9051f70 --- /dev/null +++ b/2nd-gen/packages/swc/components/progress-circle/test/vrt/progress-circle.vrt.ts @@ -0,0 +1,109 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { + PROGRESS_CIRCLE_STATIC_COLORS, + PROGRESS_CIRCLE_VALID_SIZES, +} from '@adobe/spectrum-wc-core/components/progress-circle'; + +import '@adobe/spectrum-wc/components/progress-circle/swc-progress-circle.js'; + +import { + forcedColorsVrtParameters, + row, + staticColorBackground, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Progress circle/Progress circle VRT', + component: 'swc-progress-circle', + tags: ['dev'], +}; + +export default meta; + +const progressCircleContent = () => html` + ${row( + PROGRESS_CIRCLE_VALID_SIZES.map( + (size) => html` + + ` + ), + 'Sizes' + )} + ${row( + [0, 25, 50, 75, 100].map( + (progress) => html` + + ` + ), + 'Progress values' + )} + ${row( + [ + html` + + `, + html` + + `, + ], + 'Indeterminate and CJK' + )} + ${PROGRESS_CIRCLE_STATIC_COLORS.map((color) => + staticColorBackground( + row( + PROGRESS_CIRCLE_VALID_SIZES.map( + (size) => html` + + ` + ), + `Static ${color}` + ), + color + ) + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(progressCircleContent(), 'light', 'ltr')} + ${theme(progressCircleContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(progressCircleContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/components/status-light/test/vrt/status-light-custom-properties.vrt.ts b/2nd-gen/packages/swc/components/status-light/test/vrt/status-light-custom-properties.vrt.ts new file mode 100644 index 00000000000..07804287aaf --- /dev/null +++ b/2nd-gen/packages/swc/components/status-light/test/vrt/status-light-custom-properties.vrt.ts @@ -0,0 +1,78 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '@adobe/spectrum-wc/components/status-light/swc-status-light.js'; + +import type { CustomPropertyCase } from '../../../../.storybook/helpers/index.js'; +import { + coveredCustomProperties, + customPropertyRows, + theme, + verifyCustomPropertyCoverage, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; +import customElementsManifest from '../../../../dist/custom-elements.json'; + +const meta: Meta = { + title: 'Status light/Status light VRT', + component: 'swc-status-light', + tags: ['dev'], +}; + +export default meta; + +type StatusLightCustomPropertyCase = + CustomPropertyCase<`--swc-status-light-${string}`> & { + label?: string; + }; + +const CUSTOM_PROPERTY_CASES: readonly StatusLightCustomPropertyCase[] = [ + { property: '--swc-status-light-dot-size', value: '24px' }, + { property: '--swc-status-light-dot-color', value: 'magenta' }, + { property: '--swc-status-light-font-size', value: '24px' }, + { property: '--swc-status-light-line-height', value: '32px' }, + { property: '--swc-status-light-text-to-visual', value: '32px' }, + { property: '--swc-status-light-content-color', value: 'magenta' }, +]; + +const renderStatusLightCustomProperty = ( + { label = 'Approved' }: StatusLightCustomPropertyCase, + style?: string +) => html` + + ${label} + +`; + +const coveredStatusLightCustomProperties = coveredCustomProperties( + CUSTOM_PROPERTY_CASES +); + +const verifyCoverage = async () => + verifyCustomPropertyCoverage({ + customElementsManifest, + modulePath: 'components/status-light/StatusLight.ts', + declarationName: 'StatusLight', + coveredProperties: coveredStatusLightCustomProperties, + }); + +const customPropertiesContent = () => + customPropertyRows(CUSTOM_PROPERTY_CASES, renderStatusLightCustomProperty); + +export const CustomProperties: Story = { + render: () => theme(customPropertiesContent(), 'light', 'ltr'), + parameters: vrtParameters, + play: verifyCoverage, +}; diff --git a/2nd-gen/packages/swc/components/status-light/test/vrt/status-light.vrt.ts b/2nd-gen/packages/swc/components/status-light/test/vrt/status-light.vrt.ts new file mode 100644 index 00000000000..c730d1cfb20 --- /dev/null +++ b/2nd-gen/packages/swc/components/status-light/test/vrt/status-light.vrt.ts @@ -0,0 +1,168 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { + STATUS_LIGHT_VALID_SIZES, + STATUS_LIGHT_VARIANTS_COLOR, + STATUS_LIGHT_VARIANTS_SEMANTIC, +} from '@adobe/spectrum-wc-core/components/status-light'; + +import '@adobe/spectrum-wc/components/status-light/swc-status-light.js'; + +import { + createPermutations, + forcedColorsVrtParameters, + renderStorybookPermutation, + row, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Status light/Status light VRT', + component: 'swc-status-light', + tags: ['dev'], +}; + +export default meta; + +const semanticLabels = { + info: 'Active', + neutral: 'Archived', + positive: 'Approved', + notice: 'Pending approval', + negative: 'Rejected', +} as const; + +const colorLabels = { + yellow: 'Operations', + chartreuse: 'Quality', + celery: 'Documentation', + seafoam: 'Support', + cyan: 'Analytics', + indigo: 'Engineering', + purple: 'Product', + fuchsia: 'Marketing', + magenta: 'Design', + pink: 'Creative', + turquoise: 'Training', + brown: 'Facilities', + cinnamon: 'Compliance', + silver: 'Version 1.2.10', +} as const; + +const sizeLabels = { + s: 'Small', + m: 'Medium', + l: 'Large', + xl: 'Extra-large', +} as const; + +const renderStatusLightPermutation = + renderStorybookPermutation('swc-status-light'); + +const SIZE_PERMUTATIONS = createPermutations([ + { size: STATUS_LIGHT_VALID_SIZES }, +]); + +const SEMANTIC_VARIANT_PERMUTATIONS = createPermutations([ + { variant: STATUS_LIGHT_VARIANTS_SEMANTIC }, +]); + +const COLOR_VARIANT_PERMUTATIONS = createPermutations([ + { variant: STATUS_LIGHT_VARIANTS_COLOR }, +]); + +const statusLightContent = () => html` + ${row( + SIZE_PERMUTATIONS.map(({ size }) => + renderStatusLightPermutation({ + size, + 'default-slot': sizeLabels[size], + }) + ), + 'Sizes' + )} + ${row( + SEMANTIC_VARIANT_PERMUTATIONS.map(({ variant }) => + renderStatusLightPermutation({ + variant, + 'default-slot': semanticLabels[variant], + }) + ), + 'Semantic variants' + )} + ${row( + COLOR_VARIANT_PERMUTATIONS.map(({ variant }) => + renderStatusLightPermutation({ + variant, + 'default-slot': colorLabels[variant], + }) + ), + 'Non-semantic variants' + )} + ${row( + [ + html` + + Document processing in progress - validating submission + + `, + html` + + Pending approval from legal and finance + + `, + ], + 'Wrapping' + )} + ${row( + [ + html` + + 送信が承認されました + + `, + html` + + 승인 대기 중입니다 + + `, + html` + + 正在处理请求 + + `, + ], + 'CJK language' + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(statusLightContent(), 'light', 'ltr')} + ${theme(statusLightContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(statusLightContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/components/tabs/test/vrt/tabs.vrt.ts b/2nd-gen/packages/swc/components/tabs/test/vrt/tabs.vrt.ts new file mode 100644 index 00000000000..c4ffc6dad17 --- /dev/null +++ b/2nd-gen/packages/swc/components/tabs/test/vrt/tabs.vrt.ts @@ -0,0 +1,98 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { + KEYBOARD_ACTIVATIONS, + TAB_DENSITIES, + TABS_DIRECTIONS, +} from '@adobe/spectrum-wc-core/components/tabs'; + +import '@adobe/spectrum-wc/components/tabs/swc-tabs.js'; +import '@adobe/spectrum-wc/components/tabs/swc-tab.js'; +import '@adobe/spectrum-wc/components/tabs/swc-tab-panel.js'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Tabs/Tabs VRT', + component: 'swc-tabs', + tags: ['dev'], +}; + +export default meta; + +const tabs = ({ + direction = 'horizontal', + density = 'regular', + keyboardActivation = 'automatic', + disabled = false, + cjk = false, +} = {}) => html` + + ${cjk ? '概要' : 'Overview'} + ${cjk ? '仕様' : 'Specifications'} + + ${cjk ? 'ガイドライン' : 'Guidelines'} + +

Overview content.

+ +

${cjk ? '仕様の詳細を確認します。' : 'Specifications content.'}

+
+

Guidelines content.

+
+`; + +const tabsContent = () => html` + ${row( + TABS_DIRECTIONS.map((direction) => tabs({ direction })), + 'Directions' + )} + ${row( + TAB_DENSITIES.map((density) => tabs({ density })), + 'Densities' + )} + ${row( + KEYBOARD_ACTIVATIONS.map((keyboardActivation) => + tabs({ keyboardActivation }) + ), + 'Keyboard activation' + )} + ${row([tabs({ disabled: true }), tabs({ cjk: true })], 'States and CJK')} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(tabsContent(), 'light', 'ltr')} + ${theme(tabsContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(tabsContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/components/tooltip/test/vrt/tooltip-custom-properties.vrt.ts b/2nd-gen/packages/swc/components/tooltip/test/vrt/tooltip-custom-properties.vrt.ts new file mode 100644 index 00000000000..7f1a7b7ce81 --- /dev/null +++ b/2nd-gen/packages/swc/components/tooltip/test/vrt/tooltip-custom-properties.vrt.ts @@ -0,0 +1,69 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '@adobe/spectrum-wc/components/tooltip/swc-tooltip.js'; + +import type { CustomPropertyCase } from '../../../../.storybook/helpers/index.js'; +import { + coveredCustomProperties, + customPropertyRows, + theme, + verifyCustomPropertyCoverage, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; +import customElementsManifest from '../../../../dist/custom-elements.json'; + +const meta: Meta = { + title: 'Tooltip/Tooltip VRT', + component: 'swc-tooltip', + tags: ['dev'], +}; + +export default meta; + +const CUSTOM_PROPERTY_CASES: readonly CustomPropertyCase<`--swc-tooltip-${string}`>[] = + [{ property: '--swc-tooltip-background-color', value: 'magenta' }]; + +const renderTooltipCustomProperty = ( + _: CustomPropertyCase, + style?: string +) => html` +
+ Tooltip content +
+`; + +const coveredTooltipCustomProperties = coveredCustomProperties( + CUSTOM_PROPERTY_CASES +); + +const verifyCoverage = async () => + verifyCustomPropertyCoverage({ + customElementsManifest, + modulePath: 'components/tooltip/Tooltip.ts', + declarationName: 'Tooltip', + coveredProperties: coveredTooltipCustomProperties, + }); + +export const CustomProperties: Story = { + render: () => + theme( + customPropertyRows(CUSTOM_PROPERTY_CASES, renderTooltipCustomProperty), + 'light', + 'ltr' + ), + parameters: vrtParameters, + play: verifyCoverage, +}; diff --git a/2nd-gen/packages/swc/components/tooltip/test/vrt/tooltip.vrt.ts b/2nd-gen/packages/swc/components/tooltip/test/vrt/tooltip.vrt.ts new file mode 100644 index 00000000000..ece85317721 --- /dev/null +++ b/2nd-gen/packages/swc/components/tooltip/test/vrt/tooltip.vrt.ts @@ -0,0 +1,140 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { + TOOLTIP_PLACEMENTS, + TOOLTIP_VARIANTS, +} from '@adobe/spectrum-wc-core/components/tooltip'; + +import '@adobe/spectrum-wc/components/button/swc-button.js'; +import '@adobe/spectrum-wc/components/tooltip/swc-tooltip.js'; + +import { + forcedColorsVrtParameters, + forceManualPopover, + row, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Tooltip/Tooltip VRT', + component: 'swc-tooltip', + tags: ['dev'], +}; + +export default meta; + +// `PlacementController` positions the tooltip relative to a real trigger +// element, resolved via `for="id"` (see resolveTrigger() in +// Tooltip.base.ts); with no trigger to measure against, every tooltip +// renders at the popover's unpositioned default (the viewport corner). Each +// call needs a globally unique id: `tooltipContent()` runs twice per story +// (once per theme() block), so a bare per-row counter would collide between +// the light/ltr and dark/rtl copies sharing one document. +const tooltip = ( + id: string, + { + variant = 'neutral', + placement = 'top', + content = 'Tooltip content', + gridArea, + }: { + variant?: (typeof TOOLTIP_VARIANTS)[number]; + placement?: (typeof TOOLTIP_PLACEMENTS)[number]; + content?: string; + // Set only by the Placements compass grid below; every other caller + // renders inside a plain row() instead. + gridArea?: string; + } +) => html` +
+ Trigger + + ${content} + +
+`; + +// Six tooltips forced open at once with only a 16px flex gap (the plain +// row() layout) overlap badly: a "left"/"right"/"start"/"end" tooltip's +// bubble is top-layer content positioned by translate, not clipped by its +// trigger's local box, so it spills into the neighboring cell. Arranging the +// six placements on a compass-rose grid — the same layout the docs stories +// use for a single open tooltip — gives every direction an empty cell to pop +// into, so it holds up even with all six open simultaneously. +const placementsGrid = (idPrefix: string) => html` +
+ ${TOOLTIP_PLACEMENTS.map((placement) => + tooltip(`${idPrefix}-placement-${placement}`, { + placement, + gridArea: placement, + content: placement, + }) + )} +
+`; + +const tooltipContent = (idPrefix: string) => html` + ${row( + TOOLTIP_VARIANTS.map((variant) => + tooltip(`${idPrefix}-variant-${variant}`, { + variant, + content: `${variant} tooltip`, + }) + ), + 'Variants' + )} + ${row([placementsGrid(idPrefix)], 'Placements')} + ${row( + [ + tooltip(`${idPrefix}-cjk-ja`, { content: '承認ワークフローを開始' }), + tooltip(`${idPrefix}-cjk-ko`, { content: '승인 워크플로 시작' }), + tooltip(`${idPrefix}-cjk-zh`, { content: '启动审批工作流' }), + ], + 'CJK language' + )} +`; + +// Every tooltip above renders `open`, but native `popover="auto"` (Tooltip's +// default mode) only permits one open instance page-wide: each one that +// connects light-dismisses the previously-open one, and Tooltip's own toggle +// listener syncs that dismissal back into `open`. Without this play +// function, only the last tooltip in DOM order would actually render open by +// the time Chromatic snapshots. Forcing `popover="manual"` (VRT-only; not +// how Tooltip behaves in production) lets every placement and variant stay +// open simultaneously. See `forceManualPopover` in `.storybook/helpers/vrt.ts`. +const forceOpenTooltips = forceManualPopover('swc-tooltip'); + +export const Permutations: Story = { + render: () => html` + ${theme(tooltipContent('light'), 'light', 'ltr')} + ${theme(tooltipContent('dark'), 'dark', 'rtl')} + `, + parameters: vrtParameters, + play: forceOpenTooltips, +}; + +export const ForcedColors: Story = { + render: () => theme(tooltipContent('forced'), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, + play: forceOpenTooltips, +}; diff --git a/2nd-gen/packages/swc/components/typography/test/vrt/typography.vrt.ts b/2nd-gen/packages/swc/components/typography/test/vrt/typography.vrt.ts new file mode 100644 index 00000000000..b523c62c310 --- /dev/null +++ b/2nd-gen/packages/swc/components/typography/test/vrt/typography.vrt.ts @@ -0,0 +1,112 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../.storybook/helpers/index.js'; +import { + SIZES_BY_VARIANT, + template, + VARIANTS, +} from '../../stories/typography.template.js'; + +const meta: Meta = { + title: 'Typography/Typography VRT', + tags: ['dev'], +}; + +export default meta; + +const typographyContent = () => html` + ${row( + VARIANTS.map((variant) => + template({ + variant, + size: 'M', + sampleText: `${variant} sample`, + }) + ), + 'Variants' + )} + ${VARIANTS.map((variant) => + row( + SIZES_BY_VARIANT[variant].map((size) => + template({ variant, size, sampleText: `${variant} ${size}` }) + ), + `${variant} sizes` + ) + )} + ${row( + [ + template({ + variant: 'heading', + serif: true, + sampleText: 'Serif heading', + }), + template({ + variant: 'heading', + heavy: true, + sampleText: 'Heavy heading', + }), + template({ + variant: 'body', + emphasized: true, + sampleText: 'Emphasized body copy.', + }), + template({ + variant: 'body', + margins: true, + sampleText: 'Body copy with margins.', + }), + ], + 'Modifiers' + )} + ${row( + [ + template({ + variant: 'heading', + lang: 'ja', + sampleText: '承認ワークフロー', + }), + template({ + variant: 'body', + lang: 'ko', + sampleText: '승인 워크플로 상태를 확인합니다.', + }), + template({ + variant: 'detail', + lang: 'zh', + sampleText: '审批工作流状态', + }), + ], + 'CJK language' + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(typographyContent(), 'light', 'ltr')} + ${theme(typographyContent(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(typographyContent(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/patterns/conversational-ai/conversation-thread/test/vrt/conversation-thread.vrt.ts b/2nd-gen/packages/swc/patterns/conversational-ai/conversation-thread/test/vrt/conversation-thread.vrt.ts new file mode 100644 index 00000000000..e497b163bfc --- /dev/null +++ b/2nd-gen/packages/swc/patterns/conversational-ai/conversation-thread/test/vrt/conversation-thread.vrt.ts @@ -0,0 +1,96 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '../../../conversation-turn/index.js'; +import '../../../message-feedback/index.js'; +import '../../../message-sources/index.js'; +import '../../../prompt-field/index.js'; +import '../../../response-status/index.js'; +import '../../../system-message/index.js'; +import '../../../upload-artifact/index.js'; +import '../../../user-message/index.js'; +import '../../index.js'; + +import { + forcedColorsVrtParameters, + theme, + vrtParameters, +} from '../../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Conversational AI/Conversation thread/Conversation thread VRT', + component: 'swc-conversation-thread', + tags: ['dev'], +}; + +export default meta; + +const thread = () => html` +
+ + + + Can you help me create a 45-minute presentation? + + + + + + I interpreted your request as an executive narrative task. + +
+

Big idea: The warmth of welcome

+

+ Hospitality begins the moment customers set foot off their plane. +

+
+ + + Brand brief Q1 2026 + +
+
+ + +
+ Hilton commercial assets + 2026 +
+
+ + +
+ Brand guidelines + PDF +
+
+
+
+`; + +export const Permutations: Story = { + render: () => html` + ${theme(thread(), 'light', 'ltr')} ${theme(thread(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(thread(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/patterns/conversational-ai/conversation-turn/test/vrt/conversation-turn.vrt.ts b/2nd-gen/packages/swc/patterns/conversational-ai/conversation-turn/test/vrt/conversation-turn.vrt.ts new file mode 100644 index 00000000000..46027ec5fdf --- /dev/null +++ b/2nd-gen/packages/swc/patterns/conversational-ai/conversation-turn/test/vrt/conversation-turn.vrt.ts @@ -0,0 +1,72 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '../../../message-feedback/index.js'; +import '../../../response-status/index.js'; +import '../../../system-message/index.js'; +import '../../../user-message/index.js'; +import '../../index.js'; + +import { + forcedColorsVrtParameters, + theme, + vrtParameters, +} from '../../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Conversational AI/Conversation turn/Conversation turn VRT', + component: 'swc-conversation-turn', + tags: ['dev'], +}; + +export default meta; + +const content = () => html` +
+ + + Can you summarize the attached campaign assets? + + + + + + I grouped the response by audience and channel. + +
+

Here is a concise summary based on the files you shared.

+
+ +
+
+ + 承認ワークフローを短くしてください。 + +
+`; + +export const Permutations: Story = { + render: () => html` + ${theme(content(), 'light', 'ltr')} ${theme(content(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(content(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/patterns/conversational-ai/message-feedback/test/vrt/message-feedback.vrt.ts b/2nd-gen/packages/swc/patterns/conversational-ai/message-feedback/test/vrt/message-feedback.vrt.ts new file mode 100644 index 00000000000..cc434e776b3 --- /dev/null +++ b/2nd-gen/packages/swc/patterns/conversational-ai/message-feedback/test/vrt/message-feedback.vrt.ts @@ -0,0 +1,60 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '../../index.js'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Conversational AI/Message feedback/Message feedback VRT', + component: 'swc-message-feedback', + tags: ['dev'], +}; + +export default meta; + +const content = () => html` + ${row( + [ + html` + + `, + html` + + `, + html` + + `, + ], + 'Statuses' + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(content(), 'light', 'ltr')} ${theme(content(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(content(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/patterns/conversational-ai/message-sources/test/vrt/message-sources.vrt.ts b/2nd-gen/packages/swc/patterns/conversational-ai/message-sources/test/vrt/message-sources.vrt.ts new file mode 100644 index 00000000000..9e07f7542c3 --- /dev/null +++ b/2nd-gen/packages/swc/patterns/conversational-ai/message-sources/test/vrt/message-sources.vrt.ts @@ -0,0 +1,58 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '../../index.js'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Conversational AI/Message sources/Message sources VRT', + component: 'swc-message-sources', + tags: ['dev'], +}; + +export default meta; + +const sources = (open = false, label = 'Sources') => html` + + Adobe Experience Manager documentation + Creative Cloud release notes 2026 + Firefly API getting started guide + +`; + +const content = () => html` + ${row( + [sources(false), sources(true), sources(true, 'References')], + 'Collapsed and expanded' + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(content(), 'light', 'ltr')} ${theme(content(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(content(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/patterns/conversational-ai/prompt-field/test/vrt/prompt-field.vrt.ts b/2nd-gen/packages/swc/patterns/conversational-ai/prompt-field/test/vrt/prompt-field.vrt.ts new file mode 100644 index 00000000000..5cd4f105256 --- /dev/null +++ b/2nd-gen/packages/swc/patterns/conversational-ai/prompt-field/test/vrt/prompt-field.vrt.ts @@ -0,0 +1,110 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '../../../upload-artifact/index.js'; +import '../../index.js'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Conversational AI/Prompt field/Prompt field VRT', + component: 'swc-prompt-field', + tags: ['dev'], +}; + +export default meta; + +const placeholder = 'Ask a question, share an idea, or add a task.'; + +const artifact = html` + +
+ Brand guidelines + PDF +
+`; + +const content = () => html` + ${row( + [ + html` + + `, + html` + + `, + html` + + `, + html` + + `, + ], + 'Modes' + )} + ${row( + [ + html` + + ${artifact} +
+ AI output may be inaccurate. Verify before using. +
+
+ `, + html` + + `, + ], + 'Artifacts and CJK' + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(content(), 'light', 'ltr')} ${theme(content(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(content(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/patterns/conversational-ai/response-status/test/vrt/response-status.vrt.ts b/2nd-gen/packages/swc/patterns/conversational-ai/response-status/test/vrt/response-status.vrt.ts new file mode 100644 index 00000000000..522a1e91bb8 --- /dev/null +++ b/2nd-gen/packages/swc/patterns/conversational-ai/response-status/test/vrt/response-status.vrt.ts @@ -0,0 +1,69 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '../../index.js'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Conversational AI/Response status/Response status VRT', + component: 'swc-response-status', + tags: ['dev'], +}; + +export default meta; + +const content = () => html` + ${row( + [ + html` + + `, + html` + + I grouped your request into an executive-ready outline. + + `, + html` + + Step 1: Review source files. Step 2: Build a concise narrative. + + `, + html` + + プレゼンテーションの構成を作成しました。 + + `, + ], + 'States' + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(content(), 'light', 'ltr')} ${theme(content(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(content(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/patterns/conversational-ai/suggestion-item/test/vrt/suggestion-item.vrt.ts b/2nd-gen/packages/swc/patterns/conversational-ai/suggestion-item/test/vrt/suggestion-item.vrt.ts new file mode 100644 index 00000000000..3a5e1810167 --- /dev/null +++ b/2nd-gen/packages/swc/patterns/conversational-ai/suggestion-item/test/vrt/suggestion-item.vrt.ts @@ -0,0 +1,72 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '../../index.js'; + +import { + forcedColorsVrtParameters, + forcePseudoStates, + row, + theme, + vrtParameters, +} from '../../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Conversational AI/Suggestion item/Suggestion item VRT', + component: 'swc-suggestion-item', + tags: ['dev'], +}; + +export default meta; + +const forceItemStates = forcePseudoStates('swc-suggestion-item', 'button'); + +const content = () => html` + ${row( + [ + html` + Short action + `, + html` + + Create a year-over-year growth chart + + `, + html` + + Summarize in 3 bullet points + + `, + html` + 承認ワークフローを要約 + `, + ], + 'Labels and states' + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(content(), 'light', 'ltr')} ${theme(content(), 'dark', 'rtl')} + `, + parameters: vrtParameters, + play: forceItemStates, +}; + +export const ForcedColors: Story = { + render: () => theme(content(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, + play: forceItemStates, +}; diff --git a/2nd-gen/packages/swc/patterns/conversational-ai/suggestion/test/vrt/suggestion.vrt.ts b/2nd-gen/packages/swc/patterns/conversational-ai/suggestion/test/vrt/suggestion.vrt.ts new file mode 100644 index 00000000000..336996ac688 --- /dev/null +++ b/2nd-gen/packages/swc/patterns/conversational-ai/suggestion/test/vrt/suggestion.vrt.ts @@ -0,0 +1,74 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '../../index.js'; +import '../../../suggestion-item/index.js'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Conversational AI/Suggestion group/Suggestion group VRT', + component: 'swc-suggestion-group', + tags: ['dev'], +}; + +export default meta; + +const group = ( + count: 1 | 3 | 5, + heading = 'What would you like to do next?' +) => html` + +

${heading}

+ Create a slide deck from this + ${count >= 3 + ? html` + + Summarize in 3 bullet points + + Translate to Spanish + ` + : ''} + ${count === 5 + ? html` + + Refine the executive summary + + 承認ワークフローを要約 + ` + : ''} +
+`; + +const content = () => html` + ${row([group(1), group(3), group(5, 'Suggested next actions')], 'Counts')} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(content(), 'light', 'ltr')} ${theme(content(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(content(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/patterns/conversational-ai/system-message/test/vrt/system-message.vrt.ts b/2nd-gen/packages/swc/patterns/conversational-ai/system-message/test/vrt/system-message.vrt.ts new file mode 100644 index 00000000000..d102813015b --- /dev/null +++ b/2nd-gen/packages/swc/patterns/conversational-ai/system-message/test/vrt/system-message.vrt.ts @@ -0,0 +1,86 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '../../../conversation-turn/index.js'; +import '../../../message-feedback/index.js'; +import '../../../message-sources/index.js'; +import '../../../response-status/index.js'; +import '../../../suggestion/index.js'; +import '../../../suggestion-item/index.js'; +import '../../index.js'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Conversational AI/System message/System message VRT', + component: 'swc-system-message', + tags: ['dev'], +}; + +export default meta; + +const message = ({ loading = false, cjk = false } = {}) => html` + + + + ${cjk + ? '依頼内容を分析し、要点を整理しました。' + : 'I prioritized campaign outcomes and next-step actions.'} + +
+

${cjk ? '概要' : 'Executive summary'}

+

+ ${cjk + ? '共有された資料に基づいて、簡潔な説明構成を作成しました。' + : 'Here is a concise summary based on the files you shared.'} +

+
+ + + Brand brief Q1 2026 + Creative Cloud release notes + + +

What would you like to do next?

+ Create a slide deck + Summarize in 3 bullets +
+
+
+`; + +const content = () => html` + ${row( + [message(), message({ loading: true }), message({ cjk: true })], + 'States' + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(content(), 'light', 'ltr')} ${theme(content(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(content(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/patterns/conversational-ai/upload-artifact/test/vrt/upload-artifact.vrt.ts b/2nd-gen/packages/swc/patterns/conversational-ai/upload-artifact/test/vrt/upload-artifact.vrt.ts new file mode 100644 index 00000000000..d4c99dfedc4 --- /dev/null +++ b/2nd-gen/packages/swc/patterns/conversational-ai/upload-artifact/test/vrt/upload-artifact.vrt.ts @@ -0,0 +1,88 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '../../index.js'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Conversational AI/Upload artifact/Upload artifact VRT', + component: 'swc-upload-artifact', + tags: ['dev'], +}; + +export default meta; + +const card = (title: string, subtitle = 'PDF', dismissible = false) => html` + +
+ ${title} + ${subtitle} +
+`; + +const media = (dismissible = false) => html` + +
+
+`; + +const content = () => html` + ${row( + [ + card('Brand guidelines'), + card('Hilton commercial assets', '2026', true), + media(true), + ], + 'Types' + )} + ${row( + [ + html` +
+ ${card( + 'Hotel commercial assets for marketing campaign Q1-Q2 regional rollout', + '2026 fiscal year planning deck and executive summary', + true + )} +
+ `, + card('承認ワークフロー資料', 'PDF', true), + ], + 'Overflow and CJK' + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(content(), 'light', 'ltr')} ${theme(content(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(content(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +}; diff --git a/2nd-gen/packages/swc/patterns/conversational-ai/user-message/test/vrt/user-message.vrt.ts b/2nd-gen/packages/swc/patterns/conversational-ai/user-message/test/vrt/user-message.vrt.ts new file mode 100644 index 00000000000..12599a08b1f --- /dev/null +++ b/2nd-gen/packages/swc/patterns/conversational-ai/user-message/test/vrt/user-message.vrt.ts @@ -0,0 +1,93 @@ +/** + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import { html } from 'lit'; +import type { Meta, StoryObj as Story } from '@storybook/web-components'; + +import '../../../conversation-turn/index.js'; +import '../../index.js'; + +import { + forcedColorsVrtParameters, + row, + theme, + vrtParameters, +} from '../../../../../.storybook/helpers/index.js'; + +const meta: Meta = { + title: 'Conversational AI/User message/User message VRT', + component: 'swc-user-message', + tags: ['dev'], +}; + +export default meta; + +const turn = (message: unknown) => html` + ${message} +`; + +const content = () => html` + ${row( + [ + turn(html` + + Can you create a 45-minute executive presentation? + + `), + turn(html` + +
+ Hilton commercial assets + 2026 +
+ `), + turn(html` + +
+
+ `), + ], + 'Types' + )} + ${row( + [ + turn(html` + + 承認ワークフローを3つの要点に要約してください。 + + `), + turn(html` + + Can you shorten this into a concise summary for the executive review? + + `), + ], + 'CJK and wrapping' + )} +`; + +export const Permutations: Story = { + render: () => html` + ${theme(content(), 'light', 'ltr')} ${theme(content(), 'dark', 'rtl')} + `, + parameters: vrtParameters, +}; + +export const ForcedColors: Story = { + render: () => theme(content(), 'light', 'ltr'), + parameters: forcedColorsVrtParameters, +};