Skip to content

feat: advance search component, custom directory and breadcrumbs#1065

Draft
rkeerthient wants to merge 202 commits intomainfrom
advanced-search
Draft

feat: advance search component, custom directory and breadcrumbs#1065
rkeerthient wants to merge 202 commits intomainfrom
advanced-search

Conversation

@rkeerthient
Copy link
Copy Markdown
Contributor

@rkeerthient rkeerthient commented Feb 26, 2026

This was created for an upcoming demo. The package includes:

  • An advanced search component with a complete search wrapper (search bar + search results section), supporting individual styling and customizations.
  • A custom implementation for a non-address entity type, as required for the demo. This is powered through the Content Endpoint.

Note - No test cases added here.

PLEASE DON'T DELETE THIS

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds 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
Loading
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)
Loading

Possibly related PRs

Suggested labels

create-dev-release

Suggested reviewers

  • mkilpatrick
  • briantstephan
  • benlife5
  • asanehisa
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding a search component with advanced features (search bar + results), plus custom directory and breadcrumbs components.
Description check ✅ Passed The description clearly relates to the changeset by explaining the advanced search component, custom directory implementation, and Content Endpoint integration mentioned in the code changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch advanced-search

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🟠 Major

Run 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 | 🟡 Minor

Hide textColor when CTA is effectively preset-image.

At Line 362-Line 367, textColor visibility only checks ctaVariant === "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 | 🟡 Minor

Loading skeleton renders alongside GenerativeDirectAnswer, not instead of it.

When loading is true, the skeleton UI is rendered via the conditional block (lines 13-34), but GenerativeDirectAnswer (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 GenerativeDirectAnswer only when loading is false:

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., GenerativeDirectAnswer handles 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 | 🟡 Minor

Translate 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 | 🟡 Minor

Localize this Hungarian label fully.

Line 113 uses English ("SearchBar Slot") inside the hu locale 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 | 🟡 Minor

Translation 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 | 🟡 Minor

Fix typos in missingCustomEndpointApiKey message.

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 | 🟡 Minor

Fix the typo in the missing-endpoint copy.

Add you / sectiom will ship directly to users here, and URL should 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 | 🟡 Minor

Localize the fallback placeholder.

When typing effect is off, this always renders hardcoded English text even though searchHere already 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

runSearch missing from dependency array.

The effect calls runSearch but doesn't include it in the dependency array. This could cause stale closure issues if runSearch'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

useEffect dependency array may be incomplete.

The effect at line 250-270 references hasUniversalTab, firstVerticalKey, runSearch, and updateSearchUrl but the dependency array at line 270 only includes [verticals, urlParams, firstVerticalKey]. Missing hasUniversalTab and runSearch could cause stale closure issues.

🐛 Proposed fix
-  }, [verticals, urlParams, firstVerticalKey]);
+  }, [verticals, urlParams, firstVerticalKey, hasUniversalTab, runSearch]);

Note: If runSearch is 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 | 🟡 Minor

Use slug instead of id for breadcrumb href construction.

Line 218 constructs the href as /${b.id}, but the slug property is explicitly fetched from the API and populated in all breadcrumb items. URLs should use slug for human-readable paths rather than id (UUID). Change to const href = /${b.slug}; or document why id is 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

useMemo dependency array may not prevent unnecessary re-renders.

The memo returns the params object but depends on params.vertical and params.searchTerm. Since params is a new object reference from useState, the memoized value won't provide referential stability. Either return params directly (state already handles re-renders) or reconstruct the object inside useMemo.

🔧 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: textColor is 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 .tsx extension from import path.

TypeScript/Vite projects typically omit file extensions in imports. Including .tsx is 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: Prefer const over let for variables that are never reassigned.

rawData and link are not reassigned after initialization, so const would 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 for document parameter instead of any.

Using any loses 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 on document object reference may cause unnecessary re-creation.

