From c96560ccb77d851e4f44525972bc240941323df5 Mon Sep 17 00:00:00 2001 From: Shahed Nasser Date: Wed, 26 Mar 2025 10:56:02 +0200 Subject: [PATCH] docs: remove custom searching mechanism (#11984) * docs: remove custom searching mechanism * remove grouping * changes to grouping * small title change * update resources sitemap --- www/apps/api-reference/providers/search.tsx | 10 +- .../learn/fundamentals/module-links/page.mdx | 2 +- www/apps/book/providers/search.tsx | 10 +- www/apps/resources/app/sitemap.ts | 47 ++- www/apps/resources/providers/search.tsx | 10 +- www/apps/ui/src/providers/search.tsx | 10 +- www/apps/user-guide/providers/search.tsx | 10 +- .../src/components/Search/Hits/index.tsx | 175 +++++------ .../docs-ui/src/components/Search/index.tsx | 1 + .../docs-ui/src/providers/Search/index.tsx | 291 +++++++++--------- 10 files changed, 316 insertions(+), 250 deletions(-) diff --git a/www/apps/api-reference/providers/search.tsx b/www/apps/api-reference/providers/search.tsx index 6ac630c847..f9406ceb98 100644 --- a/www/apps/api-reference/providers/search.tsx +++ b/www/apps/api-reference/providers/search.tsx @@ -21,8 +21,14 @@ const SearchProvider = ({ children }: SearchProviderProps) => { apiKey: process.env.NEXT_PUBLIC_ALGOLIA_API_KEY || "temp", mainIndexName: process.env.NEXT_PUBLIC_API_ALGOLIA_INDEX_NAME || "temp", indices: [ - process.env.NEXT_PUBLIC_API_ALGOLIA_INDEX_NAME || "temp", - process.env.NEXT_PUBLIC_DOCS_ALGOLIA_INDEX_NAME || "temp", + { + name: process.env.NEXT_PUBLIC_API_ALGOLIA_INDEX_NAME || "temp", + title: "Store & Admin API", + }, + { + name: process.env.NEXT_PUBLIC_DOCS_ALGOLIA_INDEX_NAME || "temp", + title: "Docs", + }, ], }} searchProps={{ diff --git a/www/apps/book/app/learn/fundamentals/module-links/page.mdx b/www/apps/book/app/learn/fundamentals/module-links/page.mdx index d3aef566b4..e05d1d5e51 100644 --- a/www/apps/book/app/learn/fundamentals/module-links/page.mdx +++ b/www/apps/book/app/learn/fundamentals/module-links/page.mdx @@ -1,5 +1,5 @@ export const metadata = { - title: `${pageNumber} Module Link`, + title: `${pageNumber} Define Module Link`, } # {metadata.title} diff --git a/www/apps/book/providers/search.tsx b/www/apps/book/providers/search.tsx index 514d84c3b5..e83792b688 100644 --- a/www/apps/book/providers/search.tsx +++ b/www/apps/book/providers/search.tsx @@ -16,8 +16,14 @@ const SearchProvider = ({ children }: SearchProviderProps) => { mainIndexName: process.env.NEXT_PUBLIC_DOCS_ALGOLIA_INDEX_NAME || "temp", indices: [ - process.env.NEXT_PUBLIC_DOCS_ALGOLIA_INDEX_NAME || "temp", - process.env.NEXT_PUBLIC_API_ALGOLIA_INDEX_NAME || "temp", + { + name: process.env.NEXT_PUBLIC_DOCS_ALGOLIA_INDEX_NAME || "temp", + title: "Docs", + }, + { + name: process.env.NEXT_PUBLIC_API_ALGOLIA_INDEX_NAME || "temp", + title: "Store & Admin API", + }, ], }} searchProps={{ diff --git a/www/apps/resources/app/sitemap.ts b/www/apps/resources/app/sitemap.ts index d3bc3e9a9b..5d6fa7abe0 100644 --- a/www/apps/resources/app/sitemap.ts +++ b/www/apps/resources/app/sitemap.ts @@ -5,9 +5,54 @@ import { config } from "../config" import { basePathUrl } from "../utils/base-path-url" export default function sitemap(): MetadataRoute.Sitemap { - return retrieveMdxPages({ + const items = retrieveMdxPages({ basePath: path.resolve("app"), }).map((filePath) => ({ url: `${config.baseUrl}${basePathUrl(filePath)}`, })) + + // add some references + items.push( + { + url: `${config.baseUrl}${basePathUrl("/references/file-provider-module")}`, + }, + { + url: `${config.baseUrl}${basePathUrl("/references/file-service")}`, + }, + { + url: `${config.baseUrl}${basePathUrl("/references/locking-module-provider")}`, + }, + { + url: `${config.baseUrl}${basePathUrl("/references/locking-service")}`, + }, + { + url: `${config.baseUrl}${basePathUrl("/references/notification-provider-module")}`, + }, + { + url: `${config.baseUrl}${basePathUrl("/references/notification-service")}`, + }, + { + url: `${config.baseUrl}${basePathUrl("/references/event-service")}`, + }, + { + url: `${config.baseUrl}${basePathUrl("/references/cache-service")}`, + }, + { + url: `${config.baseUrl}${basePathUrl("/references/file-service")}`, + }, + { + url: `${config.baseUrl}${basePathUrl("/references/auth/provider")}`, + }, + { + url: `${config.baseUrl}${basePathUrl("/references/fulfillment/provider")}`, + }, + { + url: `${config.baseUrl}${basePathUrl("/references/tax/provider")}`, + }, + { + url: `${config.baseUrl}${basePathUrl("/references/payment/provider")}`, + } + ) + + return items } diff --git a/www/apps/resources/providers/search.tsx b/www/apps/resources/providers/search.tsx index b30bdf5808..a21006cfb8 100644 --- a/www/apps/resources/providers/search.tsx +++ b/www/apps/resources/providers/search.tsx @@ -16,8 +16,14 @@ const SearchProvider = ({ children }: SearchProviderProps) => { mainIndexName: process.env.NEXT_PUBLIC_DOCS_ALGOLIA_INDEX_NAME || "temp", indices: [ - process.env.NEXT_PUBLIC_DOCS_ALGOLIA_INDEX_NAME || "temp", - process.env.NEXT_PUBLIC_API_ALGOLIA_INDEX_NAME || "temp", + { + name: process.env.NEXT_PUBLIC_DOCS_ALGOLIA_INDEX_NAME || "temp", + title: "Docs", + }, + { + name: process.env.NEXT_PUBLIC_API_ALGOLIA_INDEX_NAME || "temp", + title: "Store & Admin API", + }, ], }} searchProps={{ diff --git a/www/apps/ui/src/providers/search.tsx b/www/apps/ui/src/providers/search.tsx index 14189a54d8..aa4a4be4a1 100644 --- a/www/apps/ui/src/providers/search.tsx +++ b/www/apps/ui/src/providers/search.tsx @@ -16,8 +16,14 @@ const SearchProvider = ({ children }: SearchProviderProps) => { mainIndexName: process.env.NEXT_PUBLIC_DOCS_ALGOLIA_INDEX_NAME || "temp", indices: [ - process.env.NEXT_PUBLIC_API_ALGOLIA_INDEX_NAME || "temp", - process.env.NEXT_PUBLIC_DOCS_ALGOLIA_INDEX_NAME || "temp", + { + name: process.env.NEXT_PUBLIC_DOCS_ALGOLIA_INDEX_NAME || "temp", + title: "Docs", + }, + { + name: process.env.NEXT_PUBLIC_API_ALGOLIA_INDEX_NAME || "temp", + title: "Store & Admin API", + }, ], }} searchProps={{ diff --git a/www/apps/user-guide/providers/search.tsx b/www/apps/user-guide/providers/search.tsx index d019dd3032..c0b5d30284 100644 --- a/www/apps/user-guide/providers/search.tsx +++ b/www/apps/user-guide/providers/search.tsx @@ -16,8 +16,14 @@ const SearchProvider = ({ children }: SearchProviderProps) => { mainIndexName: process.env.NEXT_PUBLIC_DOCS_ALGOLIA_INDEX_NAME || "temp", indices: [ - process.env.NEXT_PUBLIC_API_ALGOLIA_INDEX_NAME || "temp", - process.env.NEXT_PUBLIC_DOCS_ALGOLIA_INDEX_NAME || "temp", + { + name: process.env.NEXT_PUBLIC_DOCS_ALGOLIA_INDEX_NAME || "temp", + title: "Docs", + }, + { + name: process.env.NEXT_PUBLIC_API_ALGOLIA_INDEX_NAME || "temp", + title: "Store & Admin API", + }, ], }} searchProps={{ diff --git a/www/packages/docs-ui/src/components/Search/Hits/index.tsx b/www/packages/docs-ui/src/components/Search/Hits/index.tsx index c5277986a2..2e61716f27 100644 --- a/www/packages/docs-ui/src/components/Search/Hits/index.tsx +++ b/www/packages/docs-ui/src/components/Search/Hits/index.tsx @@ -11,9 +11,8 @@ import { useInstantSearch, } from "react-instantsearch" import { SearchNoResult } from "../NoResults" -import { SearchHitGroupName } from "./GroupName" -import { useSearch } from "@/providers" -import { Link } from "@/components" +import { AlgoliaIndex, useSearch } from "@/providers" +import { Link, SearchHitGroupName } from "@/components" export type Hierarchy = "lvl0" | "lvl1" | "lvl2" | "lvl3" | "lvl4" | "lvl5" @@ -43,7 +42,7 @@ export type GroupedHitType = { export type SearchHitWrapperProps = { configureProps: ConfigureProps - indices: string[] + indices: AlgoliaIndex[] } & Omit export type IndexResults = { @@ -57,8 +56,8 @@ export const SearchHitsWrapper = ({ }: SearchHitWrapperProps) => { const { status } = useInstantSearch() const [hasNoResults, setHashNoResults] = useState({ - [indices[0]]: false, - [indices[1]]: false, + [indices[0].name]: false, + [indices[1].name]: false, }) const showNoResults = useMemo(() => { return Object.values(hasNoResults).every((value) => value === true) @@ -74,11 +73,14 @@ export const SearchHitsWrapper = ({ return (
{status !== "loading" && showNoResults && } - {indices.map((indexName, index) => ( + {indices.map((index, key) => ( // @ts-expect-error React v19 doesn't see this type as a React element - + + {!hasNoResults[index.name] && ( + + )} @@ -104,30 +106,6 @@ export const SearchHits = ({ const { status } = useInstantSearch() const { setIsOpen } = useSearch() - // group by lvl0 - const grouped: GroupedHitType = useMemo(() => { - const grouped: GroupedHitType = {} - hits.forEach((hit) => { - if (hit.hierarchy.lvl0) { - if (!grouped[hit.hierarchy.lvl0]) { - grouped[hit.hierarchy.lvl0] = [] - } - grouped[hit.hierarchy.lvl0].push(hit) - } - }) - - // sort groups by number of hits - const sortedGroups = Object.fromEntries( - Object.entries(grouped).sort(([, a], [, b]) => { - const lvl1CountA = a.filter((hit) => hit.type === "lvl1").length - const lvl1CountB = b.filter((hit) => hit.type === "lvl1").length - - return lvl1CountB - lvl1CountA - }) - ) - return sortedGroups - }, [hits]) - useEffect(() => { if (status !== "loading" && status !== "stalled") { setNoResults(indexName, hits.length === 0) @@ -149,76 +127,71 @@ export const SearchHits = ({ "[&_mark]:text-medusa-fg-interactive" )} > - {Object.keys(grouped).map((groupName, index) => ( - - - {grouped[groupName].map((item, index) => { - const hierarchies = Object.values(item.hierarchy) - .filter(Boolean) - .join(" › ") - return ( -
{ - const target = e.target as Element - if (target.tagName.toLowerCase() === "div") { - target.querySelector("a")?.click() - } - }} - > - + {hits.map((item, index) => { + const hierarchies = Object.values(item.hierarchy) + .filter(Boolean) + .join(" › ") + return ( +
{ + const target = e.target as Element + if (target.tagName.toLowerCase() === "div") { + target.querySelector("a")?.click() + } + }} + > + + {/* @ts-expect-error React v19 doesn't see this type as a React element */} + + + + {item.type === "content" && ( + <> {/* @ts-expect-error React v19 doesn't see this type as a React element */} - - - - {item.type === "content" && ( - <> - {/* @ts-expect-error React v19 doesn't see this type as a React element */} - - - )} - {item.type !== "content" && item.description} - + + + )} + {item.type !== "content" && item.description} + - - {hierarchies} - - { - if (checkIfInternal(item.url)) { - e.preventDefault() - window.location.href = item.url - setIsOpen(false) - } - }} - /> -
- ) - })} - - ))} + + {hierarchies} + + { + if (checkIfInternal(item.url)) { + e.preventDefault() + window.location.href = item.url + setIsOpen(false) + } + }} + /> +
+ ) + })}
) } diff --git a/www/packages/docs-ui/src/components/Search/index.tsx b/www/packages/docs-ui/src/components/Search/index.tsx index 2641f70c57..d5a9f51720 100644 --- a/www/packages/docs-ui/src/components/Search/index.tsx +++ b/www/packages/docs-ui/src/components/Search/index.tsx @@ -159,6 +159,7 @@ export const Search = ({ // to be applied between the items. facetFilters: [facetFilters], getRankingInfo: true, + hitsPerPage: 3, }} indices={algolia.indices} checkInternalPattern={checkInternalPattern} diff --git a/www/packages/docs-ui/src/providers/Search/index.tsx b/www/packages/docs-ui/src/providers/Search/index.tsx index 73cfe20a91..d9b1bfb43e 100644 --- a/www/packages/docs-ui/src/providers/Search/index.tsx +++ b/www/packages/docs-ui/src/providers/Search/index.tsx @@ -13,14 +13,10 @@ import { checkArraySameElms } from "../../utils" import { liteClient as algoliasearch, LiteClient as SearchClient, - type SearchResponses, - type SearchHits, - SearchResponse, } from "algoliasearch/lite" import clsx from "clsx" // @ts-expect-error can't install the types package because it doesn't support React v19 import { CSSTransition, SwitchTransition } from "react-transition-group" -import { searchFilters } from "../.." export type SearchCommand = { name: string @@ -46,11 +42,16 @@ export type SearchContextType = { const SearchContext = createContext(null) +export type AlgoliaIndex = { + name: string + title: string +} + export type AlgoliaProps = { appId: string apiKey: string mainIndexName: string - indices: string[] + indices: AlgoliaIndex[] } export type SearchProviderProps = { @@ -83,154 +84,170 @@ export const SearchProvider = ({ const algoliaClient = algoliasearch(algolia.appId, algolia.apiKey) return { ...algoliaClient, - async search(searchParams) { - const requests = - "requests" in searchParams ? searchParams.requests : searchParams - // always send this request, which is the main request with no filters - const mainRequest = requests[0] - const params = (mainRequest.params || {}) as Record - if (!params.query) { - return Promise.resolve({ - results: requests.map(() => ({ - hits: [], - nbHits: 0, - nbPages: 0, - page: 0, - processingTimeMS: 0, - hitsPerPage: 0, - exhaustiveNbHits: false, - query: "", - params: "", - })), - }) - } + // async search(searchParams) { + // const requests = + // "requests" in searchParams ? searchParams.requests : searchParams + // // always send this request, which is the main request with no filters + // const mainRequest = requests[0] + // const params = (mainRequest.params || {}) as Record + // if (!params.query) { + // return Promise.resolve({ + // results: requests.map(() => ({ + // hits: [], + // nbHits: 0, + // nbPages: 0, + // page: 0, + // processingTimeMS: 0, + // hitsPerPage: 0, + // exhaustiveNbHits: false, + // query: "", + // params: "", + // })), + // }) + // } - // retrieve only requests that have filters - // this is to ensure that we show no result if no filter is selected - const requestsWithFilters = requests.filter((item) => { - if ( - !item.params || - typeof item.params !== "object" || - !("facetFilters" in item.params) - ) { - return false - } + // // retrieve only requests that have filters + // // this is to ensure that we show no result if no filter is selected + // const requestsWithFilters = requests.filter((item) => { + // if ( + // !item.params || + // typeof item.params !== "object" || + // !("facetFilters" in item.params) + // ) { + // return false + // } - const facetFilters = item.params.facetFilters as string[] + // const facetFilters = item.params.facetFilters as string[] - // if no tag filters are specified, there's still one item, - // which is an empty array - return facetFilters.length >= 1 && facetFilters[0].length > 0 - }) + // // if no tag filters are specified, there's still one item, + // // which is an empty array + // return facetFilters.length >= 1 && facetFilters[0].length > 0 + // }) - // check whether a query is entered in the search box - const noQueries = requestsWithFilters.every( - (item) => - !item.facetQuery && - (!item.params || - typeof item.params !== "object" || - !("query" in item.params) || - !item.params.query) - ) + // // check whether a query is entered in the search box + // const noQueries = requestsWithFilters.every( + // (item) => + // !item.facetQuery && + // (!item.params || + // typeof item.params !== "object" || + // !("query" in item.params) || + // !item.params.query) + // ) - const newRequests: typeof requestsWithFilters = [mainRequest] - if (!noQueries) { - // split requests per tags - for (const request of requestsWithFilters) { - const params = request.params as Record - const facetFilters = (params.facetFilters as string[][])[0] + // const newRequests: typeof requestsWithFilters = [mainRequest] + // if (!noQueries) { + // // split requests per tags + // for (const request of requestsWithFilters) { + // const params = request.params as Record + // const facetFilters = (params.facetFilters as string[][])[0] - // if only one tag is selected, keep the request as-is - if (facetFilters.length === 1) { - newRequests.push(request) + // // if only one tag is selected, keep the request as-is + // if (facetFilters.length === 1) { + // newRequests.push(request) - continue - } + // continue + // } - // if multiple tags are selected, split the tags - // to retrieve a small subset of results per each tag. - newRequests.push( - ...facetFilters.map((tag) => { - // get the filter's details in case it has custom hitsPerPage - const filterDetails = searchFilters.find( - (item) => `_tags:${item.value}` === tag - ) - return { - ...request, - params: { - ...params, - facetFilters: [tag], - }, - hitsPerPage: filterDetails?.hitsPerPage || 3, - } - }) - ) - } - } + // // if multiple tags are selected, split the tags + // // to retrieve a small subset of results per each tag. + // newRequests.push( + // ...facetFilters.map((tag) => { + // // get the filter's details in case it has custom hitsPerPage + // const filterDetails = searchFilters.find( + // (item) => `_tags:${item.value}` === tag + // ) + // return { + // ...request, + // params: { + // ...params, + // facetFilters: [tag], + // }, + // hitsPerPage: filterDetails?.hitsPerPage || 3, + // } + // }) + // ) + // } + // } - return algoliaClient - .search(newRequests) - .then((response) => { - if (newRequests.length === 1) { - return response - } - // combine results of the same index and return the results - const resultsByIndex: { - [indexName: string]: SearchResponse - } = {} - // extract the response of the main request - const mainResult = response.results[0] + // return algoliaClient + // .search(newRequests) + // .then((response) => { + // if (newRequests.length === 1) { + // return response + // } + // // combine results of the same index and return the results + // const resultsByIndex: { + // [indexName: string]: SearchResponse + // } = {} + // // extract the response of the main request + // const mainResult = response.results[0] - response.results.forEach((result, indexNum) => { - if (indexNum === 0) { - // ignore the main request's result - return - } - const resultIndex = "index" in result ? result.index : undefined - const resultHits = "hits" in result ? result.hits : [] + // response.results.forEach((result, indexNum) => { + // if (indexNum === 0) { + // // ignore the main request's result + // return + // } + // const resultIndex = "index" in result ? result.index : undefined + // const resultHits = "hits" in result ? result.hits : [] - if (!resultIndex) { - return - } + // if (!resultIndex) { + // return + // } - resultsByIndex[resultIndex] = { - ...result, - ...(resultsByIndex[resultIndex] || {}), - hits: [ - ...(resultsByIndex[resultIndex]?.hits || []), - ...resultHits, - ], - nbHits: - (resultsByIndex[resultIndex]?.nbHits || 0) + - resultHits.length, - } - }) + // resultsByIndex[resultIndex] = { + // ...result, + // ...(resultsByIndex[resultIndex] || {}), + // hits: [ + // ...(resultsByIndex[resultIndex]?.hits || []), + // ...resultHits, + // ], + // nbHits: + // (resultsByIndex[resultIndex]?.nbHits || 0) + + // resultHits.length, + // } + // }) - const newResults = Object.values(resultsByIndex).flatMap( - (result) => ({ - ...result, - hits: ("hits" in result ? result.hits : []).sort((a, b) => { - const typosA = a._rankingInfo?.nbTypos || 0 - const typosB = b._rankingInfo?.nbTypos || 0 - if (a.type === "lvl1" && typosA <= typosB) { - return -1 - } + // const newResults = Object.values(resultsByIndex).flatMap( + // (result) => ({ + // ...result, + // hits: ("hits" in result ? result.hits : []).sort((a, b) => { + // const typosA = a._rankingInfo?.nbTypos || 0 + // const typosB = b._rankingInfo?.nbTypos || 0 + // const tagASortOrder = + // searchFilters.find((item) => + // a._tags.find((tag) => tag === item.value) + // )?.internalSortOrder || 0 + // const tagBSortorder = + // searchFilters.find((item) => + // b._tags.find((tag) => tag === item.value) + // )?.internalSortOrder || 0 + // if ( + // a.type === "lvl1" && + // typosA <= typosB && + // tagASortOrder >= tagBSortorder + // ) { + // return -1 + // } - if (b.type === "lvl1" && typosB <= typosA) { - return 1 - } + // if ( + // b.type === "lvl1" && + // typosB <= typosA && + // tagBSortorder >= tagASortOrder + // ) { + // return 1 + // } - return 0 - }), - }) - ) + // return 0 + // }), + // }) + // ) - return { - // append the results with the main request's results - results: [mainResult, ...newResults], - } as SearchResponses - }) - }, + // return { + // // append the results with the main request's results + // results: [mainResult, ...newResults], + // } as SearchResponses + // }) + // }, } }, [algolia.appId, algolia.apiKey])