diff --git a/MIGRATION.md b/MIGRATION.md index 7de591ad2951..4175b5833d42 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -9,6 +9,7 @@ - [6.2 Deprecations](#62-deprecations) - [Deprecated implicit PostCSS loader](#deprecated-implicit-postcss-loader) - [Deprecated default PostCSS plugins](#deprecated-default-postcss-plugins) + - [Deprecated showRoots config option](#deprecated-showroots-config-option) - [From version 6.0.x to 6.1.0](#from-version-60x-to-610) - [Addon-backgrounds preset](#addon-backgrounds-preset) - [Single story hoisting](#single-story-hoisting) @@ -230,6 +231,21 @@ module.exports = { }; ``` +#### Deprecated showRoots config option + +Config options for the sidebar are now under the `sidebar` namespace. The `showRoots` option should be set as follows: + +```js +addons.setConfig({ + sidebar: { + showRoots: false, + }, + // showRoots: false <- this is deprecated +}); +``` + +The top-level `showRoots` option will be removed in Storybook 7.0. + ## From version 6.0.x to 6.1.0 ### Addon-backgrounds preset diff --git a/docs/configure/features-and-behavior.md b/docs/configure/features-and-behavior.md index 2dc5b27049f7..b6b0596f7c5a 100644 --- a/docs/configure/features-and-behavior.md +++ b/docs/configure/features-and-behavior.md @@ -22,11 +22,17 @@ The following table details how to use the API values: | **showNav** | Boolean |Display panel that shows a list of stories |`true` | | **showPanel** | Boolean |Display panel that shows addon configurations |`true` | | **panelPosition** | String/Object |Where to show the addon panel |`bottom` or `right` | -| **sidebarAnimations** | Boolean |Sidebar tree animations |`true` | | **enableShortcuts** | Boolean |Enable/disable shortcuts |`true` | | **isToolshown** | String |Show/hide tool bar |`true` | | **theme** | Object |Storybook Theme, see next section |`undefined` | | **selectedPanel** | String |Id to select an addon panel |`my-panel` | -| **initialActive** | String |Select the default active tab on Mobile. |`sidebar` or `canvas` or `addons` | -| **showRoots** | Boolean |Display the top-level grouping as a "root" in the sidebar |`false` | +| **initialActive** | String |Select the default active tab on Mobile |`sidebar` or `canvas` or `addons` | +| **sidebar** | Object |Sidebar options, see below |`{ showRoots: false }` | +The following options are configurable under the `sidebar` namespace: + +| Name | Type | Description | Example Value | +| ----------------------|:-------------:|:-------------------------------------------------------------:|:----------------------------------------------:| +| **showRoots** | Boolean |Display the top-level nodes as a "root" in the sidebar |`false` | +| **collapsedRoots** | Array |Set of root node IDs to visually collapse by default |`['misc', 'other']` | +| **renderLabel** | Function |Create a custom label for tree nodes; must return a ReactNode |`(item) => {item.name}`| diff --git a/docs/configure/sidebar-and-urls.md b/docs/configure/sidebar-and-urls.md index 161ad961aa0b..bcf9cb2d3161 100644 --- a/docs/configure/sidebar-and-urls.md +++ b/docs/configure/sidebar-and-urls.md @@ -10,11 +10,11 @@ We recommend using a nesting scheme that mirrors the filesystem path of the comp ## Roots -By default, Storybook will treat your highest level of groups as “roots”--which are displayed in the UI as “sections” of the hierarchy. Lower level groups are displayed as expandable items in the hierarchy: +By default, Storybook will treat your top-level nodes as “roots”. Roots are displayed in the UI as “sections” of the hierarchy. Lower level groups are displayed as folders: ![Storybook sidebar story roots](./sidebar-roots.jpg) -If you’d prefer all groups to be expandable, you can set the `showRoots` option to `false` in [`./storybook/manager.js`](./overview.md#configure-story-rendering): +If you’d prefer to show top-level nodes as folders rather than roots, you can set the `sidebar.showRoots` option to `false` in [`./storybook/manager.js`](./overview.md#configure-story-rendering): @@ -30,7 +30,6 @@ If you’d prefer all groups to be expandable, you can set the `showRoots` optio As a CSF file is a JavaScript file, the exports (including the default export) can be generated dynamically. In particular you can use the `__dirname` variable to generate the title based on the path name (this example uses the paths.macro): - - ## Permalinking to stories By default, Storybook generates an `id` for each story based on the component title and the story name. This `id` in particular is used in the URL for each story and that URL can serve as a permalink (especially when you [publish](../workflows/publish-storybook.md) your Storybook). diff --git a/docs/snippets/common/storybook-config-layout.js.mdx b/docs/snippets/common/storybook-config-layout.js.mdx index 6f06abb551c7..a73cbd0ef46c 100644 --- a/docs/snippets/common/storybook-config-layout.js.mdx +++ b/docs/snippets/common/storybook-config-layout.js.mdx @@ -8,12 +8,14 @@ addons.setConfig({ showNav: true, showPanel: true, panelPosition: 'bottom', - sidebarAnimations: true, enableShortcuts: true, isToolshown: true, theme: undefined, selectedPanel: undefined, initialActive: 'sidebar', - showRoots: false, + sidebar: { + showRoots: false, + collapsedRoots: ['other'], + }, }); -``` \ No newline at end of file +``` diff --git a/docs/snippets/common/storybook-manager-disable-roots.js.mdx b/docs/snippets/common/storybook-manager-disable-roots.js.mdx index 5776c86769c8..3cf13c00b4a6 100644 --- a/docs/snippets/common/storybook-manager-disable-roots.js.mdx +++ b/docs/snippets/common/storybook-manager-disable-roots.js.mdx @@ -2,5 +2,9 @@ // ./storybook/manager.js import { addons } from '@storybook/addons'; -addons.setConfig({ showRoots: false }); +addons.setConfig({ + sidebar: { + showRoots: false, + }, +}); ``` diff --git a/examples/official-storybook/manager.js b/examples/official-storybook/manager.js index 2b983096e02b..b46efbc338c5 100644 --- a/examples/official-storybook/manager.js +++ b/examples/official-storybook/manager.js @@ -26,9 +26,9 @@ addons.setConfig({ hidden: true, }, }, - collapsedRoots: ['other'], sidebar: { - storyLabel: ({ id, name }) => { + collapsedRoots: ['other'], + renderLabel: ({ id, name }) => { const map = { addons: ( <> diff --git a/lib/api/src/lib/stories.ts b/lib/api/src/lib/stories.ts index d44212f60325..3983d06525f8 100644 --- a/lib/api/src/lib/stories.ts +++ b/lib/api/src/lib/stories.ts @@ -20,8 +20,8 @@ export interface Root { isComponent: false; isRoot: true; isLeaf: false; + label?: React.ReactNode; startCollapsed?: boolean; - storyLabel?: React.ReactNode; } export interface Group { @@ -34,7 +34,7 @@ export interface Group { isComponent: boolean; isRoot: false; isLeaf: false; - storyLabel?: React.ReactNode; + label?: React.ReactNode; // MDX docs-only stories are "Group" type parameters?: { docsOnly?: boolean; @@ -53,7 +53,7 @@ export interface Story { isComponent: boolean; isRoot: false; isLeaf: true; - storyLabel?: React.ReactNode; + label?: React.ReactNode; parameters?: { fileName: string; options: { @@ -115,6 +115,14 @@ export type SetStoriesPayload = stories: StoriesRaw; } & Record); +const warnLegacyShowRoots = deprecate( + () => {}, + dedent` + The 'showRoots' config option is deprecated and will be removed in Storybook 7.0. Use 'sidebar.showRoots' instead. + Read more about it in the migration guide: https://github.com/storybookjs/storybook/blob/master/MIGRATION.md + ` +); + const warnChangedDefaultHierarchySeparators = deprecate( () => {}, dedent` @@ -139,15 +147,6 @@ 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 } @@ -157,7 +156,12 @@ export const transformStoriesRawToStoriesHash = ( const storiesHashOutOfOrder = values.reduce((acc, item) => { const { kind, parameters } = item; - const { showRoots, collapsedRoots = [], sidebar = {} } = provider.getConfig(); + const { sidebar = {}, showRoots: deprecatedShowRoots } = provider.getConfig(); + const { showRoots = deprecatedShowRoots, collapsedRoots = [], renderLabel } = sidebar; + + if (typeof deprecatedShowRoots !== 'undefined') { + warnLegacyShowRoots(); + } const setShowRoots = typeof showRoots !== 'undefined'; if (usesOldHierarchySeparator && !setShowRoots) { @@ -192,7 +196,7 @@ export const transformStoriesRawToStoriesHash = ( isRoot: true, startCollapsed: collapsedRoots.includes(id), }; - list.push({ ...rootElement, storyLabel: sidebar.storyLabel?.(rootElement) }); + list.push({ ...rootElement, label: renderLabel?.(rootElement) }); } else { const groupElement: Group = { id, @@ -210,7 +214,7 @@ export const transformStoriesRawToStoriesHash = ( }; list.push({ ...groupElement, - storyLabel: sidebar.storyLabel?.(groupElement), + label: renderLabel?.(groupElement), }); } @@ -237,7 +241,7 @@ export const transformStoriesRawToStoriesHash = ( isComponent: false, isRoot: false, }; - acc[item.id] = { ...story, storyLabel: sidebar.storyLabel?.(story) }; + acc[item.id] = { ...story, label: renderLabel?.(story) }; return acc; }, {} as StoriesHash); diff --git a/lib/api/src/modules/layout.ts b/lib/api/src/modules/layout.ts index c8c2183cdbbb..41c4a16bdae0 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, Root, Group, Story } from '../index'; +import { State, ModuleFn } from '../index'; export type PanelPositions = 'bottom' | 'right'; export type ActiveTabsType = 'sidebar' | 'canvas' | 'addons'; @@ -27,7 +27,6 @@ export interface UI { name?: string; url?: string; enableShortcuts: boolean; - sidebarAnimations: boolean; docsMode: boolean; } @@ -58,16 +57,11 @@ 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 = { ui: { enableShortcuts: true, - sidebarAnimations: true, docsMode: false, }, layout: { diff --git a/lib/api/src/modules/provider.ts b/lib/api/src/modules/provider.ts index d79822034188..298546b1dcdd 100644 --- a/lib/api/src/modules/provider.ts +++ b/lib/api/src/modules/provider.ts @@ -2,10 +2,16 @@ import { ReactNode } from 'react'; import { Channel } from '@storybook/channels'; import { ThemeVars } from '@storybook/theming'; -import { API, State, ModuleFn } from '../index'; +import { API, State, ModuleFn, Root, Group, Story } from '../index'; import { StoryMapper, Refs } from './refs'; import { UIOptions } from './layout'; +interface SidebarOptions { + showRoots?: boolean; + collapsedRoots?: string[]; + renderLabel?: (item: Root | Group | Story) => ReactNode; +} + type IframeRenderer = ( storyId: string, viewMode: State['viewMode'], @@ -20,6 +26,7 @@ export interface Provider { renderPreview?: IframeRenderer; handleAPI(api: API): void; getConfig(): { + sidebar?: SidebarOptions; theme?: ThemeVars; refs?: Refs; StoryMapper?: StoryMapper; diff --git a/lib/api/src/modules/stories.ts b/lib/api/src/modules/stories.ts index 775b3254e829..aa9407152eba 100644 --- a/lib/api/src/modules/stories.ts +++ b/lib/api/src/modules/stories.ts @@ -77,7 +77,6 @@ interface Meta { } const deprecatedOptionsParameterWarnings: Record void> = [ - 'sidebarAnimations', 'enableShortcuts', 'theme', 'showRoots', diff --git a/lib/api/src/tests/layout.test.js b/lib/api/src/tests/layout.test.js index ed729cc06f42..30acdf9abcd6 100644 --- a/lib/api/src/tests/layout.test.js +++ b/lib/api/src/tests/layout.test.js @@ -16,7 +16,6 @@ describe('layout API', () => { currentState = { ui: { enableShortcuts: true, - sidebarAnimations: true, docsMode: false, }, layout: { diff --git a/lib/api/src/tests/stories.test.js b/lib/api/src/tests/stories.test.js index b700c9be51d6..d76257575a9c 100644 --- a/lib/api/src/tests/stories.test.js +++ b/lib/api/src/tests/stories.test.js @@ -93,7 +93,7 @@ describe('stories API', () => { api: { setStories }, } = initStories({ store, navigate, provider }); - provider.getConfig.mockReturnValue({ showRoots: false }); + provider.getConfig.mockReturnValue({ sidebar: { showRoots: false } }); setStories(storiesHash); const { storiesHash: storedStoriesHash } = store.getState(); @@ -255,7 +255,7 @@ describe('stories API', () => { api: { setStories }, } = initStories({ store, navigate, provider }); - provider.getConfig.mockReturnValue({ showRoots: true }); + provider.getConfig.mockReturnValue({ sidebar: { showRoots: true } }); setStories({ 'a-b--1': { kind: 'a/b', @@ -302,7 +302,7 @@ describe('stories API', () => { api: { setStories }, } = initStories({ store, navigate, provider }); - provider.getConfig.mockReturnValue({ showRoots: true }); + provider.getConfig.mockReturnValue({ sidebar: { showRoots: true } }); setStories({ 'a--1': { kind: 'a', diff --git a/lib/ui/src/components/sidebar/Tree.stories.tsx b/lib/ui/src/components/sidebar/Tree.stories.tsx index 326eb3197276..98a38247522e 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, - storyLabel: 🔥 Single, + label: 🔥 Single, }, 'single--single': { id: 'single--single', @@ -59,7 +59,7 @@ const singleStoryComponent = { isLeaf: true, isComponent: false, isRoot: false, - storyLabel: 🔥 Single, + label: 🔥 Single, }, }; diff --git a/lib/ui/src/components/sidebar/Tree.tsx b/lib/ui/src/components/sidebar/Tree.tsx index 401c59b55fc5..616724ccbec4 100644 --- a/lib/ui/src/components/sidebar/Tree.tsx +++ b/lib/ui/src/components/sidebar/Tree.tsx @@ -137,7 +137,7 @@ const Node = React.memo( onSelectStoryId(item.id); }} > - {item.storyLabel || item.name} + {item.label || item.name} ); } @@ -162,7 +162,7 @@ const Node = React.memo( }} > - {item.storyLabel || item.name} + {item.label || item.name} {isExpanded && ( ( if (item.isComponent && !isExpanded) onSelectStoryId(item.id); }} > - {item.storyLabel || item.name} + {item.label || item.name} ); }