If document is 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 returns undefined, empty array, or data array.

The function has three possible return paths:

  • undefined on HTTP error (line 28) or exception (implicit line 37)
  • [] when json.response is 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 remove resultsCount or 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 on searcher may be unnecessary.

provideHeadless from @yext/search-headless-react typically always returns a HeadlessSearcher instance. 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 provideHeadless behavior, or add a comment explaining when searcher could 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: useMemo dependency array may be incomplete.

The memo depends on streamDocument.id and streamDocument.locale, but buildSearchConfigFromDocument receives the full streamDocument. If other document fields that affect the search config change, the memo won't recompute.

If buildSearchConfigFromDocument only uses id and locale, this is fine. Otherwise, consider including streamDocument in 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 potential resultIndex issue.

Since this code is inside the if (isUniversal) block (line 74), the condition isUniversal ? index + 1 : index always evaluates to index + 1. The ternary is redundant and could mislead future maintainers. Simplify to just index + 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: popupRef is 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 using unknown instead of any for better type safety.

isValidVertical(v: any) uses any which bypasses type checking. Since this is a type guard function, using unknown would 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 for entityPreviewSearcher.

Using any for entityPreviewSearcher weakens 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 subsequent else branches explicitly call delete() 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.

searchBarSlot uses camelCase while SearchResultsSlot uses 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.

customBreadcrumbs uses camelCase while CustomDirectory uses 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

📥 Commits

Reviewing files that changed from the base of the PR and between 0ba7493 and 7a0269e.

