diff --git a/packages/documentation-v7/.storybook/preview.ts b/packages/documentation-v7/.storybook/preview.ts index ec8aa24698..df93e97217 100644 --- a/packages/documentation-v7/.storybook/preview.ts +++ b/packages/documentation-v7/.storybook/preview.ts @@ -1,15 +1,15 @@ import type { Preview } from '@storybook/web-components'; -import DocsLayout from './blocks/layout'; -import { format } from 'prettier'; -import { badgesConfig, prettierOptions, resetComponents } from './helpers'; -import './helpers/register-web-components'; import { - extractArgTypesFactory, + extractArgTypes, extractComponentDescription, setStencilDocJson, } from '@pxtrn/storybook-addon-docs-stencil'; import { StencilJsonDocs } from '@pxtrn/storybook-addon-docs-stencil/dist/types'; +import { format } from 'prettier'; +import DocsLayout from './blocks/layout'; +import { badgesConfig, prettierOptions, resetComponents } from './helpers'; +import './helpers/register-web-components'; import './styles/preview.scss'; import themes from './styles/themes'; @@ -58,7 +58,7 @@ const preview: Preview = { transform: (snippet: string) => format(snippet, prettierOptions), }, components: resetComponents, - extractArgTypes: extractArgTypesFactory({ dashCase: true }), + extractArgTypes, extractComponentDescription, }, actions: { argTypesRegex: '^on[A-Z].*' }, diff --git a/packages/documentation-v7/src/stories/components/collapsible/collapsible.demo.stories.ts b/packages/documentation-v7/src/stories/components/collapsible/collapsible.demo.stories.ts new file mode 100644 index 0000000000..be2718b37f --- /dev/null +++ b/packages/documentation-v7/src/stories/components/collapsible/collapsible.demo.stories.ts @@ -0,0 +1,115 @@ +import { spread } from '@open-wc/lit-helpers'; +import { useArgs } from '@storybook/preview-api'; +import { Meta, StoryContext, StoryObj } from '@storybook/web-components'; +import { html } from 'lit'; +import { unsafeHTML } from 'lit/directives/unsafe-html.js'; + +import { definedProperties } from '../../../utils'; + +const meta: Meta = { + title: 'Hidden/demos/components/Collapsible', + component: 'post-collapsible', + args: { + innerHTML: `Titulum

Contentus momentus vero siteos et accusam iretea et justo.

`, + }, + argTypes: { + innerHTML: { + description: + 'Defines the HTML markup contained in the collapsible.
' + + 'Elements with a `slot="header"` attribute are displayed in the header while others are shown in the body.', + table: { + category: 'content', + type: { + summary: 'string', + }, + }, + }, + }, + render: (args, context) => defaultRender(args, context), +}; + +export default meta; + +type Story = StoryObj; + +function defaultRender( + args: HTMLPostCollapsibleElement, + context: StoryContext, +) { + const hasHeader = args.innerHTML.indexOf('slot="header"') > -1; + const collapsibleId = `collapsible-example--${context.name.replace(/ /g, '-').toLowerCase()}`; + + const collapsibleProperties = definedProperties({ + 'collapsed': args.collapsed, + 'heading-level': args.headingLevel, + 'id': hasHeader ? undefined : collapsibleId, + }); + + const collapsibleComponent = html` + + ${unsafeHTML(args.innerHTML)} + + `; + + const [currentArgs, updateArgs] = useArgs(); + + const toggleCollapse = (open?: boolean) => { + const collapsible = document.querySelector(`#${collapsibleId}`) as HTMLPostCollapsibleElement; + collapsible.toggle(open).then((isOpen: boolean) => { + if (typeof currentArgs.collapsed !== 'undefined') updateArgs({ collapsed: !isOpen }); + }); + }; + + const togglers = [ + ['Toggle', () => toggleCollapse()], + ['Show', () => toggleCollapse(true)], + ['Hide', () => toggleCollapse(false)], + ]; + + const togglersHtml = html` +
+ ${togglers.map( + ([label, listener]) => + html` + + `, + )} +
+ `; + + return html` + ${hasHeader ? null : togglersHtml} ${collapsibleComponent} + `; +} + +export const Default: Story = {}; + +export const InitiallyCollapsed: Story = { + args: { collapsed: true }, +}; + +export const HeadingLevel: Story = { + args: { headingLevel: 6 }, +}; + +export const IntricateContent: Story = { + args: { + innerHTML: `

I am part of the body

+ Customus Titulum +  - I am part of the header +

I am part of the body too!

`, + }, +}; + +export const CustomTrigger: Story = { + args: { + innerHTML: `

Contentus momentus vero siteos et accusam iretea et justo.

`, + }, +}; diff --git a/packages/documentation-v7/src/stories/components/collapsible/collapsible.docs.mdx b/packages/documentation-v7/src/stories/components/collapsible/collapsible.docs.mdx new file mode 100644 index 0000000000..36ffe825aa --- /dev/null +++ b/packages/documentation-v7/src/stories/components/collapsible/collapsible.docs.mdx @@ -0,0 +1,61 @@ +import { Canvas, Controls, Meta } from '@storybook/blocks'; +import { BADGE } from '../../../../.storybook/constants'; +import * as CollapsibleStories from './collapsible.demo.stories'; + + + +# Collapsible + +

Toggle the visibility of content across your project.

+ +The `` component is used to show and hide content. +Collapsing an element will animate the height from its current value to 0. + + + + +## Examples + +The following examples show different use cases for the `` component. + +### Initially Collapsed + +To make the collapsible content hidden by default, just use the `collapsible="true"` property. + + + +### Heading Level + +Use the `heading-level` property to define the hierarchical level of the collapsible header within the structure of a document. + + + +### Intricate Content + +The collapsible component uses [HTML slot elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot) +to allow any HTML content to be used as part of its header and body.
+By default, all children are appended to the component's body. +For a specific child to be displayed in the header, a `slot="header"` attribute must be added. + + + +### Custom Trigger + +The `` component exposes a `.toggle()` method that allows to trigger the collapse programmatically. +This method is asynchronous and returns a promise that resolves with the current open state. +It optionally takes a boolean parameter that forces open when `true` or close when `false`. + +```typescript +const collapsible = document.querySelector('#collapsibleId') as HTMLPostCollapsibleElement; +collapsible.toggle(/* open: boolean */).then((/* isOpen: boolean */) => {}); +``` + +A header is then not mandatory and can be replaced by an external control. + +In this case, to ensure good accessibility, identify the collapsible with an `id`, +then add an `aria-controls` attribute to your control element referencing this `id`. +Also make sure to add an `aria-expanded` attribute to the control element: +if the collapsible element is closed, the attribute on the control element must have a value of `aria-expanded="false"` +and `aria-expanded="true"` otherwise. + +