diff --git a/.storybook/manager.js b/.storybook/manager.js index c67c2bf..c1f6b43 100644 --- a/.storybook/manager.js +++ b/.storybook/manager.js @@ -9,6 +9,8 @@ addons.setConfig({ description: 'This component is stable and released', }, }, + // 'single' | 'multiple' | 'none'. When set, takes precedence over sidebarDots. + sidebarTags: 'multiple', sidebarDots: 'multiple', // 'single' | 'multiple' | 'none'. 'single' is the default }, }); diff --git a/Readme.md b/Readme.md index 2bfc708..6f29a90 100644 --- a/Readme.md +++ b/Readme.md @@ -22,7 +22,7 @@ export default { }; ``` -In `manager.js` you can globally configure custom status configurations, or overwrite the built in "beta", "deprecated", "stable" & "releaseCandidate". You can also change how status dots will appear in the sidebar with the `sidebarDots` prop. +In `manager.js` you can globally configure custom status configurations, or overwrite the built in "beta", "deprecated", "stable" & "releaseCandidate". You can also change how statuses appear in the sidebar with the `sidebarDots` and `sidebarTags` props. ```js import { addons } from "storybook/manager-api"; @@ -37,10 +37,15 @@ addons.setConfig({ }, }, sidebarDots: 'single', // 'single' | 'multiple' | 'none'. 'single' is the default + sidebarTags: 'single', // 'single' | 'multiple' | 'none'. When set, overrides sidebarDots and renders full status tags in the sidebar instead of dots. }, }); ``` +By default the sidebar shows a small coloured dot next to each story. Setting `sidebarTags` to `'single'` or `'multiple'` switches the sidebar to render the full status tag — the same colour and label as the toolbar tag, but slightly smaller. `sidebarTags: 'none'` hides the indicator entirely (and overrides `sidebarDots`). When `sidebarTags` is not set, `sidebarDots` keeps its existing behaviour. + +**Note:** Status URLs are intentionally not used by sidebar tags — clicking a story row in the sidebar should always navigate to that story. Use the toolbar tag (which still renders as a link) to follow the URL. + **IMPORTANT:** The addon was previously configured using parameters in `preview.js`. This will still work as before, however newer features such as sidebar dot customisation are not available. **NOTE:** Each key will be used as the label for the status and will convert camelCase to words. diff --git a/src/components/SidebarStatusTag.jsx b/src/components/SidebarStatusTag.jsx new file mode 100644 index 0000000..940180b --- /dev/null +++ b/src/components/SidebarStatusTag.jsx @@ -0,0 +1,13 @@ +import React from 'react'; + +import StatusTagBase from './StatusTagBase'; + +const SidebarStatusTag = ({ statusConfig }) => ( + +); + +export default SidebarStatusTag; diff --git a/src/components/StatusDot.jsx b/src/components/StatusDot.jsx index 5ec99c9..52d0a3e 100644 --- a/src/components/StatusDot.jsx +++ b/src/components/StatusDot.jsx @@ -11,7 +11,6 @@ const StatusDot = styled.span` : defaultBackground)}; border-radius: 100%; height: 6px; - margin-left: 0.5em; user-select: none; width: 6px; `; diff --git a/src/components/StatusTag.jsx b/src/components/StatusTag.jsx index ac15287..b5dcfd1 100644 --- a/src/components/StatusTag.jsx +++ b/src/components/StatusTag.jsx @@ -1,32 +1,10 @@ -import startCase from 'lodash/startCase'; import React from 'react'; import { useParameter, useStorybookApi, addons } from 'storybook/manager-api'; -import { styled, css } from 'storybook/theming'; import { ADDON_ID } from '../constants'; -import { defaultStatuses, defaultBackground, defaultColor } from '../defaults'; import { getStatusConfigs } from '../getStatusConfigs'; -const tagStyles = css` - align-self: center; - border-radius: 0.25em; - padding: 0 0.5em; - text-decoration: none; - user-select: none; - font-size: 11px; - text-transform: uppercase; - line-height: 20px; - font-weight: 700; - padding: 0 0.5em; -`; - -const LinkTag = styled.a` - ${tagStyles} -`; - -const TextTag = styled.span` - ${tagStyles} -`; +import StatusTagBase from './StatusTagBase'; const StatusTag = () => { const api = useStorybookApi(); @@ -47,39 +25,15 @@ const StatusTag = () => { return ( <> - {statusConfigs.map((statusConfig) => { - const { background, color, description } = statusConfig.status; - const statusUrl = statusConfig.url; - const label = startCase(statusConfig.label); - - const style = { - color: - color ?? - (defaultStatuses[label] - ? defaultStatuses[label].color - : defaultColor), - backgroundColor: - background ?? - (defaultStatuses[label] - ? defaultStatuses[label].background - : defaultBackground), - }; - - return statusUrl ? ( - - {label} - - ) : ( - - {label} - - ); - })} + {statusConfigs.map((statusConfig) => ( + + ))} ); }; diff --git a/src/components/StatusTagBase.jsx b/src/components/StatusTagBase.jsx new file mode 100644 index 0000000..11f4c0a --- /dev/null +++ b/src/components/StatusTagBase.jsx @@ -0,0 +1,75 @@ +import startCase from 'lodash/startCase'; +import React from 'react'; +import { styled, css } from 'storybook/theming'; + +import { defaultStatuses, defaultBackground, defaultColor } from '../defaults'; + +const baseStyles = css` + align-self: center; + border-radius: 0.25em; + font-weight: 700; + text-decoration: none; + text-transform: uppercase; + user-select: none; +`; + +const toolbarStyles = css` + font-size: 11px; + line-height: 20px; + padding: 0 0.5em; +`; + +const sidebarStyles = css` + font-size: 10px; + line-height: 16px; + padding: 0 0.4em; +`; + +const variantStyles = ({ variant }) => + variant === 'sidebar' ? sidebarStyles : toolbarStyles; + +const LinkTag = styled.a` + ${baseStyles} + ${variantStyles} +`; + +const TextTag = styled.span` + ${baseStyles} + ${variantStyles} +`; + +const StatusTagBase = ({ label, status, url, variant = 'toolbar' }) => { + const resolvedColor = + status?.color ?? + (defaultStatuses[label] ? defaultStatuses[label].color : defaultColor); + const resolvedBackground = + status?.background ?? + (defaultStatuses[label] + ? defaultStatuses[label].background + : defaultBackground); + + const style = { + color: resolvedColor, + backgroundColor: resolvedBackground, + }; + + const description = status?.description; + const displayLabel = startCase(label); + const isLink = variant === 'toolbar' && !!url; + + if (isLink) { + return ( + + {displayLabel} + + ); + } + + return ( + + {displayLabel} + + ); +}; + +export default StatusTagBase; diff --git a/src/manager.jsx b/src/manager.jsx index 3cd0343..09c3ac3 100644 --- a/src/manager.jsx +++ b/src/manager.jsx @@ -2,6 +2,7 @@ import startCase from 'lodash/startCase'; import React from 'react'; import { addons, types } from 'storybook/manager-api'; +import SidebarStatusTag from './components/SidebarStatusTag'; import StatusDot from './components/StatusDot'; import StatusTag from './components/StatusTag'; import { ADDON_ID } from './constants'; @@ -31,9 +32,18 @@ addons.register(ADDON_ID, (api) => { ? existingSidebarConfig.renderLabel(item) : name; + const sidebarTagsConfig = statusAddonConfig?.sidebarTags; const sidebarDotsConfig = statusAddonConfig?.sidebarDots; - if (sidebarDotsConfig === 'none') { + // sidebarTags, when set, fully overrides sidebarDots. + const isTagMode = + sidebarTagsConfig === 'single' || sidebarTagsConfig === 'multiple'; + + if (sidebarTagsConfig === 'none') { + return fallbackLabel; + } + + if (sidebarTagsConfig === undefined && sidebarDotsConfig === 'none') { return fallbackLabel; } @@ -63,7 +73,11 @@ addons.register(ADDON_ID, (api) => { return fallbackLabel; } - if (sidebarDotsConfig !== 'multiple') { + const showMultiple = isTagMode + ? sidebarTagsConfig === 'multiple' + : sidebarDotsConfig === 'multiple'; + + if (!showMultiple) { statusConfigs = [statusConfigs[0]]; } @@ -71,6 +85,15 @@ addons.register(ADDON_ID, (api) => { <> {fallbackLabel} {statusConfigs.map((statusConfig) => { + if (isTagMode) { + return ( + + ); + } + const { label: statusName, status: { background, description },