📒 Files selected for processing (83)
  • packages/visual-editor/locales/components/cs/visual-editor.json
  • packages/visual-editor/locales/components/da/visual-editor.json
  • packages/visual-editor/locales/components/de/visual-editor.json
  • packages/visual-editor/locales/components/en-GB/visual-editor.json
  • packages/visual-editor/locales/components/en/visual-editor.json
  • packages/visual-editor/locales/components/es/visual-editor.json
  • packages/visual-editor/locales/components/et/visual-editor.json
  • packages/visual-editor/locales/components/fi/visual-editor.json
  • packages/visual-editor/locales/components/fr/visual-editor.json
  • packages/visual-editor/locales/components/hr/visual-editor.json
  • packages/visual-editor/locales/components/hu/visual-editor.json
  • packages/visual-editor/locales/components/it/visual-editor.json
  • packages/visual-editor/locales/components/ja/visual-editor.json
  • packages/visual-editor/locales/components/lt/visual-editor.json
  • packages/visual-editor/locales/components/lv/visual-editor.json
  • packages/visual-editor/locales/components/nb/visual-editor.json
  • packages/visual-editor/locales/components/nl/visual-editor.json
  • packages/visual-editor/locales/components/pl/visual-editor.json
  • packages/visual-editor/locales/components/pt/visual-editor.json
  • packages/visual-editor/locales/components/ro/visual-editor.json
  • packages/visual-editor/locales/components/sk/visual-editor.json
  • packages/visual-editor/locales/components/sv/visual-editor.json
  • packages/visual-editor/locales/components/tr/visual-editor.json
  • packages/visual-editor/locales/components/zh-TW/visual-editor.json
  • packages/visual-editor/locales/components/zh/visual-editor.json
  • packages/visual-editor/locales/platform/cs/visual-editor.json
  • packages/visual-editor/locales/platform/da/visual-editor.json
  • packages/visual-editor/locales/platform/de/visual-editor.json
  • packages/visual-editor/locales/platform/en-GB/visual-editor.json
  • packages/visual-editor/locales/platform/en/visual-editor.json
  • packages/visual-editor/locales/platform/es/visual-editor.json
  • packages/visual-editor/locales/platform/et/visual-editor.json
  • packages/visual-editor/locales/platform/fi/visual-editor.json
  • packages/visual-editor/locales/platform/fr/visual-editor.json
  • packages/visual-editor/locales/platform/hr/visual-editor.json
  • packages/visual-editor/locales/platform/hu/visual-editor.json
  • packages/visual-editor/locales/platform/it/visual-editor.json
  • packages/visual-editor/locales/platform/ja/visual-editor.json
  • packages/visual-editor/locales/platform/lt/visual-editor.json
  • packages/visual-editor/locales/platform/lv/visual-editor.json
  • packages/visual-editor/locales/platform/nb/visual-editor.json
  • packages/visual-editor/locales/platform/nl/visual-editor.json
  • packages/visual-editor/locales/platform/pl/visual-editor.json
  • packages/visual-editor/locales/platform/pt/visual-editor.json
  • packages/visual-editor/locales/platform/ro/visual-editor.json
  • packages/visual-editor/locales/platform/sk/visual-editor.json
  • packages/visual-editor/locales/platform/sv/visual-editor.json
  • packages/visual-editor/locales/platform/tr/visual-editor.json
  • packages/visual-editor/locales/platform/zh-TW/visual-editor.json
  • packages/visual-editor/locales/platform/zh/visual-editor.json
  • packages/visual-editor/src/components/atoms/cta.tsx
  • packages/visual-editor/src/components/categories/PageSectionCategory.tsx
  • packages/visual-editor/src/components/categories/SlotsCategory.tsx
  • packages/visual-editor/src/components/contentBlocks/CTAGroup.tsx
  • packages/visual-editor/src/components/contentBlocks/CtaWrapper.tsx
  • packages/visual-editor/src/components/pageSections/CustomDirectory/CustomBreadcrumbs.tsx
  • packages/visual-editor/src/components/pageSections/CustomDirectory/CustomDirectory.tsx
  • packages/visual-editor/src/components/pageSections/CustomDirectory/utils.ts
  • packages/visual-editor/src/components/pageSections/SearchSection/Cards.tsx
  • packages/visual-editor/src/components/pageSections/SearchSection/GDAResponse.tsx
  • packages/visual-editor/src/components/pageSections/SearchSection/LayoutSections.tsx
  • packages/visual-editor/src/components/pageSections/SearchSection/MapComponent.tsx
  • packages/visual-editor/src/components/pageSections/SearchSection/Search.tsx
  • packages/visual-editor/src/components/pageSections/SearchSection/SearchBarSlot.tsx
  • packages/visual-editor/src/components/pageSections/SearchSection/SearchResultsSlot.tsx
  • packages/visual-editor/src/components/pageSections/SearchSection/SourceCard.tsx
  • packages/visual-editor/src/components/pageSections/SearchSection/UniversalResultsSection.tsx
  • packages/visual-editor/src/components/pageSections/SearchSection/VerticalResultsSection.tsx
  • packages/visual-editor/src/components/pageSections/SearchSection/defaultPropsAndTypes.ts
  • packages/visual-editor/src/components/pageSections/SearchSection/search.css
  • packages/visual-editor/src/components/pageSections/SearchSection/searchConfig.ts
  • packages/visual-editor/src/components/pageSections/SearchSection/searchVisualAutoComplete.tsx
  • packages/visual-editor/src/components/pageSections/SearchSection/useTypeEffect.ts
  • packages/visual-editor/src/components/pageSections/SearchSection/utils.tsx
  • packages/visual-editor/src/components/pageSections/index.ts
  • packages/visual-editor/src/components/styles.css
  • packages/visual-editor/src/docs/ai/components.d.ts
  • packages/visual-editor/src/docs/components.md
  • packages/visual-editor/src/internal/components/InternalThemeEditor.tsx
  • packages/visual-editor/src/internal/puck/components/LayoutHeader.tsx
  • packages/visual-editor/src/internal/puck/components/ThemeHeader.tsx
  • packages/visual-editor/src/internal/types/templateMetadata.ts
  • packages/visual-editor/src/internal/utils/getPublishTooltipMessageFromHeadDeployStatus.ts
