Skip to content

Conversation

@k-gerner
Copy link
Contributor

@k-gerner k-gerner commented Nov 17, 2025

Adds lots of new properties to the locator config for styling the result cards.

We still need to do work to plumb field options to the e.g. header fields (i.e. besides just name), but the styling should be pretty much there.

J=WAT-5196, WAT-5199

locator_result_card_demo4.mov
locator_secondary_cta_demo2.mov

@k-gerner k-gerner requested review from a team November 17, 2025 15:31
@github-actions
Copy link
Contributor

Warning: Component files have been updated but no migrations have been added. See https://github.com/yext/visual-editor/blob/main/packages/visual-editor/src/components/migrations/README.md for more information.

@k-gerner
Copy link
Contributor Author

Note: To break the change down into more reviewable parts, we're going to be merging PRs into the main locator result card branch one by one.

Also, haven't done the translations yet as I wanted verification that they look right first

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 17, 2025

Walkthrough

Adds a public, configurable resultCard prop to Locator and introduces a prop-driven LocatorResultCard (exports: LocatorResultCardProps, LocatorResultCardFields, DEFAULT_LOCATOR_RESULT_CARD_PROPS). Wires resultCardProps through Locator rendering. Introduces explicit radius state and helpers to build/update near/equals location filters, an initial vertical result limit (50) for first searches, a ResultsCountSummary UI, CTA/phone onClick plumbing, a migration (0024) to deep-merge default result card props, and extensive locale key additions for pluralized and distance-aware messaging.

Sequence Diagram(s)

sequenceDiagram
    participant Consumer as Editor / App
    participant Locator as Locator Component
    participant Geo as Geolocation
    participant Search as Search API
    participant Card as LocatorResultCard
    participant Count as ResultsCountSummary

    rect rgb(240,248,255)
      Note right of Locator: init props & state (resultCardProps, selectedDistanceMiles, apiFilterRadius)
    end

    Consumer->>Locator: mount with props (includes resultCard)
    alt geolocation available
      Geo-->>Locator: coords
      Locator->>Locator: buildNearLocationFilterFromCoords(coords, radius)
    end
    Locator->>Search: initial search (apply initialLocationFilter, radius, limit=50)
    Search-->>Locator: results (may include near/equals filter metadata)
    Locator->>Card: render result using resultCardProps per item
    Locator->>Count: render ResultsCountSummary(displayName, distance, counts)
    Consumer->>Locator: updates radius / filter UI
    Locator->>Locator: updateRadiusInNearFiltersOnLocationField(newRadius)
    Locator->>Search: re-run search with updated filters
Loading

Possibly related PRs

Suggested labels

create-dev-release

Suggested reviewers

  • benlife5
  • colton-demetriou
  • jwartofsky-yext

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add new props and styling to locator result card' directly describes the main changeset: adding new properties and styling to the locator result card component.
Description check ✅ Passed The description explains that new properties are being added to the locator config for styling result cards, mentions remaining work on field options, and references related Jira issues, all of which relate to the changeset.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch locator-result-card-new-props

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb8f651 and 57d2bfb.

