@cfpb/cfpb-design-system
Version:
CFPB's UI framework
143 lines (119 loc) • 4.14 kB
text/typescript
/// <reference types="vite/client" />
import type { Meta, StoryObj } from '@storybook/web-components-vite';
import { fn, userEvent, expect } from 'storybook/test';
import { getStorybookHelpers } from '@wc-toolkit/storybook-helpers';
import type { CfpbExpandableProps } from '../../../../../storybook/custom-elements-types';
import { CfpbExpandable } from './index.js';
CfpbExpandable.init();
/**
* `properties` is excluded here. See `cfpb-button.stories.ts` for the reason.
* Specifically here `isExpanded` and `open` both describe once piece of state.
* `template()` binds both applying the prop _after_ the attribute. Leaving them in
* lets `isExpanded` default collapse the Expanded story. The prop still works on the
* element. See the behavior tests.....
*/
const { args, argTypes, template } = getStorybookHelpers<CfpbExpandableProps>(
'cfpb-expandable',
{ excludeCategories: ['methods', 'properties'] },
);
type ExpandableStoryArgs = CfpbExpandableProps & {
'header-slot'?: string;
'content-slot'?: string;
};
const meta: Meta<ExpandableStoryArgs> = {
title: 'Web Components/cfpb-expandable',
component: 'cfpb-expandable',
tags: ['autodocs'],
args: {
...args,
'header-slot': 'Section header',
'content-slot': '<p>Expandable section content</p>',
},
argTypes,
render: (args) => template(args),
};
export default meta;
type Story = StoryObj<ExpandableStoryArgs>;
export const Default: Story = {
args: { open: false },
};
export const ExpandBegin: Story = {
tags: ['!dev', '!autodocs'],
play: async ({ canvasElement }) => {
const expandable = canvasElement.querySelector(
'cfpb-expandable',
) as CfpbExpandable;
const spy = fn();
expandable.addEventListener('expandbegin', spy);
const button = expandable.shadowRoot!.querySelector('button')!;
await userEvent.click(button);
await expect(spy).toHaveBeenCalledOnce();
},
};
export const Expanded: Story = {
args: { open: true },
};
export const CollapseOnClick: Story = {
tags: ['!dev', '!autodocs'],
args: { open: true },
play: async ({ canvasElement }) => {
const expandable = canvasElement.querySelector(
'cfpb-expandable',
) as CfpbExpandable;
const spy = fn();
expandable.addEventListener('collapseend', spy);
const button = expandable.shadowRoot!.querySelector('button')!;
const content = expandable.shadowRoot!.querySelector(
'.o-expandable__content',
)!;
await userEvent.click(button);
// BaseTransition registers on 'webketTransitionEnd' in Chromium (first match
// in its prefix map), so the synthetic event must use that name (not transitioned)
content.dispatchEvent(
new TransitionEvent('webkitTransitionEnd', {
propertyName: 'max-height',
}),
);
await expect(spy).toHaveBeenCalledOnce();
},
};
/**
* Programatic expand/collapse is covered in index.spec.js
* The event tests below stay here because they are driven
* by user interaction.
*/
export const ExpandEnd: Story = {
tags: ['!dev', '!autodocs'],
play: async ({ canvasElement }) => {
const expandable = canvasElement.querySelector(
'cfpb-expandable',
) as CfpbExpandable;
const spy = fn();
expandable.addEventListener('expandend', spy);
const button = expandable.shadowRoot!.querySelector('button')!;
const content = expandable.shadowRoot!.querySelector(
'.o-expandable__content',
)!;
await userEvent.click(button);
content.dispatchEvent(
new TransitionEvent('webkitTransitionEnd', {
propertyName: 'max-height',
}),
);
await expect(spy).toHaveBeenCalledOnce();
},
};
export const CollapseBegin: Story = {
tags: ['!dev', '!autodocs'],
args: { open: true },
play: async ({ canvasElement }) => {
const expandable = canvasElement.querySelector(
'cfpb-expandable',
) as CfpbExpandable;
const spy = fn();
expandable.addEventListener('collapsebegin', spy);
const button = expandable.shadowRoot!.querySelector('button')!;
await userEvent.click(button);
await expect(spy).toHaveBeenCalledOnce();
},
};