diff --git a/packages/components/src/custom-select-control-v2/stories/default.story.tsx b/packages/components/src/custom-select-control-v2/stories/default.story.tsx index 65f00b4792378a..ee7dba0694c278 100644 --- a/packages/components/src/custom-select-control-v2/stories/default.story.tsx +++ b/packages/components/src/custom-select-control-v2/stories/default.story.tsx @@ -25,6 +25,7 @@ const meta: Meta< typeof CustomSelect > = { children: { control: { type: null } }, value: { control: { type: null } }, }, + tags: [ 'status-wip' ], parameters: { badges: [ 'wip' ], actions: { argTypesRegex: '^on.*' }, diff --git a/packages/components/src/dropdown-menu-v2/stories/index.story.tsx b/packages/components/src/dropdown-menu-v2/stories/index.story.tsx index 9e606770caa3ae..11f3c8fb1a6b1b 100644 --- a/packages/components/src/dropdown-menu-v2/stories/index.story.tsx +++ b/packages/components/src/dropdown-menu-v2/stories/index.story.tsx @@ -56,6 +56,7 @@ const meta: Meta< typeof DropdownMenu > = { children: { control: { type: null } }, trigger: { control: { type: null } }, }, + tags: [ 'status-private' ], parameters: { actions: { argTypesRegex: '^on.*' }, badges: [ 'private' ], diff --git a/packages/components/src/progress-bar/stories/index.story.tsx b/packages/components/src/progress-bar/stories/index.story.tsx index e213ea34db139a..dad1958bf0e66e 100644 --- a/packages/components/src/progress-bar/stories/index.story.tsx +++ b/packages/components/src/progress-bar/stories/index.story.tsx @@ -14,6 +14,7 @@ const meta: Meta< typeof ProgressBar > = { argTypes: { value: { control: { type: 'number', min: 0, max: 100, step: 1 } }, }, + tags: [ 'status-private' ], parameters: { badges: [ 'private' ], controls: { diff --git a/packages/components/src/tabs/stories/index.story.tsx b/packages/components/src/tabs/stories/index.story.tsx index 632aaf0ac9242c..1fe593df385361 100644 --- a/packages/components/src/tabs/stories/index.story.tsx +++ b/packages/components/src/tabs/stories/index.story.tsx @@ -28,6 +28,7 @@ const meta: Meta< typeof Tabs > = { // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 'Tabs.TabPanel': Tabs.TabPanel, }, + tags: [ 'status-private' ], parameters: { actions: { argTypesRegex: '^on.*' }, badges: [ 'private' ], diff --git a/packages/components/src/theme/stories/index.story.tsx b/packages/components/src/theme/stories/index.story.tsx index 4e231cf2624cd2..c26e0a752c0d45 100644 --- a/packages/components/src/theme/stories/index.story.tsx +++ b/packages/components/src/theme/stories/index.story.tsx @@ -18,6 +18,7 @@ const meta: Meta< typeof Theme > = { accent: { control: { type: 'color' } }, background: { control: { type: 'color' } }, }, + tags: [ 'status-private' ], parameters: { badges: [ 'private' ], controls: { expanded: true }, diff --git a/storybook/badges.js b/storybook/badges.js new file mode 100644 index 00000000000000..7787a9a67121e6 --- /dev/null +++ b/storybook/badges.js @@ -0,0 +1,30 @@ +/** + * Provides badge configuration options. + * See https://github.com/geometricpanda/storybook-addon-badges + */ + +export default { + private: { + icon: '🔒', + title: '🔒 Private', + tooltip: { + title: 'Component is locked as a private API', + desc: 'We do not yet recommend using this outside of the Gutenberg codebase.', + links: [ + { + title: 'About @wordpress/private-apis', + href: 'https://developer.wordpress.org/block-editor/reference-guides/packages/packages-private-apis/', + }, + ], + }, + }, + wip: { + icon: '🚧', + title: '🚧 WIP', + styles: { backgroundColor: '#FFF0BD' }, + tooltip: { + title: 'Component is a work in progress', + desc: 'This component is not ready for use in production, including the Gutenberg codebase. DO NOT export outside of @wordpress/components.', + }, + }, +}; diff --git a/storybook/manager.js b/storybook/manager.js index 25969095c8c49c..c63f15efe11d02 100644 --- a/storybook/manager.js +++ b/storybook/manager.js @@ -7,7 +7,9 @@ import { addons } from '@storybook/addons'; * Internal dependencies */ import theme from './theme'; +import sidebar from './sidebar'; addons.setConfig( { + sidebar, theme, } ); diff --git a/storybook/preview.js b/storybook/preview.js index 02b3ab22ed355f..a426faa41c2d71 100644 --- a/storybook/preview.js +++ b/storybook/preview.js @@ -18,6 +18,7 @@ import { WithMarginChecker } from './decorators/with-margin-checker'; import { WithMaxWidthWrapper } from './decorators/with-max-width-wrapper'; import { WithRTL } from './decorators/with-rtl'; import { WithTheme } from './decorators/with-theme'; +import badgesConfig from './badges'; export const globalTypes = { direction: { @@ -100,29 +101,7 @@ export const decorators = [ export const parameters = { // For @geometricpanda/storybook-addon-badges - badgesConfig: { - private: { - title: '🔒 Private', - tooltip: { - title: 'Component is locked as a private API', - desc: 'We do not yet recommend using this outside of the Gutenberg codebase.', - links: [ - { - title: 'About @wordpress/private-apis', - href: 'https://developer.wordpress.org/block-editor/reference-guides/packages/packages-private-apis/', - }, - ], - }, - }, - wip: { - title: '🚧 WIP', - styles: { backgroundColor: '#FFF0BD' }, - tooltip: { - title: 'Component is a work in progress', - desc: 'This component is not ready for use in production, including the Gutenberg codebase. DO NOT export outside of @wordpress/components.', - }, - }, - }, + badgesConfig, controls: { sort: 'requiredFirst', }, diff --git a/storybook/sidebar.js b/storybook/sidebar.js new file mode 100644 index 00000000000000..d8ff2ba777dd7d --- /dev/null +++ b/storybook/sidebar.js @@ -0,0 +1,79 @@ +/** + * Provides sidebar configuration options. + * See https://storybook.js.org/docs/configure/features-and-behavior + */ + +/** + * External dependencies + */ +// eslint-disable-next-line @typescript-eslint/no-restricted-imports +import { createElement, useMemo } from 'react'; +import { useStorybookApi } from '@storybook/manager-api'; +import { styled } from '@storybook/theming'; + +/** + * Internal dependencies + */ +import badges from './badges'; + +const Wrapper = styled.span( { + flexGrow: 1, + display: 'flex', + paddingRight: '20px', +} ); + +const Title = styled.span( { + flexGrow: 1, +} ); + +const Icons = styled.span( {} ); + +const Icon = styled.span( {} ); + +/** + * Fetches tags from the Storybook API, and returns Icon + * elements for any that have matching badge data + */ +function useIcons( item ) { + const api = useStorybookApi(); + const prefix = 'status-'; + + return useMemo( () => { + let data = {}; + + if ( item.isComponent && item.children?.length ) { + data = api.getData( item.children[ 0 ] ) ?? {}; + } + + const { tags = [] } = data; + + return tags + .filter( ( tag ) => tag.startsWith( prefix ) ) + .map( ( tag ) => badges[ tag.substring( prefix.length ) ] ) + .map( ( { icon, title, tooltip } ) => + icon + ? createElement( + Icon, + { title: tooltip?.title ?? title }, + icon + ) + : null + ); + }, [ api, item.children, item.isComponent ] ); +} + +/** + * Renders the item name and any associated badge icons. + */ +function Label( { item } ) { + const iconSet = useIcons( item ); + const title = createElement( Title, {}, item.name ); + const icons = createElement( Icons, { 'aria-hidden': true }, ...iconSet ); + + return createElement( Wrapper, {}, title, icons ); +} + +export default { + // Renders status icons for items tagged with `status-*` + renderLabel: ( item ) => createElement( Label, { item } ), +};