@etchteam/storybook-addon-status
Version:
Add component status to the storybook UI
270 lines (261 loc) • 8.26 kB
JavaScript
import startCase from 'lodash/startCase';
import React4 from 'react';
import { addons, types, useStorybookApi, useParameter } from 'storybook/manager-api';
import { css, styled } from 'storybook/theming';
// src/manager.jsx
// src/defaults.js
var defaultBackground = "#2f2f2f";
var defaultColor = "#ffffff";
var defaultStatuses = {
beta: {
color: "#ffffff",
background: "#ec942c",
description: "The interface for this component may change"
},
deprecated: {
color: "#ffffff",
background: "#f02c2c",
description: "This component will be removed in the next major version"
},
stable: {
color: "#ffffff",
background: "#339902",
description: "This component is stable"
},
releaseCandidate: {
color: "#ffffff",
background: "#a335ee",
description: "This component is a release candidate"
}
};
// src/components/StatusTagBase.jsx
var baseStyles = css`
align-self: center;
border-radius: 0.25em;
font-weight: 700;
text-decoration: none;
text-transform: uppercase;
user-select: none;
`;
var toolbarStyles = css`
font-size: 11px;
line-height: 20px;
padding: 0 0.5em;
`;
var sidebarStyles = css`
font-size: 10px;
line-height: 16px;
padding: 0 0.4em;
`;
var variantStyles = ({ variant }) => variant === "sidebar" ? sidebarStyles : toolbarStyles;
var LinkTag = styled.a`
${baseStyles}
${variantStyles}
`;
var TextTag = styled.span`
${baseStyles}
${variantStyles}
`;
var 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 /* @__PURE__ */ React4.createElement(LinkTag, { variant, style, title: description, href: url }, displayLabel);
}
return /* @__PURE__ */ React4.createElement(TextTag, { variant, style, title: description }, displayLabel);
};
var StatusTagBase_default = StatusTagBase;
// src/components/SidebarStatusTag.jsx
var SidebarStatusTag = ({ statusConfig }) => /* @__PURE__ */ React4.createElement(
StatusTagBase_default,
{
label: statusConfig.label,
status: statusConfig.status,
variant: "sidebar"
}
);
var SidebarStatusTag_default = SidebarStatusTag;
var StatusDot = styled.span`
align-self: center;
background-color: ${({ background, type }) => background ?? (defaultStatuses[type] ? defaultStatuses[type].background : defaultBackground)};
border-radius: 100%;
height: 6px;
user-select: none;
width: 6px;
`;
var StatusDot_default = StatusDot;
// src/constants.js
var ADDON_ID = "status";
// src/getStatusConfigs.js
var combineTagsAndParameters = (tags, parameters) => {
if (!parameters) {
return tags ?? [];
}
if (Array.isArray(parameters)) {
return [...tags, ...parameters];
}
if (typeof parameters === "string" || typeof parameters === "object") {
return [...tags, parameters];
}
return [];
};
var filterStatuses = (statuses) => {
const filteredStatuses = [];
const existingNames = [];
statuses.forEach((status) => {
const name = typeof status === "string" ? status : status.name;
if (!existingNames.includes(name)) {
filteredStatuses.push(status);
existingNames.push(name);
return;
}
if (status.name) {
const index = existingNames.indexOf(name);
if (index !== -1) {
filteredStatuses[index] = status;
}
}
});
return filteredStatuses;
};
var getStatusConfigs = ({ tags, parameters, customConfigs }) => {
if (!parameters && !tags?.length) {
return [];
}
const combinedStatuses = combineTagsAndParameters(tags, parameters?.type);
const statuses = filterStatuses(combinedStatuses);
const statusConfigMap = {
...defaultStatuses,
...customConfigs || parameters?.statuses || {}
};
let statusConfigs = statuses.map((status) => {
if (typeof status === "string") {
return {
label: status,
status: statusConfigMap[status],
url: parameters?.url
};
}
return {
label: status.name,
status: statusConfigMap[status.name],
url: status.url
};
});
statusConfigs = statusConfigs.filter((x) => x.status != null);
return statusConfigs;
};
// src/components/StatusTag.jsx
var StatusTag = () => {
const api = useStorybookApi();
const tags = api.getCurrentStoryData()?.tags ?? [];
const parameters = useParameter(ADDON_ID, null);
const customConfigs = addons.getConfig()?.[ADDON_ID]?.statuses;
const statusConfigs = getStatusConfigs({
tags,
parameters,
customConfigs
});
if (!statusConfigs?.length) {
return null;
}
return /* @__PURE__ */ React4.createElement(React4.Fragment, null, statusConfigs.map((statusConfig) => /* @__PURE__ */ React4.createElement(
StatusTagBase_default,
{
key: statusConfig.label,
label: statusConfig.label,
status: statusConfig.status,
url: statusConfig.url,
variant: "toolbar"
}
)));
};
var StatusTag_default = StatusTag;
// src/manager.jsx
addons.register(ADDON_ID, (api) => {
const addonsConfig = addons.getConfig();
const existingSidebarConfig = addonsConfig?.sidebar ?? {};
addons.add(ADDON_ID, {
title: "Status",
type: types.TOOL,
render: () => /* @__PURE__ */ React4.createElement(StatusTag_default, null)
});
const statusAddonConfig = addonsConfig?.[ADDON_ID] ?? {};
addons.setConfig({
sidebar: {
...existingSidebarConfig,
renderLabel: (item) => {
const { name, tags } = item;
const canHaveStatus = [
"root",
"group",
"component",
"docs",
"story"
].includes(item.type);
try {
const fallbackLabel = existingSidebarConfig?.renderLabel ? existingSidebarConfig.renderLabel(item) : name;
const sidebarTagsConfig = statusAddonConfig?.sidebarTags;
const sidebarDotsConfig = statusAddonConfig?.sidebarDots;
const isTagMode = sidebarTagsConfig === "single" || sidebarTagsConfig === "multiple";
if (sidebarTagsConfig === "none") {
return fallbackLabel;
}
if (sidebarTagsConfig === void 0 && sidebarDotsConfig === "none") {
return fallbackLabel;
}
const parameters = api.getParameters(item.id, ADDON_ID);
if (!canHaveStatus || tags.length === 0 && !parameters?.type) {
return fallbackLabel;
}
const customConfigs = statusAddonConfig?.statuses || api.getCurrentStoryData().parameters?.status?.statuses;
let statusConfigs = getStatusConfigs({
tags,
parameters,
customConfigs
});
if (statusConfigs.length === 0) {
return fallbackLabel;
}
const showMultiple = isTagMode ? sidebarTagsConfig === "multiple" : sidebarDotsConfig === "multiple";
if (!showMultiple) {
statusConfigs = [statusConfigs[0]];
}
return /* @__PURE__ */ React4.createElement(React4.Fragment, null, fallbackLabel, statusConfigs.map((statusConfig) => {
if (isTagMode) {
return /* @__PURE__ */ React4.createElement(
SidebarStatusTag_default,
{
key: statusConfig.label,
statusConfig
}
);
}
const {
label: statusName,
status: { background, description }
} = statusConfig;
return /* @__PURE__ */ React4.createElement(
StatusDot_default,
{
key: statusName,
type: statusName,
background,
title: `${startCase(statusName)}: ${description}`
}
);
}));
} catch (error) {
return name;
}
}
}
});
});