diff --git a/.changeset/poor-walls-occur.md b/.changeset/poor-walls-occur.md new file mode 100644 index 0000000000..a4e3819430 --- /dev/null +++ b/.changeset/poor-walls-occur.md @@ -0,0 +1,5 @@ +--- +"@primer/css": minor +--- + +ActionList Component: Primer CSS Implementation. Adding a new component `ActionList` to learn more checkout the docs [https://primer.style/css/components/action-list](https://primer.style/css/components/action-list). diff --git a/docs/content/components/action-list.md b/docs/content/components/action-list.md new file mode 100644 index 0000000000..79e7e425ee --- /dev/null +++ b/docs/content/components/action-list.md @@ -0,0 +1,482 @@ +--- +title: Action List +path: components/action-list +status: alpha +source: 'https://github.com/primer/css/tree/main/src/actionlist' +bundle: action-list +storybook: https://primer.style/css/storybook/?path=/story/components-actionlist-actionlistitem--playground +--- + +Reference the [Action list interface guidelines](https://primer.style/design/components/action-list) for details on where and how to use Action List. + +## Accessibility +### Semantic markup + +The markup for Action List changes depending on the intended use case. + +In all cases, the basic structure is as follows: + +```html + +``` + +Pay close attention to `role` attributes throughout the documentation. The `role` attribute may change depending on the context in which Action List is used. Some common use case specs: + +[Menu](https://www.w3.org/TR/wai-aria-practices-1.1/#menu) + +[Multi/Single Select Menu](https://www.w3.org/TR/wai-aria-practices-1.1/examples/menubar/menubar-2/menubar-2.html) + +[Multi/Single Select List](https://www.w3.org/TR/wai-aria-practices-1.1/#Listbox) + +Note: JS is required to make Action List accessible in most cases + +## Action List + +Action List is a `ul` list designed to contain Action List Items. + +### Arguments + +| Class | Description | +| :- | :- | +| `ActionList` | Default styles | +| `ActionList--divided` | Show dividers between items | +| `ActionList--subGroup` | If Action List is nested as a sub-list | + +#### Default + +```html live + +``` + +#### Item dividers + +```html live + +``` + +#### Nested sub list + +```html live + +``` + + +## Action List Divider + +List item `li` for separating groups of content + +### Arguments + +| Class | Description | +| :- | :- | +| `ActionList-sectionDivider` | Default subtle divider line | +| `ActionList-sectionDivider--filled` | Thicker divider line | +| `ActionList-item-description` | Optional section header secondary text | + +#### Default + +```html live + +``` + +#### Filled + +```html live + +``` +### Divider with label text + +When using a section label for a group, give the `li` an id to be referenced by the group `ul` +#### Filled with section label + +```html live + +``` + +#### Default with section label + +```html live + +``` + +#### Default with section label + description + +```html live + +``` + +## Action List Item + +List item `li` handling semantics, state and interactions + +### Arguments + +| Class | Description | +| :- | :- | +| `ActionList-item` | Default styles | +| `ActionList-item--hasSubItem` | Item contains a sub item `ul` | +| `ActionList-item--subItem` | Indent + small font size for sub item `li` (optional) | +| `ActionList-item--navActive` | Nav item and `aria-current` | +| `ActionList-item--danger` | Item is destructive | + +Kitchen sink + +```html live + + +``` + +## Action List Item Content + +Contains and places all child content within Action List Item. Can be either an `a href` tag for list link items, or a `span` for items that provide an event on Action List Item `li`. + +### Arguments + +| Class | Description | +| :- | :- | +| `ActionList-content` | Defines the overall layout grid | +| `ActionList-content--sizeMedium` | 40px row height | +| `ActionList-content--sizeLarge` | 48px row height, default for touch devices | +| `ActionList-content--visual16` | Creates left padding for sub list if leading visual exists | +| `ActionList-content--visual20` | Creates left padding for sub list if leading visual exists | +| `ActionList-content--visual24` | Creates left padding for sub list if leading visual exists | +| `ActionList-item-action` | min-height + default styles for visual slot | +| `ActionList-item-action--leading` | Slot: multi/single select | +| `ActionList-item-action--trailing` | Slot: Button, collapse icon | +| `ActionList-item-visual` | min-height + default styles for visual slot | +| `ActionList-item-visual--leading` | Slot: SVG or graphic like Avatar | +| `ActionList-item-visual--trailing` | Slot: SVG or text | +| `ActionList-item-label` | Item text | +| `ActionList-item-descriptionWrap` | Wraps label/description | +| `ActionList-item-descriptionWrap--inline` | Display description inline with label | +| `ActionList-item-description` | Item description (block by default) | + +### Basic text only item + +```html live + +``` + +### Size (all options) + +```html live + + +``` + +### Visuals (all options- leading & trailing) + +```html live + +``` + +### Trailing visual as text + +```html live + +``` + +### Inline description + +```html live + +``` + +### Active navigational item + +```html live + +``` + +### Danger item + +```html live + +``` + +### Actions + +### Leading action: single select + +```html live + +``` + +### Leading action: multi select + +```html live + +``` + +### Trailing action: collapse + +```html live + +``` diff --git a/docs/src/@primer/gatsby-theme-doctocat/nav.yml b/docs/src/@primer/gatsby-theme-doctocat/nav.yml index f216a0372c..fe4ed60819 100644 --- a/docs/src/@primer/gatsby-theme-doctocat/nav.yml +++ b/docs/src/@primer/gatsby-theme-doctocat/nav.yml @@ -55,6 +55,8 @@ - title: Components url: /components children: + - title: Action List + url: /components/action-list - title: Alerts url: /components/alerts - title: Autocomplete diff --git a/docs/src/stories/components/ActionList/Accessibility.stories.mdx b/docs/src/stories/components/ActionList/Accessibility.stories.mdx new file mode 100644 index 0000000000..9732e3c5e2 --- /dev/null +++ b/docs/src/stories/components/ActionList/Accessibility.stories.mdx @@ -0,0 +1,180 @@ +import {Meta, Story, Canvas} from '@storybook/addon-docs' + + + +### Action List + +An action list is a vertical list of interactive actions or options. + +| | | +| :------ | :------------------------------------ | +| Actions | links `a href` or events `onclick` | +| Options | checkbox role `checked` or `selected` | + +### Semantic markup + +The markup for Action List changes depending on the intended use case. + +In all cases, the basic structure is as follows: + +```html + +``` + +Pay close attention to `role` attributes throughout the documentation, and find the use case that best suits your needs. + +### Menu + +[Menu spec](https://www.w3.org/TR/wai-aria-practices-1.1/#menu) + +Note: JS is required for to provide keyboard handling along with [tab-index](https://www.w3.org/TR/wai-aria-practices-1.1/#kbd_roving_tabindex) + +| Element | Role | +| :------ | :------------------------------ | +| `ul` | `role="menu"` | +| `li` | no child link `role="menuitem"` | +| `li` | has child link `role="none"` | +| `a` | `role="menuitem"` | + +#### Example + + + + + +### Navigational menu + +[Menu spec](https://www.w3.org/TR/wai-aria-practices-1.1/#menu) + +Note: JS is required for to provide keyboard handling along with [tab-index](https://www.w3.org/TR/wai-aria-practices-1.1/#kbd_roving_tabindex) + +| Element | Role | +| :------ | :--------------------------------------- | +| `nav` | no role needed | +| `ul` | `role="menu"` | +| `li` | no child link `role="menuitem"` | +| `li` | has child link `role="none"` | +| `li` | nested menu `aria-haspopup="true"` | +| `li` | nested menu `aria-expanded="true/false"` | +| `a` | `role="menuitem"` | + +#### Example + + + + + +### Multi select menu + +[Menu spec](https://www.w3.org/TR/wai-aria-practices-1.1/examples/menubar/menubar-2/menubar-2.html) + +Note: JS is required for to provide keyboard handling along with [tab-index](https://www.w3.org/TR/wai-aria-practices-1.1/#kbd_roving_tabindex) + +| Element | Role | +| :------ | :-------------------------------------- | +| `ul` | `role="menu"` | +| `li` | no child link `role="menuitemcheckbox"` | +| `li` | `aria-checked="true/false"` | + +#### Example + + + + + +### Single select menu + +[Menu spec](https://www.w3.org/TR/wai-aria-practices-1.1/examples/menubar/menubar-2/menubar-2.html) + +Note: JS is required for to provide keyboard handling along with [tab-index](https://www.w3.org/TR/wai-aria-practices-1.1/#kbd_roving_tabindex) + +| Element | Role | +| :------ | :----------------------------------- | +| `ul` | `role="menu"` | +| `li` | no child link `role="menuitemradio"` | +| `li` | `aria-checked="true/false"` | + +#### Example + + + + + +### Multi select listbox + +[Menu spec](https://www.w3.org/TR/wai-aria-practices-1.1/#Listbox) + +Note: JS is required for to provide [keyboard handling](https://www.w3.org/TR/wai-aria-practices-1.1/#listbox_kbd_interaction) + +| Element | Role | +| :------ | :---------------------------- | +| `ul` | `role="listbox"` | +| `ul` | `aria-multiselectable="true"` | +| `li` | `role="option"` | +| `li` | `aria-selected="true/false"` | + +#### Example + + + + + +### Single select listbox + +[Menu spec](https://www.w3.org/TR/wai-aria-practices-1.1/#Listbox) + +Note: JS is required for to provide [keyboard handling](https://www.w3.org/TR/wai-aria-practices-1.1/#listbox_kbd_interaction) + +| Element | Role | +| :------ | :--------------------------- | +| `ul` | `role="listbox"` | +| `li` | `role="option"` | +| `li` | `aria-selected="true/false"` | + +#### Example + + + + + +### List of links + +No roles needed + +#### Example + + + + + +### Dividers + +| Element | Role | +| :------ | :----------------- | +| `li` | `role="separator"` | + +#### Empty example + + + + + +| Element | Role | +| :------ | :------------------------------------------ | +| `li` | `role="presentation"` | +| `li` | id for nested group `id="group-id"` | +| `li` | if it has id for group `aria-hidden="true"` | + +#### With title example + + + + diff --git a/docs/src/stories/components/ActionList/ActionList.stories.jsx b/docs/src/stories/components/ActionList/ActionList.stories.jsx new file mode 100644 index 0000000000..2b6bd6be73 --- /dev/null +++ b/docs/src/stories/components/ActionList/ActionList.stories.jsx @@ -0,0 +1,113 @@ +import React from 'react' +import clsx from 'clsx' +import {ListItemTemplate} from './ActionListItem.stories' + +export default { + title: 'Components/ActionList', + excludeStories: ['ListTemplate'], + parameters: { + design: { + type: 'figma', + url: 'https://www.figma.com/file/oMiRuexZW6gqVbMhQd6lwP/Storybook-Docs?node-id=23%3A30843' + } + }, + argTypes: { + showDividers: { + control: {type: 'boolean'}, + description: 'Show dividers between items', + table: { + category: 'CSS' + } + }, + role: { + options: [0, 1, 2, 3, 4, 5, 6], // iterator + mapping: ['menu', 'group', 'listbox', 'menubar', 'none', 'radiogroup', 'list'], // values + control: { + type: 'select', + labels: ['menu', 'group', 'listbox', 'menubar', 'none', 'radiogroup', 'list'] + }, + description: 'Semantic list role', + table: { + category: 'HTML' + } + }, + ariaLabel: { + name: 'ariaLabel', + type: 'string', + description: 'Descriptive label for menu contents', + table: { + category: 'HTML' + } + }, + ariaLabelledBy: { + name: 'ariaLabelledBy', + type: 'string', + description: 'Reference ID of section divider', + table: { + category: 'HTML' + } + }, + groupId: { + name: 'groupId', + type: 'string', + description: 'Menu group id', + table: { + category: 'HTML' + } + }, + children: { + table: { + category: 'HTML' + } + }, + subGroup: { + control: {type: 'boolean'}, + description: 'If ActionList is nested within an ActionList', + table: { + category: 'CSS' + } + }, + listboxMultiSelect: { + name: 'listboxMultiSelect', + type: 'string', + description: 'If ActionList has listbox role + multiselect children', + table: { + category: 'HTML' + } + } + } +} + +export const ListTemplate = ({ + showDividers, + children, + role, + ariaLabel, + ariaLabelledBy, + subGroup, + listboxMultiSelect +}) => ( + +) + +export const Playground = ListTemplate.bind({}) +Playground.args = { + role: 'menu', + ariaLabel: 'Menu description', + subGroup: false, + showDividers: false, + children: ( + <> + + + + ) +} diff --git a/docs/src/stories/components/ActionList/ActionListDivider.stories.jsx b/docs/src/stories/components/ActionList/ActionListDivider.stories.jsx new file mode 100644 index 0000000000..fe4df8d7be --- /dev/null +++ b/docs/src/stories/components/ActionList/ActionListDivider.stories.jsx @@ -0,0 +1,81 @@ +import React from 'react' +import clsx from 'clsx' + +export default { + title: 'Components/ActionList/ActionListDivider', + excludeStories: ['DividerTemplate'], + parameters: { + design: { + type: 'figma', + url: 'https://www.figma.com/file/oMiRuexZW6gqVbMhQd6lwP/Storybook?node-id=2%3A2' + } + }, + argTypes: { + variant: { + options: [0, 1], // iterator + mapping: ['', 'ActionList-sectionDivider--filled'], // values + control: { + type: 'select', + labels: ['subtle', 'filled'] + }, + table: { + category: 'CSS' + } + }, + title: { + defaultValue: '', + type: 'string', + name: 'title', + description: 'string', + table: { + category: 'HTML' + } + }, + description: { + defaultValue: '', + type: 'string', + name: 'description', + description: 'string', + table: { + category: 'HTML' + } + }, + id: { + defaultValue: '', + type: 'string', + name: 'id', + description: 'Used for aria-labelledby', + table: { + category: 'HTML' + } + } + }, + decorators: [ + Story => ( + + ) + ] +} + +export const DividerTemplate = ({title, description, variant, id}) => ( + <> +
  • + {title} + {description && {description}} +
  • + +) + +export const Playground = DividerTemplate.bind({}) +Playground.args = { + title: 'Section title', + description: 'Section description', + variant: 'subtle' +} diff --git a/docs/src/stories/components/ActionList/ActionListItem.stories.jsx b/docs/src/stories/components/ActionList/ActionListItem.stories.jsx new file mode 100644 index 0000000000..01cb8eb0cb --- /dev/null +++ b/docs/src/stories/components/ActionList/ActionListItem.stories.jsx @@ -0,0 +1,440 @@ +import React from 'react' +import clsx from 'clsx' +import useToggle from '../../helpers/useToggle.jsx' + +export default { + title: 'Components/ActionList/ActionListItem', + parameters: { + design: { + type: 'figma', + url: 'https://www.figma.com/file/oMiRuexZW6gqVbMhQd6lwP/Storybook-Docs?node-id=23%3A30843' + } + }, + excludeStories: ['ListItemTemplate'], + argTypes: { + size: { + options: [0, 1, 2], // iterator + mapping: ['', 'ActionList-content--sizeMedium', 'ActionList-content--sizeLarge'], // values + control: { + type: 'select', + labels: ['default', 'medium', 'large'] + }, + description: 'small (default), medium, large', + defaultValue: '', + table: { + category: 'CSS' + } + }, + variant: { + options: [0, 1, 2], // iterator + mapping: ['', 'ActionList-item--danger'], // values + control: { + type: 'select', + labels: ['default', 'danger'] + }, + defaultValue: '', + table: { + category: 'CSS' + } + }, + subItem: { + defaultValue: false, + control: {type: 'boolean'}, + table: { + category: 'CSS' + } + }, + containsSubItem: { + defaultValue: false, + control: {type: 'boolean'}, + table: { + category: 'CSS' + } + }, + leadingVisual: { + defaultValue: '', + name: 'leadingVisual', + type: 'string', + description: 'Paste [Octicon](https://primer.style/octicons/) in control field', + table: { + category: 'HTML' + } + }, + leadingVisualSize: { + options: [0, 1, 2], // iterator + mapping: ['ActionList-content--visual16', 'ActionList-content--visual20', 'ActionList-content--visual24'], // values + control: { + type: 'select', + labels: ['16px', '20px', '24px'] + }, + description: 'leading visual width', + defaultValue: 'ActionList-content--visual16', + table: { + category: 'CSS' + } + }, + trailingVisual: { + defaultValue: '', + name: 'trailingVisual', + type: 'string', + description: 'Paste [Octicon](https://primer.style/octicons/) in control field', + table: { + category: 'HTML' + } + }, + text: { + defaultValue: 'Item label', + type: 'string', + name: 'text', + description: 'string', + table: { + category: 'HTML' + } + }, + href: { + defaultValue: '', + type: 'string', + name: 'href', + description: 'Item link (href)', + table: { + category: 'HTML' + } + }, + ariaCurrent: { + options: ['location', 'page'], + control: {type: 'select'}, + description: 'location for anchor links, page for global page navigation', + table: { + category: 'HTML' + } + }, + description: { + defaultValue: '', + type: 'string', + name: 'description', + description: 'string', + table: { + category: 'HTML' + } + }, + descriptionVariant: { + options: [0, 1], // iterator + mapping: ['', 'ActionList-item-descriptionWrap--inline'], // values + control: { + type: 'select', + labels: ['block', 'inline'] + }, + description: 'block (default), inline', + defaultValue: 'ActionList-item-blockDescription', + table: { + category: 'CSS' + } + }, + id: { + defaultValue: '', + type: 'string', + name: 'id', + description: 'Used for aria-labelledby if nested group within item', + table: { + category: 'HTML' + } + }, + collapsible: { + defaultValue: false, + control: {type: 'boolean'}, + table: { + category: 'Interactive' + } + }, + singleSelect: { + defaultValue: false, + control: {type: 'boolean'}, + table: { + category: 'Interactive' + } + }, + multiSelect: { + defaultValue: false, + control: {type: 'boolean'}, + table: { + category: 'Interactive' + } + }, + listSingleSelect: { + defaultValue: false, + control: {type: 'boolean'}, + table: { + category: 'Interactive' + } + }, + listMultiSelect: { + defaultValue: false, + control: {type: 'boolean'}, + table: { + category: 'Interactive' + } + }, + ariaDisabled: { + defaultValue: false, + control: {type: 'boolean'}, + table: { + category: 'Interactive' + } + } + } +} + +export const ListItemTemplate = ({ + text, + size, + leadingVisual, + leadingVisualSize, + trailingVisual, + description, + descriptionVariant, + variant, + href, + ariaCurrent, + children, + subItem, + containsSubItem, + id, + collapsible, + trailingAction, + leadingAction, + singleSelect, + multiSelect, + listSingleSelect, + listMultiSelect, + listSemantic, + ariaDisabled +}) => { + const [isCollapsed, itemIsCollapsed] = useToggle() + const [isChecked, itemIsChecked] = useToggle() + return ( +
  • + {href ? ( + <> + + {(leadingAction || singleSelect || multiSelect || listSingleSelect || listMultiSelect) && ( + + {singleSelect || + (listSingleSelect && ( + + + + ))} + {multiSelect || + (listMultiSelect && ( + + ))} + {leadingAction} + + )} + {leadingVisual && ( + + )} + {description && ( + + {text} + {description} + + )} + {!description && text && {text}} + {trailingVisual && ( + + )} + {trailingAction || + (collapsible && ( + + {collapsible && ( + + + + )} + {trailingAction} + + ))} + + {children} + + ) : ( + <> + + {(leadingAction || singleSelect || multiSelect || listSingleSelect || listMultiSelect) && ( + + {(singleSelect || listSingleSelect) && ( + + + + )} + {(multiSelect || listMultiSelect) && ( + + )} + {leadingAction} + + )} + {leadingVisual && ( + + )} + {description && ( + + {text} + {description} + + )} + {!description && text && {text}} + + {trailingVisual && ( + + )} + {trailingAction || + (collapsible && ( + + {collapsible && ( + + + + )} + {trailingAction} + + ))} + + {children} + + )} +
  • + ) +} + +export const Playground = ListItemTemplate.bind({}) +Playground.decorators = [ + Story => ( + + ) +] diff --git a/docs/src/stories/components/ActionList/ActionListItemFeatures.stories.jsx b/docs/src/stories/components/ActionList/ActionListItemFeatures.stories.jsx new file mode 100644 index 0000000000..babe1b7340 --- /dev/null +++ b/docs/src/stories/components/ActionList/ActionListItemFeatures.stories.jsx @@ -0,0 +1,550 @@ +import React from 'react' +import clsx from 'clsx' +import {ListItemTemplate} from './ActionListItem.stories' +import {DividerTemplate} from './ActionListDivider.stories' + +export default { + title: 'Components/ActionList/ActionListItem/Features', + parameters: { + design: { + type: 'figma', + url: 'https://www.figma.com/file/oMiRuexZW6gqVbMhQd6lwP/Storybook?node-id=2%3A2' + } + }, + decorators: [ + Story => ( + + ) + ] +} + +export const TextOnly = ListItemTemplate.bind({}) +TextOnly.args = { + text: 'Basic item label' +} + +export const SizeMedium = ListItemTemplate.bind({}) +SizeMedium.args = { + ...ListItemTemplate.args, + text: 'Medium item', + size: 'ActionList-content--sizeMedium' +} + +export const SizeMediumWithDescription = ListItemTemplate.bind({}) +SizeMediumWithDescription.args = { + ...ListItemTemplate.args, + text: 'Medium item', + description: 'Some descriptive text', + size: 'ActionList-content--sizeMedium' +} + +export const SizeLarge = ListItemTemplate.bind({}) +SizeLarge.args = { + ...ListItemTemplate.args, + text: 'Large item', + size: 'ActionList-content--sizeLarge' +} + +export const SizeLargeWithDescription = ListItemTemplate.bind({}) +SizeLargeWithDescription.args = { + ...ListItemTemplate.args, + text: 'Large item', + description: 'Some descriptive text', + size: 'ActionList-content--sizeLarge' +} + +export const VisualLeading = ListItemTemplate.bind({}) +VisualLeading.storyName = '[Visuals] Leading' +VisualLeading.args = { + ...ListItemTemplate.args, + text: 'Item with leading visual', + leadingVisual: ` + + ` +} + +export const VisualTrailing = ListItemTemplate.bind({}) +VisualTrailing.storyName = '[Visuals] Trailing' +VisualTrailing.args = { + ...ListItemTemplate.args, + text: 'Item with trailing visual', + trailingVisual: ` + + ` +} + +export const VisualTrailingText = ListItemTemplate.bind({}) +VisualTrailingText.storyName = '[Visuals] Trailing text' +VisualTrailingText.args = { + ...ListItemTemplate.args, + text: 'Item with trailing text', + trailingVisual: `⌘N` +} + +export const VisualLeadingAndTrailing = ListItemTemplate.bind({}) +VisualLeadingAndTrailing.storyName = '[Visuals] Leading & trailing' +VisualLeadingAndTrailing.args = { + ...ListItemTemplate.args, + text: 'Item with trailing visual', + trailingVisual: ` + + `, + leadingVisual: ` + + ` +} + +export const DescriptionBlock = ListItemTemplate.bind({}) +DescriptionBlock.storyName = '[Description] block' +DescriptionBlock.args = { + ...ListItemTemplate.args, + text: 'Item label', + description: 'This is a description', + descriptionVariant: 'ActionList-item-blockDescription' +} + +export const DescriptionBlockWithLeadingVisual = ListItemTemplate.bind({}) +DescriptionBlockWithLeadingVisual.storyName = '[Description] block + leading visual' +DescriptionBlockWithLeadingVisual.args = { + ...ListItemTemplate.args, + text: 'Item label', + description: 'This is a description', + descriptionVariant: 'ActionList-item-blockDescription', + leadingVisual: ` + + ` +} + +export const DescriptionBlockWithTrailingVisual = ListItemTemplate.bind({}) +DescriptionBlockWithTrailingVisual.storyName = '[Description] block + trailing visual' +DescriptionBlockWithTrailingVisual.args = { + ...ListItemTemplate.args, + text: 'Item label', + description: 'This is a description', + descriptionVariant: 'ActionList-item-blockDescription', + trailingVisual: ` + + ` +} + +export const DescriptionBlockWithLeadingAndTrailingVisual = ListItemTemplate.bind({}) +DescriptionBlockWithLeadingAndTrailingVisual.storyName = '[Description] block + leading/trailing visual' +DescriptionBlockWithLeadingAndTrailingVisual.args = { + ...ListItemTemplate.args, + text: 'Item label', + description: 'This is a description', + descriptionVariant: 'ActionList-item-blockDescription', + leadingVisual: ` + + `, + trailingVisual: ` + + ` +} + +export const DescriptionInline = ListItemTemplate.bind({}) +DescriptionInline.storyName = '[Description] inline' +DescriptionInline.args = { + ...ListItemTemplate.args, + text: 'Item label', + description: 'This is a description', + descriptionVariant: 'ActionList-item-descriptionWrap--inline' +} + +export const DescriptionInlineWithLeadingVisual = ListItemTemplate.bind({}) +DescriptionInlineWithLeadingVisual.storyName = '[Description] inline + leading visual' +DescriptionInlineWithLeadingVisual.args = { + ...ListItemTemplate.args, + text: 'Item label', + description: 'This is a description', + descriptionVariant: 'ActionList-item-descriptionWrap--inline', + leadingVisual: ` + + ` +} + +export const DescriptionInlineWithTrailingVisual = ListItemTemplate.bind({}) +DescriptionInlineWithTrailingVisual.storyName = '[Description] inline + trailing visual' +DescriptionInlineWithTrailingVisual.args = { + ...ListItemTemplate.args, + text: 'Item label', + description: 'This is a description', + descriptionVariant: 'ActionList-item-descriptionWrap--inline', + trailingVisual: ` + + ` +} + +export const DescriptionInlineWithLeadingAndTrailingVisual = ListItemTemplate.bind({}) +DescriptionInlineWithLeadingAndTrailingVisual.storyName = '[Description] inline + leading/trailing visual' +DescriptionInlineWithLeadingAndTrailingVisual.args = { + ...ListItemTemplate.args, + text: 'Item label', + description: 'This is a description', + descriptionVariant: 'ActionList-item-descriptionWrap--inline', + trailingVisual: ` + + `, + leadingVisual: ` + + ` +} + +export const NavActiveAnchor = ListItemTemplate.bind({}) +NavActiveAnchor.storyName = '[Nav] Active anchor' +NavActiveAnchor.args = { + ...ListItemTemplate.args, + text: 'Im an anchor link', + href: '#someid', + ariaCurrent: 'location', + activeNavItem: true +} + +export const NavActivePage = ListItemTemplate.bind({}) +NavActivePage.storyName = '[Nav] Active page' +NavActivePage.args = { + ...ListItemTemplate.args, + text: 'Im a page level link', + href: '/', + ariaCurrent: 'page', + activeNavItem: true +} + +export const VariantDangerItem = ListItemTemplate.bind({}) +VariantDangerItem.storyName = '[Variant] Danger' +VariantDangerItem.args = { + ...ListItemTemplate.args, + text: 'Danger danger', + variant: 'ActionList-item--danger' +} + +export const VariantDangerItemLeading = ListItemTemplate.bind({}) +VariantDangerItemLeading.storyName = '[Variant] Danger + leading visual' +VariantDangerItemLeading.args = { + ...ListItemTemplate.args, + text: 'Danger danger', + variant: 'ActionList-item--danger', + leadingVisual: ` + + ` +} + +export const VariantDangerItemTrailing = ListItemTemplate.bind({}) +VariantDangerItemTrailing.storyName = '[Variant] Danger + trailing visual' +VariantDangerItemTrailing.args = { + ...ListItemTemplate.args, + text: 'Danger danger', + variant: 'ActionList-item--danger', + trailingVisual: ` + + ` +} + +export const VariantDangerItemLeadingTrailing = ListItemTemplate.bind({}) +VariantDangerItemLeadingTrailing.storyName = '[Variant] Danger + leading/trailing visual' +VariantDangerItemLeadingTrailing.args = { + ...ListItemTemplate.args, + text: 'Danger danger', + variant: 'ActionList-item--danger', + leadingVisual: ` + + `, + trailingVisual: ` + + ` +} + +export const VariantDisabledItem = ListItemTemplate.bind({}) +VariantDisabledItem.storyName = '[Variant] Disabled' +VariantDisabledItem.args = { + ...ListItemTemplate.args, + text: 'Disabled', + ariaDisabled: true +} + +export const VariantDisabledItemLeading = ListItemTemplate.bind({}) +VariantDisabledItemLeading.storyName = '[Variant] Disabled + leading visual' +VariantDisabledItemLeading.args = { + ...ListItemTemplate.args, + text: 'Disabled', + ariaDisabled: true, + leadingVisual: ` + + ` +} + +export const VariantDisabledItemTrailing = ListItemTemplate.bind({}) +VariantDisabledItemTrailing.storyName = '[Variant] Disabled + trailing visual' +VariantDisabledItemTrailing.args = { + ...ListItemTemplate.args, + text: 'Disabled', + ariaDisabled: true, + trailingVisual: ` + + ` +} + +export const VariantDisabledItemLeadingTrailing = ListItemTemplate.bind({}) +VariantDisabledItemLeadingTrailing.storyName = '[Variant] Disabled + leading/trailing visual' +VariantDisabledItemLeadingTrailing.args = { + ...ListItemTemplate.args, + text: 'Disabled', + ariaDisabled: true, + leadingVisual: ` + + `, + trailingVisual: ` + + ` +} + +export const ActionSingleSelectItem = ListItemTemplate.bind({}) +ActionSingleSelectItem.storyName = '[Actions] Single select' +ActionSingleSelectItem.args = { + text: 'Single select item', + singleSelect: true +} + +export const ActionSingleSelectItemWithLeadingVisual = ListItemTemplate.bind({}) +ActionSingleSelectItemWithLeadingVisual.storyName = '[Actions] Single select + leading visual' +ActionSingleSelectItemWithLeadingVisual.args = { + text: 'Single select item', + singleSelect: true, + leadingVisual: ` + + ` +} + +export const ActionSingleSelectItemWithTrailingVisual = ListItemTemplate.bind({}) +ActionSingleSelectItemWithTrailingVisual.storyName = '[Actions] Single select + trailing visual' +ActionSingleSelectItemWithTrailingVisual.args = { + text: 'Single select item', + singleSelect: true, + trailingVisual: ` + + ` +} + +export const ActionSingleSelectItemWithLeadingAndTrailingVisual = ListItemTemplate.bind({}) +ActionSingleSelectItemWithLeadingAndTrailingVisual.storyName = '[Actions] Single select + leading/trailing visual' +ActionSingleSelectItemWithLeadingAndTrailingVisual.args = { + text: 'Single select item', + singleSelect: true, + leadingVisual: ` + + `, + trailingVisual: ` + + ` +} + +export const ActionMultiSelectItem = ListItemTemplate.bind({}) +ActionMultiSelectItem.storyName = '[Actions] Multi select' +ActionMultiSelectItem.args = { + text: 'Multi select item', + multiSelect: true +} + +export const ActionMultiSelectItemWithLeadingVisual = ListItemTemplate.bind({}) +ActionMultiSelectItemWithLeadingVisual.storyName = '[Actions] Multi select + leading visual' +ActionMultiSelectItemWithLeadingVisual.args = { + text: 'Multi select item', + multiSelect: true, + leadingVisual: ` + + ` +} + +export const ActionMultiSelectItemWithTrailingVisual = ListItemTemplate.bind({}) +ActionMultiSelectItemWithTrailingVisual.storyName = '[Actions] Multi select + trailing visual' +ActionMultiSelectItemWithTrailingVisual.args = { + text: 'Multi select item', + multiSelect: true, + trailingVisual: ` + + ` +} + +export const ActionMultiSelectItemWithLeadingAndTrailingVisual = ListItemTemplate.bind({}) +ActionMultiSelectItemWithLeadingAndTrailingVisual.storyName = '[Actions] Multi select + leading/trailing visual' +ActionMultiSelectItemWithLeadingAndTrailingVisual.args = { + text: 'Multi select item', + multiSelect: true, + trailingVisual: ` + + `, + leadingVisual: ` + + ` +} + +export const ActionCollapsible = ListItemTemplate.bind({}) +ActionCollapsible.storyName = '[Actions] Collapsible' +ActionCollapsible.args = { + ...ListItemTemplate.args, + text: 'Collapsible', + collapsible: true, + collapsed: false +} + +export const ActionCollapsibleWithLeadingVisual = ListItemTemplate.bind({}) +ActionCollapsibleWithLeadingVisual.storyName = '[Actions] Collapsible + leading visual' +ActionCollapsibleWithLeadingVisual.args = { + ...ListItemTemplate.args, + text: 'Collapsible', + collapsible: true, + collapsed: false, + leadingVisual: ` + + ` +} + +export const ActionCollapsibleWithTrailingVisual = ListItemTemplate.bind({}) +ActionCollapsibleWithTrailingVisual.storyName = '[Actions] Collapsible + trailing visual' +ActionCollapsibleWithTrailingVisual.args = { + ...ListItemTemplate.args, + text: 'Collapsible', + collapsible: true, + collapsed: false, + trailingVisual: ` + + ` +} + +export const ActionCollapsibleWithLeadingAndTrailingVisual = ListItemTemplate.bind({}) +ActionCollapsibleWithLeadingAndTrailingVisual.storyName = '[Actions] Collapsible + leading/trailing visual' +ActionCollapsibleWithLeadingAndTrailingVisual.args = { + ...ListItemTemplate.args, + text: 'Collapsible', + collapsible: true, + collapsed: false, + leadingVisual: ` + + `, + trailingVisual: ` + + ` +} + +export const DividerEmpty = DividerTemplate.bind({}) +DividerEmpty.storyName = '[Divider] Empty default' +DividerEmpty.args = {} + +export const DividerFilled = DividerTemplate.bind({}) +DividerFilled.storyName = '[Divider] Empty filled' +DividerFilled.args = { + variant: 'ActionList-sectionDivider--filled' +} + +export const DividerText = DividerTemplate.bind({}) +DividerText.storyName = '[Divider] Title' +DividerText.args = { + title: 'Title', + id: 'some-id' +} + +export const DividerTextFilled = DividerTemplate.bind({}) +DividerTextFilled.storyName = '[Divider] Title filled' +DividerTextFilled.args = { + title: 'Title', + id: 'some-id', + variant: 'ActionList-sectionDivider--filled' +} diff --git a/docs/src/stories/components/ActionList/ActionListPatterns.stories.jsx b/docs/src/stories/components/ActionList/ActionListPatterns.stories.jsx new file mode 100644 index 0000000000..56be3cd9d8 --- /dev/null +++ b/docs/src/stories/components/ActionList/ActionListPatterns.stories.jsx @@ -0,0 +1,630 @@ +import React from 'react' +import clsx from 'clsx' +import {DividerTemplate} from './ActionListDivider.stories' +import {ListItemTemplate} from './ActionListItem.stories' +import {ListTemplate} from './ActionList.stories' + +export default { + title: 'Components/ActionList/Patterns' + // decorators: [ + // Story => ( + // + // ) + // ] +} + +export const NavWithSubItems = ListTemplate.bind({}) +NavWithSubItems.storyName = '[Nav] Nested collapsible menu' +NavWithSubItems.args = { + ...ListTemplate.args, + ...ListItemTemplate.args, + role: 'menu', + ariaLabel: 'Main menu description', + showDividers: false, + children: ( + <> + + + + + + + + + } + /> + } + /> + + ) +} +NavWithSubItems.decorators = [ + Story => ( + + ) +] + +export const NavWithSubItemsLeadingVisual16px = ListTemplate.bind({}) +NavWithSubItemsLeadingVisual16px.storyName = '[Nav] Nested collapsible menu leadingVisual 16px' +NavWithSubItemsLeadingVisual16px.args = { + ...ListTemplate.args, + ...ListItemTemplate.args, + ariaLabel: 'Main menu description', + role: 'menu', + showDividers: false, + children: ( + <> + `} + /> + `} + /> + `} + /> + `} + children={ + + + + `} + /> + + } + /> + } + /> + `} + /> + `} + /> + + ) +} +NavWithSubItemsLeadingVisual16px.decorators = [ + Story => ( + + ) +] + +export const NavWithSubItemsLeadingVisual20px = ListTemplate.bind({}) +NavWithSubItemsLeadingVisual20px.storyName = '[Nav] Nested collapsible menu leadingVisual 20px' +NavWithSubItemsLeadingVisual20px.args = { + ...ListTemplate.args, + ...ListItemTemplate.args, + ariaLabel: 'Main menu description', + role: 'menu', + showDividers: false, + children: ( + <> + `} + /> + `} + /> + `} + /> + `} + children={ + + + + `} + /> + + } + /> + } + /> + `} + /> + `} + /> + + ) +} +NavWithSubItemsLeadingVisual20px.decorators = [ + Story => ( + + ) +] + +export const NavWithSubItemsLeadingVisual24px = ListTemplate.bind({}) +NavWithSubItemsLeadingVisual24px.storyName = '[Nav] Nested collapsible menu leadingVisual 24px' +NavWithSubItemsLeadingVisual24px.args = { + ...ListTemplate.args, + ...ListItemTemplate.args, + ariaLabel: 'Main menu description', + role: 'menu', + showDividers: false, + children: ( + <> + `} + /> + `} + /> + `} + /> + `} + children={ + + + + `} + /> + + } + /> + } + /> + `} + /> + `} + /> + + ) +} +NavWithSubItemsLeadingVisual24px.decorators = [ + Story => ( + + ) +] + +export const MenuWithDivider = ListTemplate.bind({}) +MenuWithDivider.storyName = '[Menu] Divider' +MenuWithDivider.args = { + ...ListTemplate.args, + ...ListItemTemplate.args, + ariaLabel: 'Main menu description', + role: 'menu', + showDividers: false, + children: ( + <> + `} + /> + `} + /> + `} + /> + + `} + /> + `} + /> + + ) +} + +export const MenuWithSectionDivider = ListTemplate.bind({}) +MenuWithSectionDivider.storyName = '[Menu] Section divider' +MenuWithSectionDivider.args = { + ...ListTemplate.args, + ...ListItemTemplate.args, + ariaLabel: 'Main menu description', + role: 'menu', + showDividers: false, + children: ( + <> + + + + + + + + + + } + /> + } + /> + + ) +} + +export const MenuSingleSelect = ListTemplate.bind({}) +MenuSingleSelect.storyName = '[Menu] Single select' +MenuSingleSelect.args = { + ...ListTemplate.args, + ...ListItemTemplate.args, + ariaLabel: 'Main menu description', + role: 'menu', + showDividers: false, + children: ( + <> + + + + + + + ) +} + +export const MenuMultiSelect = ListTemplate.bind({}) +MenuMultiSelect.storyName = '[Menu] Multi select' +MenuMultiSelect.args = { + ...ListTemplate.args, + ...ListItemTemplate.args, + ariaLabel: 'Main menu description', + role: 'menu', + showDividers: false, + children: ( + <> + + + + + + + ) +} + +export const ListSingleSelect = ListTemplate.bind({}) +ListSingleSelect.storyName = '[Listbox] Single select' +ListSingleSelect.args = { + ...ListTemplate.args, + ...ListItemTemplate.args, + ariaLabel: 'Select an option', + role: 'listbox', + showDividers: false, + listboxMultiSelect: false, + children: ( + <> + + + + + + + ) +} + +export const ListMultiSelect = ListTemplate.bind({}) +ListMultiSelect.storyName = '[Listbox] Multi select' +ListMultiSelect.args = { + ...ListTemplate.args, + ...ListItemTemplate.args, + ariaLabel: 'Select multiple options', + role: 'listbox', + showDividers: false, + listboxMultiSelect: true, + children: ( + <> + + + + + + + ) +} + +export const List = ListTemplate.bind({}) +List.storyName = '[List] Group of links' +List.args = { + ...ListTemplate.args, + ...ListItemTemplate.args, + ariaLabel: 'Details', + role: undefined, + showDividers: false, + children: ( + <> + + `} + /> + `} + /> + `} + /> + `} + /> + + ) +} + +export const NavWithSubItemsLeadingVisual16pxSubSections = ListTemplate.bind({}) +NavWithSubItemsLeadingVisual16pxSubSections.storyName = + '[Nav] Nested collapsible menu leadingVisual 16px + sub sections' +NavWithSubItemsLeadingVisual16pxSubSections.args = { + ...ListTemplate.args, + ...ListItemTemplate.args, + ariaLabel: 'Main menu description', + role: 'menu', + showDividers: false, + children: ( + <> + `} + /> + + +