feat: advance search component, custom directory and breadcrumbs#1065
feat: advance search component, custom directory and breadcrumbs#1065rkeerthient wants to merge 202 commits intomainfrom
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds extensive search and directory features and related UI/locale updates to the visual-editor. New components: SearchComponent (with SearchBarSlot and SearchResultsSlot), MapComponent, UniversalResultsSection, VerticalResultsSection, Cards, GDAResponse, SourceCard, CustomDirectory, and CustomBreadcrumbs. Introduces search utilities (searchConfig, visual autocomplete, typing-effect hook, URL sync, fetchData), CTA textColor support, schema/type exports, CSS imports (Mapbox), and replaces headDeployStatus with deploymentInProgress. Many locale JSON files were extended/updated to include new keys and missing-configuration messages. Sequence Diagram(s)sequenceDiagram
participant User as User
participant SearchBar as SearchBarSlot
participant Headless as SearchHeadlessProvider
participant API as Search API
participant Results as SearchResultsSlot
participant Map as MapComponent
User->>SearchBar: Enter query / use voice
SearchBar->>Headless: build config & provide headless (provideHeadless)
Headless->>API: execute query (with vertical/universal params)
API-->>Headless: return search results
Headless->>Results: provide results + vertical config
Results->>Map: request map rendering (if layout=Map)
Map->>API: (optional) use Mapbox key & result coords to render
Results-->>User: render results, GDA, pagination, CTA links
sequenceDiagram
participant Viewer as Page (viewer/editor)
participant Breadcrumbs as CustomBreadcrumbs
participant FetchUtil as fetchData
participant CustomAPI as Custom Content API
Viewer->>Breadcrumbs: mount (with document uid)
Breadcrumbs->>FetchUtil: build URL + apiKey, request entity parents
FetchUtil->>CustomAPI: fetch entities (entityIds or parent lookup)
CustomAPI-->>FetchUtil: return entity list
FetchUtil-->>Breadcrumbs: mapped breadcrumb items
Breadcrumbs-->>Viewer: render breadcrumb trail (or skeleton / missing-key UI)
Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/visual-editor/locales/platform/et/visual-editor.json (1)
1-784:⚠️ Potential issue | 🟠 MajorRun locale artifact regeneration after these JSON changes.
Given the scale of locale-key additions, please run the i18n generation step and commit resulting artifacts; otherwise type/key lookups can drift and fail CI.
Based on learnings: If locale strings change, run
pnpm --dir packages/visual-editor run i18n:update.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/locales/platform/et/visual-editor.json` around lines 1 - 784, You added many locale keys in the visual editor Estonian JSON which requires regenerating the i18n artifacts; run the i18n update command (pnpm --dir packages/visual-editor run i18n:update), commit the generated artifacts, and push the changes so type/key lookups (e.g., keys like "components", "fields", "theme", "publishError") remain in sync and CI won't fail.
🟡 Minor comments (12)
packages/visual-editor/src/components/contentBlocks/CtaWrapper.tsx-362-367 (1)
362-367:⚠️ Potential issue | 🟡 MinorHide
textColorwhen CTA is effectively preset-image.At Line 362-Line 367,
textColorvisibility only checksctaVariant === "primary". For preset-image mode, this can still surface a non-functional control in the editor.♻️ Suggested fix
- const showTextColor = ctaVariant === "primary"; + const showTextColor = + ctaVariant === "primary" && effectiveCtaType !== "presetImage";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/contentBlocks/CtaWrapper.tsx` around lines 362 - 367, The visibility logic for textColor only checks ctaVariant === "primary" which leaves the control visible for preset-image CTAs; update the condition used when calling setDeep so styles.objectFields.textColor.visible is false for preset-image CTAs. Locate the showTextColor computation and replace it with a compound check (e.g., require ctaVariant === "primary" AND not preset-image mode) using the component/state property that indicates preset-image mode (the same place that defines ctaVariant), then call setDeep(updatedFields, "styles.objectFields.textColor.visible", showTextColor) with that updated boolean.packages/visual-editor/src/components/pageSections/SearchSection/GDAResponse.tsx-9-44 (1)
9-44:⚠️ Potential issue | 🟡 MinorLoading skeleton renders alongside
GenerativeDirectAnswer, not instead of it.When
loadingistrue, the skeleton UI is rendered via the conditional block (lines 13-34), butGenerativeDirectAnswer(lines 35-42) is always rendered regardless of loading state. This means both the skeleton and the actual component render simultaneously when loading.If the intent is to show the skeleton until data is ready, consider conditionally rendering
GenerativeDirectAnsweronly whenloadingisfalse:Possible fix if exclusive rendering is intended
const GDAResponse = ({ loading = true }: GDAResponseProps) => { const { t } = useTranslation(); return ( <> {loading && ( <div className="p-6 border border-gray-200 rounded-lg shadow-sm my-4 animate-pulse"> {/* skeleton content */} </div> )} - <GenerativeDirectAnswer - CitationCard={SourceCard} - customCssClasses={{ - container: "my-4", - divider: "!py-5", - citationsContainer: "grid grid-cols-1 md:grid-cols-3 gap-2 md:gap-4", - }} - /> + {!loading && ( + <GenerativeDirectAnswer + CitationCard={SourceCard} + customCssClasses={{ + container: "my-4", + divider: "!py-5", + citationsContainer: "grid grid-cols-1 md:grid-cols-3 gap-2 md:gap-4", + }} + /> + )} </> ); };If rendering both simultaneously is intentional (e.g.,
GenerativeDirectAnswerhandles its own visibility internally), please disregard this comment.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/GDAResponse.tsx` around lines 9 - 44, The GDAResponse component currently renders the loading skeleton while always rendering GenerativeDirectAnswer; change the render logic so GenerativeDirectAnswer is only mounted when loading is false (or alternatively return early when loading is true), i.e., in GDAResponse use the loading prop to conditionally render the skeleton OR the GenerativeDirectAnswer (referencing the GDAResponse component and the GenerativeDirectAnswer usage) so they are exclusive rather than rendered simultaneously.packages/visual-editor/locales/platform/fi/visual-editor.json-523-523 (1)
523-523:⚠️ Potential issue | 🟡 MinorTranslate this new Finnish key value.
Line 523 (
"universalLimit": "Universal Limit") is not localized to Finnish, creating inconsistent UI language.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/locales/platform/fi/visual-editor.json` at line 523, Replace the English string for the JSON key "universalLimit" with its Finnish translation to keep the UI consistent: update the value for the "universalLimit" key from "Universal Limit" to an appropriate Finnish phrase (for example "Yleinen rajoitus"), preserving the JSON quoting and trailing comma formatting.packages/visual-editor/locales/platform/hu/visual-editor.json-113-113 (1)
113-113:⚠️ Potential issue | 🟡 MinorLocalize this Hungarian label fully.
Line 113 uses English (
"SearchBar Slot") inside thehulocale file. Please provide a Hungarian translation for consistency in localized UI.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/locales/platform/hu/visual-editor.json` at line 113, Replace the English value for the "searchBarSlot" key with a Hungarian translation so the hu locale is consistent; locate the "searchBarSlot" entry and change its value from "SearchBar Slot" to an appropriate Hungarian string such as "Keresősáv helye".packages/visual-editor/locales/platform/it/visual-editor.json-65-65 (1)
65-65:⚠️ Potential issue | 🟡 MinorTranslation may be inaccurate for UI breadcrumbs.
"Pangrattato" refers to culinary breadcrumbs (for cooking), not navigation breadcrumbs. In Italian UI contexts, "Breadcrumb" is often kept as-is (as an English loanword) or translated as "Percorso di navigazione" / "Briciole di navigazione".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/locales/platform/it/visual-editor.json` at line 65, The translation for the key "customBreadcrumbs" is incorrect (uses culinary "Pangrattato"); update the string value for "customBreadcrumbs" to a proper UI term such as "Breadcrumb personalizzato" or "Percorso di navigazione personalizzato" (or keep "Custom Breadcrumbs" as an English loanword) so it correctly reflects navigation breadcrumbs in the UI.packages/visual-editor/locales/platform/en/visual-editor.json-602-602 (1)
602-602:⚠️ Potential issue | 🟡 MinorFix typos in
missingCustomEndpointApiKeymessage.This string contains two typos:
- "you" should be "your"
- "sectiom" should be "section"
✏️ Proposed fix
- "missingCustomEndpointApiKey": "Add you custom Content endpoint API key to view this sectiom", + "missingCustomEndpointApiKey": "Add your custom Content endpoint API key to view this section",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/locales/platform/en/visual-editor.json` at line 602, Update the localization string for the key missingCustomEndpointApiKey in visual-editor.json to correct the typos: change "Add you custom Content endpoint API key to view this sectiom" to use "your" and "section" so the message reads clearly for users; locate the missingCustomEndpointApiKey entry in packages/visual-editor/locales/platform/en/visual-editor.json and replace the text accordingly.packages/visual-editor/locales/platform/en-GB/visual-editor.json-602-603 (1)
602-603:⚠️ Potential issue | 🟡 MinorFix the typo in the missing-endpoint copy.
Add you/sectiomwill ship directly to users here, andURLshould stay capitalized.Proposed fix
- "missingCustomEndpointApiKey": "Add you custom Content endpoint API key to view this sectiom", - "missingCustomEndpointURL": "Add your custom Content endpoint url to view this section", + "missingCustomEndpointApiKey": "Add your custom Content endpoint API key to view this section", + "missingCustomEndpointURL": "Add your custom Content endpoint URL to view this section",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/locales/platform/en-GB/visual-editor.json` around lines 602 - 603, Update the two locale strings referenced by keys missingCustomEndpointApiKey and missingCustomEndpointURL to fix typos and casing: change "Add you custom Content endpoint API key to view this sectiom" to "Add your custom Content endpoint API key to view this section", and change "Add your custom Content endpoint url to view this section" to "Add your custom Content endpoint URL to view this section".packages/visual-editor/src/components/pageSections/SearchSection/SearchBarSlot.tsx-299-299 (1)
299-299:⚠️ Potential issue | 🟡 MinorLocalize the fallback placeholder.
When typing effect is off, this always renders hardcoded English text even though
searchHerealready exists in the locale catalog.Proposed fix
- placeholder={isTypingEffect ? placeholder : "Search here...."} + placeholder={ + isTypingEffect ? placeholder : t("searchHere", "Search here...") + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/SearchBarSlot.tsx` at line 299, The placeholder fallback is hardcoded English; change the placeholder prop to use the localized string instead of "Search here...." — when isTypingEffect is false return the existing localized value (e.g. the locale key searchHere or the translator function t('searchHere')) rather than the hardcoded text; update the expression around isTypingEffect and placeholder in SearchBarSlot.tsx so it uses the localized searchHere value (or call t) to keep the UI internationalized.packages/visual-editor/src/components/pageSections/SearchSection/SearchResultsSlot.tsx-297-301 (1)
297-301:⚠️ Potential issue | 🟡 Minor
runSearchmissing from dependency array.The effect calls
runSearchbut doesn't include it in the dependency array. This could cause stale closure issues ifrunSearch's dependencies change.🐛 Proposed fix
React.useEffect(() => { if (puck.isEditing) { runSearch(verticalKey, ""); } - }, [verticalKey, puck.isEditing]); + }, [verticalKey, puck.isEditing, runSearch]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/SearchResultsSlot.tsx` around lines 297 - 301, Effect in SearchResultsSlot uses runSearch but omits it from dependencies, risking stale closures; update the React.useEffect dependency array to include runSearch (i.e., [verticalKey, puck.isEditing, runSearch]) or alternatively memoize runSearch with useCallback where it’s declared so its identity is stable before adding it to the effect; adjust the effect to call runSearch(verticalKey, "") and ensure runSearch’s signature and closures remain correct.packages/visual-editor/src/components/pageSections/SearchSection/SearchResultsSlot.tsx-250-270 (1)
250-270:⚠️ Potential issue | 🟡 Minor
useEffectdependency array may be incomplete.The effect at line 250-270 references
hasUniversalTab,firstVerticalKey,runSearch, andupdateSearchUrlbut the dependency array at line 270 only includes[verticals, urlParams, firstVerticalKey]. MissinghasUniversalTabandrunSearchcould cause stale closure issues.🐛 Proposed fix
- }, [verticals, urlParams, firstVerticalKey]); + }, [verticals, urlParams, firstVerticalKey, hasUniversalTab, runSearch]);Note: If
runSearchis intentionally excluded (e.g., to avoid re-running on every runSearch change), consider using a ref or adding a lint disable comment with explanation.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/SearchResultsSlot.tsx` around lines 250 - 270, The useEffect in SearchResultsSlot references hasUniversalTab, runSearch, and updateSearchUrl but the dependency array only lists [verticals, urlParams, firstVerticalKey], which can cause stale closures; update the dependency array to include hasUniversalTab, runSearch, and updateSearchUrl (along with the existing verticals, urlParams, firstVerticalKey) so the effect re-runs with current values, or if runSearch/updateSearchUrl are intentionally unstable, wrap them in refs or memoize them and add a clear lint-disable comment with an explanation next to the useEffect to document the deliberate exclusion. Ensure you reference the same symbols (useEffect, hasUniversalTab, runSearch, updateSearchUrl, setVerticalKey, firstVerticalKey, verticals, urlParams) when making the change.packages/visual-editor/src/components/pageSections/CustomDirectory/CustomBreadcrumbs.tsx-216-236 (1)
216-236:⚠️ Potential issue | 🟡 MinorUse
sluginstead ofidfor breadcrumbhrefconstruction.Line 218 constructs the href as
/${b.id}, but theslugproperty is explicitly fetched from the API and populated in all breadcrumb items. URLs should useslugfor human-readable paths rather thanid(UUID). Change toconst href =/${b.slug};or document whyidis intentional.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/CustomDirectory/CustomBreadcrumbs.tsx` around lines 216 - 236, The breadcrumb href currently uses the UUID (b.id) which yields non-human-readable URLs; update the href construction in the fetchedBreadcrumbs map to use the slug (b.slug) instead of id so links become `/${b.slug}` (update the const href used by MaybeLink), leaving the display text logic (directoryRoot or b.name) unchanged; ensure any tests or references expecting id-based URLs are adjusted to use slug where relevant.packages/visual-editor/src/components/pageSections/SearchSection/utils.tsx-167-180 (1)
167-180:⚠️ Potential issue | 🟡 Minor
useMemodependency array may not prevent unnecessary re-renders.The memo returns the
paramsobject but depends onparams.verticalandparams.searchTerm. Sinceparamsis a new object reference fromuseState, the memoized value won't provide referential stability. Either returnparamsdirectly (state already handles re-renders) or reconstruct the object insideuseMemo.🔧 Proposed fix - Option 1: Remove unnecessary useMemo
export const useSearchUrlParams = () => { const [params, setParams] = React.useState(() => readInitialUrlParams()); React.useEffect(() => { const handlePopState = () => { setParams(readInitialUrlParams()); }; window.addEventListener("popstate", handlePopState); return () => window.removeEventListener("popstate", handlePopState); }, []); - return React.useMemo(() => params, [params.vertical, params.searchTerm]); + return params; };🔧 Proposed fix - Option 2: Properly memoize a new stable object
- return React.useMemo(() => params, [params.vertical, params.searchTerm]); + return React.useMemo( + () => ({ vertical: params.vertical, searchTerm: params.searchTerm }), + [params.vertical, params.searchTerm] + );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/utils.tsx` around lines 167 - 180, The useSearchUrlParams hook is returning params via React.useMemo but only lists params.vertical and params.searchTerm in the dependency array, which does not provide referential stability because setParams produces a new params object; remove the unnecessary useMemo and return params directly from useSearchUrlParams (referencing the existing readInitialUrlParams and params state), or if you need a stable derived object instead, wrap the desired derived shape in useMemo and include the full params (or serialize it) in the dependency array; simplest fix: delete the React.useMemo wrapper and return params.
🧹 Nitpick comments (18)
packages/visual-editor/src/components/contentBlocks/CTAGroup.tsx (1)
97-100:textColoris exposed for variants where it currently has no effect.The field is always configurable in CTAGroup, but the CTA style override is effectively primary-only. Consider making this field variant-conditional (or documenting it inline) to prevent confusing no-op settings.
Also applies to: 161-161
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/contentBlocks/CTAGroup.tsx` around lines 97 - 100, The textColor field in CTAGroup is always exposed but only affects the primary CTA style; update the field definition (textColor / msg("fields.textColor") inside CTAGroup) to render conditionally based on the selected variant (e.g., only when variant === "primary" or whatever internal variant name controls style overrides) or add an inline description that clearly states it only applies to primary CTAs; apply the same change where textColor is duplicated (the other occurrence referenced at line ~161) so the control is either hidden for non-applicable variants or annotated to prevent no-op configuration.packages/visual-editor/src/components/pageSections/SearchSection/GDAResponse.tsx (1)
2-2: Remove.tsxextension from import path.TypeScript/Vite projects typically omit file extensions in imports. Including
.tsxis non-standard and may cause issues with some bundler configurations.Suggested fix
-import SourceCard from "./SourceCard.tsx"; +import SourceCard from "./SourceCard";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/GDAResponse.tsx` at line 2, The import for SourceCard currently includes the .tsx extension; update the import statement that references SourceCard (used in GDAResponse.tsx) to remove the ".tsx" suffix so it imports by module path only (e.g., import SourceCard from "./SourceCard"), ensuring consistency with TypeScript/Vite bundler conventions.packages/visual-editor/src/components/pageSections/SearchSection/SourceCard.tsx (1)
12-16: Preferconstoverletfor variables that are never reassigned.
rawDataandlinkare not reassigned after initialization, soconstwould be more appropriate and signals immutability intent.🔧 Suggested fix
const SourceCard = (props: CitationProps) => { - let rawData: RawData = props.searchResult.rawData; - let link = rawData?.landingPageUrl || rawData?.c_primaryCTA?.link || ""; + const rawData: RawData = props.searchResult.rawData; + const link = rawData?.landingPageUrl || rawData?.c_primaryCTA?.link || ""; const name = props.searchResult?.name; const { t } = useTranslation();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/SourceCard.tsx` around lines 12 - 16, In SourceCard, rawData and link are never reassigned so change their declarations to const to express immutability: replace the let declarations for rawData (from props.searchResult.rawData) and link (computed from rawData?.landingPageUrl || rawData?.c_primaryCTA?.link || "") with const; keep the rest of the logic (name, useTranslation, CitationProps) unchanged.packages/visual-editor/src/components/pageSections/SearchSection/searchConfig.ts (2)
9-9: Consider adding proper type fordocumentparameter instead ofany.Using
anyloses type safety. Consider defining an interface for the expected document structure.♻️ Suggested type definition
interface SearchDocument { _env?: { YEXT_PUBLIC_ADV_SEARCH_API_KEY?: string; YEXT_PUBLIC_ADV_SEARCH_EXP_KEY?: string; }; locale?: string; } export const buildSearchConfigFromDocument = (document: SearchDocument): SearchConfig => { // ... }; export const useEntityPreviewSearcher = (document: SearchDocument) => { // ... };Also applies to: 20-20
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/searchConfig.ts` at line 9, The parameter typed as any in buildSearchConfigFromDocument (and similarly in useEntityPreviewSearcher) loses type safety; define a SearchDocument interface (e.g., optional _env with YEXT_PUBLIC_ADV_SEARCH_API_KEY and YEXT_PUBLIC_ADV_SEARCH_EXP_KEY and optional locale) and change the function signatures to accept document: SearchDocument instead of any, update any other occurrences of document typed as any to use this interface, and export or colocate the interface in searchConfig.ts so the functions (buildSearchConfigFromDocument, useEntityPreviewSearcher) use the new typed parameter.
20-32: Dependency ondocumentobject reference may cause unnecessary re-creation.If
documentis an object that gets a new reference on each render (even with identical values), this will recreate the headless instance unnecessarily. Consider using more stable dependencies like specific config values.♻️ More stable dependency approach
export const useEntityPreviewSearcher = (document: any) => { + const apiKey = document?._env?.YEXT_PUBLIC_ADV_SEARCH_API_KEY ?? ""; + const experienceKey = document?._env?.YEXT_PUBLIC_ADV_SEARCH_EXP_KEY ?? ""; + const locale = document?.locale ?? "en"; + return React.useMemo(() => { - const config = buildSearchConfigFromDocument(document); - - if (!config.apiKey || !config.experienceKey) { + if (!apiKey || !experienceKey) { return undefined; } return provideHeadless({ - ...config, + apiKey, + experienceKey, + locale, + experienceVersion: "PRODUCTION", + cloudRegion: CloudRegion.US, + environment: Environment.PROD, headlessId: "entity-preview-searcher", }); - }, [document]); + }, [apiKey, experienceKey, locale]); };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/searchConfig.ts` around lines 20 - 32, The hook useEntityPreviewSearcher re-creates the headless instance whenever the document reference changes; instead, derive the stable config values from buildSearchConfigFromDocument(document) first and use those specific fields (e.g., config.apiKey and config.experienceKey — or any other config fields that affect provideHeadless) as the useMemo dependencies instead of the whole document reference, then call provideHeadless with the full config inside the memo; update the dependency array and the memoization location so the headlessId creation in provideHeadless only runs when those stable values change.packages/visual-editor/src/components/pageSections/CustomDirectory/utils.ts (1)
7-38: Inconsistent return type: function returnsundefined, empty array, or data array.The function has three possible return paths:
undefinedon HTTP error (line 28) or exception (implicit line 37)[]whenjson.responseis undefined (line 32)- Data array on success
Consider adding an explicit return type and making error handling consistent.
♻️ Proposed improvement with explicit return type
+type FetchDataResponse = Record<string, unknown>[] | undefined; + -export const fetchData = async ({ +export const fetchData = async ({ endpoint, apiKey, entityIds, -}: fetchDataProps) => { +}: FetchDataProps): Promise<FetchDataResponse> => { try { const url = new URL(endpoint); // ... } catch (error) { console.error("Entity fetch error:", error); + return undefined; } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/CustomDirectory/utils.ts` around lines 7 - 38, fetchData currently returns undefined on errors and an array on success, so update the function signature to an explicit return type (e.g., Promise<any[]>) and make all code paths return an array: on non-ok responses and in the catch block return [] instead of undefined, and keep the successful path returning fetchedEntities (ensure fetchedEntities is always an array by using json.response ?? []). Modify the function declaration (fetchData) and its error/HTTP handling branches to enforce this consistent Promise<any[]> return shape.packages/visual-editor/src/components/pageSections/SearchSection/LayoutSections.tsx (1)
19-25: Either removeresultsCountor actually apply it.Right now the prop only feeds
console.log, so the section-level limit never affects the rendered results and the debug log ships with the component.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/LayoutSections.tsx` around lines 19 - 25, The LayoutSections component currently only logs resultsCount and doesn't use it; remove the console.log(resultsCount) and apply resultsCount to limit rendered cards by slicing the data passed to the CardComponent (e.g., compute a limitedItems = items.slice(0, resultsCount) or similar before mapping). Update the mapping/render logic inside LayoutSections (where CardComponent is used) to iterate over limitedItems and keep the existing layoutClasses logic unchanged.packages/visual-editor/src/components/pageSections/SearchSection/Search.tsx (2)
142-147: Defensive null check onsearchermay be unnecessary.
provideHeadlessfrom@yext/search-headless-reacttypically always returns aHeadlessSearcherinstance. This null check may be overly defensive. If the config is invalid, the error would occur during construction.Consider whether this check is needed based on
provideHeadlessbehavior, or add a comment explaining whensearchercould be null.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/Search.tsx` around lines 142 - 147, Remove or justify the defensive null-check for `searcher`: either delete the if-block that returns an empty fragment (the block checking `if (!searcher)` in the Search component) because `provideHeadless` from `@yext/search-headless-react` always returns a `HeadlessSearcher` instance, or add a short comment above the check explaining the specific scenario in which `searcher` could be null (e.g., if a wrapper returns null in tests or a future API change). Update the `searcher` usage accordingly (in the Search component) so there are no unreachable returns or unused branches.
128-131:useMemodependency array may be incomplete.The memo depends on
streamDocument.idandstreamDocument.locale, butbuildSearchConfigFromDocumentreceives the fullstreamDocument. If other document fields that affect the search config change, the memo won't recompute.If
buildSearchConfigFromDocumentonly usesidandlocale, this is fine. Otherwise, consider includingstreamDocumentin the dependency array or extracting only the needed fields.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/Search.tsx` around lines 128 - 131, The React.useMemo call creating searchConfig may miss updates because its dependency array only lists streamDocument.id and streamDocument.locale while buildSearchConfigFromDocument consumes the full streamDocument; update the dependency array to include streamDocument (or the exact additional fields used by buildSearchConfigFromDocument) so React.useMemo recalculates when any relevant document property changes—locate the React.useMemo that defines searchConfig and adjust dependencies accordingly.packages/visual-editor/src/components/pageSections/SearchSection/MapComponent.tsx (1)
105-109: Redundant ternary and potentialresultIndexissue.Since this code is inside the
if (isUniversal)block (line 74), the conditionisUniversal ? index + 1 : indexalways evaluates toindex + 1. The ternary is redundant and could mislead future maintainers. Simplify to justindex + 1.♻️ Proposed simplification
<MapPinIcon color={backgroundColors.background6.value} - resultIndex={isUniversal ? index + 1 : index} + resultIndex={index + 1} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/MapComponent.tsx` around lines 105 - 109, The MapPinIcon's resultIndex prop is using a redundant ternary (isUniversal ? index + 1 : index) inside a block that only executes when isUniversal is true; update the prop to pass the simplified value index + 1 directly (change MapPinIcon resultIndex to index + 1) to avoid confusion and potential future bugs in MapComponent where resultIndex, MapPinIcon, isUniversal, and index are referenced.packages/visual-editor/src/components/pageSections/SearchSection/VerticalResultsSection.tsx (1)
46-47:popupRefis declared but not used for click-outside detection.The ref is assigned to the popup element but there's no click-outside handler to close the filter modal when clicking outside. Users must use the close button. Consider adding click-outside handling for better UX.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/VerticalResultsSection.tsx` around lines 46 - 47, The popupRef is created but never used to detect outside clicks; add a click-outside handler that closes the filter modal by calling setShowFilterModal(false) when a click/touch occurs outside the element referenced by popupRef.current. Implement this in a useEffect inside VerticalResultsSection: on mount attach a 'mousedown'/'touchstart' listener that checks if popupRef.current exists and !popupRef.current.contains(event.target) then calls setShowFilterModal(false), and remove the listeners in the cleanup to avoid leaks; keep existing close button behavior intact.packages/visual-editor/locales/platform/hr/visual-editor.json (1)
113-115: Inconsistent key casing in component labels.The component keys use inconsistent casing:
"searchBarSlot"(camelCase)"SearchResultsSlot"(PascalCase)"searchSection"(camelCase)Consider aligning the casing for consistency, unless the design specs require different casing.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/locales/platform/hr/visual-editor.json` around lines 113 - 115, The JSON localization keys use inconsistent casing—rename the PascalCase key "SearchResultsSlot" to match the camelCase style used by "searchBarSlot" and "searchSection" (i.e., "searchResultsSlot") so all component keys use consistent camelCase; update any code references that import or lookup "SearchResultsSlot" to the new "searchResultsSlot" key to avoid runtime breakage.packages/visual-editor/src/components/pageSections/SearchSection/utils.tsx (4)
75-75: Remove commented-out debug code.This line appears to be leftover debug code and should be removed before merging.
🧹 Proposed fix
acc[v.verticalKey] = v.universalLimit; - // acc[v.verticalKey] = 50; return acc;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/utils.tsx` at line 75, Remove the leftover debug comment line "// acc[v.verticalKey] = 50;" from the utils.tsx file; locate the reducer/accumulation logic where "acc" and "v.verticalKey" are referenced (the commented assignment) and delete that commented-out line so no debug artifacts remain in the codebase.
80-80: Consider usingunknowninstead ofanyfor better type safety.
isValidVertical(v: any)usesanywhich bypasses type checking. Since this is a type guard function, usingunknownwould provide better compile-time safety while still allowing runtime checks.🛡️ Proposed fix
-const isValidVertical = (v: any): boolean => { +const isValidVertical = (v: unknown): boolean => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/utils.tsx` at line 80, The isValidVertical function currently accepts v: any which defeats TypeScript safety; change the parameter to v: unknown and keep the existing runtime checks (the body of isValidVertical) so it remains a type guard while forcing callers to handle unknown properly; update the function signature for isValidVertical to use unknown and ensure its return type remains boolean (or a proper type predicate if intended) so the typeguard behavior is preserved.
124-141: Consider adding proper typing forentityPreviewSearcher.Using
anyforentityPreviewSearcherweakens type safety. If the type is available from@yext/search-ui-react, consider importing and using it.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/utils.tsx` around lines 124 - 141, In createVisualAutocompleteConfig, replace the loose any type for the entityPreviewSearcher parameter with the proper type exported by `@yext/search-ui-react` (e.g., the Autocomplete/EntityPreviewSearcher type used by the library); import that type at the top of the file and use it in the function signature (entityPreviewSearcher: <ImportedType>), and update any related return or property typing (entityPreviewSearcher in the returned config) so the function and its returned object are fully typed instead of using any.
143-165: Remove or adjust the early return on line 151 to support clearing both URL parameters.The condition
if (!params.searchTerm && !params.vertical) return;prevents the function from clearing both parameters from the URL simultaneously. The subsequentelsebranches explicitly calldelete()to remove parameters, indicating intent to support clearing. This early return creates an inconsistency: callers cannot clear both parameters even though the logic below would support it.Either remove the condition entirely or adjust it to allow processing when parameters need to be cleared from the URL.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/utils.tsx` around lines 143 - 165, The early return in updateSearchUrl prevents clearing both URL params; change the guard so it only returns when both inputs are completely absent/undefined (e.g., params.vertical === undefined && params.searchTerm === undefined) instead of truthiness, or remove the check entirely so the subsequent set()/delete() logic runs and can delete parameters when empty/null; update the guard in the updateSearchUrl function accordingly.packages/visual-editor/locales/platform/lt/visual-editor.json (2)
113-115: Inconsistent casing between slot component keys.
searchBarSlotuses camelCase whileSearchResultsSlotuses PascalCase. This inconsistency may cause confusion when referencing these keys programmatically.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/locales/platform/lt/visual-editor.json` around lines 113 - 115, The localization keys are using inconsistent casing: searchBarSlot (camelCase) vs SearchResultsSlot (PascalCase); normalize them to a single convention (preferably camelCase) by renaming SearchResultsSlot to searchResultsSlot everywhere it’s used (including the JSON here and any code references) or alternatively rename searchBarSlot to SearchBarSlot if you choose PascalCase; update all references to the chosen identifier (e.g., searchResultsSlot/searchBarSlot) so keys and usages remain in sync.
65-67: Inconsistent casing in component keys.
customBreadcrumbsuses camelCase whileCustomDirectoryuses PascalCase. Consider aligning casing for consistency across component keys.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/locales/platform/lt/visual-editor.json` around lines 65 - 67, The locale file has inconsistent key casing: "customBreadcrumbs" and "customCodeSection" are camelCase but "CustomDirectory" is PascalCase; rename "CustomDirectory" to "customDirectory" in this JSON and update any code references (components, imports, tests, translation lookups) that use CustomDirectory to the new key to keep casing consistent across the codebase (search for "CustomDirectory" and replace with "customDirectory" in translation access points).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: a923dce7-7f28-4344-a67c-c055c3d6f4ca
📒 Files selected for processing (83)
packages/visual-editor/locales/components/cs/visual-editor.jsonpackages/visual-editor/locales/components/da/visual-editor.jsonpackages/visual-editor/locales/components/de/visual-editor.jsonpackages/visual-editor/locales/components/en-GB/visual-editor.jsonpackages/visual-editor/locales/components/en/visual-editor.jsonpackages/visual-editor/locales/components/es/visual-editor.jsonpackages/visual-editor/locales/components/et/visual-editor.jsonpackages/visual-editor/locales/components/fi/visual-editor.jsonpackages/visual-editor/locales/components/fr/visual-editor.jsonpackages/visual-editor/locales/components/hr/visual-editor.jsonpackages/visual-editor/locales/components/hu/visual-editor.jsonpackages/visual-editor/locales/components/it/visual-editor.jsonpackages/visual-editor/locales/components/ja/visual-editor.jsonpackages/visual-editor/locales/components/lt/visual-editor.jsonpackages/visual-editor/locales/components/lv/visual-editor.jsonpackages/visual-editor/locales/components/nb/visual-editor.jsonpackages/visual-editor/locales/components/nl/visual-editor.jsonpackages/visual-editor/locales/components/pl/visual-editor.jsonpackages/visual-editor/locales/components/pt/visual-editor.jsonpackages/visual-editor/locales/components/ro/visual-editor.jsonpackages/visual-editor/locales/components/sk/visual-editor.jsonpackages/visual-editor/locales/components/sv/visual-editor.jsonpackages/visual-editor/locales/components/tr/visual-editor.jsonpackages/visual-editor/locales/components/zh-TW/visual-editor.jsonpackages/visual-editor/locales/components/zh/visual-editor.jsonpackages/visual-editor/locales/platform/cs/visual-editor.jsonpackages/visual-editor/locales/platform/da/visual-editor.jsonpackages/visual-editor/locales/platform/de/visual-editor.jsonpackages/visual-editor/locales/platform/en-GB/visual-editor.jsonpackages/visual-editor/locales/platform/en/visual-editor.jsonpackages/visual-editor/locales/platform/es/visual-editor.jsonpackages/visual-editor/locales/platform/et/visual-editor.jsonpackages/visual-editor/locales/platform/fi/visual-editor.jsonpackages/visual-editor/locales/platform/fr/visual-editor.jsonpackages/visual-editor/locales/platform/hr/visual-editor.jsonpackages/visual-editor/locales/platform/hu/visual-editor.jsonpackages/visual-editor/locales/platform/it/visual-editor.jsonpackages/visual-editor/locales/platform/ja/visual-editor.jsonpackages/visual-editor/locales/platform/lt/visual-editor.jsonpackages/visual-editor/locales/platform/lv/visual-editor.jsonpackages/visual-editor/locales/platform/nb/visual-editor.jsonpackages/visual-editor/locales/platform/nl/visual-editor.jsonpackages/visual-editor/locales/platform/pl/visual-editor.jsonpackages/visual-editor/locales/platform/pt/visual-editor.jsonpackages/visual-editor/locales/platform/ro/visual-editor.jsonpackages/visual-editor/locales/platform/sk/visual-editor.jsonpackages/visual-editor/locales/platform/sv/visual-editor.jsonpackages/visual-editor/locales/platform/tr/visual-editor.jsonpackages/visual-editor/locales/platform/zh-TW/visual-editor.jsonpackages/visual-editor/locales/platform/zh/visual-editor.jsonpackages/visual-editor/src/components/atoms/cta.tsxpackages/visual-editor/src/components/categories/PageSectionCategory.tsxpackages/visual-editor/src/components/categories/SlotsCategory.tsxpackages/visual-editor/src/components/contentBlocks/CTAGroup.tsxpackages/visual-editor/src/components/contentBlocks/CtaWrapper.tsxpackages/visual-editor/src/components/pageSections/CustomDirectory/CustomBreadcrumbs.tsxpackages/visual-editor/src/components/pageSections/CustomDirectory/CustomDirectory.tsxpackages/visual-editor/src/components/pageSections/CustomDirectory/utils.tspackages/visual-editor/src/components/pageSections/SearchSection/Cards.tsxpackages/visual-editor/src/components/pageSections/SearchSection/GDAResponse.tsxpackages/visual-editor/src/components/pageSections/SearchSection/LayoutSections.tsxpackages/visual-editor/src/components/pageSections/SearchSection/MapComponent.tsxpackages/visual-editor/src/components/pageSections/SearchSection/Search.tsxpackages/visual-editor/src/components/pageSections/SearchSection/SearchBarSlot.tsxpackages/visual-editor/src/components/pageSections/SearchSection/SearchResultsSlot.tsxpackages/visual-editor/src/components/pageSections/SearchSection/SourceCard.tsxpackages/visual-editor/src/components/pageSections/SearchSection/UniversalResultsSection.tsxpackages/visual-editor/src/components/pageSections/SearchSection/VerticalResultsSection.tsxpackages/visual-editor/src/components/pageSections/SearchSection/defaultPropsAndTypes.tspackages/visual-editor/src/components/pageSections/SearchSection/search.csspackages/visual-editor/src/components/pageSections/SearchSection/searchConfig.tspackages/visual-editor/src/components/pageSections/SearchSection/searchVisualAutoComplete.tsxpackages/visual-editor/src/components/pageSections/SearchSection/useTypeEffect.tspackages/visual-editor/src/components/pageSections/SearchSection/utils.tsxpackages/visual-editor/src/components/pageSections/index.tspackages/visual-editor/src/components/styles.csspackages/visual-editor/src/docs/ai/components.d.tspackages/visual-editor/src/docs/components.mdpackages/visual-editor/src/internal/components/InternalThemeEditor.tsxpackages/visual-editor/src/internal/puck/components/LayoutHeader.tsxpackages/visual-editor/src/internal/puck/components/ThemeHeader.tsxpackages/visual-editor/src/internal/types/templateMetadata.tspackages/visual-editor/src/internal/utils/getPublishTooltipMessageFromHeadDeployStatus.ts
💤 Files with no reviewable changes (1)
- packages/visual-editor/src/internal/utils/getPublishTooltipMessageFromHeadDeployStatus.ts
packages/visual-editor/src/components/pageSections/CustomDirectory/CustomDirectory.tsx
Show resolved
Hide resolved
packages/visual-editor/src/components/pageSections/CustomDirectory/utils.ts
Show resolved
Hide resolved
packages/visual-editor/src/components/pageSections/SearchSection/Cards.tsx
Show resolved
Hide resolved
| {results.map((result, index) => ( | ||
| <CardComponent key={index} result={result} /> |
There was a problem hiding this comment.
Pass a 1-based card index here and avoid key={index}.
Cards falls back to 0 when no index prop is provided, so map/result numbering is wrong whenever result.index is absent. Using the array index as the React key also risks stale card/accordion state after reordering.
Proposed fix
- {results.map((result, index) => (
- <CardComponent key={index} result={result} />
- ))}
+ {results.map((result, index) => (
+ <CardComponent
+ key={result.id ?? result.rawData?.id ?? result.rawData?.uid ?? index}
+ result={result}
+ index={index + 1}
+ />
+ ))}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {results.map((result, index) => ( | |
| <CardComponent key={index} result={result} /> | |
| {results.map((result, index) => ( | |
| <CardComponent | |
| key={result.id ?? result.rawData?.id ?? result.rawData?.uid ?? index} | |
| result={result} | |
| index={index + 1} | |
| /> | |
| ))} |
🧰 Tools
🪛 GitHub Actions: main
[error] Build failed during step 'pnpm run build:components' (tsc && vite build) due to the TypeScript compilation error.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/visual-editor/src/components/pageSections/SearchSection/LayoutSections.tsx`
around lines 41 - 42, When mapping results in LayoutSections, stop using
key={index} and pass a 1-based card index to CardComponent: compute cardIndex =
(result.index ?? index) + 1 and pass it as the index prop to CardComponent
(e.g., index={cardIndex}), and replace the React key with a stable unique
identifier from the result (e.g., result.id or result.uid or another unique
property) so keys don't break on reorder; update the mapping call around
results.map and the CardComponent props accordingly.
| const LocatorPin: PinComponent<DefaultRawDataType> = (props) => { | ||
| const { result } = props; | ||
|
|
||
| return ( | ||
| <MapPinIcon | ||
| color={backgroundColors.background6.value} | ||
| resultIndex={result.index} | ||
| /> | ||
| ); | ||
| }; |
There was a problem hiding this comment.
resultIndex may be 0 or below, violating the >= 1 requirement.
The LocatorPin component passes result.index directly to MapPinIcon. Based on learnings, resultIndex must always be >= 1 for MapPinIcon. If result.index is 0-based from the search SDK, this would violate that constraint.
🐛 Proposed fix to ensure resultIndex >= 1
const LocatorPin: PinComponent<DefaultRawDataType> = (props) => {
const { result } = props;
return (
<MapPinIcon
color={backgroundColors.background6.value}
- resultIndex={result.index}
+ resultIndex={(result.index ?? 0) + 1}
/>
);
};Based on learnings: "ensure that the resultIndex prop used by MapPinIcon (and related components) is always >= 1."
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const LocatorPin: PinComponent<DefaultRawDataType> = (props) => { | |
| const { result } = props; | |
| return ( | |
| <MapPinIcon | |
| color={backgroundColors.background6.value} | |
| resultIndex={result.index} | |
| /> | |
| ); | |
| }; | |
| const LocatorPin: PinComponent<DefaultRawDataType> = (props) => { | |
| const { result } = props; | |
| return ( | |
| <MapPinIcon | |
| color={backgroundColors.background6.value} | |
| resultIndex={(result.index ?? 0) + 1} | |
| /> | |
| ); | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/visual-editor/src/components/pageSections/SearchSection/MapComponent.tsx`
around lines 19 - 28, LocatorPin currently forwards result.index directly to
MapPinIcon but MapPinIcon expects resultIndex >= 1; compute a safe 1-based value
before passing it (e.g. derive a resultIndex from result.index by adding 1 and
guarding with Math.max(1, …) or similar) and pass that computed value to
MapPinIcon instead of result.index; update the LocatorPin component (referencing
LocatorPin, MapPinIcon and result.index/resultIndex) to use the computed
safeResultIndex when rendering.
packages/visual-editor/src/components/pageSections/SearchSection/search.css
Outdated
Show resolved
Hide resolved
| <DropdownItem | ||
| key={result.id} | ||
| value={result.name} | ||
| onClick={() => | ||
| history.pushState(null, "", `${result.landingPageUrl}`) | ||
| } | ||
| ariaLabel={dropdownItemProps.ariaLabel} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's verify the file exists and read the relevant section
wc -l packages/visual-editor/src/components/pageSections/SearchSection/searchVisualAutoComplete.tsxRepository: yext/visual-editor
Length of output: 158
🏁 Script executed:
# Read the file around lines 60-66 with context
sed -n '40,80p' packages/visual-editor/src/components/pageSections/SearchSection/searchVisualAutoComplete.tsx | cat -nRepository: yext/visual-editor
Length of output: 1406
🏁 Script executed:
# Let's also look for dropdownItemProps definition and DropdownItem component
rg "dropdownItemProps" packages/visual-editor/src/components/pageSections/SearchSection/searchVisualAutoComplete.tsx -B 5 -A 2Repository: yext/visual-editor
Length of output: 546
🏁 Script executed:
# Check imports and overall structure
head -30 packages/visual-editor/src/components/pageSections/SearchSection/searchVisualAutoComplete.tsxRepository: yext/visual-editor
Length of output: 1018
🏁 Script executed:
# Look for DropdownItem component definition
fd -t f "DropdownItem" packages/visual-editor/src/components --glob "*DropdownItem*"Repository: yext/visual-editor
Length of output: 122
🏁 Script executed:
# Search for DropdownItem in the codebase to understand what it is
rg "export.*DropdownItem|from.*DropdownItem" packages/visual-editor/src -t tsx -t tsRepository: yext/visual-editor
Length of output: 89
🏁 Script executed:
# Look for the full dropdownItemProps type definition
sed -n '1,50p' packages/visual-editor/src/components/pageSections/SearchSection/searchVisualAutoComplete.tsx | cat -nRepository: yext/visual-editor
Length of output: 1844
🏁 Script executed:
# Find the complete function signature for renderEntityPreviews
rg "export const renderEntityPreviews" packages/visual-editor/src/components/pageSections/SearchSection/searchVisualAutoComplete.tsx -A 10Repository: yext/visual-editor
Length of output: 386
🏁 Script executed:
# Check if DropdownItem expects an onClick callback and what params it passes
rg "DropdownItem" packages/visual-editor/src -t ts | head -20Repository: yext/visual-editor
Length of output: 464
🏁 Script executed:
# Look for other uses of history.pushState or navigation patterns
rg "history\.(pushState|replaceState)" packages/visual-editor/src -B 2 -A 2Repository: yext/visual-editor
Length of output: 2330
🏁 Script executed:
# Check if dropdownItemProps.onClick is being used elsewhere
rg "dropdownItemProps\.onClick" packages/visual-editor/src -B 2 -A 2Repository: yext/visual-editor
Length of output: 44
Call the dropdown callback and perform real navigation.
history.pushState only mutates the URL without loading the landing page. Additionally, dropdownItemProps.onClick is completely bypassed, causing dropdown state/selection analytics to drift from what the search UI expects.
Proposed fix
- onClick={() =>
- history.pushState(null, "", `${result.landingPageUrl}`)
- }
+ onClick={(value, index, itemData) => {
+ dropdownItemProps.onClick(value, index, itemData);
+ if (result.landingPageUrl) {
+ window.location.assign(result.landingPageUrl);
+ }
+ }}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <DropdownItem | |
| key={result.id} | |
| value={result.name} | |
| onClick={() => | |
| history.pushState(null, "", `${result.landingPageUrl}`) | |
| } | |
| ariaLabel={dropdownItemProps.ariaLabel} | |
| <DropdownItem | |
| key={result.id} | |
| value={result.name} | |
| onClick={(value, index, itemData) => { | |
| dropdownItemProps.onClick(value, index, itemData); | |
| if (result.landingPageUrl) { | |
| window.location.assign(result.landingPageUrl); | |
| } | |
| }} | |
| ariaLabel={dropdownItemProps.ariaLabel} |
🧰 Tools
🪛 GitHub Actions: main
[error] Build failed during step 'pnpm run build:components' (tsc && vite build) due to the TypeScript compilation error.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/visual-editor/src/components/pageSections/SearchSection/searchVisualAutoComplete.tsx`
around lines 60 - 66, The onClick handler for DropdownItem currently calls
history.pushState (which only mutates the URL) and skips the provided dropdown
callback; update the DropdownItem onClick to first invoke
dropdownItemProps.onClick (if present) to let the dropdown update/analytics run,
then perform a real navigation to result.landingPageUrl (e.g., via
window.location.assign or setting window.location.href) instead of
history.pushState so the landing page actually loads; make sure to reference the
DropdownItem component, its onClick handler, dropdownItemProps.onClick, and
result.landingPageUrl when implementing the change.
| useEffect(() => { | ||
| if (!enabled) return; | ||
| const fetchPrompts = async () => { | ||
| const base = env === "PRODUCTION" ? "cdn" : "sbx-cdn"; | ||
|
|
||
| const url = `https://${base}.yextapis.com/v2/accounts/me/search/autocomplete?v=20250101&api_key=${document?._env?.YEXT_PUBLIC_ADV_SEARCH_API_KEY}&sessionTrackingEnabled=false&experienceKey=${document?._env?.YEXT_PUBLIC_ADV_SEARCH_EXP_KEY}&locale=${locale}&input=`; | ||
|
|
||
| try { | ||
| const res = await fetch(url); | ||
| const data = await res.json(); | ||
| setQueryPrompts(data.response.results.map((i: any) => i.value)); | ||
| } catch (err) { | ||
| console.error("TypingEffect fetch failed:", err); | ||
| } | ||
| }; | ||
|
|
||
| fetchPrompts(); | ||
| }, [env, enabled]); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify dependency coverage and enabled gating in useTypingEffect
rg -n -C3 'useEffect\(|enabled|locale|YEXT_PUBLIC_ADV_SEARCH_API_KEY|YEXT_PUBLIC_ADV_SEARCH_EXP_KEY' \
packages/visual-editor/src/components/pageSections/SearchSection/useTypeEffect.tsRepository: yext/visual-editor
Length of output: 1191
🏁 Script executed:
cat -n packages/visual-editor/src/components/pageSections/SearchSection/useTypeEffect.tsRepository: yext/visual-editor
Length of output: 2701
🏁 Script executed:
rg -l "useTypingEffect" packages/visual-editor/src/ --type ts --type tsxRepository: yext/visual-editor
Length of output: 89
🏁 Script executed:
rg -l "useTypingEffect" packages/visual-editor/src/ --type ts --type tsx -URepository: yext/visual-editor
Length of output: 89
🏁 Script executed:
rg "useTypingEffect" packages/visual-editor/src/ -A5 -B2 --type ts --type tsx -URepository: yext/visual-editor
Length of output: 89
🏁 Script executed:
rg "useTypingEffect" packages/visual-editor/src/ -A5 -B2Repository: yext/visual-editor
Length of output: 2667
🏁 Script executed:
fd "useTypeEffect|SearchSection" packages/visual-editor/src/ -type fRepository: yext/visual-editor
Length of output: 232
🏁 Script executed:
rg "isTypingEffect" packages/visual-editor/src/components/pageSections/SearchSection/SearchBarSlot.tsx -B10 -A5Repository: yext/visual-editor
Length of output: 2737
enabled/locale state changes are not fully honored by the effects.
At Line 40, locale (and env key inputs used in Line 28) are omitted from the fetch effect dependencies, so prompt language can go stale when document locale changes.
At Line 42-68, the typing interval runs as long as prompts exist, even if enabled becomes false, wasting CPU and leaving the animation running after the user disables typing effect.
🐛 Suggested fix
- useEffect(() => {
- if (!enabled) return;
+ useEffect(() => {
+ if (!enabled) {
+ setQueryPrompts([]);
+ setPlaceholder("");
+ return;
+ }
@@
- }, [env, enabled]);
+ }, [
+ env,
+ enabled,
+ locale,
+ document?._env?.YEXT_PUBLIC_ADV_SEARCH_API_KEY,
+ document?._env?.YEXT_PUBLIC_ADV_SEARCH_EXP_KEY,
+ ]);
useEffect(() => {
- if (queryPrompts.length === 0) return;
+ if (!enabled || queryPrompts.length === 0) return;
@@
- }, [queryPrompts]);
+ }, [enabled, queryPrompts]);🧰 Tools
🪛 GitHub Actions: main
[error] Build failed during step 'pnpm run build:components' (tsc && vite build) due to the TypeScript compilation error.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/visual-editor/src/components/pageSections/SearchSection/useTypeEffect.ts`
around lines 23 - 40, The effect in useTypeEffect omits locale and
env/credential inputs from its dependency array and does not stop the typing
interval when enabled flips false; update the fetchPrompts effect (the effect
that calls fetchPrompts and calls setQueryPrompts) to include locale and the
document?._env keys (YEXT_PUBLIC_ADV_SEARCH_API_KEY and
YEXT_PUBLIC_ADV_SEARCH_EXP_KEY or a combined env key) in the dependency array so
prompts reload when locale or keys change, and for the typing interval logic
ensure you only start the interval when enabled is true and prompts exist, store
the interval id, and clear that interval in the effect cleanup (and whenever
enabled becomes false) to stop the animation and free CPU; reference
useTypeEffect, fetchPrompts, setQueryPrompts and the typing interval/id when
making these changes.
| <button | ||
| className="w-full py-4 text-center text-palette-primary-dark font-bold font-body-fontFamily text-body-fontSize" | ||
| onClick={() => {}} | ||
| > | ||
| {t("clearAll", "Clear All")} | ||
| </button> | ||
| </div> |
There was a problem hiding this comment.
"Clear All" button has an empty click handler.
The onClick={() => {}} handler does nothing. Users will click this button expecting filters to clear, but nothing will happen. This appears to be incomplete implementation.
Do you want me to help implement the clear all functionality using searchActions.resetFacets() or similar?
🧰 Tools
🪛 GitHub Actions: main
[error] Build failed during step 'pnpm run build:components' (tsc && vite build) due to the TypeScript compilation error.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/visual-editor/src/components/pageSections/SearchSection/VerticalResultsSection.tsx`
around lines 135 - 141, The "Clear All" button in VerticalResultsSection has an
empty onClick handler; wire it to the search state reset routine (e.g., call
searchActions.resetFacets() and any related reset like
searchActions.setFilters([]) or searchActions.resetPage() as needed). Locate the
VerticalResultsSection component and replace the no-op onClick with a handler
that invokes the appropriate searchActions methods (ensure searchActions is
imported or accessed from props/context where other search calls are used in
this file) and then trigger a new search/refetch if required to update the UI.
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
packages/visual-editor/src/components/pageSections/SearchSection/MapComponent.tsx (1)
23-25:⚠️ Potential issue | 🟠 MajorNormalize
resultIndexto a guaranteed 1-based value.
LocatorPinstill forwardsresult.indexverbatim, so0, negative, or missing values can reachMapPinIconon the non-universal path. Please normalize this before rendering and add a small regression test for the guarded case.Based on learnings: "ensure that the resultIndex prop used by MapPinIcon (and related components) is always >= 1. Do not allow 0 or negative values. enforce via type definitions, default values, and runtime checks, and add tests to guard against regression."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/MapComponent.tsx` around lines 23 - 25, Normalize the resultIndex passed to MapPinIcon by ensuring it is a 1-based positive integer before rendering: in MapComponent (where MapPinIcon is used) compute a guarded value like Math.max(1, Number(result?.index ?? 1)) and pass that as resultIndex instead of result.index; also update LocatorPin usage to forward the same normalized value, strengthen the prop type for resultIndex to a positive number (non-zero) if possible, and add a regression test that renders MapComponent/LocatorPin with result.index = 0 (and a negative value / undefined) asserting the MapPinIcon receives or displays resultIndex >= 1.
🧹 Nitpick comments (2)
packages/visual-editor/src/components/pageSections/SearchSection/SearchBarSlot.tsx (2)
242-253: Voice search button remains visible when browser doesn't support SpeechRecognition.When
SpeechRecognitionConstructoris unavailable (line 248), the function silently returns but the microphone button remains visible and non-functional. Consider hiding the button or providing user feedback when the API is unsupported.Option: Hide button when unsupported
Add a state or memo to check for support and conditionally render the button:
const isSpeechSupported = React.useMemo(() => { if (typeof window === "undefined") return false; return !!(window.SpeechRecognition ?? window.webkitSpeechRecognition); }, []); // Then in JSX: {voiceSearch && isSpeechSupported && ( <button ...>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/SearchBarSlot.tsx` around lines 242 - 253, The microphone button is shown even when SpeechRecognition is unsupported; add a feature-detection flag (e.g., isSpeechSupported) using a React.useMemo that returns false when typeof window === "undefined" or when window.SpeechRecognition and window.webkitSpeechRecognition are both missing, and then use that flag to conditionally render or disable the mic control (the JSX that currently depends on voiceSearch) so the button is hidden or non-interactive; also ensure handleVoiceSearch and recognitionRef logic still guard against unsupported environments by early-returning when SpeechRecognitionConstructor is absent.
314-314: Fallback placeholder string should be internationalized.The fallback placeholder
"Search here...."is hardcoded while other UI strings uset()for translation. This creates an inconsistent experience for non-English users.Proposed fix
- placeholder={isTypingEffect ? placeholder : "Search here...."} + placeholder={isTypingEffect ? placeholder : t("searchPlaceholder", "Search here...")}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/visual-editor/src/components/pageSections/SearchSection/SearchBarSlot.tsx` at line 314, The fallback placeholder is hardcoded ("Search here...."); replace it with a translated string using the existing i18n function (e.g., change placeholder={isTypingEffect ? placeholder : "Search here...."} to placeholder={isTypingEffect ? placeholder : t('search.placeholder', 'Search here...')}), and add the key 'search.placeholder' with the default text to the i18n resource files so non-English users see the localized placeholder; keep references to isTypingEffect and placeholder unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@packages/visual-editor/src/components/pageSections/SearchSection/MapComponent.tsx`:
- Around line 47-53: The component currently treats an empty array the same as a
loading state, causing "Loading Map..." to show for completed zero-result
searches; update the MapComponent to distinguish "still fetching" from "finished
with no results" by checking a loading flag (e.g., isLoading or isFetching) or
testing for undefined/null vs empty array before returning the loader.
Specifically, change the early return that uses mapResults to only show the
loader when mapResults is null/undefined or when the explicit loading boolean is
true, and render an empty-state message/placeholder when mapResults is an empty
array; refer to mapResults and the MapComponent render path when making the
change.
- Around line 68-77: The code currently uses mapResults[0] (variable first) and
only yextDisplayCoordinate when computing center and bounds, which causes an
ungeocoded first result to suppress the map and omits entries with
rawData.coordinate; instead, build a single filtered coordinates array up front
by mapping each result to r.rawData?.yextDisplayCoordinate ??
r.rawData?.coordinate and filtering to Coordinate, then use that array for
computing the initial center (replace usage of first) and for bounds (replace
the current map over yextDisplayCoordinate); update the logic in the isUniversal
branch and the non-universal branch so both use this coordinates list and return
null only if that list is empty.
In
`@packages/visual-editor/src/components/pageSections/SearchSection/SearchBarSlot.tsx`:
- Around line 134-138: The limit field in SearchBarSlot.tsx currently allows 0
(limit: YextField(... min: 0)), but createVisualAutocompleteConfig in utils.tsx
treats limit < 1 as invalid; update the limit field's schema to use min: 1 so
the UI prevents entering 0 and matches createVisualAutocompleteConfig's
validation, ensuring the YextField configuration (limit) and the
createVisualAutocompleteConfig function are consistent.
- Around line 355-391: The resolveFields function is calling setDeep multiple
times but not capturing its return value, so updatedFields remains unchanged;
update each call in resolveFields (for example the calls that set
"styles.objectFields.visualAutoCompleteVerticalKey.visible",
"styles.objectFields.limit.visible", "styles.objectFields.height.visible",
"styles.objectFields.width.visible", "styles.objectFields.align.visible", and
the final "data") to assign the result back (e.g., updatedFields =
setDeep(updatedFields, ...)) or chain the setDeep calls so the returned modified
object is preserved before returning updatedFields; reference resolveFields,
updatedFields, setDeep and searchBarSlotFields to locate and fix the calls.
---
Duplicate comments:
In
`@packages/visual-editor/src/components/pageSections/SearchSection/MapComponent.tsx`:
- Around line 23-25: Normalize the resultIndex passed to MapPinIcon by ensuring
it is a 1-based positive integer before rendering: in MapComponent (where
MapPinIcon is used) compute a guarded value like Math.max(1,
Number(result?.index ?? 1)) and pass that as resultIndex instead of
result.index; also update LocatorPin usage to forward the same normalized value,
strengthen the prop type for resultIndex to a positive number (non-zero) if
possible, and add a regression test that renders MapComponent/LocatorPin with
result.index = 0 (and a negative value / undefined) asserting the MapPinIcon
receives or displays resultIndex >= 1.
---
Nitpick comments:
In
`@packages/visual-editor/src/components/pageSections/SearchSection/SearchBarSlot.tsx`:
- Around line 242-253: The microphone button is shown even when
SpeechRecognition is unsupported; add a feature-detection flag (e.g.,
isSpeechSupported) using a React.useMemo that returns false when typeof window
=== "undefined" or when window.SpeechRecognition and
window.webkitSpeechRecognition are both missing, and then use that flag to
conditionally render or disable the mic control (the JSX that currently depends
on voiceSearch) so the button is hidden or non-interactive; also ensure
handleVoiceSearch and recognitionRef logic still guard against unsupported
environments by early-returning when SpeechRecognitionConstructor is absent.
- Line 314: The fallback placeholder is hardcoded ("Search here...."); replace
it with a translated string using the existing i18n function (e.g., change
placeholder={isTypingEffect ? placeholder : "Search here...."} to
placeholder={isTypingEffect ? placeholder : t('search.placeholder', 'Search
here...')}), and add the key 'search.placeholder' with the default text to the
i18n resource files so non-English users see the localized placeholder; keep
references to isTypingEffect and placeholder unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 885dce51-0b23-4ab6-8763-f26bf53bfebb
📒 Files selected for processing (2)
packages/visual-editor/src/components/pageSections/SearchSection/MapComponent.tsxpackages/visual-editor/src/components/pageSections/SearchSection/SearchBarSlot.tsx
| if (!mapResults || mapResults.length === 0) { | ||
| return ( | ||
| <div className="h-[300px] flex items-center justify-center"> | ||
| {t("loadingMap", "Loading Map...")} | ||
| </div> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Zero-result searches will show a permanent loader.
This branch handles both “still fetching” and “finished with no results” because mapResults is also [] after a completed empty search. In the no-results case, the map area will keep saying “Loading Map...” forever instead of showing an empty state.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/visual-editor/src/components/pageSections/SearchSection/MapComponent.tsx`
around lines 47 - 53, The component currently treats an empty array the same as
a loading state, causing "Loading Map..." to show for completed zero-result
searches; update the MapComponent to distinguish "still fetching" from "finished
with no results" by checking a loading flag (e.g., isLoading or isFetching) or
testing for undefined/null vs empty array before returning the loader.
Specifically, change the early return that uses mapResults to only show the
loader when mapResults is null/undefined or when the explicit loading boolean is
true, and render an empty-state message/placeholder when mapResults is an empty
array; refer to mapResults and the MapComponent render path when making the
change.
| const first = | ||
| mapResults[0].rawData?.yextDisplayCoordinate ?? | ||
| mapResults[0].rawData?.coordinate; | ||
|
|
||
| if (!first) return null; | ||
|
|
||
| if (isUniversal) { | ||
| const coordinates: Coordinate[] = mapResults | ||
| .map((r) => r.rawData?.yextDisplayCoordinate) | ||
| .filter((coord): coord is Coordinate => Boolean(coord)); |
There was a problem hiding this comment.
Don’t let the first ungeocoded result suppress the whole map.
Line 72 returns null if mapResults[0] has no coordinate, even when later results do. Also, the bounds array only includes rawData.yextDisplayCoordinate, so results that only populate rawData.coordinate can be left out of the viewport. Build one filtered coordinate list up front and use it for both the initial center and bounds.
🐛 Suggested direction
- const first =
- mapResults[0].rawData?.yextDisplayCoordinate ??
- mapResults[0].rawData?.coordinate;
-
- if (!first) return null;
+ const coordinates: Coordinate[] = mapResults
+ .map(
+ (result) =>
+ result.rawData?.yextDisplayCoordinate ?? result.rawData?.coordinate
+ )
+ .filter((coord): coord is Coordinate => Boolean(coord));
+
+ if (coordinates.length === 0) return null;
+
+ const [first] = coordinates;
@@
- const coordinates: Coordinate[] = mapResults
- .map((r) => r.rawData?.yextDisplayCoordinate)
- .filter((coord): coord is Coordinate => Boolean(coord));
-
return (
<MapAlso applies to: 84-84
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/visual-editor/src/components/pageSections/SearchSection/MapComponent.tsx`
around lines 68 - 77, The code currently uses mapResults[0] (variable first) and
only yextDisplayCoordinate when computing center and bounds, which causes an
ungeocoded first result to suppress the map and omits entries with
rawData.coordinate; instead, build a single filtered coordinates array up front
by mapping each result to r.rawData?.yextDisplayCoordinate ??
r.rawData?.coordinate and filtering to Coordinate, then use that array for
computing the initial center (replace usage of first) and for bounds (replace
the current map over yextDisplayCoordinate); update the logic in the isUniversal
branch and the non-universal branch so both use this coordinates list and return
null only if that list is empty.
| limit: YextField(msg("fields.limit", "Limit"), { | ||
| type: "number", | ||
| min: 0, | ||
| max: 5, | ||
| }), |
There was a problem hiding this comment.
Limit minimum should be 1 to match validation logic.
The createVisualAutocompleteConfig function in utils.tsx (line 130) returns undefined when limit < 1. Setting min: 0 here allows users to input a value that silently disables visual autocomplete, which may be confusing.
Proposed fix
limit: YextField(msg("fields.limit", "Limit"), {
type: "number",
- min: 0,
+ min: 1,
max: 5,
}),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| limit: YextField(msg("fields.limit", "Limit"), { | |
| type: "number", | |
| min: 0, | |
| max: 5, | |
| }), | |
| limit: YextField(msg("fields.limit", "Limit"), { | |
| type: "number", | |
| min: 1, | |
| max: 5, | |
| }), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/visual-editor/src/components/pageSections/SearchSection/SearchBarSlot.tsx`
around lines 134 - 138, The limit field in SearchBarSlot.tsx currently allows 0
(limit: YextField(... min: 0)), but createVisualAutocompleteConfig in utils.tsx
treats limit < 1 as invalid; update the limit field's schema to use min: 1 so
the UI prevents entering 0 and matches createVisualAutocompleteConfig's
validation, ensuring the YextField configuration (limit) and the
createVisualAutocompleteConfig function are consistent.
packages/visual-editor/src/components/pageSections/SearchSection/SearchBarSlot.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@starter/config.yaml`:
- Line 4: The CI command in config.yaml uses the --ignore-scripts flag which
prevents workspace lifecycle hooks (e.g., packages/visual-editor prepare/build)
from running and leaves dependencies unbuilt; remove the --ignore-scripts flag
from the "command" entry (keep pnpm install --frozen-lockfile if desired) so
pnpm will run prepare and other lifecycle scripts and ensure workspace packages
are built during install.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 914af153-f89d-4eb2-b8dc-5757f8793448
⛔ Files ignored due to path filters (1)
packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] version 64 static image.pngis excluded by!**/*.png,!packages/visual-editor/src/components/testing/screenshots/**
📒 Files selected for processing (1)
starter/config.yaml
This was created for an upcoming demo. The package includes:
Note - No test cases added here.
PLEASE DON'T DELETE THIS