diff --git a/change/@ni-ok-angular-09482ac4-a62c-47fc-be43-c52b823e870e.json b/change/@ni-ok-angular-09482ac4-a62c-47fc-be43-c52b823e870e.json
new file mode 100644
index 0000000000..360a832613
--- /dev/null
+++ b/change/@ni-ok-angular-09482ac4-a62c-47fc-be43-c52b823e870e.json
@@ -0,0 +1,7 @@
+{
+ "type": "minor",
+ "comment": "feat: add OK accordion-item",
+ "packageName": "@ni/ok-angular",
+ "email": "1458528+fredvisser@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
diff --git a/change/@ni-ok-components-6037eb00-1211-4817-b88c-f456f2305a49.json b/change/@ni-ok-components-6037eb00-1211-4817-b88c-f456f2305a49.json
new file mode 100644
index 0000000000..86adb9a6c6
--- /dev/null
+++ b/change/@ni-ok-components-6037eb00-1211-4817-b88c-f456f2305a49.json
@@ -0,0 +1,7 @@
+{
+ "type": "minor",
+ "comment": "feat: add OK accordion-item",
+ "packageName": "@ni/ok-components",
+ "email": "1458528+fredvisser@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
diff --git a/packages/angular-workspace/example-client-app/src/app/app.module.ts b/packages/angular-workspace/example-client-app/src/app/app.module.ts
index 397d5a7efe..9be8dd00e3 100644
--- a/packages/angular-workspace/example-client-app/src/app/app.module.ts
+++ b/packages/angular-workspace/example-client-app/src/app/app.module.ts
@@ -34,6 +34,7 @@ import { NimbleTableColumnMenuButtonModule } from '@ni/nimble-angular/table-colu
import { NimbleRichTextViewerModule } from '@ni/nimble-angular/rich-text/viewer';
import { NimbleRichTextEditorModule } from '@ni/nimble-angular/rich-text/editor';
import { NimbleRichTextMentionUsersModule } from '@ni/nimble-angular/rich-text-mention/users';
+import { OkAccordionItemModule } from 'ok-angular/accordion-item/ok-accordion-item.module';
import { OkButtonModule } from 'ok-angular/button/ok-button.module';
import { SprightChatConversationModule } from '@ni/spright-angular/chat/conversation';
import { SprightChatInputModule } from '@ni/spright-angular/chat/input';
@@ -120,6 +121,7 @@ import { CustomAppComponent } from './customapp/customapp.component';
NimbleMappingEmptyModule,
NimbleIconPencilToRectangleModule,
NimbleIconMessagesSparkleModule,
+ OkAccordionItemModule,
OkButtonModule,
SprightChatConversationModule,
SprightChatInputModule,
diff --git a/packages/angular-workspace/example-client-app/src/app/customapp/customapp.component.html b/packages/angular-workspace/example-client-app/src/app/customapp/customapp.component.html
index 4458204fde..71d783a2fe 100644
--- a/packages/angular-workspace/example-client-app/src/app/customapp/customapp.component.html
+++ b/packages/angular-workspace/example-client-app/src/app/customapp/customapp.component.html
@@ -519,5 +519,14 @@
Button (Ok)
Ok
+
+
Accordion Item (Ok)
+
+ Calibration assets can expose operator-facing status, location, and ownership details.
+
+
+ This section starts collapsed to show the default interaction state.
+
+
diff --git a/packages/angular-workspace/ok-angular/accordion-item/ng-package.json b/packages/angular-workspace/ok-angular/accordion-item/ng-package.json
new file mode 100644
index 0000000000..3ccce86b1c
--- /dev/null
+++ b/packages/angular-workspace/ok-angular/accordion-item/ng-package.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "../../../../node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "public-api.ts"
+ }
+}
diff --git a/packages/angular-workspace/ok-angular/accordion-item/ok-accordion-item.directive.ts b/packages/angular-workspace/ok-angular/accordion-item/ok-accordion-item.directive.ts
new file mode 100644
index 0000000000..42df590b07
--- /dev/null
+++ b/packages/angular-workspace/ok-angular/accordion-item/ok-accordion-item.directive.ts
@@ -0,0 +1,48 @@
+import { booleanAttribute, Directive, ElementRef, Input, Renderer2 } from '@angular/core';
+import type { AccordionItem } from '@ni/ok-components/dist/esm/accordion-item';
+import { accordionItemTag } from '@ni/ok-components/dist/esm/accordion-item';
+import type { AccordionItemAppearance } from '@ni/ok-components/dist/esm/accordion-item/types';
+
+export type { AccordionItem };
+export { accordionItemTag };
+
+/**
+ * Directive to provide Angular integration for the accordion item.
+ */
+@Directive({
+ selector: 'ok-accordion-item',
+ standalone: false
+})
+export class OkAccordionItemDirective {
+ public get header(): string {
+ return this.elementRef.nativeElement.header;
+ }
+
+ @Input()
+ public set header(value: string) {
+ this.renderer.setProperty(this.elementRef.nativeElement, 'header', value);
+ }
+
+ public get expanded(): boolean {
+ return this.elementRef.nativeElement.expanded;
+ }
+
+ @Input({ transform: booleanAttribute })
+ public set expanded(value: boolean) {
+ this.renderer.setProperty(this.elementRef.nativeElement, 'expanded', value);
+ }
+
+ public get appearance(): AccordionItemAppearance {
+ return this.elementRef.nativeElement.appearance;
+ }
+
+ @Input()
+ public set appearance(value: AccordionItemAppearance) {
+ this.renderer.setProperty(this.elementRef.nativeElement, 'appearance', value);
+ }
+
+ public constructor(
+ private readonly elementRef: ElementRef,
+ private readonly renderer: Renderer2
+ ) {}
+}
diff --git a/packages/angular-workspace/ok-angular/accordion-item/ok-accordion-item.module.ts b/packages/angular-workspace/ok-angular/accordion-item/ok-accordion-item.module.ts
new file mode 100644
index 0000000000..22d5eb0461
--- /dev/null
+++ b/packages/angular-workspace/ok-angular/accordion-item/ok-accordion-item.module.ts
@@ -0,0 +1,12 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { OkAccordionItemDirective } from './ok-accordion-item.directive';
+
+import '@ni/ok-components/dist/esm/accordion-item';
+
+@NgModule({
+ declarations: [OkAccordionItemDirective],
+ imports: [CommonModule],
+ exports: [OkAccordionItemDirective]
+})
+export class OkAccordionItemModule { }
diff --git a/packages/angular-workspace/ok-angular/accordion-item/public-api.ts b/packages/angular-workspace/ok-angular/accordion-item/public-api.ts
new file mode 100644
index 0000000000..c02a3cc11d
--- /dev/null
+++ b/packages/angular-workspace/ok-angular/accordion-item/public-api.ts
@@ -0,0 +1,2 @@
+export * from './ok-accordion-item.directive';
+export * from './ok-accordion-item.module';
diff --git a/packages/angular-workspace/ok-angular/accordion-item/tests/accordion-item.directive.spec.ts b/packages/angular-workspace/ok-angular/accordion-item/tests/accordion-item.directive.spec.ts
new file mode 100644
index 0000000000..70e07e763c
--- /dev/null
+++ b/packages/angular-workspace/ok-angular/accordion-item/tests/accordion-item.directive.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+import { OkAccordionItemModule } from '../ok-accordion-item.module';
+
+describe('Ok accordion item', () => {
+ describe('module', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [OkAccordionItemModule]
+ });
+ });
+
+ it('custom element is defined', () => {
+ expect(customElements.get('ok-accordion-item')).not.toBeUndefined();
+ });
+ });
+});
diff --git a/packages/ok-components/src/accordion-item/index.ts b/packages/ok-components/src/accordion-item/index.ts
new file mode 100644
index 0000000000..874f7156e7
--- /dev/null
+++ b/packages/ok-components/src/accordion-item/index.ts
@@ -0,0 +1,38 @@
+import { attr } from '@ni/fast-element';
+import { DesignSystem, FoundationElement } from '@ni/fast-foundation';
+import '@ni/nimble-components/dist/esm/icons/arrow-expander-right';
+import { styles } from './styles';
+import { template } from './template';
+import { AccordionItemAppearance } from './types';
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'ok-accordion-item': AccordionItem;
+ }
+}
+
+/**
+ * An accordion item component that can be expanded or collapsed to
+ * show or hide its content.
+ */
+export class AccordionItem extends FoundationElement {
+ @attr
+ public header = '';
+
+ @attr({ mode: 'boolean' })
+ public expanded = false;
+
+ @attr()
+ public appearance: AccordionItemAppearance = AccordionItemAppearance.outline;
+}
+
+const okAccordionItem = AccordionItem.compose({
+ baseName: 'accordion-item',
+ template,
+ styles
+});
+
+DesignSystem.getOrCreate()
+ .withPrefix('ok')
+ .register(okAccordionItem());
+export const accordionItemTag = 'ok-accordion-item';
diff --git a/packages/ok-components/src/accordion-item/styles.ts b/packages/ok-components/src/accordion-item/styles.ts
new file mode 100644
index 0000000000..3cf033a381
--- /dev/null
+++ b/packages/ok-components/src/accordion-item/styles.ts
@@ -0,0 +1,118 @@
+import { css } from '@ni/fast-element';
+import {
+ bodyPlus1EmphasizedFont,
+ bodyPlus1EmphasizedFontColor,
+ borderHoverColor,
+ borderRgbPartialColor,
+ dividerWidth,
+ iconSize,
+ mediumDelay,
+ mediumPadding,
+ smallDelay,
+ standardPadding
+} from '@ni/nimble-components/dist/esm/theme-provider/design-tokens';
+import { appearanceBehavior } from '@ni/nimble-components/dist/esm/utilities/style/appearance';
+import { display } from '../utilities/style/display';
+import { AccordionItemAppearance } from './types';
+
+export const styles = css`
+ ${display('block')}
+
+ .accordion-item-details > .accordion-item-summary {
+ display: flex;
+ box-sizing: border-box;
+ height: 36px;
+ align-items: center;
+ margin-left: calc(-1 * ${mediumPadding});
+ border: ${dividerWidth} solid transparent;
+ list-style: none;
+ cursor: pointer;
+ user-select: none;
+ }
+
+ .accordion-item-details > .accordion-item-summary::-webkit-details-marker {
+ display: none;
+ }
+
+ .accordion-item-details > .accordion-item-summary::marker {
+ content: "";
+ }
+
+ .accordion-item-details > .accordion-item-summary:hover {
+ border: ${dividerWidth} solid ${borderHoverColor};
+ transition: ${smallDelay} ease-in;
+ }
+
+ .accordion-item-details > .accordion-item-summary:focus-visible {
+ outline: 2px solid ${borderHoverColor};
+ outline-offset: -2px;
+ }
+
+ .accordion-item-icon {
+ transition: transform ${mediumDelay} ease-in;
+ margin: ${mediumPadding};
+ min-width: ${iconSize};
+ }
+
+ .accordion-item-details[open] > .accordion-item-summary > .accordion-item-icon {
+ transform: rotate(90deg);
+ }
+
+ .accordion-item-title {
+ flex: 1;
+ width: 100%;
+ position: relative;
+ font: ${bodyPlus1EmphasizedFont};
+ line-height: 20px;
+ color: ${bodyPlus1EmphasizedFontColor};
+ text-align: left;
+ display: block;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ overflow-wrap: normal;
+ }
+
+ .accordion-item-content {
+ display: flex;
+ flex-direction: column;
+ gap: ${mediumPadding};
+ margin-left: calc(${iconSize} + ${mediumPadding});
+ margin-top: ${mediumPadding};
+ padding-bottom: ${standardPadding};
+ }
+`.withBehaviors(
+ appearanceBehavior(
+ AccordionItemAppearance.outline,
+ css`
+ :host {
+ border-bottom: ${dividerWidth} solid rgba(${borderRgbPartialColor}, 0.2);
+ }
+ `
+ ),
+ appearanceBehavior(
+ AccordionItemAppearance.block,
+ css`
+ :host {
+ margin-bottom: 2px;
+ }
+
+ .accordion-item-details > .accordion-item-summary {
+ background-color: rgba(${borderRgbPartialColor}, 0.1);
+ margin-left: 0;
+ padding-left: 0;
+ border: none;
+ }
+
+ .accordion-item-details > .accordion-item-summary:hover {
+ border: none;
+ outline: ${dividerWidth} solid ${borderHoverColor};
+ outline-offset: -1px;
+ }
+
+ .accordion-item-content {
+ margin-left: calc(${iconSize} + (2 * ${mediumPadding}));
+ }
+ `
+ )
+);
diff --git a/packages/ok-components/src/accordion-item/template.ts b/packages/ok-components/src/accordion-item/template.ts
new file mode 100644
index 0000000000..410436d24f
--- /dev/null
+++ b/packages/ok-components/src/accordion-item/template.ts
@@ -0,0 +1,31 @@
+import { html } from '@ni/fast-element';
+import { iconArrowExpanderRightTag } from '@ni/nimble-components/dist/esm/icons/arrow-expander-right';
+import type { AccordionItem } from '.';
+
+const arrowExpanderRightTag = iconArrowExpanderRightTag;
+
+export const template = html`
+ x.expanded}"
+ @toggle="${(x, c) => {
+ const details = c.event.target as HTMLDetailsElement;
+ x.expanded = details.open;
+ }}"
+ >
+
+ <${arrowExpanderRightTag}
+ class="accordion-item-icon"
+ >${arrowExpanderRightTag}>
+
+ ${x => x.header}
+
+
+
+
+
+
+`;
diff --git a/packages/ok-components/src/accordion-item/tests/accordion-item.spec.ts b/packages/ok-components/src/accordion-item/tests/accordion-item.spec.ts
new file mode 100644
index 0000000000..f45220d4be
--- /dev/null
+++ b/packages/ok-components/src/accordion-item/tests/accordion-item.spec.ts
@@ -0,0 +1,114 @@
+import { html } from '@ni/fast-element';
+import { waitForUpdatesAsync } from '@ni/nimble-components/dist/esm/testing/async-helpers';
+import { AccordionItem, accordionItemTag } from '..';
+import { AccordionItemAppearance } from '../types';
+import { fixture, type Fixture } from '../../utilities/tests/fixture';
+
+async function setup(
+ expanded = false
+): Promise> {
+ return await fixture(
+ html`<${accordionItemTag}
+ header="Test Header"
+ ?expanded="${() => expanded}"
+ >
+ Test content
+ ${accordionItemTag}>`
+ );
+}
+
+describe('AccordionItem', () => {
+ let element: AccordionItem;
+ let connect: () => Promise;
+ let disconnect: (() => Promise) | undefined;
+
+ afterEach(async () => {
+ await disconnect?.();
+ disconnect = undefined;
+ });
+
+ it('can construct an element instance', () => {
+ expect(document.createElement(accordionItemTag)).toBeInstanceOf(
+ AccordionItem
+ );
+ });
+
+ it('should have a details element in the shadow DOM', async () => {
+ ({ element, connect, disconnect } = await setup());
+ await connect();
+ const details = element.shadowRoot?.querySelector('details');
+ expect(details).not.toBeNull();
+ });
+
+ it('should display the header text', async () => {
+ ({ element, connect, disconnect } = await setup());
+ await connect();
+ const titleSpan = element.shadowRoot?.querySelector(
+ '.accordion-item-title'
+ );
+ expect(titleSpan?.textContent?.trim()).toBe('Test Header');
+ });
+
+ it('should be collapsed by default', async () => {
+ ({ element, connect, disconnect } = await setup(false));
+ await connect();
+ const details = element.shadowRoot?.querySelector('details');
+ expect(details?.open).toBeFalse();
+ });
+
+ it('should be expanded when expanded attribute is set', async () => {
+ ({ element, connect, disconnect } = await setup(true));
+ await connect();
+ const details = element.shadowRoot?.querySelector('details');
+ expect(details?.open).toBeTrue();
+ });
+
+ it('should have a slot for content', async () => {
+ ({ element, connect, disconnect } = await setup());
+ await connect();
+ const slot = element.shadowRoot?.querySelector('slot');
+ expect(slot).not.toBeNull();
+ });
+
+ it('should have an expander icon', async () => {
+ ({ element, connect, disconnect } = await setup());
+ await connect();
+ const icon = element.shadowRoot?.querySelector(
+ 'nimble-icon-arrow-expander-right'
+ );
+ expect(icon).not.toBeNull();
+ });
+
+ it('should update header when property changes', async () => {
+ ({ element, connect, disconnect } = await setup());
+ await connect();
+ element.header = 'New Header';
+ await waitForUpdatesAsync();
+ const titleSpan = element.shadowRoot?.querySelector(
+ '.accordion-item-title'
+ );
+ expect(titleSpan?.textContent?.trim()).toBe('New Header');
+ });
+
+ it('should default to outline appearance', async () => {
+ ({ element, connect, disconnect } = await setup());
+ await connect();
+ expect(element.appearance).toBe(AccordionItemAppearance.outline);
+ });
+
+ it('should reflect appearance attribute', async () => {
+ ({ element, connect, disconnect } = await setup());
+ await connect();
+ element.appearance = AccordionItemAppearance.ghost;
+ await waitForUpdatesAsync();
+ expect(element.getAttribute('appearance')).toBe('ghost');
+ });
+
+ it('should support block appearance', async () => {
+ ({ element, connect, disconnect } = await setup());
+ await connect();
+ element.appearance = AccordionItemAppearance.block;
+ await waitForUpdatesAsync();
+ expect(element.getAttribute('appearance')).toBe('block');
+ });
+});
diff --git a/packages/ok-components/src/accordion-item/types.ts b/packages/ok-components/src/accordion-item/types.ts
new file mode 100644
index 0000000000..6737bce07a
--- /dev/null
+++ b/packages/ok-components/src/accordion-item/types.ts
@@ -0,0 +1,6 @@
+export const AccordionItemAppearance = {
+ outline: 'outline',
+ ghost: 'ghost',
+ block: 'block'
+} as const;
+export type AccordionItemAppearance = (typeof AccordionItemAppearance)[keyof typeof AccordionItemAppearance];
diff --git a/packages/ok-components/src/all-components.ts b/packages/ok-components/src/all-components.ts
index a07b59ba2d..19386f03b7 100644
--- a/packages/ok-components/src/all-components.ts
+++ b/packages/ok-components/src/all-components.ts
@@ -6,5 +6,6 @@
import '@ni/spright-components/dist/esm/all-components';
+import './accordion-item';
import './button';
import './icon-dynamic';
diff --git a/packages/storybook/src/ok/accordion-item/accordion-item-matrix.stories.ts b/packages/storybook/src/ok/accordion-item/accordion-item-matrix.stories.ts
new file mode 100644
index 0000000000..17407a2e61
--- /dev/null
+++ b/packages/storybook/src/ok/accordion-item/accordion-item-matrix.stories.ts
@@ -0,0 +1,58 @@
+import type { StoryFn, Meta } from '@storybook/html-vite';
+import { html, ViewTemplate } from '@ni/fast-element';
+import { accordionItemTag } from '@ni/ok-components/dist/esm/accordion-item';
+import { AccordionItemAppearance } from '@ni/ok-components/dist/esm/accordion-item/types';
+import { textFieldTag } from '@ni/nimble-components/dist/esm/text-field';
+import {
+ createMatrix,
+ sharedMatrixParameters,
+ createMatrixThemeStory
+} from '../../utilities/matrix';
+import { createStory } from '../../utilities/storybook';
+import { hiddenWrapper } from '../../utilities/hidden';
+
+const expandedStates = [
+ ['Expanded', true],
+ ['Collapsed', false]
+] as const;
+type ExpandedState = (typeof expandedStates)[number];
+
+const appearanceStates = [
+ ['Ghost', AccordionItemAppearance.ghost],
+ ['Outline', AccordionItemAppearance.outline],
+ ['Block', AccordionItemAppearance.block]
+] as const;
+type AppearanceState = (typeof appearanceStates)[number];
+
+const metadata: Meta = {
+ title: 'Tests Ok/Accordion Item',
+ parameters: {
+ ...sharedMatrixParameters()
+ }
+};
+
+export default metadata;
+
+const component = (
+ [expandedName, expanded]: ExpandedState,
+ [appearanceName, appearance]: AppearanceState
+): ViewTemplate => html`
+ <${accordionItemTag}
+ header="${() => `${appearanceName} ${expandedName}`}"
+ ?expanded="${() => expanded}"
+ appearance="${() => appearance}"
+ style="margin-right: 8px; margin-bottom: 8px; width: 300px;">
+ <${textFieldTag} placeholder="Enter name" appearance="underline">${textFieldTag}>
+ <${textFieldTag} placeholder="Enter category" appearance="underline">${textFieldTag}>
+ ${accordionItemTag}>
+`;
+
+export const themeMatrix: StoryFn = createMatrixThemeStory(
+ createMatrix(component, [expandedStates, appearanceStates])
+);
+
+export const hidden: StoryFn = createStory(
+ hiddenWrapper(
+ html`<${accordionItemTag} hidden header="Hidden">Content${accordionItemTag}>`
+ )
+);
diff --git a/packages/storybook/src/ok/accordion-item/accordion-item.mdx b/packages/storybook/src/ok/accordion-item/accordion-item.mdx
new file mode 100644
index 0000000000..d80e310535
--- /dev/null
+++ b/packages/storybook/src/ok/accordion-item/accordion-item.mdx
@@ -0,0 +1,65 @@
+import { Controls, Canvas, Meta, Title } from '@storybook/addon-docs/blocks';
+import * as accordionItemStories from './accordion-item.stories';
+import ComponentApisLink from '../../docs/component-apis-link.mdx';
+
+
+
+
+An accordion item is a collapsible section that can be expanded or collapsed
+to show or hide its content. It uses a header with an expander icon to toggle
+visibility.
+
+
+
+## API
+
+
+
+
+## Appearance
+
+### Ghost
+
+No borders or background. Use for nested accordions or minimal visual chrome.
+
+
+
+### Outline
+
+A bottom border divider between items. This is the default appearance.
+
+
+
+### Block
+
+A filled background on each header row.
+
+
+
+## Examples
+
+### Outline Group
+
+Multiple outline accordion items used together as a form layout (e.g. checkout flow).
+
+
+
+### Block Group
+
+Multiple block accordion items for settings or configuration panels.
+
+
+
+### Nested Accordion
+
+Accordion items nested inside other accordion items. Per the design spec, nested
+accordions should always use the ghost appearance.
+
+
+
+### Mixed Content
+
+Accordion items containing various types of interactive content — checkboxes,
+selects, and number fields.
+
+
diff --git a/packages/storybook/src/ok/accordion-item/accordion-item.stories.ts b/packages/storybook/src/ok/accordion-item/accordion-item.stories.ts
new file mode 100644
index 0000000000..3d43c10cdc
--- /dev/null
+++ b/packages/storybook/src/ok/accordion-item/accordion-item.stories.ts
@@ -0,0 +1,213 @@
+import type { Meta, StoryObj } from '@storybook/html-vite';
+import { html } from '@ni/fast-element';
+import { accordionItemTag } from '@ni/ok-components/dist/esm/accordion-item';
+import { AccordionItemAppearance } from '@ni/ok-components/dist/esm/accordion-item/types';
+import { textFieldTag } from '@ni/nimble-components/dist/esm/text-field';
+import { numberFieldTag } from '@ni/nimble-components/dist/esm/number-field';
+import { checkboxTag } from '@ni/nimble-components/dist/esm/checkbox';
+import { selectTag } from '@ni/nimble-components/dist/esm/select';
+import { listOptionTag } from '@ni/nimble-components/dist/esm/list-option';
+import {
+ apiCategory,
+ createUserSelectedThemeStory
+} from '../../utilities/storybook';
+
+interface AccordionItemArgs {
+ header: string;
+ expanded: boolean;
+ appearance: AccordionItemAppearance;
+}
+
+const metadata: Meta = {
+ title: 'Ok/Accordion Item',
+ parameters: {
+ actions: {}
+ },
+ render: createUserSelectedThemeStory(html`
+ <${accordionItemTag}
+ header="${x => x.header}"
+ ?expanded="${x => x.expanded}"
+ appearance="${x => x.appearance}"
+ >
+ <${textFieldTag} placeholder="Enter name" appearance="underline">${textFieldTag}>
+ <${textFieldTag} placeholder="Enter category" appearance="underline">${textFieldTag}>
+ ${accordionItemTag}>
+ `),
+ argTypes: {
+ header: {
+ description:
+ 'The text displayed in the accordion item header.',
+ table: { category: apiCategory.attributes }
+ },
+ expanded: {
+ description:
+ 'Controls whether the accordion item content is visible.',
+ table: { category: apiCategory.attributes }
+ },
+ appearance: {
+ description:
+ 'The visual appearance of the accordion item: ghost, outline, or block.',
+ options: Object.values(AccordionItemAppearance),
+ control: { type: 'radio' },
+ table: { category: apiCategory.attributes }
+ }
+ },
+ args: {
+ header: 'Expanded Accordion',
+ expanded: true,
+ appearance: AccordionItemAppearance.outline
+ }
+};
+
+export default metadata;
+
+export const ghost: StoryObj = {
+ args: {
+ header: 'Ghost Accordion',
+ expanded: true,
+ appearance: AccordionItemAppearance.ghost
+ }
+};
+
+export const outline: StoryObj = {
+ args: {
+ header: 'Outline Accordion',
+ expanded: true,
+ appearance: AccordionItemAppearance.outline
+ }
+};
+
+export const block: StoryObj = {
+ args: {
+ header: 'Block Accordion',
+ expanded: true,
+ appearance: AccordionItemAppearance.block
+ }
+};
+
+export const outlineGroup: StoryObj = {
+ render: createUserSelectedThemeStory(html`
+
+ <${accordionItemTag} header="Shipping" appearance="${x => x.appearance}" expanded>
+ <${textFieldTag} appearance="underline">Address${textFieldTag}>
+ <${textFieldTag} appearance="underline">City${textFieldTag}>
+ <${selectTag}>
+ State
+ <${listOptionTag} value="CA">California${listOptionTag}>
+ <${listOptionTag} value="NY">New York${listOptionTag}>
+ <${listOptionTag} value="TX">Texas${listOptionTag}>
+ ${selectTag}>
+ ${accordionItemTag}>
+ <${accordionItemTag} header="Payment" appearance="${x => x.appearance}">
+ <${textFieldTag} appearance="underline">Card number${textFieldTag}>
+ <${numberFieldTag} appearance="underline">CVV${numberFieldTag}>
+ ${accordionItemTag}>
+ <${accordionItemTag} header="Delivery" appearance="${x => x.appearance}">
+ <${checkboxTag}>Express shipping${checkboxTag}>
+ <${checkboxTag}>Gift wrapping${checkboxTag}>
+ ${accordionItemTag}>
+
+ `),
+ args: {
+ appearance: AccordionItemAppearance.outline
+ },
+ argTypes: {
+ header: { control: false, table: { disable: true } },
+ expanded: { control: false, table: { disable: true } }
+ }
+};
+
+export const blockGroup: StoryObj = {
+ render: createUserSelectedThemeStory(html`
+
+ <${accordionItemTag} header="General" appearance="${x => x.appearance}" expanded>
+ <${textFieldTag} appearance="underline">Project name${textFieldTag}>
+ <${textFieldTag} appearance="underline">Description${textFieldTag}>
+ ${accordionItemTag}>
+ <${accordionItemTag} header="Configuration" appearance="${x => x.appearance}">
+ <${checkboxTag}>Enable notifications${checkboxTag}>
+ <${checkboxTag}>Auto-save${checkboxTag}>
+ ${accordionItemTag}>
+ <${accordionItemTag} header="Advanced" appearance="${x => x.appearance}">
+ <${numberFieldTag} appearance="underline">Timeout (ms)${numberFieldTag}>
+ <${numberFieldTag} appearance="underline">Max retries${numberFieldTag}>
+ ${accordionItemTag}>
+
+ `),
+ args: {
+ appearance: AccordionItemAppearance.block
+ },
+ argTypes: {
+ header: { control: false, table: { disable: true } },
+ expanded: { control: false, table: { disable: true } }
+ }
+};
+
+export const nestedAccordion: StoryObj = {
+ render: createUserSelectedThemeStory(html`
+
+ <${accordionItemTag} header="Versioning" appearance="${x => x.appearance}" expanded>
+ <${accordionItemTag} header="GET / API information" appearance="ghost" expanded>
+
Returns information about API versions and available operations.
+ ${accordionItemTag}>
+ <${accordionItemTag} header="GET / v2 API version information" appearance="ghost">
+
Returns details for the v2 API endpoint.
+ ${accordionItemTag}>
+ ${accordionItemTag}>
+ <${accordionItemTag} header="Subscriptions" appearance="${x => x.appearance}">
+ <${accordionItemTag} header="List subscriptions" appearance="ghost">
+
Returns all active subscriptions.
+ ${accordionItemTag}>
+ <${accordionItemTag} header="Create subscription" appearance="ghost">
+ <${textFieldTag} appearance="underline">Name${textFieldTag}>
+ ${accordionItemTag}>
+ ${accordionItemTag}>
+ <${accordionItemTag} header="Tags" appearance="${x => x.appearance}">
+ <${checkboxTag}>Include archived${checkboxTag}>
+ ${accordionItemTag}>
+
+ `),
+ args: {
+ appearance: AccordionItemAppearance.outline
+ },
+ argTypes: {
+ header: { control: false, table: { disable: true } },
+ expanded: { control: false, table: { disable: true } }
+ }
+};
+
+export const mixedContent: StoryObj = {
+ render: createUserSelectedThemeStory(html`
+
+ <${accordionItemTag} header="Size" appearance="${x => x.appearance}" expanded>
+
+ <${checkboxTag}>XS${checkboxTag}>
+ <${checkboxTag}>S${checkboxTag}>
+ <${checkboxTag}>M${checkboxTag}>
+ <${checkboxTag}>L${checkboxTag}>
+ <${checkboxTag}>XL${checkboxTag}>
+ <${checkboxTag}>XXL${checkboxTag}>
+
+ ${accordionItemTag}>
+ <${accordionItemTag} header="Color" appearance="${x => x.appearance}">
+ <${selectTag}>
+ Color
+ <${listOptionTag} value="red">Red${listOptionTag}>
+ <${listOptionTag} value="blue">Blue${listOptionTag}>
+ <${listOptionTag} value="green">Green${listOptionTag}>
+ ${selectTag}>
+ ${accordionItemTag}>
+ <${accordionItemTag} header="Price" appearance="${x => x.appearance}">
+ <${numberFieldTag} appearance="underline">Min${numberFieldTag}>
+ <${numberFieldTag} appearance="underline">Max${numberFieldTag}>
+ ${accordionItemTag}>
+
+ `),
+ args: {
+ appearance: AccordionItemAppearance.outline
+ },
+ argTypes: {
+ header: { control: false, table: { disable: true } },
+ expanded: { control: false, table: { disable: true } }
+ }
+};