From 6bd5099a6537db3386608832a2b879c8e7063262 Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Sun, 17 Mar 2024 00:57:42 +0800 Subject: [PATCH 1/4] Search: Add searchable headings to index --- code/lib/core-server/package.json | 2 +- .../src/utils/StoryIndexGenerator.test.ts | 62 +++++++++++++++++++ .../src/utils/StoryIndexGenerator.ts | 5 ++ .../utils/__tests__/index-extraction.test.ts | 9 +++ .../src/utils/stories-json.test.ts | 13 ++++ code/lib/types/src/modules/indexer.ts | 1 + code/yarn.lock | 10 +-- 7 files changed, 96 insertions(+), 6 deletions(-) diff --git a/code/lib/core-server/package.json b/code/lib/core-server/package.json index 6a2428523cb6..b3c8148e9578 100644 --- a/code/lib/core-server/package.json +++ b/code/lib/core-server/package.json @@ -64,7 +64,7 @@ "@storybook/core-events": "workspace:*", "@storybook/csf": "^0.1.2", "@storybook/csf-tools": "workspace:*", - "@storybook/docs-mdx": "3.0.0", + "@storybook/docs-mdx": "3.0.1--canary.16.72d7283.0", "@storybook/global": "^5.0.0", "@storybook/manager": "workspace:*", "@storybook/manager-api": "workspace:*", diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts index ad4aafd4a26e..324fd9817fc4 100644 --- a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts +++ b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts @@ -318,6 +318,9 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "b--docs": { + "headings": [ + "Story One", + ], "id": "b--docs", "importPath": "./src/B.stories.ts", "name": "docs", @@ -341,6 +344,9 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "d--docs": { + "headings": [ + "Story One", + ], "id": "d--docs", "importPath": "./src/D.stories.jsx", "name": "docs", @@ -374,6 +380,9 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "h--docs": { + "headings": [ + "Story One", + ], "id": "h--docs", "importPath": "./src/H.stories.mjs", "name": "docs", @@ -530,6 +539,9 @@ describe('StoryIndexGenerator', () => { { "entries": { "b--docs": { + "headings": [ + "Story One", + ], "id": "b--docs", "importPath": "./src/B.stories.ts", "name": "docs", @@ -542,6 +554,9 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "b--metaofnoname": { + "headings": [ + "Docs with of", + ], "id": "b--metaofnoname", "importPath": "./errors/MetaOfNoName.mdx", "name": "MetaOfNoName", @@ -589,6 +604,9 @@ describe('StoryIndexGenerator', () => { { "entries": { "b--docs": { + "headings": [ + "Story One", + ], "id": "b--docs", "importPath": "./src/B.stories.ts", "name": "docs", @@ -601,6 +619,9 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "b--name": { + "headings": [ + "Docs with of", + ], "id": "b--name", "importPath": "./errors/MetaOfName.mdx", "name": "name", @@ -652,6 +673,9 @@ describe('StoryIndexGenerator', () => { { "entries": { "a--docs": { + "headings": [ + "Docs with of", + ], "id": "a--docs", "importPath": "./errors/A.mdx", "name": "docs", @@ -696,6 +720,9 @@ describe('StoryIndexGenerator', () => { { "entries": { "duplicate-a--docs": { + "headings": [ + "Story One", + ], "id": "duplicate-a--docs", "importPath": "./duplicate/A.stories.js", "name": "docs", @@ -768,6 +795,9 @@ describe('StoryIndexGenerator', () => { { "entries": { "my-component-a--docs": { + "headings": [ + "Story One", + ], "id": "my-component-a--docs", "importPath": "./docs-id-generation/A.stories.jsx", "name": "docs", @@ -806,6 +836,9 @@ describe('StoryIndexGenerator', () => { { "entries": { "a--metaof": { + "headings": [ + "Docs with of", + ], "id": "a--metaof", "importPath": "./src/docs2/MetaOf.mdx", "name": "MetaOf", @@ -820,6 +853,9 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "a--second-docs": { + "headings": [ + "Second Docs", + ], "id": "a--second-docs", "importPath": "./src/docs2/SecondMetaOf.mdx", "name": "Second Docs", @@ -846,6 +882,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "componentreference--docs": { + "headings": [], "id": "componentreference--docs", "importPath": "./src/docs2/ComponentReference.mdx", "name": "docs", @@ -858,6 +895,9 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "docs2-yabbadabbadooo--docs": { + "headings": [ + "Docs with title", + ], "id": "docs2-yabbadabbadooo--docs", "importPath": "./src/docs2/Title.mdx", "name": "docs", @@ -870,6 +910,9 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "notitle--docs": { + "headings": [ + "Docs with no title", + ], "id": "notitle--docs", "importPath": "./src/docs2/NoTitle.mdx", "name": "docs", @@ -929,6 +972,9 @@ describe('StoryIndexGenerator', () => { { "entries": { "a--metaof": { + "headings": [ + "Docs with of", + ], "id": "a--metaof", "importPath": "./src/docs2/MetaOf.mdx", "name": "MetaOf", @@ -943,6 +989,9 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "a--second-docs": { + "headings": [ + "Second Docs", + ], "id": "a--second-docs", "importPath": "./src/docs2/SecondMetaOf.mdx", "name": "Second Docs", @@ -969,6 +1018,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "componentreference--info": { + "headings": [], "id": "componentreference--info", "importPath": "./src/docs2/ComponentReference.mdx", "name": "Info", @@ -981,6 +1031,9 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "docs2-yabbadabbadooo--info": { + "headings": [ + "Docs with title", + ], "id": "docs2-yabbadabbadooo--info", "importPath": "./src/docs2/Title.mdx", "name": "Info", @@ -993,6 +1046,9 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, "notitle--info": { + "headings": [ + "Docs with no title", + ], "id": "notitle--info", "importPath": "./src/docs2/NoTitle.mdx", "name": "Info", @@ -1047,6 +1103,9 @@ describe('StoryIndexGenerator', () => { "type": "story", }, "b--twostoryreferences": { + "headings": [ + "This file references two story files", + ], "id": "b--twostoryreferences", "importPath": "./complex/TwoStoryReferences.mdx", "name": "TwoStoryReferences", @@ -1085,6 +1144,9 @@ describe('StoryIndexGenerator', () => { { "entries": { "my-component-b--docs": { + "headings": [ + "Docs with of", + ], "id": "my-component-b--docs", "importPath": "./docs-id-generation/B.docs.mdx", "name": "docs", diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts index a650f04c1ffa..37f70203fd4e 100644 --- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts +++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts @@ -328,6 +328,7 @@ export class StoryIndexGenerator { type: 'docs', tags: [...metaTags, 'docs', ...(!hasAutodocsTag && !isStoriesMdx ? [AUTODOCS_TAG] : [])], storiesImports: [], + headings: indexInputs.map((input) => input.name).filter(Boolean) as string[], }); } @@ -357,8 +358,11 @@ export class StoryIndexGenerator { isTemplate?: boolean; imports?: Path[]; tags?: Tag[]; + headings?: string[]; } = analyze(content); + // console.log({ absolutePath, headings: result.headings }); + // Templates are not indexed if (result.isTemplate) return false; @@ -434,6 +438,7 @@ export class StoryIndexGenerator { type: 'docs', // FIXME: update this to use the index entry's metaTags once we update this to run on `IndexInputs` tags: [...(result.tags || []), csfEntry ? 'attached-mdx' : 'unattached-mdx', 'docs'], + headings: result.headings || [], }; return docsEntry; } catch (err) { diff --git a/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts b/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts index a1936c6719dd..838bb961382c 100644 --- a/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts +++ b/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts @@ -400,6 +400,9 @@ describe('docs entries from story extraction', () => { "dependents": [], "entries": [ { + "headings": [ + "Story One", + ], "id": "a--docs", "importPath": "./src/A.stories.js", "name": "docs", @@ -460,6 +463,9 @@ describe('docs entries from story extraction', () => { "dependents": [], "entries": [ { + "headings": [ + "Story One", + ], "id": "a--docs", "importPath": "./src/A.stories.js", "name": "docs", @@ -569,6 +575,9 @@ describe('docs entries from story extraction', () => { "dependents": [], "entries": [ { + "headings": [ + "Story One", + ], "id": "a--docs", "importPath": "./src/A.stories.js", "name": "docs", diff --git a/code/lib/core-server/src/utils/stories-json.test.ts b/code/lib/core-server/src/utils/stories-json.test.ts index a621f5443ce3..058dd14ce451 100644 --- a/code/lib/core-server/src/utils/stories-json.test.ts +++ b/code/lib/core-server/src/utils/stories-json.test.ts @@ -106,6 +106,9 @@ describe('useStoriesJson', () => { { "entries": { "a--metaof": { + "headings": [ + "Docs with of", + ], "id": "a--metaof", "importPath": "./src/docs2/MetaOf.mdx", "name": "MetaOf", @@ -120,6 +123,9 @@ describe('useStoriesJson', () => { "type": "docs", }, "a--second-docs": { + "headings": [ + "Second Docs", + ], "id": "a--second-docs", "importPath": "./src/docs2/SecondMetaOf.mdx", "name": "Second Docs", @@ -168,6 +174,7 @@ describe('useStoriesJson', () => { "type": "story", }, "docs2-componentreference--docs": { + "headings": [], "id": "docs2-componentreference--docs", "importPath": "./src/docs2/ComponentReference.mdx", "name": "docs", @@ -180,6 +187,9 @@ describe('useStoriesJson', () => { "type": "docs", }, "docs2-notitle--docs": { + "headings": [ + "Docs with no title", + ], "id": "docs2-notitle--docs", "importPath": "./src/docs2/NoTitle.mdx", "name": "docs", @@ -192,6 +202,9 @@ describe('useStoriesJson', () => { "type": "docs", }, "docs2-yabbadabbadooo--docs": { + "headings": [ + "Docs with title", + ], "id": "docs2-yabbadabbadooo--docs", "importPath": "./src/docs2/Title.mdx", "name": "docs", diff --git a/code/lib/types/src/modules/indexer.ts b/code/lib/types/src/modules/indexer.ts index 1064354eefe0..ea16bf77f686 100644 --- a/code/lib/types/src/modules/indexer.ts +++ b/code/lib/types/src/modules/indexer.ts @@ -83,6 +83,7 @@ export type StoryIndexEntry = BaseIndexEntry & { export type DocsIndexEntry = BaseIndexEntry & { storiesImports: Path[]; type: 'docs'; + headings: string[]; }; export type IndexEntry = StoryIndexEntry | DocsIndexEntry; diff --git a/code/yarn.lock b/code/yarn.lock index 763b21745403..88ed5387886e 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -5620,7 +5620,7 @@ __metadata: "@storybook/core-events": "workspace:*" "@storybook/csf": "npm:^0.1.2" "@storybook/csf-tools": "workspace:*" - "@storybook/docs-mdx": "npm:3.0.0" + "@storybook/docs-mdx": "npm:3.0.1--canary.16.72d7283.0" "@storybook/global": "npm:^5.0.0" "@storybook/manager": "workspace:*" "@storybook/manager-api": "workspace:*" @@ -5728,10 +5728,10 @@ __metadata: languageName: node linkType: hard -"@storybook/docs-mdx@npm:3.0.0": - version: 3.0.0 - resolution: "@storybook/docs-mdx@npm:3.0.0" - checksum: 4f4242fc05b57e8dc239204c71fd0d1481c9abbf20d12dd0f3dace74f77a7ff7cbe0bd07d7d785873b45747be64cad273423d3dc0cf89b52e9f117592a4b054f +"@storybook/docs-mdx@npm:3.0.1--canary.16.72d7283.0": + version: 3.0.1--canary.16.72d7283.0 + resolution: "@storybook/docs-mdx@npm:3.0.1--canary.16.72d7283.0" + checksum: b755fa240fefdea5fe3305d985bcc3d1f46d8ada3fc370b29884a45c9be229349fd4021780409f553739675da312dbcb6d535a6046171bafdd15f07a8ec9efa9 languageName: node linkType: hard From ad5574833511eefd9f9f154108ed1dfa5b4fe44c Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Wed, 4 Sep 2024 17:59:57 +0800 Subject: [PATCH 2/4] Use canary docs-mdx with headings support --- code/core/package.json | 2 +- code/yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/code/core/package.json b/code/core/package.json index 2da448ffa6e0..48ccf0332141 100644 --- a/code/core/package.json +++ b/code/core/package.json @@ -309,7 +309,7 @@ "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-scroll-area": "^1.0.5", "@radix-ui/react-slot": "^1.0.2", - "@storybook/docs-mdx": "4.0.0-next.1", + "@storybook/docs-mdx": "3.1.1--canary.16.129f63c.0", "@storybook/global": "^5.0.0", "@storybook/icons": "^1.2.10", "@tanstack/react-virtual": "^3.3.0", diff --git a/code/yarn.lock b/code/yarn.lock index 9a02fe0f6a5f..f7c41915686f 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -6106,7 +6106,7 @@ __metadata: "@radix-ui/react-scroll-area": "npm:^1.0.5" "@radix-ui/react-slot": "npm:^1.0.2" "@storybook/csf": "npm:^0.1.11" - "@storybook/docs-mdx": "npm:4.0.0-next.1" + "@storybook/docs-mdx": "npm:3.1.1--canary.16.129f63c.0" "@storybook/global": "npm:^5.0.0" "@storybook/icons": "npm:^1.2.10" "@tanstack/react-virtual": "npm:^3.3.0" @@ -6265,12 +6265,12 @@ __metadata: languageName: node linkType: hard -"@storybook/docs-mdx@npm:4.0.0-next.1": - version: 4.0.0-next.1 - resolution: "@storybook/docs-mdx@npm:4.0.0-next.1" +"@storybook/docs-mdx@npm:3.1.1--canary.16.129f63c.0": + version: 3.1.1--canary.16.129f63c.0 + resolution: "@storybook/docs-mdx@npm:3.1.1--canary.16.129f63c.0" dependencies: acorn: "npm:^8.12.1" - checksum: 10c0/8779279014a0a48c00d5884d310b3ca7828a49057c7403371e4eaf0fd053d8c93a412084cbbd6e5ea65e509e27f96752e8de7dadacdfa89198158b8b10deabdc + checksum: 10c0/bd3b009feee72958bb0b4af6f7ea5c6f24c7db73d7009f4cf1cf7dec3997ce724c064bc986b0c1b5e6885e3e944569f89f0c286d203895e949871a21cb51d0b4 languageName: node linkType: hard From 0812ec47684fb394e692c367599aff31f2fb9dbe Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Wed, 4 Sep 2024 18:31:56 +0800 Subject: [PATCH 3/4] WIP: connect indexEntry.headings to search --- .../src/manager/components/sidebar/Search.tsx | 11 +++- .../sidebar/SearchResults.stories.tsx | 23 +++++++- .../components/sidebar/SearchResults.tsx | 11 +++- .../manager/components/sidebar/TreeNode.tsx | 55 ++++++++++--------- .../src/manager/components/sidebar/types.ts | 7 ++- code/core/src/manager/utils/tree.ts | 4 +- code/core/src/types/modules/api-stories.ts | 1 + 7 files changed, 79 insertions(+), 33 deletions(-) diff --git a/code/core/src/manager/components/sidebar/Search.tsx b/code/core/src/manager/components/sidebar/Search.tsx index d09863a28aab..c83f525d2c26 100644 --- a/code/core/src/manager/components/sidebar/Search.tsx +++ b/code/core/src/manager/components/sidebar/Search.tsx @@ -43,8 +43,9 @@ const options = { maxPatternLength: 32, minMatchCharLength: 1, keys: [ - { name: 'name', weight: 0.7 }, + { name: 'name', weight: 0.6 }, { name: 'path', weight: 0.3 }, + { name: 'headings', weight: 0.1 }, ], } as FuseOptions; @@ -210,6 +211,9 @@ export const Search = React.memo<{ }; }) ); + // if(item.headings) { + // tems.headings. + // } } return acc; }, []); @@ -219,6 +223,7 @@ export const Search = React.memo<{ const getResults = useCallback( (input: string) => { const fuse = makeFuse(); + console.log({ fuse }); if (!input) { return []; @@ -226,7 +231,8 @@ export const Search = React.memo<{ let results: DownshiftItem[] = []; const resultIds: Set = new Set(); - const distinctResults = (fuse.search(input) as SearchResult[]).filter(({ item }) => { + const allResults = fuse.search(input) as SearchResult[]; + const distinctResults = allResults.filter(({ item }) => { if ( !(item.type === 'component' || item.type === 'docs' || item.type === 'story') || // @ts-expect-error (non strict) @@ -237,7 +243,6 @@ export const Search = React.memo<{ resultIds.add(item.id); return true; }); - if (distinctResults.length) { results = distinctResults.slice(0, allComponents ? 1000 : DEFAULT_MAX_SEARCH_RESULTS); if (distinctResults.length > DEFAULT_MAX_SEARCH_RESULTS && !allComponents) { diff --git a/code/core/src/manager/components/sidebar/SearchResults.stories.tsx b/code/core/src/manager/components/sidebar/SearchResults.stories.tsx index 114f529bcd85..77d9eacec59c 100644 --- a/code/core/src/manager/components/sidebar/SearchResults.stories.tsx +++ b/code/core/src/manager/components/sidebar/SearchResults.stories.tsx @@ -59,7 +59,22 @@ const results = stories .filter(({ name }) => name.includes('A2')) .map((item) => { const i = item.name.indexOf('A2'); - return { item, matches: [{ value: item.name, indices: [[i, i + 1]] }], score: 0 }; + return { + item, + matches: [{ value: item.name, indices: [[i, i + 1]] }], + score: 0, + }; + }); + +const headingResults = stories + .filter(({ name }) => name.includes('A2')) + .map((item, index) => { + const i = item.name.indexOf('A2'); + return { + item: { ...item, type: 'docs', headings: [`Heading ${index}`] }, + matches: [{ value: item.name, indices: [[i, i + 1]], key: 'headings', arrayIndex: 0 }], + score: 0, + }; }); const recents = stories @@ -77,6 +92,10 @@ export const searching = { getItemProps: passKey, highlightedIndex: 0, }; +const headings = { + ...searching, + results: headingResults, +}; export const noResults = { ...searching, results: [] as any, @@ -92,6 +111,8 @@ export const lastViewed = { export const Searching = () => ; +export const DocsHeadings = () => ; + export const NoResults = () => ; export const LastViewed = () => ; diff --git a/code/core/src/manager/components/sidebar/SearchResults.tsx b/code/core/src/manager/components/sidebar/SearchResults.tsx index 7573befe34a1..21f7c964aa61 100644 --- a/code/core/src/manager/components/sidebar/SearchResults.tsx +++ b/code/core/src/manager/components/sidebar/SearchResults.tsx @@ -190,6 +190,10 @@ const Result: FC< const [icon] = item.status ? statusMapping[item.status] : []; + const heading = item.heading && ` ${item.heading}`; + console.log('Result', item); + + // FIXME: result.heading ? 'heading' : 'document' return ( @@ -204,14 +208,17 @@ const Result: FC< )} {!(item.type === 'component' || item.type === 'story') && ( - + )} - <Highlight match={nameMatch}>{item.name}</Highlight> + <Highlight match={nameMatch}> + {item.name} + {heading} + </Highlight> {item.path.map((group, index) => ( diff --git a/code/core/src/manager/components/sidebar/TreeNode.tsx b/code/core/src/manager/components/sidebar/TreeNode.tsx index e0b23c2ef0a4..ee69658dcc64 100644 --- a/code/core/src/manager/components/sidebar/TreeNode.tsx +++ b/code/core/src/manager/components/sidebar/TreeNode.tsx @@ -8,31 +8,35 @@ import { transparentize } from 'polished'; import { UseSymbol } from './IconSymbols'; import { CollapseIcon } from './components/CollapseIcon'; -export const TypeIcon = styled.svg<{ type: 'component' | 'story' | 'group' | 'document' }>( - ({ theme, type }) => ({ - width: 14, - height: 14, - flex: '0 0 auto', - color: (() => { - if (type === 'group') { - return theme.base === 'dark' ? theme.color.primary : theme.color.ultraviolet; - } - - if (type === 'component') { - return theme.color.secondary; - } - - if (type === 'document') { - return theme.base === 'dark' ? theme.color.gold : '#ff8300'; - } - - if (type === 'story') { - return theme.color.seafoam; - } - return 'currentColor'; - })(), - }) -); +export const TypeIcon = styled.svg<{ + type: 'component' | 'story' | 'group' | 'document' | 'heading'; +}>(({ theme, type }) => ({ + width: 14, + height: 14, + flex: '0 0 auto', + color: (() => { + if (type === 'group') { + return theme.base === 'dark' ? theme.color.primary : theme.color.ultraviolet; + } + + if (type === 'component') { + return theme.color.secondary; + } + + if (type === 'document') { + return theme.base === 'dark' ? theme.color.gold : '#ff8300'; + } + + if (type === 'story') { + return theme.color.seafoam; + } + + if (type === 'heading') { + return theme.color.secondary; + } + return 'currentColor'; + })(), +})); const BranchNode = styled.button<{ depth?: number; @@ -138,6 +142,7 @@ export const ComponentNode: FC> = React.memo( export const DocumentNode: FC & { docsMode: boolean }> = React.memo( function DocumentNode({ theme, children, docsMode, ...props }) { + console.log({ props }); return ( diff --git a/code/core/src/manager/components/sidebar/types.ts b/code/core/src/manager/components/sidebar/types.ts index abf0c686b374..7ac0960aa768 100644 --- a/code/core/src/manager/components/sidebar/types.ts +++ b/code/core/src/manager/components/sidebar/types.ts @@ -45,7 +45,12 @@ export interface ExpandType { moreCount: number; } -export type SearchItem = Item & { refId: string; path: string[]; status?: API_StatusValue }; +export type SearchItem = Item & { + refId: string; + path: string[]; + status?: API_StatusValue; + heading?: string; +}; export type SearchResult = Fuse.FuseResultWithMatches & Fuse.FuseResultWithScore; diff --git a/code/core/src/manager/utils/tree.ts b/code/core/src/manager/utils/tree.ts index 3002eb97a77f..5df0176de99c 100644 --- a/code/core/src/manager/utils/tree.ts +++ b/code/core/src/manager/utils/tree.ts @@ -64,7 +64,9 @@ export function getPath(item: Item, ref: RefType): string[] { } export const searchItem = (item: Item, ref: RefType): SearchItem => { - return { ...item, refId: ref.id, path: getPath(item, ref) }; + // @ts-expect-error fix types + const name = item.name + ' ' + item.headings?.join(' '); + return { ...item, refId: ref.id, name, path: getPath(item, ref) }; }; export function cycle(array: T[], index: number, delta: number): number { diff --git a/code/core/src/types/modules/api-stories.ts b/code/core/src/types/modules/api-stories.ts index 29e82c40e74a..becd9a1ab867 100644 --- a/code/core/src/types/modules/api-stories.ts +++ b/code/core/src/types/modules/api-stories.ts @@ -39,6 +39,7 @@ export interface API_DocsEntry extends API_BaseEntry { parameters?: { [parameterName: string]: any; }; + headings?: string[]; } export interface API_StoryEntry extends API_BaseEntry { From 192bf87c9a446a69438834bf36ccb7e42cd0e64e Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Wed, 25 Sep 2024 20:27:21 +0200 Subject: [PATCH 4/4] feat: make docs-mode header appear in search tree --- .../src/manager/components/sidebar/Search.tsx | 57 ++++++++++++------- .../components/sidebar/SearchResults.tsx | 1 - .../manager/components/sidebar/TreeNode.tsx | 1 - .../src/manager/components/sidebar/types.ts | 3 +- code/core/src/manager/utils/status.tsx | 2 + code/core/src/manager/utils/tree.ts | 4 +- 6 files changed, 42 insertions(+), 26 deletions(-) diff --git a/code/core/src/manager/components/sidebar/Search.tsx b/code/core/src/manager/components/sidebar/Search.tsx index c83f525d2c26..6da70d49e819 100644 --- a/code/core/src/manager/components/sidebar/Search.tsx +++ b/code/core/src/manager/components/sidebar/Search.tsx @@ -12,6 +12,7 @@ import Downshift from 'downshift'; import type { FuseOptions } from 'fuse.js'; import Fuse from 'fuse.js'; +import type { API_HashEntry, API_StatusValue } from '../../../types'; import { getGroupStatus, getHighestStatus } from '../../utils/status'; import { scrollIntoView, searchItem } from '../../utils/tree'; import { useLayout } from '../layout/LayoutProvider'; @@ -194,36 +195,52 @@ export const Search = React.memo<{ const [isFileSearchModalOpen, setIsFileSearchModalOpen] = useState(false); const makeFuse = useCallback(() => { - const list = dataset.entries.reduce((acc, [refId, { index, status }]) => { + const list: SearchItem[] = []; + + for (const [refId, { index, status }] of dataset.entries) { + if (!index) { + continue; + } + // @ts-expect-error (non strict) const groupStatus = getGroupStatus(index || {}, status); + const datasetValues: API_HashEntry[] = Object.values(index); - if (index) { - acc.push( - ...Object.values(index).map((item) => { - const statusValue = - status && status[item.id] - ? getHighestStatus(Object.values(status[item.id] || {}).map((s) => s.status)) - : null; - return { - ...searchItem(item, dataset.hash[refId]), - status: statusValue || groupStatus[item.id] || null, - }; - }) - ); - // if(item.headings) { - // tems.headings. - // } + for (const datasetValue of datasetValues) { + const statusValue: API_StatusValue | null = status?.[datasetValue.id] + ? getHighestStatus(Object.values(status[datasetValue.id] || {}).map((s) => s.status)) + : null; + + list.push({ + ...searchItem(datasetValue, dataset.hash[refId]), + status: statusValue || groupStatus[datasetValue.id] || null, + }); + + // Narrow type down to give typescript a chance to infer datasetValue as API_DocsEntry + if (datasetValue.type !== 'docs') { + continue; + } + + const headings = datasetValue.headings ?? []; + headings.forEach((heading: string) => { + list.push({ + ...searchItem(datasetValue, dataset.hash[refId]), + // TODO set "#" to a proper id value that can be scrolled into view when selected + id: `${datasetValue.id}#${heading}`, + name: `${datasetValue.name} / ${heading}`, + status: statusValue || groupStatus[datasetValue.id] || null, + }); + }); } - return acc; - }, []); + } + return new Fuse(list, options); }, [dataset]); const getResults = useCallback( (input: string) => { const fuse = makeFuse(); - console.log({ fuse }); + // console.log({ fuse }); if (!input) { return []; diff --git a/code/core/src/manager/components/sidebar/SearchResults.tsx b/code/core/src/manager/components/sidebar/SearchResults.tsx index 21f7c964aa61..ae2915d8a999 100644 --- a/code/core/src/manager/components/sidebar/SearchResults.tsx +++ b/code/core/src/manager/components/sidebar/SearchResults.tsx @@ -191,7 +191,6 @@ const Result: FC< const [icon] = item.status ? statusMapping[item.status] : []; const heading = item.heading && ` ${item.heading}`; - console.log('Result', item); // FIXME: result.heading ? 'heading' : 'document' return ( diff --git a/code/core/src/manager/components/sidebar/TreeNode.tsx b/code/core/src/manager/components/sidebar/TreeNode.tsx index ee69658dcc64..e402240d3180 100644 --- a/code/core/src/manager/components/sidebar/TreeNode.tsx +++ b/code/core/src/manager/components/sidebar/TreeNode.tsx @@ -142,7 +142,6 @@ export const ComponentNode: FC> = React.memo( export const DocumentNode: FC & { docsMode: boolean }> = React.memo( function DocumentNode({ theme, children, docsMode, ...props }) { - console.log({ props }); return ( diff --git a/code/core/src/manager/components/sidebar/types.ts b/code/core/src/manager/components/sidebar/types.ts index 7ac0960aa768..cf0139d5f659 100644 --- a/code/core/src/manager/components/sidebar/types.ts +++ b/code/core/src/manager/components/sidebar/types.ts @@ -48,7 +48,8 @@ export interface ExpandType { export type SearchItem = Item & { refId: string; path: string[]; - status?: API_StatusValue; + // TODO should allow null, check for side-effects or if undefined can be removed + status?: API_StatusValue | null; heading?: string; }; diff --git a/code/core/src/manager/utils/status.tsx b/code/core/src/manager/utils/status.tsx index 14c2c6a613c5..ed135d517213 100644 --- a/code/core/src/manager/utils/status.tsx +++ b/code/core/src/manager/utils/status.tsx @@ -57,6 +57,8 @@ export function getGroupStatus( collapsedData: { [x: string]: Partial; }, + // TODO does this also need to allow "| undefined" since it is treated as optional in the function body? + // the consumer of this function has to use @ts-expect-error because of the missing "| undefined" status: API_StatusState ): Record { return Object.values(collapsedData).reduce>((acc, item) => { diff --git a/code/core/src/manager/utils/tree.ts b/code/core/src/manager/utils/tree.ts index 5df0176de99c..4b82573c1f91 100644 --- a/code/core/src/manager/utils/tree.ts +++ b/code/core/src/manager/utils/tree.ts @@ -64,9 +64,7 @@ export function getPath(item: Item, ref: RefType): string[] { } export const searchItem = (item: Item, ref: RefType): SearchItem => { - // @ts-expect-error fix types - const name = item.name + ' ' + item.headings?.join(' '); - return { ...item, refId: ref.id, name, path: getPath(item, ref) }; + return { ...item, refId: ref.id, name: item.name, path: getPath(item, ref) }; }; export function cycle(array: T[], index: number, delta: number): number {