💤 Files with no reviewable changes (1)
  • packages/visual-editor/src/internal/utils/getPublishTooltipMessageFromHeadDeployStatus.ts

Comment on lines +41 to +42
{results.map((result, index) => (
<CardComponent key={index} result={result} />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
{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.

Comment on lines +19 to +28
const LocatorPin: PinComponent<DefaultRawDataType> = (props) => {
const { result } = props;

return (
<MapPinIcon
color={backgroundColors.background6.value}
resultIndex={result.index}
/>
);
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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.

Comment on lines +60 to +66
<DropdownItem
key={result.id}
value={result.name}
onClick={() =>
history.pushState(null, "", `${result.landingPageUrl}`)
}
ariaLabel={dropdownItemProps.ariaLabel}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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.tsx

Repository: 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 -n

Repository: 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 2

Repository: yext/visual-editor

Length of output: 546


🏁 Script executed:

# Check imports and overall structure
head -30 packages/visual-editor/src/components/pageSections/SearchSection/searchVisualAutoComplete.tsx

Repository: 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 ts

Repository: 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 -n

Repository: 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 10

Repository: 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 -20

Repository: 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 2

Repository: 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 2

Repository: 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.

Suggested change
<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.

Comment on lines +23 to +40
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]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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.ts

Repository: yext/visual-editor

Length of output: 1191


🏁 Script executed:

cat -n packages/visual-editor/src/components/pageSections/SearchSection/useTypeEffect.ts

Repository: yext/visual-editor

Length of output: 2701


🏁 Script executed:

rg -l "useTypingEffect" packages/visual-editor/src/ --type ts --type tsx

Repository: yext/visual-editor

Length of output: 89


🏁 Script executed:

rg -l "useTypingEffect" packages/visual-editor/src/ --type ts --type tsx -U

Repository: yext/visual-editor

Length of output: 89


🏁 Script executed:

rg "useTypingEffect" packages/visual-editor/src/ -A5 -B2 --type ts --type tsx -U

Repository: yext/visual-editor

Length of output: 89


🏁 Script executed:

rg "useTypingEffect" packages/visual-editor/src/ -A5 -B2

Repository: yext/visual-editor

Length of output: 2667


🏁 Script executed:

fd "useTypeEffect|SearchSection" packages/visual-editor/src/ -type f

Repository: yext/visual-editor

Length of output: 232


🏁 Script executed:

rg "isTypingEffect" packages/visual-editor/src/components/pageSections/SearchSection/SearchBarSlot.tsx -B10 -A5

Repository: 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.

Comment on lines +135 to +141
<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>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

"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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
packages/visual-editor/src/components/pageSections/SearchSection/MapComponent.tsx (1)

23-25: ⚠️ Potential issue | 🟠 Major

Normalize resultIndex to a guaranteed 1-based value.

LocatorPin still forwards result.index verbatim, so 0, negative, or missing values can reach MapPinIcon on 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 SpeechRecognitionConstructor is 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 use t() 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

📥 Commits

Reviewing files that changed from the base of the PR and between 7a0269e and e4e1ab6.

📒 Files selected for processing (2)
  • packages/visual-editor/src/components/pageSections/SearchSection/MapComponent.tsx
  • packages/visual-editor/src/components/pageSections/SearchSection/SearchBarSlot.tsx

Comment on lines +47 to +53
if (!mapResults || mapResults.length === 0) {
return (
<div className="h-[300px] flex items-center justify-center">
{t("loadingMap", "Loading Map...")}
</div>
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +68 to +77
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));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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 (
       <Map

Also 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.

Comment on lines +134 to +138
limit: YextField(msg("fields.limit", "Limit"), {
type: "number",
min: 0,
max: 5,
}),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between e4e1ab6 and b022090.

⛔ Files ignored due to path filters (1)
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] version 64 static image.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
📒 Files selected for processing (1)
  • starter/config.yaml

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant