Skip to content
2 changes: 2 additions & 0 deletions .storybook/manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
});
7 changes: 6 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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.
Comment thread
gavmck marked this conversation as resolved.

**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.
Expand Down
13 changes: 13 additions & 0 deletions src/components/SidebarStatusTag.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';

import StatusTagBase from './StatusTagBase';

const SidebarStatusTag = ({ statusConfig }) => (

Check warning on line 5 in src/components/SidebarStatusTag.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'statusConfig' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=etchteam_storybook-addon-status&issues=AZ3dvB1QlXZXcrDpPRjL&open=AZ3dvB1QlXZXcrDpPRjL&pullRequest=105
<StatusTagBase
label={statusConfig.label}

Check warning on line 7 in src/components/SidebarStatusTag.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'statusConfig.label' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=etchteam_storybook-addon-status&issues=AZ3dvB1QlXZXcrDpPRjM&open=AZ3dvB1QlXZXcrDpPRjM&pullRequest=105
status={statusConfig.status}

Check warning on line 8 in src/components/SidebarStatusTag.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'statusConfig.status' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=etchteam_storybook-addon-status&issues=AZ3dvB1QlXZXcrDpPRjN&open=AZ3dvB1QlXZXcrDpPRjN&pullRequest=105
variant="sidebar"
/>
);

export default SidebarStatusTag;
1 change: 0 additions & 1 deletion src/components/StatusDot.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const StatusDot = styled.span`
: defaultBackground)};
border-radius: 100%;
height: 6px;
margin-left: 0.5em;
user-select: none;
width: 6px;
`;
Expand Down
66 changes: 10 additions & 56 deletions src/components/StatusTag.jsx
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -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 ? (
<LinkTag
key={statusConfig.label}
style={style}
title={description}
href={statusUrl}
>
{label}
</LinkTag>
) : (
<TextTag key={statusConfig.label} style={style} title={description}>
{label}
</TextTag>
);
})}
{statusConfigs.map((statusConfig) => (
<StatusTagBase
key={statusConfig.label}
label={statusConfig.label}
status={statusConfig.status}
url={statusConfig.url}
variant="toolbar"
/>
))}
</>
);
};
Expand Down
75 changes: 75 additions & 0 deletions src/components/StatusTagBase.jsx
Original file line number Diff line number Diff line change
@@ -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' }) => {

Check warning on line 41 in src/components/StatusTagBase.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'label' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=etchteam_storybook-addon-status&issues=AZ3dvBy4lXZXcrDpPRjE&open=AZ3dvBy4lXZXcrDpPRjE&pullRequest=105

Check warning on line 41 in src/components/StatusTagBase.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'url' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=etchteam_storybook-addon-status&issues=AZ3dvBy4lXZXcrDpPRjG&open=AZ3dvBy4lXZXcrDpPRjG&pullRequest=105

Check warning on line 41 in src/components/StatusTagBase.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'status' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=etchteam_storybook-addon-status&issues=AZ3dvBy4lXZXcrDpPRjF&open=AZ3dvBy4lXZXcrDpPRjF&pullRequest=105

Check warning on line 41 in src/components/StatusTagBase.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'variant' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=etchteam_storybook-addon-status&issues=AZ3dvBy4lXZXcrDpPRjH&open=AZ3dvBy4lXZXcrDpPRjH&pullRequest=105
const resolvedColor =
status?.color ??

Check warning on line 43 in src/components/StatusTagBase.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'status.color' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=etchteam_storybook-addon-status&issues=AZ3dvBy4lXZXcrDpPRjI&open=AZ3dvBy4lXZXcrDpPRjI&pullRequest=105
(defaultStatuses[label] ? defaultStatuses[label].color : defaultColor);
const resolvedBackground =
status?.background ??

Check warning on line 46 in src/components/StatusTagBase.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'status.background' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=etchteam_storybook-addon-status&issues=AZ3dvBy4lXZXcrDpPRjJ&open=AZ3dvBy4lXZXcrDpPRjJ&pullRequest=105
(defaultStatuses[label]
? defaultStatuses[label].background
: defaultBackground);

const style = {
color: resolvedColor,
backgroundColor: resolvedBackground,
};

const description = status?.description;

Check warning on line 56 in src/components/StatusTagBase.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'status.description' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=etchteam_storybook-addon-status&issues=AZ3dvBy4lXZXcrDpPRjK&open=AZ3dvBy4lXZXcrDpPRjK&pullRequest=105
const displayLabel = startCase(label);
const isLink = variant === 'toolbar' && !!url;

if (isLink) {
return (
<LinkTag variant={variant} style={style} title={description} href={url}>
{displayLabel}
</LinkTag>
);
}

return (
<TextTag variant={variant} style={style} title={description}>
{displayLabel}
</TextTag>
);
};

export default StatusTagBase;
27 changes: 25 additions & 2 deletions src/manager.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -63,14 +73,27 @@ addons.register(ADDON_ID, (api) => {
return fallbackLabel;
}

if (sidebarDotsConfig !== 'multiple') {
const showMultiple = isTagMode
? sidebarTagsConfig === 'multiple'
: sidebarDotsConfig === 'multiple';

if (!showMultiple) {
statusConfigs = [statusConfigs[0]];
}

return (
<>
{fallbackLabel}
{statusConfigs.map((statusConfig) => {
if (isTagMode) {
return (
<SidebarStatusTag
key={statusConfig.label}
statusConfig={statusConfig}
/>
);
}

const {
label: statusName,
status: { background, description },
Expand Down
Loading