Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
8548b6d
feat(swc): add dedicated VRT stories, scoped Chromatic to snap only them
rubencarvalho Jul 1, 2026
cde34f8
feat(swc): flesh out Button VRT story with theme/RTL, icon anatomy, a…
rubencarvalho Jul 1, 2026
11678ed
feat(swc): deploy full dev-mode Storybook to PR preview deploys
rubencarvalho Jul 1, 2026
0480a60
feat(swc): force hover/focus-visible/active states in Button VRT
rubencarvalho Jul 1, 2026
f5de0ba
feat(swc): validate forcePseudoState against Tabs' :host()-based states
rubencarvalho Jul 1, 2026
a9c7686
fix(swc): set text color in VRT theme() helper for dark mode
rubencarvalho Jul 1, 2026
100452d
feat(swc): cover secondary variant's distinct tokens in static-color VRT
rubencarvalho Jul 2, 2026
65c289c
chore(swc): remove Tabs VRT story, keep this PR Button-only
rubencarvalho Jul 3, 2026
2289cc9
Merge branch 'main' into rcarvalho/feat-dedicated-vrt-stories
rubencarvalho Jul 3, 2026
b5a7cda
refactor(swc): rewrite Storybook stories config as a per-mode map
rubencarvalho Jul 6, 2026
3458194
feat(swc): add forced-colors VRT coverage for Button
rubencarvalho Jul 6, 2026
1f8119d
fix(swc): restore .vrt.ts stories to dev mode's Storybook build
rubencarvalho Jul 6, 2026
849f838
fix(swc): make VRT-only Chromatic builds work regardless of trigger
rubencarvalho Jul 6, 2026
765d834
refactor(swc): drop dead dev-mode check on experimental_indexers
rubencarvalho Jul 6, 2026
d013c70
feat(swc): support forcing pseudo-states on plain (shadow-less) elements
rubencarvalho Jul 6, 2026
f87d590
feat(swc): cover justified, global button styles, and --mod properties
rubencarvalho Jul 6, 2026
a06714f
Merge remote-tracking branch 'origin/main' into rcarvalho/feat-dedica…
rubencarvalho Jul 6, 2026
3f2aa27
refactor(swc): simplify VRT helper code and drop dead chromatic.disab…
rubencarvalho Jul 7, 2026
deea1dc
feat(swc): expand icon anatomy coverage across all sizes in Button VRT
rubencarvalho Jul 7, 2026
0ecabdb
revert(swc): drop dev-mode PR preview deploy override from this branch
rubencarvalho Jul 7, 2026
efe4df3
Merge remote-tracking branch 'origin/main' into rcarvalho/feat-dedica…
rubencarvalho Jul 13, 2026
0558fed
test(button): standardize button vrt coverage
rubencarvalho Jul 13, 2026
30e5344
test(button): simplify button vrt follow-up stories
rubencarvalho Jul 13, 2026
51e0594
docs(testing): document dedicated vrt authoring
rubencarvalho Jul 13, 2026
176bff0
test(button): add cjk language vrt coverage
rubencarvalho Jul 13, 2026
64ba9b1
docs(testing): extend vrt authoring guidance to patterns
rubencarvalho Jul 13, 2026
096fe38
Merge branch 'main' into rcarvalho/feat-dedicated-vrt-stories
rubencarvalho Jul 15, 2026
c28d1f1
test: add dedicated vrt stories for components and patterns
rubencarvalho Jul 13, 2026
9af05d7
test(button): name vrt group by component and cover disabled/pending …
rubencarvalho Jul 15, 2026
2481097
test: name vrt story groups by component
rubencarvalho Jul 15, 2026
5961a66
Merge remote-tracking branch 'origin/main' into rcarvalho/feat-dedica…
rubencarvalho Jul 15, 2026
9f29fb1
test(button): update core import to @adobe/spectrum-wc-core
rubencarvalho Jul 15, 2026
1ab7442
Merge branch 'rcarvalho/feat-dedicated-vrt-stories' into rcarvalho/te…
rubencarvalho Jul 15, 2026
af4b672
test: update vrt core imports to @adobe/spectrum-wc-core
rubencarvalho Jul 15, 2026
08fa9d3
test(button): group vrt permutations into per-variant and per-state rows
rubencarvalho Jul 16, 2026
7d27302
docs(testing): document per-component vrt row grouping decision
rubencarvalho Jul 16, 2026
e64790a
Merge branch 'rcarvalho/feat-dedicated-vrt-stories' into rcarvalho/te…
rubencarvalho Jul 16, 2026
e9edff7
test: exclude vrt stories from coverage
rubencarvalho Jul 16, 2026
4bb1431
Merge branch 'rcarvalho/feat-dedicated-vrt-stories' into rcarvalho/te…
rubencarvalho Jul 16, 2026
859beab
test: split ambiguous vrt state rows into per-state labeled rows
rubencarvalho Jul 16, 2026
924cb5c
docs(testing): require identifiable items in vrt rows
rubencarvalho Jul 16, 2026
1e7479a
Merge branch 'rcarvalho/feat-dedicated-vrt-stories' into rcarvalho/te…
rubencarvalho Jul 16, 2026
e0fa73b
test(vrt): sentence-case vrt story names at index time
rubencarvalho Jul 16, 2026
1aa7c6e
Merge branch 'rcarvalho/feat-dedicated-vrt-stories' into rcarvalho/te…
rubencarvalho Jul 16, 2026
74cc12b
fix(test): force manual popover mode so multiple tooltips render open
rubencarvalho Jul 16, 2026
863a7ce
fix(test): anchor vrt tooltips to a real trigger element
rubencarvalho Jul 16, 2026
f9c815c
fix(test): lay out vrt tooltip placements on a compass grid
rubencarvalho Jul 16, 2026
6548567
test(tooltip): use placement name as vrt placement content
rubencarvalho Jul 16, 2026
13172a8
test(vrt): increase row spacing across all vrt stories
rubencarvalho Jul 16, 2026
73ee451
test(vrt): increase row label-to-content spacing
rubencarvalho Jul 16, 2026
b4eb3be
test(vrt): further increase between-row spacing
rubencarvalho Jul 16, 2026
a2d0885
test(vrt): revert between-row spacing to 32px
rubencarvalho Jul 16, 2026
560c683
test(vrt): increase and tokenize row() item gap
rubencarvalho Jul 16, 2026
1d2a610
test(vrt): tokenize remaining hardcoded gaps in theme() and staticCol…
rubencarvalho Jul 16, 2026
cb14717
test(vrt): tokenize and increase row spacing
rubencarvalho Jul 16, 2026
06b2c93
test(popover): migrate vrt coverage to dedicated dense-row file
rubencarvalho Jul 16, 2026
bff6c9d
build(vrt): verify custom-elements.json exists before the vrt storybo…
rubencarvalho Jul 16, 2026
00ac78a
Merge branch 'rcarvalho/feat-dedicated-vrt-stories' into rcarvalho/te…
rubencarvalho Jul 16, 2026
c3f7aa6
Merge remote-tracking branch 'origin/main' into rcarvalho/feat-dedica…
rubencarvalho Jul 16, 2026
0f156f1
fix(vrt): follow main's custom-elements.json move to dist/
rubencarvalho Jul 16, 2026
a9b0257
Merge branch 'rcarvalho/feat-dedicated-vrt-stories' into rcarvalho/te…
rubencarvalho Jul 16, 2026
a88b4c3
fix(vrt): follow main's custom-elements.json move to dist/ across all…
rubencarvalho Jul 16, 2026
3054791
revert(vrt): drop verify-cem-manifest.mjs
rubencarvalho Jul 16, 2026
58ab595
Merge remote-tracking branch 'origin/main' into rcarvalho/test-add-vr…
rubencarvalho Jul 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 2nd-gen/packages/swc/.storybook/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export type { ForcedPseudoState } from './pseudo-state.js';
export {
coveredCustomProperties,
customPropertyRows,
forceManualPopover,
forcedColorsVrtParameters,
forcePseudoStates,
FORCED_STATES,
Expand Down
46 changes: 46 additions & 0 deletions 2nd-gen/packages/swc/.storybook/helpers/vrt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<HTMLElement & { open?: boolean }>(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',
Expand Down
Original file line number Diff line number Diff line change
@@ -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`
<swc-accordion style=${style ?? ''}>
<swc-accordion-item open>
<span slot="label">Personal information</span>
<p>Manage contact details.</p>
</swc-accordion-item>
<swc-accordion-item>
<span slot="label">Billing address</span>
<p>Used for payment verification.</p>
</swc-accordion-item>
</swc-accordion>
`;

const accordionItemExample = (
{ state }: AccordionItemCustomPropertyCase,
style?: string
) => html`
<div style=${style ?? ''}>
<swc-accordion>
<swc-accordion-item open data-force-state=${state ?? ''}>
<span slot="label">Personal information</span>
<p>Manage contact details.</p>
</swc-accordion-item>
</swc-accordion>
</div>
`;

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<ReturnType<typeof forcePseudoStates>>[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,
};
119 changes: 119 additions & 0 deletions 2nd-gen/packages/swc/components/accordion/test/vrt/accordion.vrt.ts
Original file line number Diff line number Diff line change
@@ -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`
<swc-accordion
size=${size ?? nothing}
density=${density}
?quiet=${quiet}
?disabled=${disabled}
allow-multiple
>
<swc-accordion-item open data-force-state=${state ?? nothing}>
<span slot="label">${cjk ? '個人情報' : 'Personal information'}</span>
<p>${cjk ? '名前と連絡先を管理します。' : 'Manage contact details.'}</p>
</swc-accordion-item>
<swc-accordion-item>
<span slot="label">${cjk ? '請求先住所' : 'Billing address'}</span>
<swc-button slot="actions" size="s" variant="secondary">Edit</swc-button>
<p>
${cjk
? '支払い方法の確認に使用します。'
: 'Used for payment verification.'}
</p>
</swc-accordion-item>
<swc-accordion-item disabled>
<span slot="label">${cjk ? '支払い方法' : 'Payment method'}</span>
<p>
${cjk ? '管理者に連絡してください。' : 'Contact your administrator.'}
</p>
</swc-accordion-item>
</swc-accordion>
`;

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,
};
Loading
Loading