From 7b91f4b896f51852adacf2e58dfec3ec019cf7dd Mon Sep 17 00:00:00 2001 From: Karen Li Date: Fri, 10 Apr 2026 21:30:02 -0400 Subject: [PATCH] fix: improve search to find terms in page headings and body content --- src/hooks/__tests__/use-search.test.js | 47 ++++++++++++++++++++++++++ src/hooks/use-search.js | 11 ++++++ src/util/search.worker.js | 9 ++++- 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/hooks/__tests__/use-search.test.js diff --git a/src/hooks/__tests__/use-search.test.js b/src/hooks/__tests__/use-search.test.js new file mode 100644 index 00000000000..9bd6cb4c9a8 --- /dev/null +++ b/src/hooks/__tests__/use-search.test.js @@ -0,0 +1,47 @@ +import {flattenHeadings} from '../use-search' + +describe('flattenHeadings', () => { + test('returns empty array for null/undefined input', () => { + expect(flattenHeadings(null)).toEqual([]) + expect(flattenHeadings(undefined)).toEqual([]) + }) + + test('returns empty array for empty items', () => { + expect(flattenHeadings([])).toEqual([]) + }) + + test('flattens single level of headings', () => { + const items = [{title: 'Description'}, {title: 'Configuration'}] + expect(flattenHeadings(items)).toEqual(['Description', 'Configuration']) + }) + + test('flattens nested headings', () => { + const items = [ + { + title: 'Description', + items: [{title: 'before'}, {title: 'min-release-age'}], + }, + ] + expect(flattenHeadings(items)).toEqual(['Description', 'before', 'min-release-age']) + }) + + test('flattens deeply nested headings', () => { + const items = [ + { + title: 'Top', + items: [ + { + title: 'Mid', + items: [{title: 'Deep'}], + }, + ], + }, + ] + expect(flattenHeadings(items)).toEqual(['Top', 'Mid', 'Deep']) + }) + + test('skips items without a title', () => { + const items = [{items: [{title: 'child'}]}, {title: 'sibling'}] + expect(flattenHeadings(items)).toEqual(['child', 'sibling']) + }) +}) diff --git a/src/hooks/use-search.js b/src/hooks/use-search.js index 18f43040d16..d9d3982015a 100644 --- a/src/hooks/use-search.js +++ b/src/hooks/use-search.js @@ -6,6 +6,15 @@ import usePage from './use-page' import * as getNav from '../util/get-nav' import {CLI_PATH} from '../constants' +export const flattenHeadings = items => { + if (!items) return [] + return items.reduce((acc, item) => { + if (item.title) acc.push(item.title) + if (item.items) acc.push(...flattenHeadings(item.items)) + return acc + }, []) +} + const useSearchData = () => { const data = useStaticQuery(graphql` { @@ -15,6 +24,7 @@ const useSearchData = () => { frontmatter { title } + tableOfContents body } } @@ -40,6 +50,7 @@ const useSearchData = () => { return { path: node.path, title: mdxNode.frontmatter.title, + headings: flattenHeadings(mdxNode.tableOfContents?.items).join(' '), body: mdxNode.body, } }) diff --git a/src/util/search.worker.js b/src/util/search.worker.js index 6ba458ccea6..a92076fcd6f 100644 --- a/src/util/search.worker.js +++ b/src/util/search.worker.js @@ -36,7 +36,14 @@ class Fuse { const items = this.allItems.filter(item => item.path.startsWith(this.cliVersion.root) ? item.path.startsWith(this.cliVersion.current) : true, ) - this.instances.set(this.cliVersion.current, new FuseJs(items, {threshold: 0.2, keys: ['title', 'body']})) + this.instances.set( + this.cliVersion.current, + new FuseJs(items, { + threshold: 0.2, + ignoreLocation: true, + keys: ['title', 'headings', 'body'], + }), + ) } setItems(items) {