From bede72f9e0e9d9d583b5ba88a5f1d4949105411b Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Thu, 14 May 2026 15:40:56 -0500 Subject: [PATCH 01/28] chore: scaffold core package --- .../components/accordion/Accordion.types.ts | 19 +++++++++++++++++++ .../components/accordion/AccordionBase.ts | 15 +++++++++++++++ .../accordion/AccordionItem.types.ts | 13 +++++++++++++ .../components/accordion/AccordionItemBase.ts | 15 +++++++++++++++ .../core/components/accordion/index.ts | 15 +++++++++++++++ 5 files changed, 77 insertions(+) create mode 100644 2nd-gen/packages/core/components/accordion/Accordion.types.ts create mode 100644 2nd-gen/packages/core/components/accordion/AccordionBase.ts create mode 100644 2nd-gen/packages/core/components/accordion/AccordionItem.types.ts create mode 100644 2nd-gen/packages/core/components/accordion/AccordionItemBase.ts create mode 100644 2nd-gen/packages/core/components/accordion/index.ts diff --git a/2nd-gen/packages/core/components/accordion/Accordion.types.ts b/2nd-gen/packages/core/components/accordion/Accordion.types.ts new file mode 100644 index 0000000000..44b0aba7eb --- /dev/null +++ b/2nd-gen/packages/core/components/accordion/Accordion.types.ts @@ -0,0 +1,19 @@ +/** + * 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. + */ + +export const ACCORDION_VALID_SIZES = ['s', 'm', 'l', 'xl'] as const; +export type AccordionSize = (typeof ACCORDION_VALID_SIZES)[number]; + +export const ACCORDION_DENSITIES = ['compact', 'regular', 'spacious'] as const; +export type AccordionDensity = (typeof ACCORDION_DENSITIES)[number]; + +export const SWC_ACCORDION_ITEM_TOGGLE_EVENT = 'swc-accordion-item-toggle'; diff --git a/2nd-gen/packages/core/components/accordion/AccordionBase.ts b/2nd-gen/packages/core/components/accordion/AccordionBase.ts new file mode 100644 index 0000000000..3b35a08de9 --- /dev/null +++ b/2nd-gen/packages/core/components/accordion/AccordionBase.ts @@ -0,0 +1,15 @@ +/** + * 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 { SpectrumElement } from '@spectrum-web-components/core/element/index.js'; + +export abstract class AccordionBase extends SpectrumElement {} diff --git a/2nd-gen/packages/core/components/accordion/AccordionItem.types.ts b/2nd-gen/packages/core/components/accordion/AccordionItem.types.ts new file mode 100644 index 0000000000..349458211e --- /dev/null +++ b/2nd-gen/packages/core/components/accordion/AccordionItem.types.ts @@ -0,0 +1,13 @@ +/** + * 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. + */ + +// Item-specific types go here as the accordion item API is defined in subsequent commits. diff --git a/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts b/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts new file mode 100644 index 0000000000..5d2a344f87 --- /dev/null +++ b/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts @@ -0,0 +1,15 @@ +/** + * 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 { SpectrumElement } from '@spectrum-web-components/core/element/index.js'; + +export abstract class AccordionItemBase extends SpectrumElement {} diff --git a/2nd-gen/packages/core/components/accordion/index.ts b/2nd-gen/packages/core/components/accordion/index.ts new file mode 100644 index 0000000000..a725fdc5fe --- /dev/null +++ b/2nd-gen/packages/core/components/accordion/index.ts @@ -0,0 +1,15 @@ +/** + * 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. + */ + +export * from './AccordionBase.js'; +export * from './AccordionItemBase.js'; +export * from './Accordion.types.js'; From 79b82b32349b41dedcce95466da759cd05a3556a Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Thu, 14 May 2026 15:48:07 -0500 Subject: [PATCH 02/28] chore: scaffold swc package --- .../swc/components/accordion/Accordion.ts | 38 +++++++++++++++++++ .../swc/components/accordion/AccordionItem.ts | 36 ++++++++++++++++++ .../swc/components/accordion/accordion.css | 13 +++++++ .../swc/components/accordion/index.ts | 14 +++++++ .../accordion/swc-accordion-item.ts | 23 +++++++++++ .../swc/components/accordion/swc-accordion.ts | 23 +++++++++++ 6 files changed, 147 insertions(+) create mode 100644 2nd-gen/packages/swc/components/accordion/Accordion.ts create mode 100644 2nd-gen/packages/swc/components/accordion/AccordionItem.ts create mode 100644 2nd-gen/packages/swc/components/accordion/accordion.css create mode 100644 2nd-gen/packages/swc/components/accordion/index.ts create mode 100644 2nd-gen/packages/swc/components/accordion/swc-accordion-item.ts create mode 100644 2nd-gen/packages/swc/components/accordion/swc-accordion.ts diff --git a/2nd-gen/packages/swc/components/accordion/Accordion.ts b/2nd-gen/packages/swc/components/accordion/Accordion.ts new file mode 100644 index 0000000000..3f15bb156e --- /dev/null +++ b/2nd-gen/packages/swc/components/accordion/Accordion.ts @@ -0,0 +1,38 @@ +/** + * 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 { CSSResultArray, html, TemplateResult } from 'lit'; + +import { AccordionBase } from '@spectrum-web-components/core/components/accordion'; + +import styles from './accordion.css'; + +/** + * An accordion component that groups expandable content sections. + * + * @element swc-accordion + * @since 2.0.0 + * + * @example + * + * Content + * + */ +export class Accordion extends AccordionBase { + public static override get styles(): CSSResultArray { + return [styles]; + } + + protected override render(): TemplateResult { + return html``; + } +} diff --git a/2nd-gen/packages/swc/components/accordion/AccordionItem.ts b/2nd-gen/packages/swc/components/accordion/AccordionItem.ts new file mode 100644 index 0000000000..b59479c7ae --- /dev/null +++ b/2nd-gen/packages/swc/components/accordion/AccordionItem.ts @@ -0,0 +1,36 @@ +/** + * 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 { CSSResultArray, html, TemplateResult } from 'lit'; + +import { AccordionItemBase } from '@spectrum-web-components/core/components/accordion'; + +import styles from './accordion.css'; + +/** + * An accordion item component that wraps a single expandable content section. + * + * @element swc-accordion-item + * @since 2.0.0 + * + * @example + * Content + */ +export class AccordionItem extends AccordionItemBase { + public static override get styles(): CSSResultArray { + return [styles]; + } + + protected override render(): TemplateResult { + return html``; + } +} diff --git a/2nd-gen/packages/swc/components/accordion/accordion.css b/2nd-gen/packages/swc/components/accordion/accordion.css new file mode 100644 index 0000000000..40f33594a2 --- /dev/null +++ b/2nd-gen/packages/swc/components/accordion/accordion.css @@ -0,0 +1,13 @@ +/** + * 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. + */ + +/* S2 token imports and selector definitions land here in Phase 4. */ diff --git a/2nd-gen/packages/swc/components/accordion/index.ts b/2nd-gen/packages/swc/components/accordion/index.ts new file mode 100644 index 0000000000..f8f7131062 --- /dev/null +++ b/2nd-gen/packages/swc/components/accordion/index.ts @@ -0,0 +1,14 @@ +/** + * 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. + */ + +export * from './Accordion.js'; +export * from './AccordionItem.js'; diff --git a/2nd-gen/packages/swc/components/accordion/swc-accordion-item.ts b/2nd-gen/packages/swc/components/accordion/swc-accordion-item.ts new file mode 100644 index 0000000000..cd9853027e --- /dev/null +++ b/2nd-gen/packages/swc/components/accordion/swc-accordion-item.ts @@ -0,0 +1,23 @@ +/** + * 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 { defineElement } from '@spectrum-web-components/core/element/index.js'; + +import { AccordionItem } from './AccordionItem.js'; + +declare global { + interface HTMLElementTagNameMap { + 'swc-accordion-item': AccordionItem; + } +} + +defineElement('swc-accordion-item', AccordionItem); diff --git a/2nd-gen/packages/swc/components/accordion/swc-accordion.ts b/2nd-gen/packages/swc/components/accordion/swc-accordion.ts new file mode 100644 index 0000000000..d51f7f1b1e --- /dev/null +++ b/2nd-gen/packages/swc/components/accordion/swc-accordion.ts @@ -0,0 +1,23 @@ +/** + * 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 { defineElement } from '@spectrum-web-components/core/element/index.js'; + +import { Accordion } from './Accordion.js'; + +declare global { + interface HTMLElementTagNameMap { + 'swc-accordion': Accordion; + } +} + +defineElement('swc-accordion', Accordion); From 8c69ece14f064b62d1557013618839b351cd9ba0 Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Thu, 14 May 2026 15:54:09 -0500 Subject: [PATCH 03/28] chore: add Chevron300Icon for xl accordion --- .../icon/elements/Chevron300Icon.ts | 22 +++++++++++++++++++ .../swc/components/icon/elements/index.ts | 1 + 2 files changed, 23 insertions(+) create mode 100644 2nd-gen/packages/swc/components/icon/elements/Chevron300Icon.ts diff --git a/2nd-gen/packages/swc/components/icon/elements/Chevron300Icon.ts b/2nd-gen/packages/swc/components/icon/elements/Chevron300Icon.ts new file mode 100644 index 0000000000..72956bf4b1 --- /dev/null +++ b/2nd-gen/packages/swc/components/icon/elements/Chevron300Icon.ts @@ -0,0 +1,22 @@ +/** + * 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, TemplateResult } from 'lit'; + +export const Chevron300Icon = (): TemplateResult => { + return html` + + + + `; +}; diff --git a/2nd-gen/packages/swc/components/icon/elements/index.ts b/2nd-gen/packages/swc/components/icon/elements/index.ts index b30f66ddd2..8d6cf638b2 100644 --- a/2nd-gen/packages/swc/components/icon/elements/index.ts +++ b/2nd-gen/packages/swc/components/icon/elements/index.ts @@ -20,6 +20,7 @@ export * from './Chevron50Icon.js'; export * from './Chevron75Icon.js'; export * from './Chevron100Icon.js'; export * from './Chevron200Icon.js'; +export * from './Chevron300Icon.js'; export * from './CornerTriangle300Icon.js'; export * from './Cross75Icon.js'; export * from './Cross100Icon.js'; From cb55ae1246fd39e3db839d34e4e79aa506843836 Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Thu, 14 May 2026 15:58:12 -0500 Subject: [PATCH 04/28] chore: set up storybook story --- .../accordion/stories/accordion.stories.ts | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 2nd-gen/packages/swc/components/accordion/stories/accordion.stories.ts diff --git a/2nd-gen/packages/swc/components/accordion/stories/accordion.stories.ts b/2nd-gen/packages/swc/components/accordion/stories/accordion.stories.ts new file mode 100644 index 0000000000..a6f2c6bb15 --- /dev/null +++ b/2nd-gen/packages/swc/components/accordion/stories/accordion.stories.ts @@ -0,0 +1,65 @@ +/** + * 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'; + +// ──────────────── +// METADATA +// ──────────────── + +/** + * An accordion groups related content sections, each behind a header that can + * be expanded or collapsed. Args, argTypes, and controls are added in a later commit + * once the component API is defined. + */ +const meta: Meta = { + title: 'Accordion', + component: 'swc-accordion', + parameters: { + docs: { + subtitle: 'Groups related content sections behind expandable headers.', + }, + }, + tags: ['migrated'], +}; + +export default meta; + +// ──────────────────── +// AUTODOCS STORY +// ──────────────────── + +export const Playground: Story = { + render: () => html` + + Sample content + + `, + tags: ['autodocs', 'dev'], +}; + +// ────────────────────────── +// OVERVIEW STORY +// ────────────────────────── + +export const Overview: Story = { + render: () => html` + + Sample content + + `, + tags: ['overview'], +}; From 8c616872ff9b74695c477f8a3171a05cb8e911e7 Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Thu, 14 May 2026 15:58:31 -0500 Subject: [PATCH 05/28] chore: commit preview.ts updates --- 2nd-gen/packages/swc/.storybook/preview.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/2nd-gen/packages/swc/.storybook/preview.ts b/2nd-gen/packages/swc/.storybook/preview.ts index 73e4673e0a..68e75baf54 100644 --- a/2nd-gen/packages/swc/.storybook/preview.ts +++ b/2nd-gen/packages/swc/.storybook/preview.ts @@ -324,11 +324,7 @@ const preview = { 'Components', [ 'Accordion', - [ - 'Accessibility migration analysis', - 'Migration plan', - 'Rendering and styling migration analysis', - ], + ['Accessibility migration analysis', 'Migration plan'], 'Action button', ['Rendering and styling migration analysis'], 'Action group', From 629c23389167173af47969d057ddf225c1414b1e Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Thu, 14 May 2026 17:12:40 -0500 Subject: [PATCH 06/28] feat: implement AccordionItem API and render template --- .../components/accordion/AccordionItemBase.ts | 73 +++++++++++++++++- .../swc/components/accordion/AccordionItem.ts | 77 ++++++++++++++++++- 2 files changed, 147 insertions(+), 3 deletions(-) diff --git a/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts b/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts index 5d2a344f87..3aa2f07483 100644 --- a/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts +++ b/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts @@ -10,6 +10,77 @@ * governing permissions and limitations under the License. */ +import { property } from 'lit/decorators.js'; + import { SpectrumElement } from '@spectrum-web-components/core/element/index.js'; -export abstract class AccordionItemBase extends SpectrumElement {} +import { + type AccordionSize, + SWC_ACCORDION_ITEM_TOGGLE_EVENT, +} from './Accordion.types.js'; + +/** + * Base class for accordion item components. Manages open/disabled state, + * heading level (set by the parent accordion), and the toggle event. + * + * @slot label - The heading text for this accordion item. + * @slot actions - Optional actions rendered adjacent to the heading, outside + * the toggle button so they remain independently interactive. + * @slot - The panel content revealed when the item is open. + */ +export abstract class AccordionItemBase extends SpectrumElement { + // ────────────────── + // PUBLIC API + // ────────────────── + + /** + * Whether the accordion item panel is expanded. + */ + @property({ type: Boolean, reflect: true }) + public open: boolean = false; + + /** + * Whether the accordion item is disabled. A disabled item keeps its header + * in the tab order but blocks toggling. + */ + @property({ type: Boolean, reflect: true }) + public disabled: boolean = false; + + /** + * The size of the item. Inherited from the parent accordion; controls which + * chevron icon is displayed. Has no effect when the item is used standalone. + */ + @property({ type: String, reflect: true }) + public size?: AccordionSize; + + // ────────────────────── + // INTERNAL STATE + // ────────────────────── + + /** + * @internal + * Heading level (2–6) propagated by the parent accordion. Defaults to 3 + * for standalone items. Used by the concrete render() to emit the correct + * element; made reactive in a later commit. + */ + protected heading: number = 3; + + // ────────────────────── + // IMPLEMENTATION + // ────────────────────── + + /** + * @internal + * Dispatches the toggle event. The concrete class wires this to the + * header button's click handler and adds open-state management. + */ + protected toggle(): void { + this.dispatchEvent( + new Event(SWC_ACCORDION_ITEM_TOGGLE_EVENT, { + bubbles: true, + composed: true, + cancelable: true, + }) + ); + } +} diff --git a/2nd-gen/packages/swc/components/accordion/AccordionItem.ts b/2nd-gen/packages/swc/components/accordion/AccordionItem.ts index b59479c7ae..61cd39872c 100644 --- a/2nd-gen/packages/swc/components/accordion/AccordionItem.ts +++ b/2nd-gen/packages/swc/components/accordion/AccordionItem.ts @@ -14,6 +14,13 @@ import { CSSResultArray, html, TemplateResult } from 'lit'; import { AccordionItemBase } from '@spectrum-web-components/core/components/accordion'; +import '../icon/swc-icon.js'; + +import { Chevron75Icon } from '../icon/elements/Chevron75Icon.js'; +import { Chevron100Icon } from '../icon/elements/Chevron100Icon.js'; +import { Chevron200Icon } from '../icon/elements/Chevron200Icon.js'; +import { Chevron300Icon } from '../icon/elements/Chevron300Icon.js'; + import styles from './accordion.css'; /** @@ -23,14 +30,80 @@ import styles from './accordion.css'; * @since 2.0.0 * * @example - * Content + * + * Section heading + * Panel content goes here. + * */ export class AccordionItem extends AccordionItemBase { + // ────────────────────────────── + // RENDERING & STYLING + // ────────────────────────────── + public static override get styles(): CSSResultArray { return [styles]; } + private chevronForSize(): TemplateResult { + switch (this.size) { + case 's': + return Chevron75Icon(); + case 'l': + return Chevron200Icon(); + case 'xl': + return Chevron300Icon(); + case 'm': + default: + return Chevron100Icon(); + } + } + + private handleActionsSlotChange(event: Event): void { + const slot = event.target as HTMLSlotElement; + const container = slot.parentElement as HTMLElement | null; + if (container) { + container.hidden = slot.assignedNodes({ flatten: true }).length === 0; + } + } + + private handleActionsContainerInteraction(event: Event): void { + event.stopPropagation(); + } + protected override render(): TemplateResult { - return html``; + //

