From 6fec9ed3203b738198dc7eed79b92d60a58500e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DUNGLER?= Date: Sun, 15 Nov 2020 14:24:03 +0100 Subject: [PATCH 1/2] UI: add capacity to prefix roots, groups, stories --- examples/official-storybook/manager.js | 19 ++++++++++++++++++- lib/api/src/lib/stories.ts | 10 +++++++++- .../src/components/sidebar/Tree.stories.tsx | 2 ++ lib/ui/src/components/sidebar/Tree.tsx | 8 +++++++- lib/ui/src/components/sidebar/TreeNode.tsx | 1 - 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/examples/official-storybook/manager.js b/examples/official-storybook/manager.js index 7f92c9e1b8fe..aa42e4c4966d 100644 --- a/examples/official-storybook/manager.js +++ b/examples/official-storybook/manager.js @@ -1,10 +1,21 @@ +import React from 'react'; import { addons } from '@storybook/addons'; -import { themes } from '@storybook/theming'; +import { themes, styled } from '@storybook/theming'; +import { Icons } from '@storybook/components'; import addHeadWarning from './head-warning'; addHeadWarning('manager-head-not-loaded', 'Manager head not loaded'); +const PrefixIcon = styled(Icons)(({ theme }) => ({ + marginRight: 8, + fontSize: 'inherit', + height: '1em', + width: '1em', + display: 'inline', + alignSelf: 'center', +})); + addons.setConfig({ theme: themes.light, // { base: 'dark', brandTitle: 'Storybook!' }, previewTabs: { @@ -15,4 +26,10 @@ addons.setConfig({ hidden: true, }, }, + storyPrefix: { + addons: , + 'addons-a11y': , + 'addons-a11y-basebutton': , + 'addons-a11y-basebutton--default': , + }, }); diff --git a/lib/api/src/lib/stories.ts b/lib/api/src/lib/stories.ts index 602ec4d75541..cb2cae987ecd 100644 --- a/lib/api/src/lib/stories.ts +++ b/lib/api/src/lib/stories.ts @@ -1,3 +1,4 @@ +import React from 'react'; import deprecate from 'util-deprecate'; import dedent from 'ts-dedent'; import { sanitize } from '@storybook/csf'; @@ -19,6 +20,7 @@ export interface Root { isComponent: false; isRoot: true; isLeaf: false; + prefix?: React.ReactNode; } export interface Group { @@ -31,6 +33,7 @@ export interface Group { isComponent: boolean; isRoot: false; isLeaf: false; + prefix?: React.ReactNode; // MDX docs-only stories are "Group" type parameters?: { docsOnly?: boolean; @@ -49,6 +52,7 @@ export interface Story { isComponent: boolean; isRoot: false; isLeaf: true; + prefix?: React.ReactNode; parameters?: { fileName: string; options: { @@ -67,6 +71,7 @@ export interface StoryInput { refId?: string; kind: StoryKind; children: string[]; + prefix?: React.ReactNode; parameters: { fileName: string; options: { @@ -152,7 +157,7 @@ export const transformStoriesRawToStoriesHash = ( .filter(Boolean) .reduce((acc, item) => { const { kind, parameters } = item; - const { showRoots } = provider.getConfig(); + const { showRoots, storyPrefix = {} } = provider.getConfig(); const setShowRoots = typeof showRoots !== 'undefined'; if (anyKindMatchesOldHierarchySeparators && !setShowRoots) { @@ -197,6 +202,7 @@ export const transformStoriesRawToStoriesHash = ( isComponent: false, isLeaf: false, isRoot: true, + prefix: storyPrefix[id], }; return soFar.concat([result]); } @@ -209,6 +215,7 @@ export const transformStoriesRawToStoriesHash = ( isComponent: false, isLeaf: false, isRoot: false, + prefix: storyPrefix[id], parameters: { docsOnly: parameters?.docsOnly, viewMode: parameters?.viewMode, @@ -236,6 +243,7 @@ export const transformStoriesRawToStoriesHash = ( isLeaf: true, isComponent: false, isRoot: false, + prefix: storyPrefix[item.id], }; acc[item.id] = story; diff --git a/lib/ui/src/components/sidebar/Tree.stories.tsx b/lib/ui/src/components/sidebar/Tree.stories.tsx index 8cb2c0ef791d..725acbe2d3e6 100644 --- a/lib/ui/src/components/sidebar/Tree.stories.tsx +++ b/lib/ui/src/components/sidebar/Tree.stories.tsx @@ -44,6 +44,7 @@ const singleStoryComponent = { isComponent: true, isLeaf: false, isRoot: false, + prefix: 🔥, }, 'single--single': { id: 'single--single', @@ -58,6 +59,7 @@ const singleStoryComponent = { isLeaf: true, isComponent: false, isRoot: false, + prefix: 🔥, }, }; diff --git a/lib/ui/src/components/sidebar/Tree.tsx b/lib/ui/src/components/sidebar/Tree.tsx index 83b737fe1f58..6559b9657bdc 100644 --- a/lib/ui/src/components/sidebar/Tree.tsx +++ b/lib/ui/src/components/sidebar/Tree.tsx @@ -16,6 +16,7 @@ export const Action = styled.button(({ theme }) => ({ width: 20, height: 20, margin: 0, + marginLeft: 'auto', padding: 0, outline: 0, lineHeight: 'normal', @@ -90,6 +91,7 @@ const Node = React.memo( onSelectStoryId(item.id); }} > + {item.prefix} {item.name} ); @@ -98,7 +100,10 @@ const Node = React.memo( if (isRoot(item)) { return ( - {item.name} + + {item.prefix} + {item.name} + ( if (item.isComponent && !isExpanded) onSelectStoryId(item.id); }} > + {item.prefix} {item.name} ); diff --git a/lib/ui/src/components/sidebar/TreeNode.tsx b/lib/ui/src/components/sidebar/TreeNode.tsx index aed38bbe1e2b..db5338a9c348 100644 --- a/lib/ui/src/components/sidebar/TreeNode.tsx +++ b/lib/ui/src/components/sidebar/TreeNode.tsx @@ -123,7 +123,6 @@ export const Path = styled.span(({ theme }) => ({ export const RootNode = styled.span(({ theme }) => ({ display: 'flex', alignItems: 'center', - justifyContent: 'space-between', margin: '16px 20px 4px 20px', fontSize: `${theme.typography.size.s1 - 1}px`, fontWeight: theme.typography.weight.black, From 7d69c42aaf5e0e98482506522162580699e908b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DUNGLER?= Date: Mon, 1 Feb 2021 22:35:27 +0100 Subject: [PATCH 2/2] Feedbacks --- examples/official-storybook/manager.js | 35 ++++++++++++++++--- lib/api/src/lib/stories.ts | 34 +++++++++++------- lib/api/src/modules/layout.ts | 6 +++- .../src/components/sidebar/Tree.stories.tsx | 4 +-- lib/ui/src/components/sidebar/Tree.tsx | 11 ++---- 5 files changed, 62 insertions(+), 28 deletions(-) diff --git a/examples/official-storybook/manager.js b/examples/official-storybook/manager.js index aa42e4c4966d..8e185e19c1f0 100644 --- a/examples/official-storybook/manager.js +++ b/examples/official-storybook/manager.js @@ -26,10 +26,35 @@ addons.setConfig({ hidden: true, }, }, - storyPrefix: { - addons: , - 'addons-a11y': , - 'addons-a11y-basebutton': , - 'addons-a11y-basebutton--default': , + sidebar: { + storyLabel: ({ id, name }) => { + const map = { + addons: ( + <> + + {name} + + ), + 'addons-a11y': ( + <> + + {name} + + ), + 'addons-a11y-basebutton': ( + <> + + {name} + + ), + 'addons-a11y-basebutton--default': ( + <> + + {name} + + ), + }; + return map[id]; + }, }, }); diff --git a/lib/api/src/lib/stories.ts b/lib/api/src/lib/stories.ts index c87e7b09bb94..1c0a330d8d23 100644 --- a/lib/api/src/lib/stories.ts +++ b/lib/api/src/lib/stories.ts @@ -20,7 +20,7 @@ export interface Root { isComponent: false; isRoot: true; isLeaf: false; - prefix?: React.ReactNode; + storyLabel?: React.ReactNode; } export interface Group { @@ -33,7 +33,7 @@ export interface Group { isComponent: boolean; isRoot: false; isLeaf: false; - prefix?: React.ReactNode; + storyLabel?: React.ReactNode; // MDX docs-only stories are "Group" type parameters?: { docsOnly?: boolean; @@ -52,7 +52,7 @@ export interface Story { isComponent: boolean; isRoot: false; isLeaf: true; - prefix?: React.ReactNode; + storyLabel?: React.ReactNode; parameters?: { fileName: string; options: { @@ -71,7 +71,6 @@ export interface StoryInput { refId?: string; kind: StoryKind; children: string[]; - prefix?: React.ReactNode; parameters: { fileName: string; options: { @@ -137,6 +136,15 @@ export const denormalizeStoryParameters = ({ })); }; +// The client call can return undefined, let's return something by default +const storyLabelSafe = ( + item: Root | Group | Story, + fn?: (item: Root | Group | Story) => React.ReactNode +) => { + const fnResult = fn(item); + return fnResult || item.name; +}; + export const transformStoriesRawToStoriesHash = ( input: StoriesRaw, { provider }: { provider: Provider } @@ -146,7 +154,7 @@ export const transformStoriesRawToStoriesHash = ( const storiesHashOutOfOrder = values.reduce((acc, item) => { const { kind, parameters } = item; - const { showRoots, storyPrefix = {} } = provider.getConfig(); + const { showRoots, sidebar = {} } = provider.getConfig(); const setShowRoots = typeof showRoots !== 'undefined'; if (usesOldHierarchySeparator && !setShowRoots) { @@ -171,7 +179,7 @@ export const transformStoriesRawToStoriesHash = ( } if (root.length && index === 0) { - list.push({ + const rootElement: Root = { id, name, depth: index, @@ -179,10 +187,10 @@ export const transformStoriesRawToStoriesHash = ( isComponent: false, isLeaf: false, isRoot: true, - prefix: storyPrefix[id], - }); + }; + list.push({ ...rootElement, storyLabel: sidebar.storyLabel?.(rootElement) }); } else { - list.push({ + const groupElement: Group = { id, name, parent, @@ -191,11 +199,14 @@ export const transformStoriesRawToStoriesHash = ( isComponent: false, isLeaf: false, isRoot: false, - prefix: storyPrefix[id], parameters: { docsOnly: parameters?.docsOnly, viewMode: parameters?.viewMode, }, + }; + list.push({ + ...groupElement, + storyLabel: sidebar.storyLabel?.(groupElement), }); } @@ -221,9 +232,8 @@ export const transformStoriesRawToStoriesHash = ( isLeaf: true, isComponent: false, isRoot: false, - prefix: storyPrefix[item.id], }; - acc[item.id] = story; + acc[item.id] = { ...story, storyLabel: sidebar.storyLabel?.(story) }; return acc; }, {} as StoriesHash); diff --git a/lib/api/src/modules/layout.ts b/lib/api/src/modules/layout.ts index 93f26915f730..f46c1b9b8494 100644 --- a/lib/api/src/modules/layout.ts +++ b/lib/api/src/modules/layout.ts @@ -4,7 +4,7 @@ import deepEqual from 'fast-deep-equal'; import { themes, ThemeVars } from '@storybook/theming'; import merge from '../lib/merge'; -import { State, ModuleFn } from '../index'; +import { State, ModuleFn, Root, Group, Story } from '../index'; export type PanelPositions = 'bottom' | 'right'; export type ActiveTabsType = 'sidebar' | 'canvas' | 'addons'; @@ -58,6 +58,10 @@ export interface UIOptions { addonPanelInRight: boolean; theme?: ThemeVars; selectedPanel?: string; + sidebar?: { + /** Used to render a custom label based on the current item */ + storyLabel?: (item: Root | Group | Story) => React.ReactNode | undefined; + }; } const defaultState: SubState = { diff --git a/lib/ui/src/components/sidebar/Tree.stories.tsx b/lib/ui/src/components/sidebar/Tree.stories.tsx index 725acbe2d3e6..326eb3197276 100644 --- a/lib/ui/src/components/sidebar/Tree.stories.tsx +++ b/lib/ui/src/components/sidebar/Tree.stories.tsx @@ -44,7 +44,7 @@ const singleStoryComponent = { isComponent: true, isLeaf: false, isRoot: false, - prefix: 🔥, + storyLabel: 🔥 Single, }, 'single--single': { id: 'single--single', @@ -59,7 +59,7 @@ const singleStoryComponent = { isLeaf: true, isComponent: false, isRoot: false, - prefix: 🔥, + storyLabel: 🔥 Single, }, }; diff --git a/lib/ui/src/components/sidebar/Tree.tsx b/lib/ui/src/components/sidebar/Tree.tsx index 54a0adaaab98..7f0811034c8f 100644 --- a/lib/ui/src/components/sidebar/Tree.tsx +++ b/lib/ui/src/components/sidebar/Tree.tsx @@ -92,8 +92,7 @@ const Node = React.memo( onSelectStoryId(item.id); }} > - {item.prefix} - {item.name} + {item.storyLabel || item.name} ); } @@ -108,10 +107,7 @@ const Node = React.memo( data-item-id={item.id} data-nodetype="root" > - - {item.prefix} - {item.name} - + {item.storyLabel || item.name} ( if (item.isComponent && !isExpanded) onSelectStoryId(item.id); }} > - {item.prefix} - {item.name} + {item.storyLabel || item.name} ); }