From 19adfd52bcb0090527a4aeaaffe6eb534c17622e Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Mon, 18 Jul 2022 11:16:12 +1000 Subject: [PATCH 1/6] Only load docs entries into the hash in docsMode --- lib/api/src/lib/stories.ts | 7 +- lib/api/src/modules/stories.ts | 9 +- lib/api/src/tests/stories.test.ts | 170 +++++++++++++++++++++++++----- 3 files changed, 151 insertions(+), 35 deletions(-) diff --git a/lib/api/src/lib/stories.ts b/lib/api/src/lib/stories.ts index 87b9c70f16f..08404b3af2c 100644 --- a/lib/api/src/lib/stories.ts +++ b/lib/api/src/lib/stories.ts @@ -268,10 +268,11 @@ export interface PreparedStoryIndex { export const transformSetStoriesStoryDataToStoriesHash = ( data: SetStoriesStoryData, - { provider }: { provider: Provider } + { provider, docsMode }: { provider: Provider; docsMode: boolean } ) => transformStoryIndexToStoriesHash(transformSetStoriesStoryDataToPreparedStoryIndex(data), { provider, + docsMode, }); const transformSetStoriesStoryDataToPreparedStoryIndex = ( @@ -339,8 +340,10 @@ export const transformStoryIndexToStoriesHash = ( index: PreparedStoryIndex, { provider, + docsMode, }: { provider: Provider; + docsMode: boolean; } ): StoriesHash => { if (!index.v) throw new Error('Composition: Missing stories.json version'); @@ -361,6 +364,8 @@ export const transformStoryIndexToStoriesHash = ( } const storiesHashOutOfOrder = Object.values(entryValues).reduce((acc, item) => { + if (docsMode && item.type !== 'docs') return acc; + // First, split the title into a set of names, separated by '/' and trimmed. const { title } = item; const groups = title.trim().split(TITLE_PATH_SEPARATOR); diff --git a/lib/api/src/modules/stories.ts b/lib/api/src/modules/stories.ts index 29ae812858a..7f8b791998c 100644 --- a/lib/api/src/modules/stories.ts +++ b/lib/api/src/modules/stories.ts @@ -39,7 +39,7 @@ import type { import type { Args, ModuleFn } from '../index'; import type { ComposedRef } from './refs'; -const { DOCS_MODE, FEATURES, fetch } = global; +const { FEATURES, fetch } = global; const STORY_INDEX_PATH = './index.json'; type Direction = -1 | 1; @@ -194,11 +194,6 @@ export const init: ModuleFn = ({ const { storiesHash, storyId, refs, refId } = store.getState(); const story = api.getData(storyId, refId); - if (DOCS_MODE) { - api.jumpToComponent(direction); - return; - } - // cannot navigate when there's no current selection if (!story) { return; @@ -215,6 +210,7 @@ export const init: ModuleFn = ({ // Now create storiesHash by reordering the above by group const hash = transformSetStoriesStoryDataToStoriesHash(input, { provider, + docsMode: global.DOCS_MODE, }); await store.setState({ @@ -362,6 +358,7 @@ export const init: ModuleFn = ({ setStoryList: async (storyIndex: StoryIndex) => { const hash = transformStoryIndexToStoriesHash(storyIndex, { provider, + docsMode: global.DOCS_MODE, }); await store.setState({ diff --git a/lib/api/src/tests/stories.test.ts b/lib/api/src/tests/stories.test.ts index 2d5ef2397d9..5daf43d8bcc 100644 --- a/lib/api/src/tests/stories.test.ts +++ b/lib/api/src/tests/stories.test.ts @@ -1181,36 +1181,59 @@ describe('stories API', () => { expect(storedStoriesHash['component-c--story-4'].type).toBe('story'); }); - // Skip this for now, will come back soon - it.skip('prefers parameters.docsOnly to inferred docsOnly status', async () => { - mockStories.mockReset().mockReturnValue({ - 'component-a--docs': { - type: 'story', - title: 'Component A', - name: 'Docs', // Called 'Docs' rather than 'Page' - importPath: './path/to/component-a.ts', - parameters: { - docsOnly: true, + describe('docMode = true', () => { + beforeEach(() => { + global.DOCS_MODE = true; + }); + afterEach(() => { + global.DOCS_MODE = false; + }); + + it('strips out story entries', async () => { + mockStories.mockReset().mockReturnValue({ + 'component-a--page': { + id: 'component-a--page', + title: 'Component A', + name: 'Page', + importPath: './path/to/component-a.ts', }, - }, + 'component-a--story-2': { + id: 'component-a--story-2', + title: 'Component A', + name: 'Story 2', + importPath: './path/to/component-a.ts', + }, + 'component-b': { + type: 'docs', + id: 'component-b--docs', + title: 'Component B', + name: 'Docs', + importPath: './path/to/component-b.ts', + storiesImports: [], + }, + 'component-c--story-4': { + id: 'component-c--story-4', + title: 'Component c', + name: 'Story 4', + importPath: './path/to/component-c.ts', + }, + }); + + const navigate = jest.fn(); + const store = createMockStore(); + const fullAPI = Object.assign(new EventEmitter(), { + setStories: jest.fn(), + }); + + const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + Object.assign(fullAPI, api); + + await init(); + + const { storiesHash: storedStoriesHash } = store.getState(); + + expect(Object.keys(storedStoriesHash)).toEqual(['component-b', 'component-b--docs']); }); - - const navigate = jest.fn(); - const store = createMockStore(); - const fullAPI = Object.assign(new EventEmitter(), { - setStories: jest.fn(), - }); - - const { api, init } = initStories({ store, navigate, provider, fullAPI }); - Object.assign(fullAPI, api); - - await init(); - - const { storiesHash: storedStoriesHash } = store.getState(); - - // We need exact key ordering, even if in theory JS doesn't guarantee it - expect(Object.keys(storedStoriesHash)).toEqual(['component-a', 'component-a--docs']); - expect(storedStoriesHash['component-a--docs'].parameters.docsOnly).toBe(true); }); }); @@ -1354,6 +1377,97 @@ describe('stories API', () => { }); }); + it('prefers parameters.docsOnly to inferred docsOnly status', async () => { + const navigate = jest.fn(); + const store = createMockStore(); + const fullAPI = Object.assign(new EventEmitter(), { + setOptions: jest.fn(), + findRef: jest.fn(), + }); + + const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + Object.assign(fullAPI, api); + + await init(); + + const setStoriesPayload = { + v: 2, + globalParameters: { global: 'global' }, + kindParameters: { a: { kind: 'kind' } }, + stories: { + 'component-a--docs': { + type: 'story', + kind: 'Component A', + name: 'Docs', // Called 'Docs' rather than 'Page' + importPath: './path/to/component-a.ts', + parameters: { + docsOnly: true, + }, + }, + }, + }; + fullAPI.emit(SET_STORIES, setStoriesPayload); + + const { storiesHash: storedStoriesHash } = store.getState(); + expect(storedStoriesHash['component-a--docs']).toMatchObject({ + type: 'docs', + id: 'component-a--docs', + parent: 'component-a', + title: 'Component A', + name: 'Docs', + }); + }); + + describe('when DOCS_MODE = true', () => { + beforeEach(() => { + global.DOCS_MODE = true; + }); + afterEach(() => { + global.DOCS_MODE = false; + }); + + it('strips out stories entries', async () => { + const navigate = jest.fn(); + const store = createMockStore(); + const fullAPI = Object.assign(new EventEmitter(), { + setOptions: jest.fn(), + findRef: jest.fn(), + }); + + const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + Object.assign(fullAPI, api); + + await init(); + + const setStoriesPayload = { + v: 2, + globalParameters: { global: 'global' }, + kindParameters: { a: { kind: 'kind' } }, + stories: { + 'component-a--docs': { + type: 'story', + kind: 'Component A', + name: 'Docs', // Called 'Docs' rather than 'Page' + importPath: './path/to/component-a.ts', + parameters: { + docsOnly: true, + }, + }, + 'component-a--story': { + title: 'Story', + kind: 'Component A', + importPath: './path/to/component-a.ts', + parameters: { story: 'story' }, + }, + }, + }; + fullAPI.emit(SET_STORIES, setStoriesPayload); + + const { storiesHash: storedStoriesHash } = store.getState(); + expect(Object.keys(storedStoriesHash)).toEqual(['component-a', 'component-a--docs']); + }); + }); + it('normalizes parameters and calls setRef for external stories', () => { const fullAPI = Object.assign(new EventEmitter()); const navigate = jest.fn(); From b9432bbc31e0a233656ba5f3d29d9df6b90d7e2f Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Mon, 18 Jul 2022 11:19:12 +1000 Subject: [PATCH 2/6] No need for `collapse` any more! --- lib/ui/src/components/sidebar/Refs.tsx | 2 +- .../src/components/sidebar/Search.stories.tsx | 2 +- lib/ui/src/components/sidebar/Search.tsx | 2 +- .../components/sidebar/Sidebar.stories.tsx | 3 +- lib/ui/src/components/sidebar/Sidebar.tsx | 25 +--- .../src/components/sidebar/Tree.stories.tsx | 2 +- .../components/sidebar/__tests__/data.test.ts | 130 ------------------ lib/ui/src/components/sidebar/data.ts | 69 ---------- lib/ui/src/components/sidebar/utils.ts | 2 +- 9 files changed, 10 insertions(+), 227 deletions(-) delete mode 100644 lib/ui/src/components/sidebar/__tests__/data.test.ts delete mode 100644 lib/ui/src/components/sidebar/data.ts diff --git a/lib/ui/src/components/sidebar/Refs.tsx b/lib/ui/src/components/sidebar/Refs.tsx index 763a7944c40..1771def7434 100644 --- a/lib/ui/src/components/sidebar/Refs.tsx +++ b/lib/ui/src/components/sidebar/Refs.tsx @@ -14,7 +14,7 @@ import { AuthBlock, ErrorBlock, LoaderBlock, EmptyBlock } from './RefBlocks'; import { RefIndicator } from './RefIndicator'; import { Tree } from './Tree'; import { CollapseIcon } from './TreeNode'; -import { DEFAULT_REF_ID } from './data'; +import { DEFAULT_REF_ID } from './Sidebar'; import { Highlight, RefType } from './types'; import { getStateType } from './utils'; diff --git a/lib/ui/src/components/sidebar/Search.stories.tsx b/lib/ui/src/components/sidebar/Search.stories.tsx index abf551b84ab..909c9fde395 100644 --- a/lib/ui/src/components/sidebar/Search.stories.tsx +++ b/lib/ui/src/components/sidebar/Search.stories.tsx @@ -5,7 +5,7 @@ import { stories } from './mockdata.large'; import { Search } from './Search'; import { SearchResults } from './SearchResults'; import { noResults } from './SearchResults.stories'; -import { DEFAULT_REF_ID } from './data'; +import { DEFAULT_REF_ID } from './Sidebar'; import { Selection } from './types'; const refId = DEFAULT_REF_ID; diff --git a/lib/ui/src/components/sidebar/Search.tsx b/lib/ui/src/components/sidebar/Search.tsx index 76bc3bdd7e3..02ed05e9376 100644 --- a/lib/ui/src/components/sidebar/Search.tsx +++ b/lib/ui/src/components/sidebar/Search.tsx @@ -7,7 +7,7 @@ import global from 'global'; import { transparentize } from 'polished'; import React, { useMemo, useRef, useState, useCallback } from 'react'; -import { DEFAULT_REF_ID } from './data'; +import { DEFAULT_REF_ID } from './Sidebar'; import { CombinedDataset, SearchItem, diff --git a/lib/ui/src/components/sidebar/Sidebar.stories.tsx b/lib/ui/src/components/sidebar/Sidebar.stories.tsx index 36590a09d8a..029a7352ed4 100644 --- a/lib/ui/src/components/sidebar/Sidebar.stories.tsx +++ b/lib/ui/src/components/sidebar/Sidebar.stories.tsx @@ -1,9 +1,8 @@ import React from 'react'; -import { Sidebar } from './Sidebar'; +import { Sidebar, DEFAULT_REF_ID } from './Sidebar'; import { standardData as standardHeaderData } from './Heading.stories'; import { mockDataset } from './mockdata'; -import { DEFAULT_REF_ID } from './data'; import { RefType } from './types'; export default { diff --git a/lib/ui/src/components/sidebar/Sidebar.tsx b/lib/ui/src/components/sidebar/Sidebar.tsx index 9171d90cb13..d3714c33f37 100644 --- a/lib/ui/src/components/sidebar/Sidebar.tsx +++ b/lib/ui/src/components/sidebar/Sidebar.tsx @@ -3,18 +3,17 @@ import React, { FunctionComponent, useMemo } from 'react'; import { styled } from '@storybook/theming'; import { ScrollArea, Spaced } from '@storybook/components'; -import type { StoriesHash, State, ComposedRef } from '@storybook/api'; +import type { StoriesHash, State } from '@storybook/api'; import { Heading } from './Heading'; -import { DEFAULT_REF_ID, collapseAllStories } from './data'; import { Explorer } from './Explorer'; import { Search } from './Search'; import { SearchResults } from './SearchResults'; import { Refs, CombinedDataset, Selection } from './types'; import { useLastViewed } from './useLastViewed'; -const { DOCS_MODE } = global; +export const DEFAULT_REF_ID = 'storybook_internal'; const Container = styled.nav({ position: 'absolute', @@ -92,7 +91,7 @@ export const Sidebar: FunctionComponent = React.memo( ({ storyId = null, refId = DEFAULT_REF_ID, - stories: storiesHash, + stories, storiesConfigured, storiesFailed, menu, @@ -100,25 +99,9 @@ export const Sidebar: FunctionComponent = React.memo( enableShortcuts = true, refs = {}, }) => { - const collapseFn = DOCS_MODE ? collapseAllStories : (x: StoriesHash) => x; const selected: Selection = useMemo(() => storyId && { storyId, refId }, [storyId, refId]); - const stories = useMemo(() => collapseFn(storiesHash), [DOCS_MODE, storiesHash]); - const adaptedRefs = useMemo(() => { - return Object.entries(refs).reduce((acc: Refs, [id, ref]: [string, ComposedRef]) => { - if (ref.stories) { - acc[id] = { - ...ref, - stories: collapseFn(ref.stories), - }; - } else { - acc[id] = ref; - } - return acc; - }, {}); - }, [DOCS_MODE, refs]); - - const dataset = useCombination(stories, storiesConfigured, storiesFailed, adaptedRefs); + const dataset = useCombination(stories, storiesConfigured, storiesFailed, refs); const isLoading = !dataset.hash[DEFAULT_REF_ID].ready; const lastViewedProps = useLastViewed(selected); diff --git a/lib/ui/src/components/sidebar/Tree.stories.tsx b/lib/ui/src/components/sidebar/Tree.stories.tsx index 43dc7910736..d6fe104489e 100644 --- a/lib/ui/src/components/sidebar/Tree.stories.tsx +++ b/lib/ui/src/components/sidebar/Tree.stories.tsx @@ -6,7 +6,7 @@ import { screen } from '@testing-library/dom'; import { Tree } from './Tree'; import { stories } from './mockdata.large'; -import { DEFAULT_REF_ID } from './data'; +import { DEFAULT_REF_ID } from './Sidebar'; export default { component: Tree, diff --git a/lib/ui/src/components/sidebar/__tests__/data.test.ts b/lib/ui/src/components/sidebar/__tests__/data.test.ts deleted file mode 100644 index a8fa0b8ff47..00000000000 --- a/lib/ui/src/components/sidebar/__tests__/data.test.ts +++ /dev/null @@ -1,130 +0,0 @@ -import type { StoriesHash } from '@storybook/api'; -import { collapseAllStories } from '../data'; - -type Item = StoriesHash[keyof StoriesHash]; - -const root: Item = { - type: 'root', - id: 'root', - name: 'root', - depth: 0, - children: ['a', 'b'], -}; -const a: Item = { - type: 'component', - id: 'a', - name: 'a', - depth: 1, - parent: 'root', - children: ['a1'], -}; -const a1: Item = { - type: 'story', - id: 'a1', - name: 'a1', - title: 'a', - depth: 2, - parent: 'a', - args: {}, - prepared: true, - importPath: './a.js', -}; -const b: Item = { - type: 'component', - id: 'b', - name: 'b', - depth: 1, - parent: 'root', - children: ['b1', 'b2'], -}; -const b1: Item = { - type: 'story', - id: 'b1', - name: 'b1', - title: 'b', - depth: 2, - parent: 'b', - args: {}, - prepared: true, - importPath: './b1.js', -}; -const b2: Item = { - type: 'story', - id: 'b2', - name: 'b2', - title: 'b', - depth: 2, - parent: 'b', - args: {}, - prepared: true, - importPath: './b2.js', -}; - -const stories: StoriesHash = { root, a, a1, b, b1, b2 }; - -describe('collapse all stories', () => { - it('collapses normal stories', () => { - const collapsed = collapseAllStories(stories); - - const expected: StoriesHash = { - a1: { - type: 'story', - id: 'a1', - depth: 1, - name: 'a', - title: 'a', - parent: 'root', - args: {}, - prepared: true, - importPath: './a.js', - }, - b1: { - type: 'story', - id: 'b1', - depth: 1, - name: 'b', - title: 'b', - parent: 'root', - args: {}, - prepared: true, - importPath: './b1.js', - }, - root: { - type: 'root', - id: 'root', - name: 'root', - depth: 0, - children: ['a1', 'b1'], - }, - }; - - expect(collapsed).toEqual(expected); - }); - - it('collapses docs-only stories', () => { - const hasDocsOnly: StoriesHash = { - ...stories, - a1: { - type: 'docs', - id: 'a1', - name: 'a1', - title: 'a', - depth: 2, - parent: 'a', - importPath: './a.js', - }, - }; - - const collapsed = collapseAllStories(hasDocsOnly); - - expect(collapsed.a1).toEqual({ - type: 'docs', - id: 'a1', - name: 'a', - title: 'a', - depth: 1, - parent: 'root', - importPath: './a.js', - }); - }); -}); diff --git a/lib/ui/src/components/sidebar/data.ts b/lib/ui/src/components/sidebar/data.ts deleted file mode 100644 index 397bef64ad4..00000000000 --- a/lib/ui/src/components/sidebar/data.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type { HashEntry, StoriesHash, StoryEntry } from '@storybook/api'; -import { Item } from './types'; - -export const DEFAULT_REF_ID = 'storybook_internal'; - -function isLeaf(entry: HashEntry) { - return entry.type === 'story' || entry.type === 'docs'; -} - -export const collapseAllStories = (stories: StoriesHash) => { - // keep track of component IDs that have been rewritten to the ID of their first leaf child - const componentIdToLeafId: Record = {}; - - // 1) remove all leaves - const leavesRemoved = Object.values(stories).filter((item) => !isLeaf(item)); - - // 2) make all components leaves and rewrite their ID's to the first leaf child - const componentsFlattened = leavesRemoved.map((item: HashEntry) => { - // this is a folder, so just leave it alone - if (item.type !== 'component') { - return item; - } - - const { id, children, name, parent, depth } = item; - - const nonLeafChildren: string[] = []; - const leafChildren: string[] = []; - children.forEach((child: string) => - (isLeaf(stories[child]) ? leafChildren : nonLeafChildren).push(child) - ); - - if (leafChildren.length === 0) { - return item; // pass through, we'll handle you later - } - - const leaf = stories[leafChildren[0]] as StoryEntry; - const component = { - ...leaf, - name, - parent, - depth, - }; - componentIdToLeafId[id] = leaf.id; - - // this is a component, so it should not have any non-leaf children - if (nonLeafChildren.length !== 0) { - throw new Error(`Unexpected '${item.id}': ${JSON.stringify({ nonLeafChildren })}`); - } - - return component; - }); - - // 3) rewrite all the children as needed - const childrenRewritten = componentsFlattened.map((item) => { - if (item.type === 'root' || item.type === 'group' || item.type === 'component') { - const { children, ...rest } = item; - const rewritten = children.map((child: string) => componentIdToLeafId[child] || child); - - return { children: rewritten, ...rest }; - } - return item; - }); - - const result = {} as StoriesHash; - childrenRewritten.forEach((item) => { - result[item.id] = item as Item; - }); - return result; -}; diff --git a/lib/ui/src/components/sidebar/utils.ts b/lib/ui/src/components/sidebar/utils.ts index bb503107236..f6f8f38cfe9 100644 --- a/lib/ui/src/components/sidebar/utils.ts +++ b/lib/ui/src/components/sidebar/utils.ts @@ -3,7 +3,7 @@ import global from 'global'; import { SyntheticEvent } from 'react'; import type { StoriesHash } from '@storybook/api'; -import { DEFAULT_REF_ID } from './data'; +import { DEFAULT_REF_ID } from './Sidebar'; import { Item, RefType, Dataset, SearchItem } from './types'; const { document, window: globalWindow, DOCS_MODE } = global; From 285214671d2d6f7dd08b37563119a42e1008ef4f Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Mon, 18 Jul 2022 12:01:23 +1000 Subject: [PATCH 3/6] Remove (almost all) references to `DOCS_MODE` directly --- lib/api/src/index.tsx | 22 +++---------- lib/api/src/modules/layout.ts | 6 ++-- lib/api/src/modules/stories.ts | 5 +-- lib/api/src/tests/layout.test.js | 1 - lib/api/src/tests/stories.test.ts | 32 +++++++++---------- .../src/components/sidebar/SearchResults.tsx | 27 ++++++---------- lib/ui/src/components/sidebar/Tree.tsx | 2 +- lib/ui/src/components/sidebar/utils.ts | 9 +++--- 8 files changed, 39 insertions(+), 65 deletions(-) diff --git a/lib/api/src/index.tsx b/lib/api/src/index.tsx index a9f62cb9eb5..964b1308e48 100644 --- a/lib/api/src/index.tsx +++ b/lib/api/src/index.tsx @@ -106,11 +106,11 @@ export interface Combo { interface ProviderData { provider: provider.Provider; + docsMode: boolean; } export type ManagerProviderProps = RouterData & ProviderData & { - docsMode: boolean; children: ReactNode | ((props: Combo) => ReactNode); }; @@ -189,22 +189,9 @@ class ManagerProvider extends Component { setState: (stateChange: Partial, callback) => this.setState(stateChange, callback), }); - const routeData = { location, path, viewMode, singleStory, storyId, refId }; + const routeData = { location, path, viewMode, singleStory, storyId, refId, docsMode }; - // Initialize the state to be the initial (persisted) state of the store. - // This gives the modules the chance to read the persisted state, apply their defaults - // and override if necessary - const docsModeState = { - layout: { showToolbar: false, showPanel: false }, - ui: { docsMode: true }, - }; - - this.state = store.getInitialState( - getInitialState({ - ...routeData, - ...(docsMode ? docsModeState : null), - }) - ); + this.state = store.getInitialState(getInitialState(routeData)); const apiData = { navigate, @@ -245,8 +232,7 @@ class ManagerProvider extends Component { location: props.location, path: props.path, refId: props.refId, - // if its a docsOnly page, even the 'story' view mode is considered 'docs' - viewMode: (props.docsMode && props.viewMode) === 'story' ? 'docs' : props.viewMode, + viewMode: props.viewMode, storyId: props.storyId, }; } diff --git a/lib/api/src/modules/layout.ts b/lib/api/src/modules/layout.ts index c0e3c53d9b6..0cd7c12f608 100644 --- a/lib/api/src/modules/layout.ts +++ b/lib/api/src/modules/layout.ts @@ -10,7 +10,7 @@ import { dedent } from 'ts-dedent'; import merge from '../lib/merge'; import type { State, ModuleFn } from '../index'; -const { DOCS_MODE, document } = global; +const { document } = global; export type PanelPositions = 'bottom' | 'right'; export type ActiveTabsType = 'sidebar' | 'canvas' | 'addons'; @@ -38,7 +38,6 @@ export interface UI { name?: string; url?: string; enableShortcuts: boolean; - docsMode: boolean; } export interface SubState { @@ -73,11 +72,10 @@ export interface UIOptions { const defaultState: SubState = { ui: { enableShortcuts: true, - docsMode: false, }, layout: { initialActive: ActiveTabs.CANVAS, - showToolbar: !DOCS_MODE, + showToolbar: true, isFullscreen: false, showPanel: true, showNav: true, diff --git a/lib/api/src/modules/stories.ts b/lib/api/src/modules/stories.ts index 7f8b791998c..9b9de197822 100644 --- a/lib/api/src/modules/stories.ts +++ b/lib/api/src/modules/stories.ts @@ -122,6 +122,7 @@ export const init: ModuleFn = ({ provider, storyId: initialStoryId, viewMode: initialViewMode, + docsMode, }) => { const api: SubAPI = { storyId: toId, @@ -210,7 +211,7 @@ export const init: ModuleFn = ({ // Now create storiesHash by reordering the above by group const hash = transformSetStoriesStoryDataToStoriesHash(input, { provider, - docsMode: global.DOCS_MODE, + docsMode, }); await store.setState({ @@ -358,7 +359,7 @@ export const init: ModuleFn = ({ setStoryList: async (storyIndex: StoryIndex) => { const hash = transformStoryIndexToStoriesHash(storyIndex, { provider, - docsMode: global.DOCS_MODE, + docsMode, }); await store.setState({ diff --git a/lib/api/src/tests/layout.test.js b/lib/api/src/tests/layout.test.js index fb4c791437b..f2032ba1e11 100644 --- a/lib/api/src/tests/layout.test.js +++ b/lib/api/src/tests/layout.test.js @@ -10,7 +10,6 @@ beforeEach(() => { currentState = { ui: { enableShortcuts: true, - docsMode: false, }, layout: { showToolbar: true, diff --git a/lib/api/src/tests/stories.test.ts b/lib/api/src/tests/stories.test.ts index 5daf43d8bcc..e80f6ba3cb6 100644 --- a/lib/api/src/tests/stories.test.ts +++ b/lib/api/src/tests/stories.test.ts @@ -1181,14 +1181,7 @@ describe('stories API', () => { expect(storedStoriesHash['component-c--story-4'].type).toBe('story'); }); - describe('docMode = true', () => { - beforeEach(() => { - global.DOCS_MODE = true; - }); - afterEach(() => { - global.DOCS_MODE = false; - }); - + describe('when DOCS_MODE = true', () => { it('strips out story entries', async () => { mockStories.mockReset().mockReturnValue({ 'component-a--page': { @@ -1225,7 +1218,13 @@ describe('stories API', () => { setStories: jest.fn(), }); - const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + const { api, init } = initStories({ + store, + navigate, + provider, + fullAPI, + docsMode: true, + } as any); Object.assign(fullAPI, api); await init(); @@ -1419,13 +1418,6 @@ describe('stories API', () => { }); describe('when DOCS_MODE = true', () => { - beforeEach(() => { - global.DOCS_MODE = true; - }); - afterEach(() => { - global.DOCS_MODE = false; - }); - it('strips out stories entries', async () => { const navigate = jest.fn(); const store = createMockStore(); @@ -1434,7 +1426,13 @@ describe('stories API', () => { findRef: jest.fn(), }); - const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + const { api, init } = initStories({ + store, + navigate, + provider, + fullAPI, + docsMode: true, + } as any); Object.assign(fullAPI, api); await init(); diff --git a/lib/ui/src/components/sidebar/SearchResults.tsx b/lib/ui/src/components/sidebar/SearchResults.tsx index 3dfbe036c7e..6053067ad47 100644 --- a/lib/ui/src/components/sidebar/SearchResults.tsx +++ b/lib/ui/src/components/sidebar/SearchResults.tsx @@ -22,7 +22,7 @@ import { import { getLink } from './utils'; import { matchesKeyCode, matchesModifiers } from '../../keybinding'; -const { document, DOCS_MODE } = global; +const { document } = global; const ResultsList = styled.ol({ listStyle: 'none', @@ -148,24 +148,17 @@ const Result: FunctionComponent< ); const title = `${item.path.join(' / ')} / ${item.name}`; - if (DOCS_MODE) { - return ( - - - {label} - - - ); + const nodeProps = { depth: 0, onClick: click, title, children: label }; + let node; + if (item.type === 'component') { + node = ; + } else if (item.type === 'story') { + node = ; + } else { + node = ; } - const TreeNode = item.type === 'component' ? ComponentNode : StoryNode; - return ( - - - {label} - - - ); + return {node}; }); export const SearchResults: FunctionComponent<{ diff --git a/lib/ui/src/components/sidebar/Tree.tsx b/lib/ui/src/components/sidebar/Tree.tsx index 01ef68f225b..c782e089df0 100644 --- a/lib/ui/src/components/sidebar/Tree.tsx +++ b/lib/ui/src/components/sidebar/Tree.tsx @@ -163,7 +163,7 @@ const Node = React.memo( data-selected={isSelected} data-highlightable={isDisplayed} depth={isOrphan ? item.depth : item.depth - 1} - href={getLink(item.id, refId)} + href={getLink(item, refId)} onClick={(event) => { event.preventDefault(); onSelectStoryId(item.id); diff --git a/lib/ui/src/components/sidebar/utils.ts b/lib/ui/src/components/sidebar/utils.ts index f6f8f38cfe9..93780d48833 100644 --- a/lib/ui/src/components/sidebar/utils.ts +++ b/lib/ui/src/components/sidebar/utils.ts @@ -1,19 +1,18 @@ import memoize from 'memoizerific'; import global from 'global'; import { SyntheticEvent } from 'react'; -import type { StoriesHash } from '@storybook/api'; +import type { HashEntry, StoriesHash } from '@storybook/api'; import { DEFAULT_REF_ID } from './Sidebar'; import { Item, RefType, Dataset, SearchItem } from './types'; -const { document, window: globalWindow, DOCS_MODE } = global; +const { document, window: globalWindow } = global; export const createId = (itemId: string, refId?: string) => !refId || refId === DEFAULT_REF_ID ? itemId : `${refId}_${itemId}`; -export const getLink = (itemId: string, refId?: string) => { - const type = DOCS_MODE ? 'docs' : 'story'; - return `${document.location.pathname}?path=/${type}/${createId(itemId, refId)}`; +export const getLink = (item: HashEntry, refId?: string) => { + return `${document.location.pathname}?path=/${item.type}/${createId(item.id, refId)}`; }; export const prevent = (e: SyntheticEvent) => { From f630174073b48a555ab32f80ea50aebeb7e61cb0 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Mon, 18 Jul 2022 12:14:49 +1000 Subject: [PATCH 4/6] Remove last use of `DOCS_MODE` --- lib/ui/src/components/sidebar/TreeNode.tsx | 28 +++++++++++-------- .../sidebar/__tests__/Sidebar.test.tsx | 2 -- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/lib/ui/src/components/sidebar/TreeNode.tsx b/lib/ui/src/components/sidebar/TreeNode.tsx index a64309c5d2f..ef2342e5a93 100644 --- a/lib/ui/src/components/sidebar/TreeNode.tsx +++ b/lib/ui/src/components/sidebar/TreeNode.tsx @@ -1,11 +1,9 @@ import { styled } from '@storybook/theming'; import type { Color, Theme } from '@storybook/theming'; import { Icons } from '@storybook/components'; -import global from 'global'; import { transparentize } from 'polished'; import React, { FunctionComponent, ComponentProps } from 'react'; - -const { DOCS_MODE } = global; +import { Combo, Consumer } from '@storybook/api'; export const CollapseIcon = styled.span<{ isExpanded: boolean }>(({ theme, isExpanded }) => ({ display: 'inline-block', @@ -24,13 +22,15 @@ export const CollapseIcon = styled.span<{ isExpanded: boolean }>(({ theme, isExp const iconColors = { light: { - document: DOCS_MODE ? 'secondary' : '#ff8300', + document: '#ff8300', + docsModeDocument: 'secondary', bookmarkhollow: 'seafoam', component: 'secondary', folder: 'ultraviolet', }, dark: { - document: DOCS_MODE ? 'secondary' : 'gold', + document: 'gold', + docsModeDocument: 'secondary', bookmarkhollow: 'seafoam', component: 'secondary', folder: 'primary', @@ -46,9 +46,11 @@ const TypeIcon = styled(Icons)( marginRight: 5, flex: '0 0 auto', }, - ({ theme, icon, symbol = icon }) => { + ({ theme, icon, symbol = icon, docsMode }) => { const colors = theme.base === 'dark' ? iconColors.dark : iconColors.light; - const color = colors[symbol as keyof typeof colors]; + const colorKey: keyof typeof colors = + docsMode && symbol === 'document' ? 'docsModeDocument' : symbol; + const color = colors[colorKey]; return { color: isColor(theme, color) ? theme.color[color] : color }; } ); @@ -164,10 +166,14 @@ export const ComponentNode: FunctionComponent> export const DocumentNode: FunctionComponent> = React.memo( ({ theme, children, ...props }) => ( - - - {children} - + ({ docsMode: state.docsMode })}> + {({ docsMode }) => ( + + + {children} + + )} + ) ); diff --git a/lib/ui/src/components/sidebar/__tests__/Sidebar.test.tsx b/lib/ui/src/components/sidebar/__tests__/Sidebar.test.tsx index 34aca73c345..f2199dd3d15 100644 --- a/lib/ui/src/components/sidebar/__tests__/Sidebar.test.tsx +++ b/lib/ui/src/components/sidebar/__tests__/Sidebar.test.tsx @@ -8,8 +8,6 @@ import type { RenderResult } from '@testing-library/react'; import { Sidebar } from '../Sidebar'; import type { SidebarProps } from '../Sidebar'; -global.DOCS_MODE = false; - const DOCS_NAME = 'Docs'; const factory = (props: Partial): RenderResult => { From a1fd7e24fa5310936cdf90aae55145855e35b5b4 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Mon, 18 Jul 2022 12:41:36 +1000 Subject: [PATCH 5/6] Unused import --- lib/ui/src/components/sidebar/Sidebar.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ui/src/components/sidebar/Sidebar.tsx b/lib/ui/src/components/sidebar/Sidebar.tsx index d3714c33f37..7be55266c6f 100644 --- a/lib/ui/src/components/sidebar/Sidebar.tsx +++ b/lib/ui/src/components/sidebar/Sidebar.tsx @@ -1,4 +1,3 @@ -import global from 'global'; import React, { FunctionComponent, useMemo } from 'react'; import { styled } from '@storybook/theming'; From efea2155260eddaeb6b08c7ace79932011ab5e31 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Mon, 18 Jul 2022 12:43:36 +1000 Subject: [PATCH 6/6] Pass docsMode from refs too --- lib/api/src/modules/refs.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/api/src/modules/refs.ts b/lib/api/src/modules/refs.ts index b8f207f4ce0..41369b466c1 100644 --- a/lib/api/src/modules/refs.ts +++ b/lib/api/src/modules/refs.ts @@ -133,7 +133,7 @@ const map = ( }; export const init: ModuleFn = ( - { store, provider, singleStory }, + { store, provider, singleStory, docsMode }, { runCheck = true } = {} ) => { const api: SubAPI = { @@ -249,12 +249,10 @@ export const init: ModuleFn = ( if (setStoriesData) { storiesHash = transformSetStoriesStoryDataToStoriesHash( map(setStoriesData, ref, { storyMapper }), - { - provider, - } + { provider, docsMode } ); } else if (storyIndex) { - storiesHash = transformStoryIndexToStoriesHash(storyIndex, { provider }); + storiesHash = transformStoryIndexToStoriesHash(storyIndex, { provider, docsMode }); } if (storiesHash) storiesHash = addRefIds(storiesHash, ref);