is a static stub; the heading level becomes dynamic in a later commit. + return html` +

+ +

+ +
+ +
+ `; } } From 176c95aa3271846ce2656bdb195254094135c4ea Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Fri, 15 May 2026 08:10:12 -0500 Subject: [PATCH 07/28] feat: implement Accordion API and propagation --- .../components/accordion/AccordionBase.ts | 112 +++++++++++++++++- .../components/accordion/AccordionItemBase.ts | 5 +- .../swc/components/accordion/Accordion.ts | 13 +- 3 files changed, 120 insertions(+), 10 deletions(-) diff --git a/2nd-gen/packages/core/components/accordion/AccordionBase.ts b/2nd-gen/packages/core/components/accordion/AccordionBase.ts index 3b35a08de9..9d7521f595 100644 --- a/2nd-gen/packages/core/components/accordion/AccordionBase.ts +++ b/2nd-gen/packages/core/components/accordion/AccordionBase.ts @@ -10,6 +10,116 @@ * governing permissions and limitations under the License. */ +import { html, PropertyValues, TemplateResult } from 'lit'; +import { property } from 'lit/decorators.js'; + import { SpectrumElement } from '@spectrum-web-components/core/element/index.js'; -export abstract class AccordionBase extends SpectrumElement {} +import { + type AccordionDensity, + type AccordionSize, +} from './Accordion.types.js'; +import { AccordionItemBase } from './AccordionItemBase.js'; + +/** + * Base class for accordion components. Manages item propagation, heading + * level, density, and the exclusive-open constraint. + * + * @slot - One or more `swc-accordion-item` elements. + */ +export abstract class AccordionBase extends SpectrumElement { + // ────────────────── + // PUBLIC API + // ────────────────── + + /** + * When set, multiple items may be open at the same time. By default only + * one item can be open. + */ + @property({ type: Boolean, reflect: true, attribute: 'allow-multiple' }) + public allowMultiple: boolean = false; + + /** + * Heading level applied to every item header (2–6). Defaults to 3. + * Values outside that range are clamped. + */ + @property({ type: Number, reflect: true }) + public level: number = 3; + + /** + * Size applied to all items. When set, overrides any size set on individual + * items. + */ + @property({ type: String, reflect: true }) + public size?: AccordionSize; + + /** + * Controls vertical spacing between items. + * + * @default regular + */ + @property({ type: String, reflect: true }) + public density: AccordionDensity = 'regular'; + + /** + * Renders the accordion in its quiet (no-border) visual variant. + */ + @property({ type: Boolean, reflect: true }) + public quiet: boolean = false; + + /** + * Disables all items in the accordion. Individual items may also be + * disabled independently. + */ + @property({ type: Boolean, reflect: true }) + public disabled: boolean = false; + + // ────────────────────── + // IMPLEMENTATION + // ────────────────────── + + private updateItems(): void { + const slot = this.shadowRoot?.querySelector('slot'); + if (!slot) { + return; + } + const items = slot + .assignedElements({ flatten: true }) + .filter((el): el is AccordionItemBase => el instanceof AccordionItemBase); + for (const item of items) { + item.heading = this.level; + item.size = this.size; + } + } + + protected override update(changedProperties: PropertyValues): void { + if (changedProperties.has('level')) { + const clamped = Math.min(6, Math.max(2, this.level)); + if (this.level !== clamped) { + this.level = clamped; + } + } + if (changedProperties.has('level') || changedProperties.has('size')) { + this.updateItems(); + } + super.update(changedProperties); + } + + protected override firstUpdated(changedProperties: PropertyValues): void { + super.firstUpdated(changedProperties); + if (window.__swc?.DEBUG && !this.hasAttribute('density')) { + window.__swc.warn( + this, + `<${this.localName}> should have an explicit "density" attribute set. Defaulting to "regular", which corresponds to the 1st-gen default.`, + 'https://opensource.adobe.com/spectrum-web-components/components/accordion/', + { type: 'api', level: 'low' } + ); + } + } + + protected override render(): TemplateResult { + return html` + + `; + } +} diff --git a/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts b/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts index 3aa2f07483..f13a014039 100644 --- a/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts +++ b/2nd-gen/packages/core/components/accordion/AccordionItemBase.ts @@ -61,9 +61,10 @@ export abstract class AccordionItemBase extends SpectrumElement { * @internal * Heading level (2–6) propagated by the parent accordion. Defaults to 3 * for standalone items. Used by the concrete render() to emit the correct - * element; made reactive in a later commit. + * element; made reactive in a later commit. Public so AccordionBase + * can write to instances without a type assertion. */ - protected heading: number = 3; + public heading: number = 3; // ────────────────────── // IMPLEMENTATION diff --git a/2nd-gen/packages/swc/components/accordion/Accordion.ts b/2nd-gen/packages/swc/components/accordion/Accordion.ts index 3f15bb156e..f89f575f90 100644 --- a/2nd-gen/packages/swc/components/accordion/Accordion.ts +++ b/2nd-gen/packages/swc/components/accordion/Accordion.ts @@ -10,7 +10,7 @@ * governing permissions and limitations under the License. */ -import { CSSResultArray, html, TemplateResult } from 'lit'; +import { CSSResultArray } from 'lit'; import { AccordionBase } from '@spectrum-web-components/core/components/accordion'; @@ -23,16 +23,15 @@ import styles from './accordion.css'; * @since 2.0.0 * * @example - * - * Content + * + * + * Section heading + * Panel content goes here. + * * */ export class Accordion extends AccordionBase { public static override get styles(): CSSResultArray { return [styles]; } - - protected override render(): TemplateResult { - return html``; - } } From 5896710b896a1e5661c48c86a15c3e309166dc74 Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Fri, 15 May 2026 09:56:59 -0500 Subject: [PATCH 08/28] feat: implement open and toggle logic --- .../components/accordion/AccordionBase.ts | 43 ++++++++++ .../swc/components/accordion/AccordionItem.ts | 45 +++++++++- .../accordion/stories/accordion.stories.ts | 86 ++++++++++++++++--- 3 files changed, 159 insertions(+), 15 deletions(-) diff --git a/2nd-gen/packages/core/components/accordion/AccordionBase.ts b/2nd-gen/packages/core/components/accordion/AccordionBase.ts index 9d7521f595..7692e48d45 100644 --- a/2nd-gen/packages/core/components/accordion/AccordionBase.ts +++ b/2nd-gen/packages/core/components/accordion/AccordionBase.ts @@ -18,6 +18,7 @@ import { SpectrumElement } from '@spectrum-web-components/core/element/index.js' import { type AccordionDensity, type AccordionSize, + SWC_ACCORDION_ITEM_TOGGLE_EVENT, } from './Accordion.types.js'; import { AccordionItemBase } from './AccordionItemBase.js'; @@ -78,6 +79,32 @@ export abstract class AccordionBase extends SpectrumElement { // IMPLEMENTATION // ────────────────────── + private handleItemToggle = (event: Event): void => { + if (this.disabled) { + event.preventDefault(); + return; + } + if (this.allowMultiple) { + return; + } + const toggling = event.target; + if (!(toggling instanceof AccordionItemBase) || !toggling.open) { + return; + } + const slot = this.shadowRoot?.querySelector('slot'); + if (!slot) { + return; + } + const items = slot + .assignedElements({ flatten: true }) + .filter((el): el is AccordionItemBase => el instanceof AccordionItemBase); + for (const item of items) { + if (item !== toggling) { + item.open = false; + } + } + }; + private updateItems(): void { const slot = this.shadowRoot?.querySelector('slot'); if (!slot) { @@ -92,6 +119,22 @@ export abstract class AccordionBase extends SpectrumElement { } } + public override connectedCallback(): void { + super.connectedCallback(); + this.addEventListener( + SWC_ACCORDION_ITEM_TOGGLE_EVENT, + this.handleItemToggle + ); + } + + public override disconnectedCallback(): void { + super.disconnectedCallback(); + this.removeEventListener( + SWC_ACCORDION_ITEM_TOGGLE_EVENT, + this.handleItemToggle + ); + } + protected override update(changedProperties: PropertyValues): void { if (changedProperties.has('level')) { const clamped = Math.min(6, Math.max(2, this.level)); diff --git a/2nd-gen/packages/swc/components/accordion/AccordionItem.ts b/2nd-gen/packages/swc/components/accordion/AccordionItem.ts index 61cd39872c..293a883ec1 100644 --- a/2nd-gen/packages/swc/components/accordion/AccordionItem.ts +++ b/2nd-gen/packages/swc/components/accordion/AccordionItem.ts @@ -12,7 +12,10 @@ import { CSSResultArray, html, TemplateResult } from 'lit'; -import { AccordionItemBase } from '@spectrum-web-components/core/components/accordion'; +import { + AccordionItemBase, + SWC_ACCORDION_ITEM_TOGGLE_EVENT, +} from '@spectrum-web-components/core/components/accordion'; import '../icon/swc-icon.js'; @@ -37,13 +40,48 @@ import styles from './accordion.css'; */ export class AccordionItem extends AccordionItemBase { // ────────────────────────────── - // RENDERING & STYLING + // STYLING // ────────────────────────────── public static override get styles(): CSSResultArray { return [styles]; } + // ────────────────────────────── + // DELEGATION + // ────────────────────────────── + + public override focus(options?: FocusOptions): void { + this.shadowRoot?.getElementById('header')?.focus(options); + } + + public override click(): void { + this.shadowRoot?.getElementById('header')?.click(); + } + + // ────────────────────────────── + // TOGGLE BEHAVIOR + // ────────────────────────────── + + protected override toggle(): void { + if (this.disabled) { + return; + } + this.open = !this.open; + const event = new Event(SWC_ACCORDION_ITEM_TOGGLE_EVENT, { + bubbles: true, + composed: true, + cancelable: true, + }); + if (!this.dispatchEvent(event)) { + this.open = !this.open; + } + } + + // ────────────────────────────── + // RENDERING + // ────────────────────────────── + private chevronForSize(): TemplateResult { switch (this.size) { case 's': @@ -77,8 +115,9 @@ export class AccordionItem extends AccordionItemBase { + `; return html` -

- -

+ ${this.renderHeadingWrapper(button)} From d4d945d3f1b95bcc4374ca74b3fb0c96bd8b1524 Mon Sep 17 00:00:00 2001 From: Rise Erpelding Date: Fri, 15 May 2026 11:50:56 -0500 Subject: [PATCH 11/28] fix: handle space keyboard behavior (SWC-1487) --- .../packages/swc/components/accordion/AccordionItem.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/2nd-gen/packages/swc/components/accordion/AccordionItem.ts b/2nd-gen/packages/swc/components/accordion/AccordionItem.ts index 4d5c6a5d1e..8312fc7e44 100644 --- a/2nd-gen/packages/swc/components/accordion/AccordionItem.ts +++ b/2nd-gen/packages/swc/components/accordion/AccordionItem.ts @@ -109,6 +109,13 @@ export class AccordionItem extends AccordionItemBase { event.stopPropagation(); } + private handleHeaderKeydown(event: Event): void { + if ((event as KeyboardEvent).key === ' ') { + event.preventDefault(); + this.toggle(); + } + } + private renderHeadingWrapper(content: TemplateResult): TemplateResult { switch (this.heading) { case 2: @@ -145,6 +152,7 @@ export class AccordionItem extends AccordionItemBase { this.disabled || this.parentDisabled ? 'true' : undefined )} @click=${this.toggle} + @keydown=${this.handleHeaderKeydown} >