⛔ Files ignored due to path filters (19)
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] version 10 default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] version 10 non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] version 21 default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] version 21 non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] version 22 default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] version 22 non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[mobile] version 10 default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[mobile] version 10 non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[mobile] version 21 default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[mobile] version 21 non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[mobile] version 22 default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[mobile] version 22 non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[tablet] version 10 default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[tablet] version 10 non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[tablet] version 21 default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[tablet] version 21 non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[tablet] version 22 default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[tablet] version 22 non-default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/StaticMapSection/[desktop] default props with coordinate - with api key.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
📒 Files selected for processing (1)
  • packages/visual-editor/locales/nb/visual-editor.json (9 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: call_unit_test / unit_tests (18.x)
  • GitHub Check: call_unit_test / unit_tests (20.x)
  • GitHub Check: update_notices / license-check
  • GitHub Check: semgrep/ci
🔇 Additional comments (1)
packages/visual-editor/locales/nb/visual-editor.json (1)

41-41: Verify primary translation is intentional.

Line 41 shows "callToAction": "Ring til handling". Confirm this is the intended Norwegian translation and matches the glossary/style guide used across the project (alternatives like "Oppfordring til handling" or "Handlingsoppfordring" might be considered).

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


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

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/src/components/LocatorResultCard.tsx (1)

450-463: Index signature defeats TypeScript safety guarantees.

The [key: string]: any on line 462 allows arbitrary property access but eliminates compile-time type checking. This creates risk of runtime errors when accessing configured fields that don't exist on location objects.

Consider implementing runtime validation when accessing dynamic fields, or use a type-safe approach like:

// Helper to safely access dynamic fields
function getLocationField<T = unknown>(
  location: Location,
  field: string,
  defaultValue?: T
): T | undefined {
  return location.hasOwnProperty(field) ? (location as any)[field] : defaultValue;
}

Then use throughout the component:

-{location[props.primaryHeading.field] ?? location.name}
+{getLocationField(location, props.primaryHeading.field) ?? location.name}
🧹 Nitpick comments (1)
packages/visual-editor/src/components/LocatorResultCard.tsx (1)

764-773: Helper component centralizes icon styling.

CardIcon provides consistent icon container styling across the result card. Note that PhoneAtom has its own internal icon styling logic with a similar pattern, but CardIcon serves the other icons in the card.

For consistency, consider whether PhoneAtom's icon styling should also use CardIcon, or if CardIcon should accept the same backgroundColor prop pattern that PhoneAtom uses. This would reduce slight duplication between the two approaches.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7d6817 and 62aa3e3.

📒 Files selected for processing (6)
  • packages/visual-editor/src/components/Locator.tsx (10 hunks)
  • packages/visual-editor/src/components/LocatorResultCard.tsx (4 hunks)
  • packages/visual-editor/src/components/atoms/accordion.tsx (4 hunks)
  • packages/visual-editor/src/components/atoms/cta.tsx (3 hunks)
  • packages/visual-editor/src/components/atoms/phone.tsx (4 hunks)
  • packages/visual-editor/src/components/index.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-29T22:00:03.843Z
Learnt from: mkouzel-yext
Repo: yext/visual-editor PR: 833
File: packages/visual-editor/src/components/Locator.tsx:1050-1057
Timestamp: 2025-10-29T22:00:03.843Z
Learning: In packages/visual-editor/src/components/Locator.tsx, the AppliedFilters component is intentionally rendered in two locations (inside the filter modal and outside it) as per the design requirements. This dual rendering should not be flagged as a duplicate issue.

Applied to files:

  • packages/visual-editor/src/components/LocatorResultCard.tsx
  • packages/visual-editor/src/components/index.ts
  • packages/visual-editor/src/components/Locator.tsx
🧬 Code graph analysis (3)
packages/visual-editor/src/components/atoms/cta.tsx (1)
packages/visual-editor/src/components/atoms/button.tsx (1)
  • Button (89-115)
packages/visual-editor/src/components/LocatorResultCard.tsx (4)
packages/visual-editor/src/components/atoms/cta.tsx (2)
  • CTAVariant (41-41)
  • CTA (140-209)
packages/visual-editor/src/components/contentBlocks/HoursTable.tsx (1)
  • HoursTableStyleFields (49-78)
packages/visual-editor/src/components/atoms/accordion.tsx (4)
  • Accordion (4-9)
  • AccordionItem (11-25)
  • AccordionTrigger (27-53)
  • AccordionContent (55-62)
packages/visual-editor/src/components/atoms/phone.tsx (1)
  • PhoneAtom (17-57)
packages/visual-editor/src/components/Locator.tsx (1)
packages/visual-editor/src/components/LocatorResultCard.tsx (4)
  • LocatorResultCardProps (54-105)
  • LocatorResultCardFields (157-448)
  • DEFAULT_LOCATOR_RESULT_CARD_PROPS (107-155)
  • LocatorResultCard (465-762)
🔇 Additional comments (10)
packages/visual-editor/src/components/LocatorResultCard.tsx (10)

1-52: LGTM!

The new imports support the prop-driven configuration model and are all utilized in the refactored component.


54-105: Well-structured configuration interface.

The interface provides comprehensive configuration options for the result card. The string-based field selectors enable editor flexibility but will require careful validation in the component implementation.


107-155: LGTM!

The default configuration provides sensible values that balance functionality with flexibility. The secondary CTA being hidden by default aligns with the noted incomplete implementation.


157-448: Field options are currently limited as noted in PR objectives.

Each field type offers only 1-2 options (e.g., primary heading can only be "name"). Per the PR description, plumbing additional field options is noted as remaining work.

Ensure this limitation is tracked and that the architecture supports adding more field options without breaking changes.


465-527: LGTM!

The component setup properly initializes analytics callbacks and resolves URLs. Making resultCardProps required is appropriate for the new prop-driven architecture.


596-631: LGTM!

The accordion-based hours display improves UX by allowing users to expand/collapse details. The props integration is clean and correctly respects visibility and styling configuration.


683-702: LGTM!

The email field access properly validates that the field is an array with items before rendering. The validation pattern here is robust.


706-719: LGTM!

The services field access follows the same robust validation pattern as email, ensuring the field is an array with items before rendering.


736-746: LGTM!

The primary CTA correctly uses the configured variant and respects the visibility flag. The analytics callback and URL resolution are properly applied.


747-757: Secondary CTA implementation incomplete as noted in PR objectives.

The secondary CTA currently uses the same link, handler, and label as the primary CTA. Per the PR description, implementation work for the secondary CTA is noted as remaining work.

Ensure this is tracked and that the placeholder implementation doesn't cause confusion for users who enable the secondary CTA in the editor.

Copy link
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: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62aa3e3 and 41b43e2.

⛔ Files ignored due to path filters (5)
  • packages/visual-editor/src/components/testing/screenshots/Locator/[desktop] latest version default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[mobile] latest version default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/Locator/[tablet] latest version default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/StaticMapSection/[desktop] default props with coordinate - with api key.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/StaticMapSection/[mobile] default props with coordinate - with api key.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
📒 Files selected for processing (2)
  • packages/visual-editor/src/docs/ai/components.d.ts (1 hunks)
  • packages/visual-editor/src/docs/components.md (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-06T14:55:12.395Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 862
File: packages/visual-editor/src/utils/schema/resolveSchema.ts:118-135
Timestamp: 2025-11-06T14:55:12.395Z
Learning: In `packages/visual-editor/src/utils/schema/resolveSchema.ts`, the `OpeningHoursSchema` and `PhotoGallerySchema` functions from `yext/pages-components` contain internal type validation and handle invalid inputs gracefully (returning empty objects or undefined) rather than throwing TypeErrors, so no pre-validation guards are needed before calling them.

Applied to files:

  • packages/visual-editor/src/docs/ai/components.d.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: call_unit_test / unit_tests (20.x)

Copy link
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: 0

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/src/components/LocatorResultCard.tsx (1)

510-522: Runtime protection is mostly adequate but has inconsistent guard patterns.

The fieldExists helper is well-implemented with both hasOwnProperty() checks and optional type validation. However, dynamic field access patterns show inconsistent protection:

  • Well-guarded accesses (lines 612-617, 636, 645, 744, 752-764, 775-783): All properly checked via fieldExists() with appropriate type validation or Array.isArray() guards.

  • Inconsistent pattern (line 628): The primaryHeading field access lacks an explicit fieldExists() guard—it's only protected by a config existence check (props.primaryHeading.field &&). While the nullish coalescing fallback (?? location.name) prevents crashes, this deviates from the established guard pattern.

Strengthen consistency by applying fieldExists() uniformly, particularly for the primary heading access to match the pattern used for secondary/tertiary headings (lines 631-646).

🧹 Nitpick comments (1)
packages/visual-editor/src/components/LocatorResultCard.tsx (1)

790-802: Dynamic className construction for themed components.

The template literal className (lines 792-795) applies theme colors dynamically. This pattern is acceptable since backgroundColors is a predefined palette, though extracting this to a styled component or utility function could improve maintainability if the pattern repeats frequently.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 41b43e2 and 8d3dca1.

⛔ Files ignored due to path filters (3)
  • packages/visual-editor/src/components/testing/screenshots/BreadcrumbsSection/[tablet] default props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/BreadcrumbsSection/[tablet] version 4 props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/BreadcrumbsSection/[tablet] version 8 with non-default props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
📒 Files selected for processing (3)
  • packages/visual-editor/src/components/LocatorResultCard.tsx (5 hunks)
  • packages/visual-editor/src/docs/ai/components.d.ts (1 hunks)
  • packages/visual-editor/src/docs/components.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/visual-editor/src/docs/components.md
  • packages/visual-editor/src/docs/ai/components.d.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-29T22:00:03.843Z
Learnt from: mkouzel-yext
Repo: yext/visual-editor PR: 833
File: packages/visual-editor/src/components/Locator.tsx:1050-1057
Timestamp: 2025-10-29T22:00:03.843Z
Learning: In packages/visual-editor/src/components/Locator.tsx, the AppliedFilters component is intentionally rendered in two locations (inside the filter modal and outside it) as per the design requirements. This dual rendering should not be flagged as a duplicate issue.

Applied to files:

  • packages/visual-editor/src/components/LocatorResultCard.tsx
📚 Learning: 2025-11-06T14:55:12.395Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 862
File: packages/visual-editor/src/utils/schema/resolveSchema.ts:118-135
Timestamp: 2025-11-06T14:55:12.395Z
Learning: In `packages/visual-editor/src/utils/schema/resolveSchema.ts`, the `OpeningHoursSchema` and `PhotoGallerySchema` functions from `yext/pages-components` contain internal type validation and handle invalid inputs gracefully (returning empty objects or undefined) rather than throwing TypeErrors, so no pre-validation guards are needed before calling them.

Applied to files:

  • packages/visual-editor/src/components/LocatorResultCard.tsx
🧬 Code graph analysis (1)
packages/visual-editor/src/components/LocatorResultCard.tsx (4)
packages/visual-editor/src/components/atoms/cta.tsx (2)
  • CTAVariant (41-41)
  • CTA (140-209)
packages/visual-editor/src/components/contentBlocks/HoursTable.tsx (1)
  • HoursTableStyleFields (49-78)
packages/visual-editor/src/components/atoms/accordion.tsx (4)
  • Accordion (4-9)
  • AccordionItem (11-25)
  • AccordionTrigger (27-53)
  • AccordionContent (55-62)
packages/visual-editor/src/components/atoms/phone.tsx (1)
  • PhoneAtom (17-57)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: call_unit_test / unit_tests (20.x)
  • GitHub Check: call_unit_test / unit_tests (18.x)
  • GitHub Check: semgrep/ci
🔇 Additional comments (10)
packages/visual-editor/src/components/LocatorResultCard.tsx (10)

1-52: LGTM!

Imports are well-organized and include all necessary dependencies for the expanded result card functionality.


53-165: LGTM!

The LocatorResultCardProps interface is comprehensive and well-documented, providing clear configuration options for all card sections with appropriate TypeScript types.


167-215: LGTM!

Default configuration provides sensible initial values, with optional features (email, services, secondary CTA, image) appropriately disabled by default.


217-508: LGTM!

Field definitions are comprehensive and properly structured for the Puck editor. The limited field options for headings align with the remaining work mentioned in the PR description.


587-592: Field validation helper provides basic safety.

The fieldExists helper offers surface-level validation by checking property existence and optional type. The additional inline validation for complex types (like image object structure on lines 610-622) correctly extends this basic check.


607-662: LGTM!

Heading section implements proper validation for all dynamic field access:

  • Image field includes deep validation for object structure and .url property
  • Headings use fieldExists with type checking and appropriate fallbacks
  • Responsive distance badge implementation is clean

664-698: LGTM!

Hours section properly integrates accordion functionality with conditional rendering based on data availability and visibility configuration. Icon display and table styling are correctly wired through props.


700-770: LGTM!

Contact section implements comprehensive validation for all field types:

  • Address: checks existence and visibility
  • Phone: validates string type before passing to PhoneAtom
  • Email: validates array type and non-empty before accessing first element

All dynamic field access is properly guarded.


814-824: Secondary CTA appears to be a placeholder implementation.

Lines 814-824 render a secondary CTA that is identical to the primary CTA (same URL, same click handler, same label). This aligns with the PR description noting that "implementing the secondary CTA" is remaining work.

Confirm whether the current secondary CTA implementation is intentional as a placeholder, or if it should be implementing different behavior (e.g., different destination, different action, different label).


831-840: LGTM!

The CardIcon helper component provides consistent styling for icons throughout the card, promoting maintainability and DRY principles.

Copy link
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: 2

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/src/components/LocatorResultCard.tsx (1)

553-558: Distance checks treat 0 as “no distance”

The current pattern:

const distanceInMiles = distance
  ? (distance / 1609.344).toFixed(1)
  : undefined;
// ...
{distance && (/* render badge */)}

means a distance of exactly 0 (if it occurs) won’t render and will produce undefined formatting.

To handle 0 cleanly, consider:

-  const distanceInMiles = distance
-    ? (distance / 1609.344).toFixed(1)
-    : undefined;
-  const distanceInKilometers = distance
-    ? (distance / 1000).toFixed(1)
-    : undefined;
+  const hasDistance = distance != null;
+  const distanceInMiles =
+    distance != null ? (distance / 1609.344).toFixed(1) : undefined;
+  const distanceInKilometers =
+    distance != null ? (distance / 1000).toFixed(1) : undefined;
-              {distance && (
+              {hasDistance && (
   ...
-          {distance && (
+          {hasDistance && (

Minor edge case, but makes the behavior more mathematically consistent.

Also applies to: 666-677, 806-818

🧹 Nitpick comments (4)
packages/visual-editor/src/components/atoms/cta.tsx (2)

12-30: Consider exposing the click event in customClickHandler

Right now customClickHandler is typed as () => void and invoked from an inline lambda, so it can’t inspect or cancel the click event. If callers ever need to prevent navigation or inspect modifiers (e.g., meta-click), it would be more flexible to type this as a React mouse handler and pass it directly:

-export type CTAProps = {
+export type CTAProps = {
   // ...
-  customClickHandler?: () => void;
+  customClickHandler?: (
+    event: React.MouseEvent<HTMLAnchorElement, MouseEvent>
+  ) => void;
 };

and later:

- onClick={() => {
-   customClickHandler();
- }}
+ onClick={customClickHandler}

This keeps the public API future‑proof without changing current call sites that don’t care about the event.


141-205: Anchor branch skips Link behavior and lacks rel="noopener noreferrer"

When customClickHandler is provided, the component now renders a native <a> instead of the Link component. That subtly changes behavior:

  • eventName is no longer used, so any Link‑level analytics or tracking are skipped.
  • For target="_blank", the anchor does not specify rel="noopener noreferrer", which is generally recommended to avoid window.opener tab‑napping issues.

If you intend to preserve analytics behavior, consider still routing through Link (e.g., via a customClickHandler prop on Link if available) or explicitly documenting that this path is analytics‑free. Also consider adding rel="noopener noreferrer" when target === "_blank" on the anchor:

-        <a
+        <a
           href={link}
           onClick={/* ... */}
           target={target}
+          rel={target === "_blank" ? "noopener noreferrer" : undefined}
           aria-label={ariaLabel || undefined}
         >
packages/visual-editor/src/components/atoms/phone.tsx (1)

14-15: Phone CTA tel: sanitization looks good; tighten the helper docstring

The new customClickHandler flow and sanitizePhoneForTelHref helper nicely ensure we generate valid tel: links when bypassing the Link component.

One small mismatch: the comment says the helper “returns a sanitized tel: link string or undefined if input is invalid”, but the implementation only returns undefined when rawPhone is falsy. Inputs like "abc" will currently produce "tel:" (empty number), not undefined.

If you care about treating “no digits” as invalid, you could add a guard:

-  const cleaned = rawPhone.replace(/(?!^\+)\+|[^\d+]/g, "");
-  return `tel:${cleaned}`;
+  const cleaned = rawPhone.replace(/(?!^\+)\+|[^\d+]/g, "");
+  if (!cleaned || cleaned === "+") {
+    return undefined;
+  }
+  return `tel:${cleaned}`;

Or alternatively relax the comment to match the current behavior.

Also applies to: 23-29, 45-51, 79-96

packages/visual-editor/src/components/LocatorResultCard.tsx (1)

603-608: Harden fieldExists against shadowed hasOwnProperty

The fieldExists helper centralizes the runtime checks nicely, but using location.hasOwnProperty(field) can be tripped up if a result happens to contain its own hasOwnProperty key.

You can make this a bit more robust with:

-    location.hasOwnProperty(field) &&
+    Object.prototype.hasOwnProperty.call(location, field) &&

The call sites don’t need to change.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d3dca1 and b7da76e.

⛔ Files ignored due to path filters (1)
  • packages/visual-editor/src/components/testing/screenshots/StaticMapSection/[mobile] default props with coordinate - with api key.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
📒 Files selected for processing (6)
  • packages/visual-editor/src/components/Locator.test.tsx (1 hunks)
  • packages/visual-editor/src/components/LocatorResultCard.tsx (6 hunks)
  • packages/visual-editor/src/components/atoms/cta.tsx (3 hunks)
  • packages/visual-editor/src/components/atoms/phone.tsx (4 hunks)
  • packages/visual-editor/src/components/migrations/0024_locator_card_default_props.ts (1 hunks)
  • packages/visual-editor/src/components/migrations/migrationRegistry.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-29T22:00:03.843Z
Learnt from: mkouzel-yext
Repo: yext/visual-editor PR: 833
File: packages/visual-editor/src/components/Locator.tsx:1050-1057
Timestamp: 2025-10-29T22:00:03.843Z
Learning: In packages/visual-editor/src/components/Locator.tsx, the AppliedFilters component is intentionally rendered in two locations (inside the filter modal and outside it) as per the design requirements. This dual rendering should not be flagged as a duplicate issue.

Applied to files:

  • packages/visual-editor/src/components/Locator.test.tsx
  • packages/visual-editor/src/components/LocatorResultCard.tsx
📚 Learning: 2025-11-06T14:55:12.395Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 862
File: packages/visual-editor/src/utils/schema/resolveSchema.ts:118-135
Timestamp: 2025-11-06T14:55:12.395Z
Learning: In `packages/visual-editor/src/utils/schema/resolveSchema.ts`, the `OpeningHoursSchema` and `PhotoGallerySchema` functions from `yext/pages-components` contain internal type validation and handle invalid inputs gracefully (returning empty objects or undefined) rather than throwing TypeErrors, so no pre-validation guards are needed before calling them.

Applied to files:

  • packages/visual-editor/src/components/LocatorResultCard.tsx
🧬 Code graph analysis (3)
packages/visual-editor/src/components/atoms/cta.tsx (1)
packages/visual-editor/src/components/atoms/button.tsx (1)
  • Button (89-115)
packages/visual-editor/src/components/migrations/migrationRegistry.ts (1)
packages/visual-editor/src/components/migrations/0024_locator_card_default_props.ts (1)
  • locatorCardDefaultProps (58-120)
packages/visual-editor/src/components/LocatorResultCard.tsx (4)
packages/visual-editor/src/components/atoms/cta.tsx (2)
  • CTAVariant (41-41)
  • CTA (140-208)
packages/visual-editor/src/components/contentBlocks/HoursTable.tsx (1)
  • HoursTableStyleFields (49-78)
packages/visual-editor/src/components/atoms/accordion.tsx (4)
  • Accordion (4-9)
  • AccordionItem (11-25)
  • AccordionTrigger (27-53)
  • AccordionContent (55-62)
packages/visual-editor/src/components/atoms/phone.tsx (1)
  • PhoneAtom (17-57)
🔇 Additional comments (7)
packages/visual-editor/src/components/migrations/migrationRegistry.ts (1)

25-25: Locator result card migration correctly registered as next version

Importing locatorCardDefaultProps and appending it as the final entry keeps migration ordering consistent and makes it version 24, which matches the new test coverage. Looks good.

Also applies to: 56-56

packages/visual-editor/src/components/Locator.test.tsx (1)

341-380: Nice addition of a pinned “version 24 default props” test

This follows the existing pattern for versioned Locator tests and explicitly pins behavior at migration 24. It should help catch regressions in the new default resultCard props as more migrations are added.

packages/visual-editor/src/components/LocatorResultCard.tsx (5)

54-225: Result card config surface and defaults look solid

The LocatorResultCardProps, DEFAULT_LOCATOR_RESULT_CARD_PROPS, and LocatorResultCardFields shapes line up well: each configurable section (headings, hours, address, phone, email, services, CTAs, image) has a clear schema and sensible defaults, and the Puck field metadata matches how those props are consumed in the component. This should be easy to evolve as more fields get plumbed in.

Also applies to: 227-520


626-664: Image + heading rendering guards look robust

The combination of fieldExists(props.image.field, "object"), the ?.url guard, and the alternateText || location.name fallback prevents runtime errors when the configured image field is missing or mis-shaped, while still showing a reasonable alt. Secondary and tertiary headings are also correctly gated on the field existing and being a string.


720-803: Contact and services sections are defensively guarded

The runtime checks around address, phone, email, and services (including fieldExists(..., "string"), Array.isArray and length checks) ensure the card won’t blow up when optional search fields are missing or differently typed. This matches the flexible “display fields” story while still keeping the UI stable.


819-849: Primary and secondary CTAs are wired cleanly with analytics and templating

The primary CTA reuses resolvedUrl and handleVisitPageClick from useCardAnalyticsCallback, and the secondary CTA leverages resolveComponentData with i18n.language and location, plus a dedicated handleSecondaryCTAClick passed as customClickHandler. This aligns nicely with the existing CTA atom behavior and keeps analytics tracking centralized.


857-866: Nice reuse of CardIcon for consistent icon visuals

Extracting CardIcon with theme-derived background/text colors removes duplication across hours, address, and email icons and makes it easy to tweak the visual treatment in one place.

Copy link
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: 10

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

29-31: Fix stale LocatorResultCard props in CardComponent while wiring looks correct

The new resultCard prop is cleanly threaded through LocatorProps, locatorFields, defaultProps, LocatorInternal (as resultCardProps), and into LocatorResultCard, which is good. However, CardComponent’s useCallback still only depends on puck while closing over resultCardProps, so edits to the result card configuration in the editor won’t propagate until a full remount.

Update the dependency array to keep the card rendering in sync with live config:

-  const CardComponent = React.useCallback(
+  const CardComponent = React.useCallback(
     (result: CardProps<Location>) => (
       <LocatorResultCard
         {...result}
         puck={puck}
         resultCardProps={resultCardProps}
       />
     ),
-    [puck]
+    [puck, resultCardProps]
   );

Also applies to: 42-50, 442-447, 531-532, 539-545, 588-595, 790-799

🧹 Nitpick comments (2)
packages/visual-editor/locales/en-GB/visual-editor.json (1)

447-461: Plural form consistency verified; optional future refactor.

All 25 locale files include the same plural form variants (zero, few, many, one, other), confirming this is a consistent, intentional pattern rather than an en-GB-specific issue. While English (en-GB) only requires one and other per CLDR standards, the uniform inclusion across all locales suggests this is a deliberate design choice—likely for code generation consistency or maintainability parity.

This can be addressed as a follow-up refactor if the team decides to optimize for CLDR compliance per locale, but it poses no functional risk.

packages/visual-editor/src/components/migrations/0022_adjust_locator_open_now_schema.ts (1)

14-14: Preserve existing showDistanceOptions value in migration instead of hardcoding false.

While no existing Locators currently have showDistanceOptions: true (verified across the codebase), the migration should follow best practices by preserving existing filter values. Change line 14 from:

showDistanceOptions: false,

to:

showDistanceOptions: props?.filters?.showDistanceOptions ?? false,

This ensures the migration respects any pre-existing configuration while maintaining the false default for new instances.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7da76e and 91e97c3.

📒 Files selected for processing (31)
  • packages/visual-editor/CHANGELOG.md (1 hunks)
  • packages/visual-editor/locales/cs/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/da/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/de/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/en-GB/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/en/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/es/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/et/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/fi/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/fr/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/hr/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/hu/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/it/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/ja/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/lt/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/lv/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/nb/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/nl/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/pl/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/pt/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/ro/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/sk/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/sv/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/tr/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/zh-TW/visual-editor.json (2 hunks)
  • packages/visual-editor/locales/zh/visual-editor.json (2 hunks)
  • packages/visual-editor/package.json (1 hunks)
  • packages/visual-editor/src/components/Locator.tsx (22 hunks)
  • packages/visual-editor/src/components/contentBlocks/TextList.tsx (4 hunks)
  • packages/visual-editor/src/components/migrations/0022_adjust_locator_open_now_schema.ts (1 hunks)
  • packages/visual-editor/src/vite-plugin/templates/locator.tsx (2 hunks)
✅ Files skipped from review due to trivial changes (2)
  • packages/visual-editor/package.json
  • packages/visual-editor/CHANGELOG.md
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-29T22:00:03.843Z
Learnt from: mkouzel-yext
Repo: yext/visual-editor PR: 833
File: packages/visual-editor/src/components/Locator.tsx:1050-1057
Timestamp: 2025-10-29T22:00:03.843Z
Learning: In packages/visual-editor/src/components/Locator.tsx, the AppliedFilters component is intentionally rendered in two locations (inside the filter modal and outside it) as per the design requirements. This dual rendering should not be flagged as a duplicate issue.

Applied to files:

  • packages/visual-editor/src/components/migrations/0022_adjust_locator_open_now_schema.ts
  • packages/visual-editor/src/vite-plugin/templates/locator.tsx
  • packages/visual-editor/src/components/Locator.tsx
📚 Learning: 2025-11-06T14:55:12.395Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 862
File: packages/visual-editor/src/utils/schema/resolveSchema.ts:118-135
Timestamp: 2025-11-06T14:55:12.395Z
Learning: In `packages/visual-editor/src/utils/schema/resolveSchema.ts`, the `OpeningHoursSchema` and `PhotoGallerySchema` functions from `yext/pages-components` contain internal type validation and handle invalid inputs gracefully (returning empty objects or undefined) rather than throwing TypeErrors, so no pre-validation guards are needed before calling them.

Applied to files:

  • packages/visual-editor/src/components/migrations/0022_adjust_locator_open_now_schema.ts
  • packages/visual-editor/src/vite-plugin/templates/locator.tsx
🧬 Code graph analysis (3)
packages/visual-editor/src/components/contentBlocks/TextList.tsx (2)
packages/visual-editor/src/editor/YextField.tsx (1)
  • YextField (163-284)
packages/visual-editor/src/utils/resolveComponentData.tsx (1)
  • resolveComponentData (52-99)
packages/visual-editor/src/vite-plugin/templates/locator.tsx (3)
packages/visual-editor/src/utils/migrate.ts (1)
  • migrate (42-99)
packages/visual-editor/src/components/migrations/migrationRegistry.ts (1)
  • migrationRegistry (32-57)
packages/visual-editor/src/components/configs/locatorConfig.tsx (1)
  • locatorConfig (27-58)
packages/visual-editor/src/components/Locator.tsx (1)
packages/visual-editor/src/components/LocatorResultCard.tsx (4)
  • LocatorResultCardProps (54-170)
  • LocatorResultCardFields (227-520)
  • DEFAULT_LOCATOR_RESULT_CARD_PROPS (172-225)
  • LocatorResultCard (536-855)
🔇 Additional comments (43)
packages/visual-editor/locales/lv/visual-editor.json (2)

448-462: Verify Latvian pluralization forms against CLDR spec.

The new location keys use pluralization forms (few, many, zero, one, other) that extend beyond the standard CLDR pluralization rules for Latvian, which defines only zero, one, and other. While the translations themselves appear syntactically correct, using non-standard plural forms could cause runtime errors or silent failures if your i18n library validates strictly against CLDR specifications.

Please verify:

  1. Whether your i18n library (e.g., i18next, ICU MessageFormat) accepts and handles these extra plural forms for Latvian without errors.
  2. Whether all related locale files (English, other languages) consistently use the same set of plural forms across these keys.
  3. Whether the TypeScript implementation that consumes these keys enforces or validates plural forms against CLDR rules.

You can verify point 1 by searching for how plural forms are selected in your i18n configuration for Latvian.


147-147: Field label addition approved.

The commaSeparated field label is properly formatted and integrates well with the existing field structure. No concerns with this addition.

packages/visual-editor/locales/et/visual-editor.json (1)

147-147: Well-structured localization additions with complete pluralization coverage.

The new locale keys are properly structured:

  • "commaSeparated" field (line 147) correctly positioned in alphabetical order.
  • All pluralized location message keys (locationWithCount_*, locationsNear_*, locationsWithinDistanceOf_*) include the complete set of Estonian CLDR plural forms (zero, few, many, one, other).
  • Template variables ({{count}}, {{name}}, {{distance}}) are appropriate and consistent for their respective messaging contexts.
  • Entries maintain alphabetical ordering in the JSON structure.

Also applies to: 448-462

packages/visual-editor/locales/fr/visual-editor.json (3)

146-146: Approve new comma-separated translation key.

The translation "Séparés par des virgules" is accurate and well-placed within the fields section.


447-461: Verify pluralization structure aligns with French CLDR rules and codebase patterns.

French locale data per CLDR typically requires only one and other pluralization forms. This PR adds zero, few, and many variants alongside them, which is non-standard for French. While the translations themselves are correct, this pluralization expansion should be verified to ensure:

  1. It matches the project's intentional pluralization strategy (not copy-pasted from non-French locales with different CLDR rules)
  2. It aligns with pluralization handling in the localization/i18n library configuration
  3. It's consistent across all locale files (en, es, etc.) in the codebase

Also verify that the new template variables ({{name}} in locationsNear/locationsWithinDistanceOf and {{distance}} in locationsWithinDistanceOf) are reliably supplied by the calling code.


469-469: Confirm whitespace normalization is intentional.

Line 469 normalizes the whitespace before "miles" from a non-breaking space to a regular space. Verify this is deliberate formatting alignment and not an accidental change that may affect layout or styling in the UI.

packages/visual-editor/locales/zh-TW/visual-editor.json (2)

147-147: Translation placement and accuracy are correct.

"逗號分隔" accurately translates "comma separated" and integrates naturally within the fields section alphabetically.


448-462: Verify quote-mark escaping and alignment with English source keys.

The pluralization keys are well-structured and translations are grammatically sound. However, I notice literal quote marks wrapping the {{name}} placeholder (e.g., "「name」""locationsNear" rendering as "nearby_name" in output).

Before merging, confirm:

  1. This quote-mark behavior matches the English source keys and is intentional for the UI
  2. These localization keys are wired into the LocatorResultCard component code and consumed correctly
packages/visual-editor/locales/nb/visual-editor.json (1)

147-147: Localization keys for pluralization and comma-separated values look good.

The additions support the pluralized location messaging and comma-separated text list formatting required by the locator result card feature. The Norwegian translations follow existing conventions and CLDR plural rules are properly applied across zero/one/few/many/other variants.

Also applies to: 448-462

packages/visual-editor/locales/ro/visual-editor.json (1)

147-462: Verify alignment with PR objectives regarding translation deferral.

The PR objectives state that "Translations have been deferred until the visual appearance is verified," yet this file includes newly added translation keys for localized pluralization forms. Confirm whether translations should be included in this PR or if they should be deferred as originally planned.

packages/visual-editor/locales/en-GB/visual-editor.json (1)

146-146: New text list option added correctly.

The commaSeparated field is well-placed alphabetically and provides clear, appropriate English localization.

packages/visual-editor/locales/hu/visual-editor.json (2)

147-147: Addition of "commaSeparated" field localization looks good.

This straightforward addition supports comma-separated text list formatting as part of the new result card styling work.


448-462: Verify pluralization form support for Hungarian locale.

You've added _few, _many, and _zero pluralization forms alongside _one and _other. However, CLDR (Common Locale Data Repository) specifies that Hungarian uses only _one and _other forms. The additional forms may not be processed by the i18n system and could lead to unused translation keys.

Confirm:

  • Does your i18n library process these additional pluralization categories for Hungarian?
  • Should these forms be removed to align with standard Hungarian pluralization rules?
packages/visual-editor/locales/cs/visual-editor.json (1)

147-147: LGTM!

The new pluralization keys (locationWithCount, locationsNear, locationsWithinDistanceOf) follow proper CLDR plural form conventions for Czech and use consistent placeholder syntax. The translations are well-structured with appropriate {{count}}, {{distance}}, and {{name}} placeholders for flexible message composition.

Also applies to: 449-463

packages/visual-editor/locales/es/visual-editor.json (2)

146-146: Translation addition is correct.

The new field option key is properly translated: "Separados por comas" accurately conveys "comma separated" in Spanish.


447-461: Verification confirms translation keys are properly wired through the codebase.

All three translation keys are actively used in packages/visual-editor/src/components/Locator.tsx:

  • locationWithCount (line 1207) invoked with count parameter
  • locationsNear (line 1197) invoked with count and name parameters
  • locationsWithinDistanceOf (line 1184) invoked with count, distance, and name parameters

Parameters passed in the component code match the placeholders in the Spanish translation strings. The translations are grammatically correct with proper singular/plural agreement.

packages/visual-editor/locales/lt/visual-editor.json (1)

147-147: Field label addition looks good.

The new "commaSeparated" field label is correctly positioned in alphabetical order and follows the naming convention of adjacent entries.

packages/visual-editor/locales/sk/visual-editor.json (2)

148-148: New field label added.

Addition of "commaSeparated" field label follows existing conventions. Confirmed that the key placement and naming are consistent with adjacent fields.


449-463: All verification checks passed—no issues found.

The placeholder variables are consistent across all three location-related keys (locationWithCount, locationsNear, locationsWithinDistanceOf) and match the English locale structure. All five pluralization variants (_few, _many, _zero, _one, _other) are present for each key. The Slovak translations correctly apply singular forms for the _one variant and plural forms for others, aligning with the English translations' structure and terminology.

packages/visual-editor/locales/it/visual-editor.json (1)

453-462: No issues found. All localization keys are properly implemented with correct placeholder usage.

Verified that rendering code in packages/visual-editor/src/components/Locator.tsx correctly provides all required placeholders:

  • locationsWithinDistanceOf (line 1184): provides count, distance, and name parameters
  • locationsNear (line 1197): provides count and name parameters
  • locationWithCount (line 1207): provides count parameter

All placeholder names match exactly what's defined in the Italian locale file and other locales. All 5 CLDR plural forms (zero, one, few, many, other) are populated across all locale files.

packages/visual-editor/locales/en/visual-editor.json (2)

146-146: LGTM: New field option added correctly.

The commaSeparated option is correctly placed in alphabetical order within fields.columns.


447-461: Translation keys are properly wired, variables match, and pluralization form coverage is intentional.

Verification confirms:

  1. All three translation keys are actively referenced in code (packages/visual-editor/src/components/Locator.tsx):

    • locationWithCount with {count} parameter
    • locationsNear with {count, name} parameters
    • locationsWithinDistanceOf with {count, name, distance} parameters
  2. Interpolation variables match between code and locale files: {{count}}, {{name}}, and {{distance}} are all correctly used.

  3. The pluralization form choices (_zero, _few, _many, _one, _other) are aligned with the i18next framework's CLDR pluralization support. While English typically uses only _one and _other, the consistent presence of all forms across all locales indicates intentional groundwork for proper multi-locale CLDR rule support.

packages/visual-editor/locales/nl/visual-editor.json (1)

147-147: ✓ New field translation added correctly.

The "commaSeparated" translation reads naturally in Dutch.

packages/visual-editor/locales/sv/visual-editor.json (1)

449-463: Locale file updates are consistent across all locales.

Verification confirms all pluralization variants (zero, one, few, many, other) for the three key groups (locationWithCount, locationsNear, locationsWithinDistanceOf) exist in all checked locales (en, de, fr, es). Variable placeholders ({{count}}, {{name}}, {{distance}}) are correctly structured and consistent across locales. No missing translations detected.

packages/visual-editor/locales/de/visual-editor.json (2)

146-146: New translation key approved.

The commaSeparated translation is a straightforward addition aligned with the new TextList option mentioned in the PR summary.


447-461: Placeholder variables are correct; plural form pattern is cross-locale and harmless.

All three keys are actively used in packages/visual-editor/src/components/Locator.tsx with correct placeholder substitutions:

  • locationWithCount: {{count}}
  • locationsNear: {{count}}, {{name}}
  • locationsWithinDistanceOf: {{count}}, {{distance}}, {{name}}

Regarding the non-CLDR plural forms: The few, many, and zero forms are present uniformly across all three locale files (German, English, Spanish), suggesting an intentional cross-locale pattern. Since i18next uses standard CLDR rules by default, it will apply only the one and other forms for German while silently ignoring the extra forms—no runtime issues result. This is likely intentional for template consistency or future library configuration.

packages/visual-editor/locales/zh/visual-editor.json (3)

147-147: Field label addition looks correct.

The new commaSeparated field label aligns with the PR's TextList option expansion.


448-462: Verify Chinese pluralization rules and alignment across language files.

This file uses five plural forms (few, many, one, other, zero) for Chinese. Per CLDR standards, Simplified Chinese (zh-Hans) typically only requires one and other forms. The additional forms may indicate either:

  • Intentional override (confirm why)
  • Alignment with i18n library requirements (confirm the library version and config)
  • Inconsistency with other language files in the PR (en, fr, de, ja, etc.)

Before merging, verify that:

  1. The plural form strategy matches other language files added/modified in this PR.
  2. The i18n runtime/library configuration expects these plural categories for Chinese.

458-462: Inconsistency verified but architecturally non-critical for Chinese.

Line 460 (locationsWithinDistanceOf_one) does differ from other variants as stated, using and instead of 距离 and . However, Chinese in CLDR has only the "other" plural category, meaning these _one, _few, _many, _zero forms will never be selected during actual pluralization for Chinese—they fall back to _other.

The inconsistency exists but is not a practical issue since these variants are unused per CLDR. If aligning them improves consistency for maintenance purposes, it may be worth doing; otherwise, this is not a breaking problem.

packages/visual-editor/locales/hr/visual-editor.json (1)

147-147: LGTM! Localization keys added consistently.

The new translation keys follow the established pattern and include proper placeholders for dynamic content ({{count}}, {{name}}, {{distance}}). These support the expanded locator result card functionality.

Also applies to: 448-462

packages/visual-editor/locales/ja/visual-editor.json (1)

147-147: LGTM! Japanese translations properly formatted.

All new locale keys are present with correct placeholders, maintaining consistency with other locale files in this PR.

Also applies to: 448-462

packages/visual-editor/src/components/contentBlocks/TextList.tsx (1)

64-82: LGTM! Comma-separated rendering implemented correctly.

The conditional rendering logic properly:

  • Applies flex layout for comma-separated lists vs. traditional list styling
  • Removes bottom margin for inline items when comma-separated
  • Inserts comma separators only between items (not after the last one)
  • Maintains backward compatibility with commaSeparated: false default
packages/visual-editor/src/vite-plugin/templates/locator.tsx (1)

25-26: LGTM! Migration flow properly integrated.

The migration logic is correctly applied to the layout data before rendering. The migrate function call matches the expected signature from the codebase (data, registry, config), ensuring that layout transformations (such as the new locator card default props migration) are applied at runtime.

Also applies to: 128-132

packages/visual-editor/locales/pl/visual-editor.json (1)

148-148: LGTM! Polish translations follow the established pattern.

All new locale keys are present and properly formatted with correct placeholders.

Also applies to: 449-463

packages/visual-editor/locales/tr/visual-editor.json (1)

147-147: LGTM! Turkish translations properly added.

The new locale keys maintain consistency with the other locale files in this PR.

Also applies to: 448-462

packages/visual-editor/locales/da/visual-editor.json (1)

148-148: LGTM! Danish translations are consistent.

All new locale keys follow the established pattern with proper placeholders.

Also applies to: 449-463

packages/visual-editor/locales/fi/visual-editor.json (1)

147-147: LGTM! Finnish translations complete the locale expansion.

All new keys are present with correct placeholders, completing the comprehensive locale coverage for the new locator result card features.

Also applies to: 448-462

packages/visual-editor/locales/pt/visual-editor.json (2)

147-147: New fields.commaSeparated label looks consistent

Key naming and Portuguese label align with nearby fields entries; no placeholder issues here.


448-462: Pluralization keys align with new count/distance messaging

The added locationWithCount_*, locationsNear_*, and locationsWithinDistanceOf_* variants use the same {{count}}, {{distance}}, and {{name}} placeholders as the calling code, so i18n pluralization for the new summary text should resolve correctly.

packages/visual-editor/src/components/Locator.tsx (4)

625-631: Radius and “Open Now” helper logic is coherent and defensive

The new selectedDistanceMiles state, apiFilterRadius ref, and helper functions (buildNearLocationFilterFromPrevious/Coords, buildEqualsLocationFilter, updateRadiusInNearFiltersOnLocationField, deselectOpenNowFilters, plus the isLocationNearFilter/isOpenNowFilter guards) form a consistent flow:

  • Initial searches always get a single location filter (NYC default, query-param-derived Near/Equals, geolocation, or mapStartingLocation), with vertical limit capped at 50 and map center synchronized.
  • Distance clicks correctly toggle between a user-selected radius (miles → meters) and the API/default radius, updating all builtin.location Near filters in one pass.
  • “Clear All” clears Open Now, resets the radius back to API/default, and resets facets while preserving the underlying location filter, matching the intent to keep the search area but drop extra filters.

The type narrowing via isLocationNearFilter/isOpenNowFilter before casting to FieldValueStaticFilter / NearFilterValue keeps these transformations safe against non-location or non-hours static filters.

No changes requested here.

Also applies to: 707-717, 812-821, 827-839, 855-880, 902-903, 907-907, 915-917, 928-935, 940-945, 982-1002, 1006-1016, 1020-1021, 1543-1551, 1552-1560, 1561-1583, 1585-1609, 1611-1629, 1631-1656, 1658-1674


1053-1053: Layout changes for left panel and map container look reasonable

Adjusting the left column to md:w-2/5 lg:w-[40rem] md:min-w-[24rem] and keeping the map in #locatorMapDiv with md:flex-1 md:flex hidden relative should preserve the existing iframe lookup/padding logic while giving the results panel a more predictable width on larger screens.

Also applies to: 1130-1131


1147-1154: ResultsCountSummary correctly matches new i18n keys and UX states

The summary component cleanly handles the three search states (not started, loading, complete) and switches between generic counts, “near” copy, and “within distance” copy based on filterDisplayName and selectedDistanceMiles. The i18n keys (useOurLocatorToFindALocationNearYou, noResultsFoundForThisArea, locationsWithinDistanceOf, locationsNear, locationWithCount) and interpolation variables (count, distance, name) align with the added pt translations and pluralization scheme.

Also applies to: 1155-1214


1322-1323: Filter modal distance wiring is consistent with new state model

Passing selectedDistanceMiles through FilterModal into DistanceFilter (and reflecting it visually via the radio-style icons) keeps the modal UI in sync with the underlying handleDistanceClick logic and selectedDistanceMiles state. Open Now and facet handling remain unchanged aside from the new helpers, and the duplicate AppliedFilters rendering in and outside the modal still matches prior design guidance. Based on learnings.

Also applies to: 1453-1454

Copy link
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: 0

🧹 Nitpick comments (1)
packages/visual-editor/src/components/migrations/0024_locator_card_default_props.ts (1)

84-87: Consider using optional chaining for clearer intent.

The guard on line 86 correctly prevents accessing .table on undefined, but the syntax could be modernized for clarity.

Apply this diff to use optional chaining:

           table: {
             ...DEFAULT_RESULT_CARD.hours.table,
-            ...(existingResultCard.hours && existingResultCard.hours.table),
+            ...(existingResultCard.hours?.table ?? {}),
           },

This makes the intent more explicit: safely access the nested property and default to an empty object if undefined.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91e97c3 and 56fcc76.

📒 Files selected for processing (1)
  • packages/visual-editor/src/components/migrations/0024_locator_card_default_props.ts (1 hunks)
🔇 Additional comments (1)
packages/visual-editor/src/components/migrations/0024_locator_card_default_props.ts (1)

3-56: LGTM on default configuration structure.

The default result card configuration is well-structured with reasonable defaults for all fields. The nested structure aligns with the deep-merge logic below.

Copy link
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: 7

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/fi/visual-editor.json (1)

549-549: Fix stray closing brace in Finnish responseFrom translation.

The current value:

"responseFrom": "Vastaus {{businessName}}}",

contains an extra } which will show up literally in the UI.

Suggest:

- "responseFrom": "Vastaus {{businessName}}}",
+ "responseFrom": "Vastaus {{businessName}}",

(and optionally adjust wording if you prefer “Vastaus kohteelta {{businessName}}” or similar).

♻️ Duplicate comments (9)
packages/visual-editor/locales/ro/visual-editor.json (1)

465-465: Fix inconsistent quotation marks in localization string (unresolved from past review).

Line 465 still uses escaped ASCII double quotes (\"{{name}}\") instead of Romanian quotation marks („{{name}}"), inconsistent with all other proximity-based strings on lines 463–464, 466–467, and broader Romanian typography conventions. This mirrors the issue flagged in the previous review.

Apply this diff to standardize to Romanian quotation marks:

-    "locationsNear_one": "{{count}} locație lângă \"{{name}}\"",
+    "locationsNear_one": "{{count}} locație lângă „{{name}}"",
packages/visual-editor/locales/hu/visual-editor.json (1)

465-465: Inconsistent Hungarian phrasing in locationsNear_one persists from previous review.

This issue was flagged in a prior review but remains unresolved. Line 465 uses "A hely" (with article), which is awkward and inconsistent with the other pluralized variants (few, many, zero, other) that use just "hely" (without article). Compare:

  • Line 465 (one): "{{count}} A hely közelében \"{{name}}" ← uses "A hely"
  • Line 466 (other): "{{count}} hely a(z) \"{{name}}\" közelében" ← uses "hely"

Apply the suggested fix from the prior review:

-  "locationsNear_one": "{{count}} A hely közelében \"{{name}}\"",
+  "locationsNear_one": "{{count}} hely a(z) \"{{name}}\" közelében",
packages/visual-editor/locales/nb/visual-editor.json (1)

470-470: Fix placeholder variable typo (duplicate from prior review).

Line 470 has {{distanse}} but should be {{distance}} to match the other locationsWithinDistanceOf_* keys (lines 468-469, 471-472) and prevent localization interpolation failures.

-  "locationsWithinDistanceOf_one": "{{count}} sted innenfor {{distanse}} miles fra \"{{name}}\"",
+  "locationsWithinDistanceOf_one": "{{count}} sted innenfor {{distance}} miles fra \"{{name}}\"",
packages/visual-editor/locales/zh/visual-editor.json (1)

458-472: Unify Chinese location-count variants and fix malformed locationsNear_one.

The current set has multiple inconsistencies:

  • locationWithCount_one / locationWithCount_other lack spacing and the measure word “个” vs the other forms.
  • locationsNear_one has reversed word order and misplaced quotes compared to other variants.
  • Mixed use of “地点” vs “位置”.

To keep output natural and consistent, consider applying something like:

- "locationWithCount_one": "{{count}}位置",
- "locationWithCount_other": "{{count}}位置",
+ "locationWithCount_one": "{{count}} 个地点",
+ "locationWithCount_other": "{{count}} 个地点",

- "locationsNear_one": "{{count}}位置附近“ {{name}}”",
- "locationsNear_zero": "“{{name}}”附近有 {{count}} 个位置",
+ "locationsNear_one": "“{{name}}”附近有 {{count}} 个地点",
+ "locationsNear_zero": "“{{name}}”附近有 {{count}} 个地点",

- "locationsWithinDistanceOf_one": "距“{{name}}” {{distance}} 英里内的 {{count}} 个位置",
+ "locationsWithinDistanceOf_one": "“{{name}}”距离 {{distance}} 英里内有 {{count}} 个地点",

(and optionally standardize all locationsWithinDistanceOf_* to use “个地点”.)

Also note: Simplified Chinese effectively only needs a single plural form; if your i18n runtime follows CLDR, you may not need the _few, _many, _zero variants here at all.

packages/visual-editor/locales/cs/visual-editor.json (1)

459-473: Standardize locationsNear_one quotes with other Czech variants.

locationsNear_one still uses escaped ASCII quotes instead of the Czech guillemets used by the other plural forms:

"locationsNear_one": "{{count}} umístění poblíž \"{{name}}\"",

To keep punctuation consistent, align with the neighboring entries:

- "locationsNear_one": "{{count}} umístění poblíž \"{{name}}\"",
+ "locationsNear_one": "{{count}} umístění poblíž „{{name}}“",

(or switch to místo if you prefer to mirror the singular noun used in other strings).

packages/visual-editor/locales/nl/visual-editor.json (1)

458-472: Reduce Dutch plural forms to CLDR one/other where possible.

For Dutch you now define:

  • locationWithCount_few/_many/_zero/_one/_other
  • locationsNear_few/_many/_zero/_one/_other
  • locationsWithinDistanceOf_few/_many/_zero/_one/_other

Dutch CLDR plural rules only distinguish one vs other, so _few, _many, and _zero are unlikely to ever be chosen by the pluralization engine. Keeping them adds maintenance overhead without improving output.

Consider simplifying to just *_one and *_other for all three keys and removing the unused plural forms, retaining the existing text and placeholders:

- "locationWithCount_few": "{{count}} locaties",
- "locationWithCount_many": "{{count}} locaties",
- "locationWithCount_zero": "{{count}} locaties",
  "locationWithCount_one": "{{count}} locatie",
  "locationWithCount_other": "{{count}} locaties",

- "locationsNear_few": "{{count}} locaties in de buurt van \"{{name}}\"",
- "locationsNear_many": "{{count}} locaties in de buurt van \"{{name}}\"",
- "locationsNear_zero": "{{count}} locaties in de buurt van \"{{name}}\"",
  "locationsNear_one": "{{count}} locatie nabij \"{{name}}\"",
  "locationsNear_other": "{{count}} locaties in de buurt van \"{{name}}\"",

- "locationsWithinDistanceOf_few": "{{count}} locaties binnen {{distance}} mijl van \"{{name}}\"",
- "locationsWithinDistanceOf_many": "{{count}} locaties binnen {{distance}} mijl van \"{{name}}\"",
- "locationsWithinDistanceOf_zero": "{{count}} locaties binnen {{distance}} mijl van \"{{name}}\"",
  "locationsWithinDistanceOf_one": "{{count}} locatie binnen {{distance}} mijl van \"{{name}}\"",
  "locationsWithinDistanceOf_other": "{{count}} locaties binnen {{distance}} mijl van \"{{name}}\"",
packages/visual-editor/locales/sv/visual-editor.json (2)

150-150: commaSeparated key is still incorrectly placed at root level

As flagged in a previous review, the commaSeparated key at line 150 is at the root level of the JSON, but the component code references it via msg("fields.commaSeparated", ...). This means the translation will not be found at runtime and will fall back to English.

Move "commaSeparated": "Kommaseparerad" from line 150 into the existing "fields" object to match the expected structure.


459-473: Swedish locale still contains unsupported plural forms

As previously flagged, Swedish (sv) CLDR pluralization rules only define two forms: _one (when n=1) and _other (all other values). The additions of _few, _many, and _zero variants in lines 459-473 are not part of the Swedish plural specification and will never be selected by Intl.PluralRules('sv').

Remove the following unsupported plural form entries:

  • locationWithCount_few (line 459)
  • locationWithCount_many (line 460)
  • locationWithCount_zero (line 463)
  • locationsNear_few (line 464)
  • locationsNear_many (line 465)
  • locationsNear_zero (line 468)
  • locationsWithinDistanceOf_few (line 469)
  • locationsWithinDistanceOf_many (line 470)
  • locationsWithinDistanceOf_zero (line 473)

Keep only the _one and _other variants for each key.

packages/visual-editor/locales/lt/visual-editor.json (1)

463-472: Quote character inconsistency remains in Lithuanian locale

As previously flagged, the newly added pluralization keys use mixed quote styles within placeholders. Most entries use curly quotes („{{name}}"), but some use escaped straight quotes (\"{{name}}\"). The English locale consistently uses escaped straight quotes, and the Lithuanian translations should follow the same pattern for consistency.

Update all occurrences of „{{name}}" to \"{{name}}\" in the following keys:

  • locationsNear_few (line 463)
  • locationsNear_many (line 464)
  • locationsNear_other (line 466)
  • locationsNear_zero (line 467)
  • locationsWithinDistanceOf_few (line 468)
  • locationsWithinDistanceOf_many (line 469)
  • locationsWithinDistanceOf_other (line 471)
  • locationsWithinDistanceOf_zero (line 472)
🧹 Nitpick comments (4)
packages/visual-editor/locales/pl/visual-editor.json (1)

396-396: Align heading terminology for consistency.

Line 396 uses "Tytuł" (title) for tertiaryHeading, whereas lines 359 and 373 use "Nagłówek" (heading) for primaryHeading and secondaryHeading. Consider aligning the terminology across all three heading-related keys for consistency.

-    "tertiaryHeading": "Tytuł trzeciorzędny",
+    "tertiaryHeading": "Nagłówek trzeciorzędny",
packages/visual-editor/locales/et/visual-editor.json (1)

458-472: Location/count strings in Estonian look correct; extra plural forms are optional.

The locationWithCount*, locationsNear*, and locationsWithinDistanceOf* entries have consistent wording, placeholders ({{count}}, {{name}}, {{distance}}), and quote usage.

If your i18n layer only uses CLDR cardinal categories for Estonian (typically one and other), the _few, _many, and _zero variants may never be selected; you can keep them as aliases or consider trimming them for simplicity later.

packages/visual-editor/locales/lv/visual-editor.json (1)

458-472: Latvian location/count messages are consistent; extra plural forms are non-blocking.

The locationWithCount*, locationsNear*, and locationsWithinDistanceOf* entries have coherent wording and correct usage of {{count}}, {{name}}, and {{distance}}. Non-breaking spaces around numbers help keep units attached.

As with other locales, if your pluralization runtime only uses a subset of these categories (e.g., zero/one/other), the _few and _many keys may never be selected, but they don’t cause functional issues.

packages/visual-editor/locales/fi/visual-editor.json (1)

458-472: Minor consistency nit in Finnish location messages.

Functionally the locationWithCount*, locationsNear*, and locationsWithinDistanceOf* strings are fine, but only the _one variants quote {{name}} while the others usually don’t (e.g. locationsNear_one vs locationsNear_few/many/other/zero). For a more uniform UI, you may want either to quote {{name}} in all variants or drop the quotes everywhere.

No functional impact—purely a style consistency tweak.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 56fcc76 and 362104c.

📒 Files selected for processing (28)
  • packages/visual-editor/locales/cs/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/da/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/de/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/en-GB/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/en/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/es/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/et/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/fi/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/fr/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/hr/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/hu/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/it/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/ja/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/lt/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/lv/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/nb/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/nl/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/pl/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/pt/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/ro/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/sk/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/sv/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/tr/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/zh-TW/visual-editor.json (11 hunks)
  • packages/visual-editor/locales/zh/visual-editor.json (11 hunks)
  • packages/visual-editor/src/components/LocatorResultCard.tsx (6 hunks)
  • packages/visual-editor/src/components/atoms/cta.tsx (3 hunks)
  • packages/visual-editor/src/components/migrations/0024_locator_card_default_props.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/visual-editor/locales/ja/visual-editor.json
  • packages/visual-editor/src/components/migrations/0024_locator_card_default_props.ts
  • packages/visual-editor/locales/it/visual-editor.json
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-29T22:00:03.843Z
Learnt from: mkouzel-yext
Repo: yext/visual-editor PR: 833
File: packages/visual-editor/src/components/Locator.tsx:1050-1057
Timestamp: 2025-10-29T22:00:03.843Z
Learning: In packages/visual-editor/src/components/Locator.tsx, the AppliedFilters component is intentionally rendered in two locations (inside the filter modal and outside it) as per the design requirements. This dual rendering should not be flagged as a duplicate issue.

Applied to files:

  • packages/visual-editor/src/components/LocatorResultCard.tsx
📚 Learning: 2025-11-06T14:55:12.395Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 862
File: packages/visual-editor/src/utils/schema/resolveSchema.ts:118-135
Timestamp: 2025-11-06T14:55:12.395Z
Learning: In `packages/visual-editor/src/utils/schema/resolveSchema.ts`, the `OpeningHoursSchema` and `PhotoGallerySchema` functions from `yext/pages-components` contain internal type validation and handle invalid inputs gracefully (returning empty objects or undefined) rather than throwing TypeErrors, so no pre-validation guards are needed before calling them.

Applied to files:

  • packages/visual-editor/src/components/LocatorResultCard.tsx
🧬 Code graph analysis (2)
packages/visual-editor/src/components/atoms/cta.tsx (1)
packages/visual-editor/src/components/atoms/button.tsx (1)
  • Button (89-115)
packages/visual-editor/src/components/LocatorResultCard.tsx (4)
packages/visual-editor/src/components/atoms/cta.tsx (2)
  • CTAVariant (43-43)
  • CTA (142-209)
packages/visual-editor/src/components/contentBlocks/HoursTable.tsx (1)
  • HoursTableStyleFields (49-78)
packages/visual-editor/src/components/atoms/accordion.tsx (4)
  • Accordion (4-9)
  • AccordionItem (11-25)
  • AccordionTrigger (27-53)
  • AccordionContent (55-62)
packages/visual-editor/src/components/atoms/phone.tsx (1)
  • PhoneAtom (17-57)
🔇 Additional comments (28)
packages/visual-editor/locales/ro/visual-editor.json (1)

41-41: New localization keys added correctly.

The additions for new fields (callToAction, CTAVariant, commaSeparated, icons, mainPhone, primaryHeading, secondaryHeading, tertiaryHeading, resultCard, services), pluralized location variants, and phone/services references follow the existing naming conventions and structure consistently. All entries use proper Romanian quotation marks except for the noted issue at line 465.

Also applies to: 127-127, 149-149, 200-200, 231-231, 345-345, 358-358, 363-363, 372-372, 376-376, 395-395, 458-462, 468-472, 561-561

packages/visual-editor/locales/hu/visual-editor.json (1)

41-41: New localization keys properly added.

The newly added translation keys (callToAction, CTAVariant, commaSeparated, icons, mainPhone, phone, primaryHeading, resultCard, secondaryHeading, services, tertiaryHeading, and the pluralized locationWithCount/locationsWithinDistanceOf variants) are properly structured and appear contextually appropriate for the locator result card feature.

Also applies to: 127-127, 149-149, 200-200, 231-231, 345-345, 358-358, 363-363, 372-372, 376-376, 395-395, 458-464, 467-472

packages/visual-editor/locales/da/visual-editor.json (2)

464-473: Verify special whitespace characters in location variant strings.

Lines 464-473 contain unusual whitespace (possibly zero-width spaces or non-breaking spaces) between af and the opening quote in the location phrases:

"i nærheden af ​​\"{{name}}\""

This pattern appears inconsistent with other strings in the file and may be a copy-paste artifact from a rich-text editor or Word document. Verify that these are intentional and won't cause rendering or string-matching issues downstream. If unintentional, remove the extra whitespace.


41-41: Structure and localization coverage look good.

All new keys from the AI summary are present and properly ordered within the fields section. Pluralization variants follow standard i18n conventions (_few, _many, _one, _other, _zero), and placeholder usage ({{count}}, {{name}}, {{distance}}) is consistent. The phasing update from {{filterDisplayName}} to {{name}} is correctly reflected in the locationsNear_* variants.

Also applies to: 128-128, 150-150, 201-201, 232-232, 346-346, 359-359, 364-364, 373-373, 377-377, 396-396, 459-473

packages/visual-editor/locales/en-GB/visual-editor.json (4)

457-471: Verify pluralization forms for en-GB English.

English has only 2 plural forms: singular (for count=1) and plural (for everything else, including zero). The added keys use _few, _many, and _zero suffixes, which are plural forms typically needed by languages like Arabic, not English.

For en-GB, you should typically have only _one and _other forms (or omit the underscore variants entirely if not needed). Confirm whether these extra forms are intentional—perhaps for consistency across localization files even if not actively used by English.


344-344: Potential duplicate "phone" key.

Line 344 defines "phone": "Phone" inside the fields object, and line 498 defines "phone": "Phone" at the root level. Verify whether this duplication is intentional or if one should be removed.

Also applies to: 498-498


375-375: Potential duplicate "services" key.

Line 375 defines "services": "Services" inside the fields object, and line 560 defines "services": "Services" at the root level. Verify whether this duplication is intentional or if one should be removed.

Also applies to: 560-560


41-41: Verify corresponding translations in other locale files.

This PR adds localization keys for the new locator result card feature. Ensure that corresponding updates are made to all other locale files (not just en-GB) to maintain consistency. The PR notes mention that "translations have not been implemented yet," so clarify the plan for rolling out translations across all supported locales.

Also applies to: 126-126, 148-148, 199-199, 230-230, 357-357, 362-362, 371-371, 394-394

packages/visual-editor/locales/sk/visual-editor.json (1)

41-41: Verify that translations are complete and have been appropriately reviewed.

The PR description states "Translations have not been implemented yet and will be added after verifying visual appearance," but this file contains complete Slovak translations for all new locale keys introduced by this PR. Please clarify the translation status:

  • Are these translations final or preliminary?
  • Have they been reviewed by a Slovak speaker or a professional translator?
  • Should these translations be marked as draft/pending review before merge?

All new keys appear structurally sound (proper nesting in fields object, consistent use of pluralization forms few/many/one/other/zero, appropriate placeholder variables {{name}}, {{distance}}, {{count}}), but content accuracy cannot be verified without Slovak language expertise.

Also applies to: 128-128, 150-150, 201-201, 232-232, 346-346, 359-359, 364-364, 373-373, 377-377, 396-396, 459-473

packages/visual-editor/locales/pl/visual-editor.json (1)

41-41: Polish localization additions look solid.

All new translation keys are valid Polish and contextually appropriate. The pluralization variants (lines 459–473) correctly follow Polish plural forms (one, few, many, other, zero) with appropriate grammatical agreement. The new field labels align with the PR's goal of expanding locator result card configuration options.

Also applies to: 128-128, 150-150, 201-201, 232-232, 346-346, 359-359, 364-364, 373-373, 377-377, 396-396, 459-473

packages/visual-editor/locales/nb/visual-editor.json (1)

458-472: Verify pluralization keys align with CLDR for Norwegian.

The locationWithCount_*, locationsNear_*, and locationsWithinDistanceOf_* keys use CLDR plural forms (few, many, one, other, zero). For Norwegian Bokmål, the standard plural forms are: one and other. Verify whether few, many, and zero are intentional extensions or should be simplified to align with Norwegian's standard plural rules.

packages/visual-editor/locales/en/visual-editor.json (2)

457-471: Verify pluralization variants are used in the codebase.

English has only two plural forms: one and other. The additions of locationWithCount_few/many/zero and locationsNear_few/many/zero variants are unusual for English, where these categories are typically used for languages like Polish or Russian that have different plural rules.

Consider whether:

  1. All these variants are actually used by the i18n framework when rendering for English
  2. The _zero variants are intentional overrides (some frameworks support special zero handling)
  3. The _few and _many variants will ever be selected for English, or if they're dead code

Additionally, verify the template variables ({{count}}, {{name}}, {{distance}}) are correctly provided by the code when rendering these strings.


41-41: Verify new field labels are referenced in LocatorResultCard component.

These new translation keys were added to support the refactored LocatorResultCard component and its props. Ensure that:

  1. All added keys (callToAction, CTAVariant, commaSeparated, icons, mainPhone, primaryHeading, resultCard, secondaryHeading, services, tertiaryHeading) are actually used in the component code
  2. Key references in the UI match the exact key names defined here (case-sensitive)
  3. The duplicate keys (services at lines 375 and 560, phone at lines 344 and 498) are intentionally used for different contexts

Also applies to: 126-126, 148-148, 199-199, 230-230, 344-344, 357-357, 362-362, 371-371, 375-375, 394-394, 560-560

packages/visual-editor/locales/hr/visual-editor.json (2)

561-561: Verify duplicate "services" key placement.

The key "services" appears both at the root level (line 561) and within the fields section (line 376). Confirm this is intentional or if one is a duplication error. If both are needed for different contexts, consider clarifying the structure to avoid confusion.


41-41: New localization keys align well with PR objectives.

The additions for callToAction, CTAVariant, commaSeparated, icons, mainPhone, headings, resultCard, services, and expanded plural forms for location strings all support the locator result card styling enhancements. The plural forms correctly follow i18next conventions and use proper interpolation variables ({{count}}, {{name}}, {{distance}}).

Also applies to: 127-127, 149-149, 200-200, 231-231, 358-358, 363-363, 372-372, 376-376, 395-395, 458-472

packages/visual-editor/locales/de/visual-editor.json (1)

1-471: File structure, translations, and localization patterns are correct.

All new keys follow established naming conventions and JSON is properly formatted. The comprehensive pluralization support (_few, _many, _one, _other, _zero) is consistently applied across all locale files in this PR (verified across German, English, and French). This unified approach enables consistent behavior across languages while maintaining flexibility for language-specific pluralization rules.

packages/visual-editor/locales/fr/visual-editor.json (3)

479-479: String update maintains semantic meaning.

The modification to nearbyLocationsEmptyState (spacing adjustment from non-breaking to regular space before "miles") preserves the message intent and is appropriate for French typography.


41-41: Verification confirms French translations are aligned with English locale and linguistically sound.

All new field keys (CTAVariant, commaSeparated, icons, mainPhone, primaryHeading, resultCard, secondaryHeading, services, tertiaryHeading) have corresponding English entries, and their French translations are grammatically correct and semantically equivalent—e.g., "Titre principal" (Primary Heading), "Carte de résultat" (Result Card), "Séparés par des virgules" (Comma Separated). The translations maintain proper French conventions and are consistent with existing terminology in both locales.


457-471: French pluralization structure is complete and consistent with codebase design.

Verification confirms all five pluralization forms (_zero, _one, _few, _many, _other) are present for each location key group (locationWithCount, locationsNear, locationsWithinDistanceOf). While this structure exceeds French CLDR requirements (which only use _one and _other), it matches the intentional design pattern already used in the English locale. i18next correctly resolves French plurals using only the required forms per CLDR rules, and the extra forms cause no functional issues. All French translations are grammatically correct and properly formatted.

packages/visual-editor/locales/zh/visual-editor.json (1)

41-41: New CTA/result-card field labels look consistent.

The added keys (callToAction, fields.CTAVariant, commaSeparated, icons, mainPhone, phone, primaryHeading, resultCard, secondaryHeading, services, tertiaryHeading, plus root services) are semantically correct and consistent with existing terminology and placeholder usage. No issues from a functional or i18n standpoint.

Also applies to: 127-231, 345-395, 561-561

packages/visual-editor/locales/cs/visual-editor.json (1)

41-41: CTA/result card field translations look good in Czech.

The new callToAction, fields.CTAVariant, commaSeparated, icons, mainPhone, phone, primaryHeading, resultCard, secondaryHeading, services, tertiaryHeading, and root services keys all read naturally and match existing terminology and placeholder usage. No functional or i18n issues.

Also applies to: 127-232, 346-359, 364-377, 396-396, 562-562

packages/visual-editor/locales/et/visual-editor.json (1)

41-41: Estonian CTA/result-card labels are consistent and well-formed.

The added callToAction, fields.CTAVariant, commaSeparated, icons, mainPhone, phone, primaryHeading, resultCard, secondaryHeading, services, tertiaryHeading, and root services look correct and match existing naming patterns.

Also applies to: 127-231, 345-359, 363-377, 395-395, 561-561

packages/visual-editor/locales/nl/visual-editor.json (1)

41-41: Dutch CTA/result-card field labels look coherent.

The new callToAction, field-level CTA/result-card/heading/service labels (CTAVariant, commaSeparated, icons, mainPhone, phone, primaryHeading, resultCard, secondaryHeading, services, tertiaryHeading) and root services are semantically fine and consistent with existing terminology.

Also applies to: 127-231, 345-359, 363-377, 395-395, 561-561

packages/visual-editor/locales/lv/visual-editor.json (1)

41-41: Latvian CTA/result-card field translations look solid.

The added callToAction, fields.CTAVariant, commaSeparated, icons, mainPhone, phone, primaryHeading, resultCard, secondaryHeading, services, tertiaryHeading, plus root services, are idiomatic and consistent with existing naming.

Also applies to: 127-231, 345-359, 363-377, 395-395, 561-561

packages/visual-editor/locales/fi/visual-editor.json (1)

41-41: Finnish CTA/result-card labels are well-formed.

The new callToAction, field-level CTA/heading/service/result-card keys (CTAVariant, commaSeparated, icons, mainPhone, phone, primaryHeading, resultCard, secondaryHeading, services, tertiaryHeading) and root services look consistent and match the existing Finnish terminology.

Also applies to: 127-231, 345-359, 363-377, 395-395, 561-561

packages/visual-editor/src/components/atoms/cta.tsx (1)

161-183: Caret/linkContent refactor keeps behavior while simplifying JSX.

Extracting caretIcon and linkContent into locals maintains the existing caret logic (showCaret, ctaType !== "presetImage", directoryLink handling) and makes the render tree easier to follow. No behavior change concerns here.

packages/visual-editor/src/components/LocatorResultCard.tsx (1)

53-859: LGTM! Well-structured prop-driven refactor

The refactor successfully introduces a comprehensive prop-driven API for the locator result card:

  • LocatorResultCardProps interface provides clear configuration options for all card elements
  • DEFAULT_LOCATOR_RESULT_CARD_PROPS offers sensible defaults
  • LocatorResultCardFields exposes the configuration in the visual editor
  • Runtime field validation via fieldExists helper prevents errors from misconfigured fields
  • Past validation concerns have been properly addressed

The PR objectives note that plumbing field options beyond the name field is remaining work, which explains the current single-option field selectors—this is acceptable for the current scope.

packages/visual-editor/locales/tr/visual-editor.json (1)

41-41: All translation keys are properly aligned and correctly referenced.

Verification confirms that all new Turkish translation keys (primaryHeading, secondaryHeading, tertiaryHeading, resultCard, CTAVariant, commaSeparated) are:

  • Present in the locale file under the "fields" namespace at the correct lines
  • Correctly referenced in component files (LocatorResultCard.tsx, Locator.tsx) with matching msg() function calls
  • Free of typos and namespace inconsistencies

No action needed.

Comment on lines 457 to 471
"locationWithCount_few": "{{count}} ubicaciones",
"locationWithCount_many": "{{count}} ubicaciones",
"locationWithCount_one": "{{count}} ubicación",
"locationWithCount_other": "{{count}} ubicaciones",
"locationsNear_one": "{{count}} ubicación cerca de \"{{filterDisplayName}}\"",
"locationWithCount_zero": "{{count}} ubicaciones",
"locationsNear_few": "{{count}} ubicaciones cerca de \"{{name}}\"",
"locationsNear_many": "{{count}} ubicaciones cerca de \"{{name}}\"",
"locationsNear_one": "{{count}} ubicación cerca de \"{{name}}\"",
"locationsNear_other": "{{count}} ubicaciones cerca de \"{{name}}\"",
"locationsNear_zero": "{{count}} ubicaciones cerca de \"{{name}}\"",
"locationsWithinDistanceOf_few": "{{count}} ubicaciones dentro de {{distance}} millas de \"{{name}}\"",
"locationsWithinDistanceOf_many": "{{count}} ubicaciones dentro de {{distance}} millas de \"{{name}}\"",
"locationsWithinDistanceOf_one": "{{count}} ubicación dentro de {{distance}} millas de \"{{name}}\"",
"locationsWithinDistanceOf_other": "{{count}} ubicaciones dentro de {{distance}} millas de \"{{name}}\"",
"locationsWithinDistanceOf_zero": "{{count}} ubicaciones dentro de {{distance}} millas de \"{{name}}\"",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove unsupported plural forms from Spanish locale

Spanish (es) CLDR pluralization rules only define two forms: _one (when n=1) and _other (all other values). The _few, _many, and _zero variants added in lines 457-471 are not part of the Spanish plural specification and will never be selected by Intl.PluralRules('es').

Remove these unsupported entries:

  • locationWithCount_few (line 457)
  • locationWithCount_many (line 458)
  • locationWithCount_zero (line 461)
  • locationsNear_few (line 462)
  • locationsNear_many (line 463)
  • locationsNear_zero (line 466)
  • locationsWithinDistanceOf_few (line 467)
  • locationsWithinDistanceOf_many (line 468)
  • locationsWithinDistanceOf_zero (line 471)

Retain only _one and _other variants for each key.

🤖 Prompt for AI Agents
In packages/visual-editor/locales/es/visual-editor.json around lines 457 to 471,
the Spanish locale includes unsupported plural forms (_few, _many, _zero) which
should be removed; delete the entries locationWithCount_few,
locationWithCount_many, locationWithCount_zero, locationsNear_few,
locationsNear_many, locationsNear_zero, locationsWithinDistanceOf_few,
locationsWithinDistanceOf_many, and locationsWithinDistanceOf_zero and leave
only the _one and _other variants for each key so the file conforms to Spanish
CLDR pluralization rules.

"locationWithCount_zero": "{{count}} lokacija",
"locationsNear_few": "{{count}} lokacija u blizini \"{{name}}\"",
"locationsNear_many": "{{count}} lokacija u blizini \"{{name}}\"",
"locationsNear_one": "{{count}} Lokacija u blizini \"{{name}}\"",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Inconsistent capitalization in locationsNear_one.

Line 465 uses capitalized "Lokacija" while the other locationsNear variants (lines 463, 464, 466, 467) use lowercase "lokacija". Ensure the capitalization is intentional and aligns with your style guide.

-  "locationsNear_one": "{{count}} Lokacija u blizini \"{{name}}\"",
+  "locationsNear_one": "{{count}} lokacija u blizini \"{{name}}\"",
📝 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
"locationsNear_one": "{{count}} Lokacija u blizini \"{{name}}\"",
"locationsNear_one": "{{count}} lokacija u blizini \"{{name}}\"",
🤖 Prompt for AI Agents
In packages/visual-editor/locales/hr/visual-editor.json around line 465, the
translation key "locationsNear_one" uses "Lokacija" (capitalized) while the
surrounding variants use lowercase "lokacija"; to fix, make the capitalization
consistent by changing "Lokacija" to "lokacija" (or adjust the other entries if
uppercase is intended) so all locationsNear variants follow the same style
guide.

Comment on lines 458 to 472
"locationWithCount_few": "{{count}} locais",
"locationWithCount_many": "{{count}} locais",
"locationWithCount_one": "{{count}} localização",
"locationWithCount_other": "{{count}} localizações",
"locationsNear_one": "{{count}} localização próxima a \"{{filterDisplayName}}\"",
"locationWithCount_zero": "{{count}} locais",
"locationsNear_few": "{{count}} locais próximos a \"{{name}}\"",
"locationsNear_many": "{{count}} locais próximos a \"{{name}}\"",
"locationsNear_one": "{{count}} localização próxima a \"{{name}}\"",
"locationsNear_other": "{{count}} locais próximos a \"{{name}}\"",
"locationsNear_zero": "{{count}} locais próximos a \"{{name}}\"",
"locationsWithinDistanceOf_few": "{{count}} locais dentro de {{distance}} milhas de \"{{name}}\"",
"locationsWithinDistanceOf_many": "{{count}} locais dentro de {{distance}} milhas de \"{{name}}\"",
"locationsWithinDistanceOf_one": "{{count}} localização dentro de {{distance}} milhas de \"{{name}}\"",
"locationsWithinDistanceOf_other": "{{count}} locais dentro de {{distance}} milhas de \"{{name}}\"",
"locationsWithinDistanceOf_zero": "{{count}} locais dentro de {{distance}} milhas de \"{{name}}\"",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove unsupported plural forms from Portuguese locale

Portuguese (pt) CLDR pluralization rules only define two forms: _one (when n=1) and _other (all other values). The _few, _many, and _zero variants added in lines 458-472 are not part of the Portuguese plural specification and will never be selected by Intl.PluralRules('pt').

Remove these unsupported entries:

  • locationWithCount_few (line 458)
  • locationWithCount_many (line 459)
  • locationWithCount_zero (line 462)
  • locationsNear_few (line 463)
  • locationsNear_many (line 464)
  • locationsNear_zero (line 467)
  • locationsWithinDistanceOf_few (line 468)
  • locationsWithinDistanceOf_many (line 469)
  • locationsWithinDistanceOf_zero (line 472)

Retain only _one and _other variants for each key.

🤖 Prompt for AI Agents
In packages/visual-editor/locales/pt/visual-editor.json around lines 458 to 472,
remove the unsupported Portuguese plural keys (_few, _many, _zero) so only the
CLDR-supported _one and _other forms remain for each message; specifically
delete locationWithCount_few (line 458), locationWithCount_many (459),
locationWithCount_zero (462), locationsNear_few (463), locationsNear_many (464),
locationsNear_zero (467), locationsWithinDistanceOf_few (468),
locationsWithinDistanceOf_many (469), and locationsWithinDistanceOf_zero (472),
leaving only the existing _one and _other entries for locationWithCount,
locationsNear, and locationsWithinDistanceOf.

Comment on lines 458 to 472
"locationWithCount_few": "{{count}} konum",
"locationWithCount_many": "{{count}} konum",
"locationWithCount_one": "{{count}} Konum",
"locationWithCount_other": "{{count}} konumlar",
"locationsNear_one": "{{count}} konum \"{{filterDisplayName}}\" yakınında yer",
"locationWithCount_zero": "{{count}} konum",
"locationsNear_few": "\"{{name}}\" yakınında {{count}} konum",
"locationsNear_many": "\"{{name}}\" yakınında {{count}} konum",
"locationsNear_one": "{{count}} konum \"{{name}}\" yakınında yer",
"locationsNear_other": "\"{{name}}\" yakınındaki {{count}} konum",
"locationsNear_zero": "\"{{name}}\" yakınındaki {{count}} konum",
"locationsWithinDistanceOf_few": "\"{{name}}\" 'e {{distance}} mil mesafedeki {{count}} konum",
"locationsWithinDistanceOf_many": "\"{{name}}\" {{distance}} mil dahilindeki {{count}} konum",
"locationsWithinDistanceOf_one": "\"{{name}}\" 'a {{distance}} mil mesafedeki {{count}} konum",
"locationsWithinDistanceOf_other": "\"{{name}}\" 'in {{distance}} mil yakınındaki {{count}} konum",
"locationsWithinDistanceOf_zero": "\"{{name}}\" 'ya {{distance}} mil mesafedeki {{count}} konum",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify Turkish pluralization rules for new plural forms.

The new plural key variants use _few and _many categories (lines 458–472), which do not align with standard CLDR pluralization rules for Turkish. Turkish typically supports only zero, one, and other categories. If the _few and _many forms are included but not recognized by your i18n library, these strings will not be selected at runtime, causing fallback behavior or missing translations.

Please verify:

  1. Whether your i18n library (e.g., i18next) is configured to use extended pluralization rules for Turkish
  2. Whether other language locale files in the repository follow the same pattern with _few and _many categories
  3. The actual CLDR plural rules your system is enforcing

If Turkish should only use zero, one, and other, consider removing the _few and _many variants to avoid confusion and ensure correct string selection.


🏁 Script executed:

#!/bin/bash
# Description: Verify i18n pluralization configuration and check for pluralization patterns across locale files

# Check if there are i18n configuration files
echo "=== Searching for i18n configuration files ==="
fd -e 'js|json|ts' -p 'i18n|locale' | head -20

# Check for pluralization rule definitions
echo "=== Searching for pluralization configuration ==="
rg -i 'plural|cldr' --type=ts --type=js -C 3 | head -50

# Check pluralization patterns in all locale files
echo "=== Checking pluralization patterns in locale files ==="
fd 'visual-editor.json' -x rg '_few|_many|_zero|_one|_other' {} -l | head -10

# Inspect one other locale file to compare patterns
echo "=== Comparing patterns with English locale ==="
fd 'en/visual-editor.json' -x rg 'locationWithCount|locationsNear' {} -C 2

Length of output: 1401


I need to understand the i18n library being used and verify whether this pluralization pattern is intentional across all languages.

I need to verify the i18next configuration and check if this pattern is consistent across other languages in the codebase.


---

🏁 Script executed:

```shell
#!/bin/bash
# Check package.json for i18next version
echo "=== Checking i18next dependencies ==="
fd 'package.json' | head -5 | xargs -I {} rg -l 'i18next' {} 

echo "=== Extracting i18next version ==="
fd 'package.json' -p 'packages/visual-editor' -x cat {} | rg 'i18next' -C 2

# Check for i18n initialization/config files
echo "=== Checking for i18n setup/init files ==="
fd -e 'ts|js' -p 'i18n|locale' | grep -E 'init|config|setup' | head -10

# Compare pluralization patterns across different locales
echo "=== Comparing patterns: German locale ==="
fd 'de/visual-editor.json' -x rg 'locationWithCount|locationsNear' {} -A 1

echo "=== Comparing patterns: Portuguese locale ==="
fd 'pt/visual-editor.json' -x rg 'locationWithCount|locationsNear' {} -A 1

Length of output: 1069



🏁 Script executed:

#!/bin/bash
# List all locale files and their structure
echo "=== Locale directory structure ==="
fd 'locales' -type d -p 'packages/visual-editor'

echo "=== All locale files available ==="
fd 'visual-editor.json' -p 'packages/visual-editor/locales'

# Check for i18next configuration files
echo "=== i18next-scanner config ==="
fd 'i18next-scanner.config' 

echo "=== Looking for i18next initialization ==="
rg -i 'i18next\.init|i18next\.use' --type=ts --type=js -C 5 | head -40

# Sample actual content from German locale (locationWithCount section)
echo "=== German locale sample (all pluralization variants) ==="
fd 'de/visual-editor.json' -p 'visual-editor' -x rg '"locationWithCount' {} -A 5

# Sample Portuguese locale 
echo "=== Portuguese locale sample (all pluralization variants) ==="
fd 'pt/visual-editor.json' -p 'visual-editor' -x rg '"locationWithCount' {} -A 5

Length of output: 2017


I also need to verify i18next v25 default behavior regarding pluralization rules.


🏁 Script executed:

#!/bin/bash
# Read i18next scanner configuration
echo "=== i18next-scanner.config.cjs ==="
cat packages/visual-editor/i18next-scanner.config.cjs

echo ""
echo "=== German locale (locationWithCount keys) ==="
cat packages/visual-editor/locales/de/visual-editor.json | grep -A 4 'locationWithCount_'

echo ""
echo "=== Portuguese locale (locationWithCount keys) ==="
cat packages/visual-editor/locales/pt/visual-editor.json | grep -A 4 'locationWithCount_'

echo ""
echo "=== French locale (locationWithCount keys) ==="
cat packages/visual-editor/locales/fr/visual-editor.json | grep -A 4 'locationWithCount_'

echo ""
echo "=== Searching for i18next init more broadly ==="
rg 'initReactI18next|i18next\.use|i18next\.init' --type=ts --type=js -B 2 -A 5 | head -50

Length of output: 5543


🏁 Script executed:

#!/bin/bash
# Web search: Verify i18next v25 pluralization behavior and CLDR support

Length of output: 59


🌐 Web query:

i18next version 25 pluralization CLDR rules Turkish

💡 Result:

Short answer: i18next (v25) follows CLDR/Intl plural rules for locales — Turkish (tr) has two cardinal plural categories: "one" when n = 1 (including 1.0, 1.00, …) and "other" for all other numbers (i.e. n != 1). Use i18next.t('key', { count }) and provide key_one / key_other (i18next also supports a special _zero key). [1][2]

Sources:

  • i18next plurals docs. [1]
  • Unicode CLDR — Turkish plural rules (n = 1 → one; otherwise other). [2]

🏁 Script executed:

#!/bin/bash
# Check how the new keys are used in component code
echo "=== Usage of new plural keys in components ==="
rg 'locationWithCount|locationsNear|locationsWithinDistanceOf' --type=ts --type=tsx -B 2 -A 2 | head -60

echo ""
echo "=== Check if components pass count parameter ==="
rg 't\(' --type=ts --type=tsx | grep -E 'count:|count\}' | head -20

Length of output: 152


I need to verify Portuguese CLDR rules to understand the exception.


🏁 Script executed:

#!/bin/bash
# Try searching without tsx type specification
echo "=== Usage of new plural keys ==="
rg 'locationWithCount|locationsNear|locationsWithinDistanceOf' --type=ts -B 1 -A 1 | head -40

echo ""
echo "=== Check CLDR rules for other languages in the file ==="
echo "German (de) CLDR rules: _one, _other"
echo "French (fr) CLDR rules: _one, _other"
echo "Portuguese (pt) CLDR rules: _one, _few, _many, _other"
echo "Turkish (tr) CLDR rules: _one, _other"

Length of output: 1076


🏁 Script executed:

#!/bin/bash
# Verify Portuguese CLDR plural rules to confirm it's the exception
echo "Portuguese plural rules check completed in verification"

Length of output: 55


🌐 Web query:

Portuguese CLDR plural rules categories

💡 Result:

Portuguese (CLDR) plural categories (cardinal): one, other.

  • Cardinal rule: "one" applies for i = 0..1 (samples: 0, 1 and decimals 0.0~1.5); "other" for the remaining numbers.
  • Ordinals: only "other".
    Source: Unicode CLDR language plural rules (Portuguese). [1]

References
[1] Unicode CLDR — Language Plural Rules (Portuguese entry).


Remove unsupported plural form variants from Turkish locale (and all other locales with standard CLDR rules).

i18next v25 follows strict CLDR plural rules per language. Turkish supports only _one and _other categories (plus the special _zero form). The new entries in lines 458–472 include _few and _many variants, which are dead code for Turkish—i18next will never match them at runtime.

This pattern appears system-wide across all 26 language locales. Since most languages (German, French, Turkish, etc.) follow similar CLDR constraints, either:

  1. Remove _few and _many variants to align with language-specific CLDR rules, or
  2. Configure i18next with custom pluralization rules if intentional over-provisioning is desired

Components correctly pass the count parameter (verified in Locator.tsx), so functionality will work via fallback to _other, but the unused entries should be cleaned up.

🤖 Prompt for AI Agents
packages/visual-editor/locales/tr/visual-editor.json lines 458-472: the Turkish
locale contains unsupported plural keys (_few and _many) which i18next/CLDR will
never use; remove the _few and _many entries for these keys
(locationsWithCount_*, locationsNear_*, locationsWithinDistanceOf_*) and ensure
the remaining valid plural forms (_one, _other, and _zero if needed) contain the
correct translations (move any unique text from removed variants into _other or
_one as appropriate); repeat the same cleanup across other locale files that
include dead _few/_many variants or alternatively, if non-CLDR pluralization is
required, configure i18next with custom plural rules instead of adding
unsupported keys.

Comment on lines 458 to 472
"locationWithCount_few": "{{count}}位置",
"locationWithCount_many": "{{count}}位置",
"locationWithCount_one": "{{count}}位置",
"locationWithCount_other": "{{count}}位置",
"locationsNear_one": "{{count}}位置附近“ {{filterDisplayName}}”",
"locationWithCount_zero": "{{count}}位置",
"locationsNear_few": "“{{name}}”附近有{{count}}位置",
"locationsNear_many": "“{{name}}”附近有{{count}}位置",
"locationsNear_one": "“{{name}}”附近有{{count}}位置",
"locationsNear_other": "“{{name}}”附近有{{count}}位置",
"locationsNear_zero": "“{{name}}”附近有{{count}}位置",
"locationsWithinDistanceOf_few": "“{{name}}”距離{{distance}}英里內有{{count}}位置",
"locationsWithinDistanceOf_many": "“{{name}}”距離{{distance}}英里內有{{count}}位置",
"locationsWithinDistanceOf_one": "\"{{name}}\"距離{{distance}}英里內有{{count}}位置",
"locationsWithinDistanceOf_other": "“{{name}}”距離{{distance}}英里內有{{count}}位置",
"locationsWithinDistanceOf_zero": "“{{name}}”距離{{distance}}英里內有{{count}}位置",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove all plural form variants except _other from Traditional Chinese locale

Traditional Chinese (zh-TW) does not have grammatical number distinctions in CLDR pluralization rules. All counts use the same form, represented by the _other category. The additions of _few, _many, _zero, and _one variants in lines 458-472 are not part of the Chinese plural specification and will never be selected by Intl.PluralRules('zh-TW').

Remove these unsupported plural form entries:

  • locationWithCount_few (line 458)
  • locationWithCount_many (line 459)
  • locationWithCount_one (line 460)
  • locationWithCount_zero (line 462)
  • locationsNear_few (line 463)
  • locationsNear_many (line 464)
  • locationsNear_one (line 465)
  • locationsNear_zero (line 467)
  • locationsWithinDistanceOf_few (line 468)
  • locationsWithinDistanceOf_many (line 469)
  • locationsWithinDistanceOf_one (line 470)
  • locationsWithinDistanceOf_zero (line 472)

Keep only the _other variant (lines 461, 466, 471) for each key, as Chinese uses a single form for all counts.

🤖 Prompt for AI Agents
In packages/visual-editor/locales/zh-TW/visual-editor.json around lines 458-472,
the Traditional Chinese locale incorrectly includes plural variants (_few,
_many, _one, _zero) that are never used by zh-TW; remove the listed keys
(locationWithCount_few/many/one/zero at lines 458-462,
locationsNear_few/many/one/zero at lines 463-467, and
locationsWithinDistanceOf_few/many/one/zero at lines 468-472) and keep only the
corresponding _other entries (the existing locationWithCount_other,
locationsNear_other, locationsWithinDistanceOf_other) so the file contains a
single Chinese plural form per key.

@k-gerner k-gerner merged commit 361c69e into locator-result-cards Nov 20, 2025
15 checks passed
@k-gerner k-gerner deleted the locator-result-card-new-props branch November 20, 2025 20:02
mkouzel-yext added a commit that referenced this pull request Nov 21, 2025
Adds all the new functionality to support customizable result cards in
the locator. This functionality was broken into #905 and #912 (which
also included #915), and now all the new features are jointly being
merged into main.

J=[PB-26919](https://yext.atlassian.net/browse/PB-26919)

---------

Co-authored-by: Kyle Gerner <kgerner@yext.com>
Co-authored-by: Kyle Gerner <49618240+k-gerner@users.noreply.github.com>
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
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.

3 participants