Skip to content

Conversation

@benlife5
Copy link
Contributor

@benlife5 benlife5 commented Dec 22, 2025

Also made the compact hero changes described in https://yext.atlassian.net/browse/OPAQF-9

Removed the RTF text color from the promo data since we no longer allow you to change the text color in KG

@github-actions
Copy link
Contributor

github-actions bot commented Dec 22, 2025

⚠️ Deleted Translation Keys Detected

🔤 Deleted Translation Keys

fields

Key Languages Removed
fields.imageOrientation cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.mediaOrientation cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

@benlife5 benlife5 added the create-dev-release Triggers dev release workflow label Dec 22, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

Walkthrough

Refactors PromoSection into a variant-driven implementation and adds four variants: classic, immersive, spotlight, and compact. Introduces PromoContent, PromoMedia, PromoEmptyState, and per-variant components (ClassicPromo, ImmersivePromo, SpotlightPromo, CompactPromo). Expands PromoStyles and PromoData (adds variant, desktopImagePosition, mobileImagePosition, containerAlignment, imageHeight, and backgroundImage). Adds migration 0050_promo_variants and registers it. Adds locale keys (containerAlignment, bottom_direction, top_direction, promoBanner) across locales, updates tests and testing harness, and minor edits to Video and HeroSection.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Editor as Visual Editor (client)
    participant Migration as MigrationRegistry / 0050_promo_variants
    participant Promo as PromoSection
    participant Resolver as resolveFields / resolveData
    participant Selector as Variant Selector
    participant Classic as ClassicPromo
    participant Immersive as ImmersivePromo
    participant Spotlight as SpotlightPromo
    participant Compact as CompactPromo
    participant Empty as PromoEmptyState
    participant Content as PromoContent
    participant Media as PromoMedia

    Editor->>Promo: mount with props & locale
    Promo->>Migration: apply propTransformation
    Migration-->>Promo: transformed props (variant, positions, backgroundImage)
    Promo->>Resolver: resolve fields & data for variant
    Resolver-->>Promo: resolved fields/data
    Promo->>Selector: read styles.variant
    alt styles.variant == "classic"
        Selector->>Classic: render ClassicPromo (slots, styles)
        Classic->>Media: render PromoMedia
        Classic->>Content: render PromoContent
    else styles.variant == "immersive"
        Selector->>Immersive: render ImmersivePromo
        Immersive->>Promo: resolve backgroundImage & overlay
        Immersive->>Content: render PromoContent
    else styles.variant == "spotlight"
        Selector->>Spotlight: render SpotlightPromo
        Spotlight->>Content: render PromoContent
    else styles.variant == "compact"
        Selector->>Compact: render CompactPromo
        Compact->>Media: render responsive PromoMedia
        Compact->>Content: render PromoContent
    else editor && no mapped data
        Selector->>Empty: render PromoEmptyState
        Empty-->>Editor: show editor placeholder
    end
    Content-->>Editor: apply alignment/layout classes
    Media-->>Editor: render image/video
    Promo-->>Editor: composed DOM
Loading

Possibly related PRs

Suggested reviewers

  • briantstephan
  • asanehisa

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: promo variants' directly and clearly summarizes the main change—introducing multiple promo variant components (Classic, Immersive, Spotlight, Compact) to replace the monolithic PromoSection approach.
Description check ✅ Passed The description mentions compact hero changes and removal of RTF text color from promo data, both of which are present in the changeset (HeroSection/CompactHero updates and description HTML simplifications).
✨ Finishing touches
  • 📝 Generate docstrings

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.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 22, 2025

commit: 867c399

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

🧹 Nitpick comments (15)
starter/localData/dev-location-stream__en__a5ac5ff0d6330870779568c3bc865bc7.json (1)

457-457: Simplify redundant bold markup.

The HTML uses nested <b><strong style="font-weight: bold;"> tags. Since <strong> already provides bold styling and semantic emphasis, the outer <b> tag and the inline font-weight: bold; style are redundant.

🔎 Suggested simplification
-      "html": "<p dir=\"ltr\" style=\"font-size: 14.67px; font-weight: 400; line-height: 18.67px; color: rgb(0, 0, 0); margin: 0; padding: 3px 2px 3px 2px; position: relative;\"><span>Our out-of-this-world </span><b><strong style=\"font-weight: bold;\">burgers</strong></b><span> and</span><b><strong style=\"font-weight: bold;\"> fresh salads</strong></b><span> are a flavor journey you won&#39;t forget. Explore a galaxy of taste, where every ingredient composes a symphony of flavors. Come visit us for a stellar dining experience!</span></p>"
+      "html": "<p dir=\"ltr\" style=\"font-size: 14.67px; font-weight: 400; line-height: 18.67px; color: rgb(0, 0, 0); margin: 0; padding: 3px 2px 3px 2px; position: relative;\"><span>Our out-of-this-world </span><strong>burgers</strong><span> and </span><strong>fresh salads</strong><span> are a flavor journey you won&#39;t forget. Explore a galaxy of taste, where every ingredient composes a symphony of flavors. Come visit us for a stellar dining experience!</span></p>"
starter/localData/dev-location-stream__zh_hans_hk__cbafb9cd1c3e63d9814e236ba9181377.json (1)

448-448: Data update aligns with promo standardization.

The HTML simplification in the promo description is consistent with the PR's broader PromoSection refactoring objectives.

Optional: Consider removing redundant bold markup

The <b><strong> double-wrapping is semantically redundant since both tags produce bold text. You could simplify to just <strong> for slightly cleaner markup:

-<b><strong style="font-weight: bold;">burgers</strong></b>
+<strong style="font-weight: bold;">burgers</strong>

Same for "fresh salads". This is a minor data quality improvement and can be deferred or skipped if the HTML is generated by a rich text editor.

starter/localData/dev-location-stream__en__cbafb9cd1c3e63d9814e236ba9181377.json (1)

448-448: Consider simplifying redundant HTML markup.

The HTML uses <b><strong style="font-weight: bold;"> which is redundant—<strong> alone provides semantic bold styling. The inline font-weight: bold; style is also unnecessary.

🔎 Proposed simplification
-      "html": "<p dir=\"ltr\" style=\"font-size: 14.67px; font-weight: 400; line-height: 18.67px; color: rgb(0, 0, 0); margin: 0; padding: 3px 2px 3px 2px; position: relative;\"><span>Our out-of-this-world </span><b><strong style=\"font-weight: bold;\">burgers</strong></b><span> and</span><b><strong style=\"font-weight: bold;\"> fresh salads</strong></b><span> are a flavor journey you won&#39;t forget. Explore a galaxy of taste, where every ingredient composes a symphony of flavors. Come visit us for a stellar dining experience!</span></p>"
+      "html": "<p dir=\"ltr\" style=\"font-size: 14.67px; font-weight: 400; line-height: 18.67px; color: rgb(0, 0, 0); margin: 0; padding: 3px 2px 3px 2px; position: relative;\"><span>Our out-of-this-world </span><strong>burgers</strong><span> and</span><strong> fresh salads</strong><span> are a flavor journey you won&#39;t forget. Explore a galaxy of taste, where every ingredient composes a symphony of flavors. Come visit us for a stellar dining experience!</span></p>"
packages/visual-editor/locales/nb/visual-editor.json (1)

299-299: Consider verifying translation consistency for directional terms.

There's a minor asymmetry in the translation approach:

  • "bottom_direction": "Nederst" uses a superlative form (bottommost), distinct from "bottom": "Bunn" (line 298)
  • "top_direction": "Topp" uses the same term as "top": "Topp" (line 393)

While both translations are valid Norwegian, consider verifying whether this difference is intentional or if "bottom_direction" should use "Bunn" to maintain parallel structure with "top_direction".

Also applies to: 394-394

starter/localData/dev-location-stream__en__8573da7e3961090ddb59214a1f01e59a.json (1)

459-459: LGTM! Optional: simplify redundant bold markup.

The markup simplification preserves the content correctly. The nested <b><strong> tags are functionally redundant—<strong> alone provides semantic emphasis and bold styling.

Optional cleanup
-      "html": "<p dir=\"ltr\" style=\"font-size: 14.67px; font-weight: 400; line-height: 18.67px; color: rgb(0, 0, 0); margin: 0; padding: 3px 2px 3px 2px; position: relative;\"><span>Our out-of-this-world </span><b><strong style=\"font-weight: bold;\">burgers</strong></b><span> and</span><b><strong style=\"font-weight: bold;\"> fresh salads</strong></b><span> are a flavor journey you won&#39;t forget. Explore a galaxy of taste, where every ingredient composes a symphony of flavors. Come visit us for a stellar dining experience!</span></p>"
+      "html": "<p dir=\"ltr\" style=\"font-size: 14.67px; font-weight: 400; line-height: 18.67px; color: rgb(0, 0, 0); margin: 0; padding: 3px 2px 3px 2px; position: relative;\"><span>Our out-of-this-world </span><strong style=\"font-weight: bold;\">burgers</strong><span> and </span><strong style=\"font-weight: bold;\">fresh salads</strong><span> are a flavor journey you won&#39;t forget. Explore a galaxy of taste, where every ingredient composes a symphony of flavors. Come visit us for a stellar dining experience!</span></p>"
starter/src/dev.config.ts (1)

1109-1229: c_emptyPromo schema mirrors c_examplePromo appropriately

The new c_emptyPromo definition (registryId, type.promo_section, and nested image/title/description/cta fields) cleanly mirrors c_examplePromo, so it should behave as a structurally identical promo slot.

If you want this field to appear with a friendly label anywhere that uses apiNamesToDisplayNames, consider adding an entry for c_emptyPromo there (similar to other non-example custom fields), but it’s optional and consistent with how c_examplePromo is currently handled.

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

611-611: Translation includes slight redundancy but may be intentional.

The translation "Reklaminė reklamjuostė" (promotional banner) contains the root word "reklam-" twice, which creates mild redundancy. While "Reklamjuostė" alone might suffice, the current translation emphasizes the promotional nature explicitly, which may be intentional for clarity in the UI context.

If this redundancy is unintentional, consider simplifying to "Reklamjuostė" or an alternative like "Promo reklamjuostė".

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

2360-2361: Remove or complete the empty if block.

This no-op conditional appears to be leftover debug code or an incomplete implementation. It should either be removed or completed with the intended logic.

🔎 Proposed fix
-      if (name.includes("[compact] version 31 props with constant")) {
-      }
packages/visual-editor/src/components/pageSections/PromoSection/PromoMedia.tsx (1)

32-38: Remove commented-out code.

The commented className prop on line 37 should be removed if it's not needed, or uncommented and properly configured if it is needed.

🔎 Proposed fix
      <EntityField
        displayName={pt("fields.media", "Media")}
        fieldId={data.promo.field}
        constantValueEnabled={data.promo.constantValueEnabled}
        fullHeight
-       // className="flex"
      >
packages/visual-editor/src/components/pageSections/PromoSection/SpotlightPromo.tsx (1)

27-35: Remove redundant background styling.

Background properties are specified both in the className (bg-no-repeat bg-center bg-cover) and inline style. The inline styles will override the CSS utilities, making the className properties redundant.

🔎 Proposed fix
     <div
-      className="bg-no-repeat bg-center bg-cover"
       style={{
         backgroundImage: resolvedBackgroundImage?.url
           ? `url(${getImageUrl(resolvedBackgroundImage.url, resolvedBackgroundImage.width, resolvedBackgroundImage.height)})`
           : undefined,
         backgroundSize: "cover",
         backgroundPosition: "center",
         backgroundRepeat: "no-repeat",
       }}
     >
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx (1)

125-129: Consider uncommenting or removing the conditionalRender comment.

The commented-out conditionalRender in PromoVariantProps suggests future functionality. If this is planned, consider adding a TODO comment with context; otherwise, remove the dead comment.

packages/visual-editor/src/components/pageSections/PromoSection/PromoContent.tsx (2)

5-7: Trailing whitespace in template string.

Line 6 has a trailing space at the end of the template string which will be included in the className.

🔎 Proposed fix
 export const promoContentParentCn = (styles: PromoVariantProps["styles"]) => {
-  return `flex flex-col gap-y-8 sm:gap-y-10 w-full break-words ${styles.containerAlignment === "left" ? "items-start" : styles.containerAlignment === "center" ? "items-center" : "items-end"} `;
+  return `flex flex-col gap-y-8 sm:gap-y-10 w-full break-words ${styles.containerAlignment === "left" ? "items-start" : styles.containerAlignment === "center" ? "items-center" : "items-end"}`;
 };

12-15: Duplicated alignment logic between helper and component.

The alignment class logic on line 14 duplicates the logic in promoContentParentCn (line 6). Consider extracting the alignment ternary into a shared helper or reusing the existing function to avoid divergence.

🔎 Proposed refactor
+const getAlignmentClass = (alignment: PromoVariantProps["styles"]["containerAlignment"]) =>
+  alignment === "left" ? "items-start" : alignment === "center" ? "items-center" : "items-end";
+
 export const promoContentParentCn = (styles: PromoVariantProps["styles"]) => {
-  return `flex flex-col gap-y-8 sm:gap-y-10 w-full break-words ${styles.containerAlignment === "left" ? "items-start" : styles.containerAlignment === "center" ? "items-center" : "items-end"} `;
+  return `flex flex-col gap-y-8 sm:gap-y-10 w-full break-words ${getAlignmentClass(styles.containerAlignment)}`;
 };

 export const PromoContent: PuckComponent<PromoVariantProps> = (props) => {
   const { slots, styles } = props;

   return (
     <div
-      className={`flex flex-col justify-center gap-y-6 sm:gap-y-8 w-full break-words ${styles.containerAlignment === "left" ? "items-start" : styles.containerAlignment === "center" ? "items-center" : "items-end"}`}
+      className={`flex flex-col justify-center gap-y-6 sm:gap-y-8 w-full break-words ${getAlignmentClass(styles.containerAlignment)}`}
     >
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.test.tsx (1)

2714-2737: Duplicate axe violation handling logic.

The axe violation handling (brand color check + conditional expect) is duplicated between the initial render (lines 2714-2722) and the post-interaction check (lines 2729-2737). Consider extracting this into a helper function.

🔎 Proposed refactor
+const assertNoAxeViolations = (results: any, props: any, testName: string) => {
+  if (isBrandColorTest(props)) {
+    console.warn(`IGNORING axe violations for brand color test: ${testName}`, results);
+  } else {
+    expect(results).toHaveNoViolations();
+  }
+};

 // Then use it:
-      const results = await axe(container);
-      if (isBrandColorTest(props)) {
-        console.warn(
-          `IGNORING axe violations for brand color test: ${name}`,
-          results
-        );
-      } else {
-        expect(results).toHaveNoViolations();
-      }
+      const results = await axe(container);
+      assertNoAxeViolations(results, props, name);
packages/visual-editor/src/components/pageSections/PromoSection/ImmersivePromo.tsx (1)

18-22: Unnecessary optional chaining on data.

Since data is destructured from props on line 14 and is a required property of PromoVariantProps, the optional chaining on data?.backgroundImage (line 20) is unnecessary.

🔎 Proposed fix
   const resolvedBackgroundImage = resolveYextEntityField(
     streamDocument,
-    data?.backgroundImage,
+    data.backgroundImage,
     locale
   );
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 493d1a4 and f9444e7.

📒 Files selected for processing (55)
  • packages/visual-editor/locales/cs/visual-editor.json
  • packages/visual-editor/locales/da/visual-editor.json
  • packages/visual-editor/locales/de/visual-editor.json
  • packages/visual-editor/locales/en-GB/visual-editor.json
  • packages/visual-editor/locales/en/visual-editor.json
  • packages/visual-editor/locales/es/visual-editor.json
  • packages/visual-editor/locales/et/visual-editor.json
  • packages/visual-editor/locales/fi/visual-editor.json
  • packages/visual-editor/locales/fr/visual-editor.json
  • packages/visual-editor/locales/hr/visual-editor.json
  • packages/visual-editor/locales/hu/visual-editor.json
  • packages/visual-editor/locales/it/visual-editor.json
  • packages/visual-editor/locales/ja/visual-editor.json
  • packages/visual-editor/locales/lt/visual-editor.json
  • packages/visual-editor/locales/lv/visual-editor.json
  • packages/visual-editor/locales/nb/visual-editor.json
  • packages/visual-editor/locales/nl/visual-editor.json
  • packages/visual-editor/locales/pl/visual-editor.json
  • packages/visual-editor/locales/pt/visual-editor.json
  • packages/visual-editor/locales/ro/visual-editor.json
  • packages/visual-editor/locales/sk/visual-editor.json
  • packages/visual-editor/locales/sv/visual-editor.json
  • packages/visual-editor/locales/tr/visual-editor.json
  • packages/visual-editor/locales/zh-TW/visual-editor.json
  • packages/visual-editor/locales/zh/visual-editor.json
  • packages/visual-editor/src/components/categories/PageSectionCategory.tsx
  • packages/visual-editor/src/components/contentBlocks/Video.tsx
  • packages/visual-editor/src/components/migrations/0049_promo_variants.ts
  • packages/visual-editor/src/components/migrations/migrationRegistry.ts
  • packages/visual-editor/src/components/pageSections/HeroSection.test.tsx
  • packages/visual-editor/src/components/pageSections/HeroSection.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection.test.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/ClassicPromo.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/CompactPromo.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/ImmersivePromo.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/PromoContent.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/PromoEmptyState.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/PromoMedia.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.test.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/SpotlightPromo.tsx
  • packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx
  • packages/visual-editor/src/components/pageSections/heroVariants/HeroContent.tsx
  • packages/visual-editor/src/components/pageSections/index.ts
  • packages/visual-editor/src/components/testing/componentTests.setup.ts
  • packages/visual-editor/src/docs/ai/components.d.ts
  • packages/visual-editor/src/docs/components.md
  • starter/localData/dev-location-stream__en__8573da7e3961090ddb59214a1f01e59a.json
  • starter/localData/dev-location-stream__en__a5ac5ff0d6330870779568c3bc865bc7.json
  • starter/localData/dev-location-stream__en__aa6fc9407fdca53fc4133ee078b93231.json
  • starter/localData/dev-location-stream__en__cbafb9cd1c3e63d9814e236ba9181377.json
  • starter/localData/dev-location-stream__en__d7f52f17aa2a321e60e8c1a8fae53637.json
  • starter/localData/dev-location-stream__es__cbafb9cd1c3e63d9814e236ba9181377.json
  • starter/localData/dev-location-stream__zh_hans_hk__cbafb9cd1c3e63d9814e236ba9181377.json
  • starter/src/dev.config.ts
💤 Files with no reviewable changes (1)
  • packages/visual-editor/src/components/pageSections/PromoSection.test.tsx
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-12T20:17:37.033Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 946
File: packages/visual-editor/src/components/migrations/migrationRegistry.ts:47-48
Timestamp: 2025-12-12T20:17:37.033Z
Learning: In the yext/visual-editor repository, empty object placeholders (`{}`) in the migrationRegistry array are sometimes intentionally used to reserve migration slots for parallel PRs to prevent merge conflicts and maintain consistent migration indices.

Applied to files:

  • packages/visual-editor/src/components/migrations/0049_promo_variants.ts
  • packages/visual-editor/src/components/migrations/migrationRegistry.ts
📚 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/pageSections/PromoSection/PromoSection.tsx
🧬 Code graph analysis (7)
packages/visual-editor/src/components/pageSections/PromoSection/SpotlightPromo.tsx (3)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx (1)
  • PromoVariantProps (125-129)
packages/visual-editor/scripts/generateFontRegistry.js (1)
  • data (42-42)
packages/visual-editor/src/components/pageSections/PromoSection/PromoContent.tsx (2)
  • promoContentParentCn (5-7)
  • PromoContent (9-25)
packages/visual-editor/src/components/pageSections/PromoSection/PromoContent.tsx (1)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx (1)
  • PromoVariantProps (125-129)
packages/visual-editor/src/components/pageSections/PromoSection/ClassicPromo.tsx (3)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx (1)
  • PromoVariantProps (125-129)
packages/visual-editor/src/components/pageSections/PromoSection/PromoMedia.tsx (1)
  • PromoMedia (6-57)
packages/visual-editor/src/components/pageSections/PromoSection/PromoContent.tsx (1)
  • PromoContent (9-25)
packages/visual-editor/src/components/pageSections/PromoSection/PromoMedia.tsx (3)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx (2)
  • PromoData (36-50)
  • PromoStyles (52-89)
packages/visual-editor/src/components/pageSections/index.ts (2)
  • PromoData (55-55)
  • PromoStyles (56-56)
packages/visual-editor/scripts/generateFontRegistry.js (1)
  • data (42-42)
packages/visual-editor/src/components/pageSections/PromoSection/ImmersivePromo.tsx (2)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx (1)
  • PromoVariantProps (125-129)
packages/visual-editor/src/components/pageSections/PromoSection/PromoContent.tsx (2)
  • promoContentParentCn (5-7)
  • PromoContent (9-25)
packages/visual-editor/src/components/pageSections/HeroSection.test.tsx (1)
packages/visual-editor/src/components/testing/componentTests.setup.ts (1)
  • axe (77-81)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.test.tsx (4)
packages/visual-editor/src/components/testing/componentTests.setup.ts (1)
  • ComponentTest (109-121)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx (1)
  • PromoSection (292-643)
packages/visual-editor/src/components/pageSections/index.ts (1)
  • PromoSection (53-53)
packages/visual-editor/src/components/migrations/migrationRegistry.ts (1)
  • migrationRegistry (57-107)
🪛 Biome (2.1.2)
packages/visual-editor/src/components/pageSections/PromoSection/PromoEmptyState.tsx

[error] 15-15: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx

[error] 433-435: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)


[error] 448-455: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)

⏰ 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: create-dev-release
  • GitHub Check: semgrep/ci
🔇 Additional comments (59)
starter/localData/dev-location-stream__es__cbafb9cd1c3e63d9814e236ba9181377.json (1)

448-448: LGTM!

The update to the example promo description is appropriate for the promo variants refactoring. The HTML is valid and properly escaped for JSON serialization. Since this is dev/test data, the change poses no risk to the system.

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

196-196: LGTM! Localization additions support the new promo variants feature.

The added localization keys (containerAlignment, bottom_direction, top_direction, promoBanner) are correctly placed in alphabetical order and align with the PR's introduction of multi-variant promo layouts. The JSON structure is valid and the Traditional Chinese translations are provided.

Also applies to: 299-299, 395-395, 612-612

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

196-196: LGTM! Localization additions are structurally sound.

The JSON structure is valid, and the added translations are properly positioned within their respective sections. The Croatian translations appear contextually appropriate:

  • "Usklađivanje spremnika" (container alignment)
  • "Dno" (bottom direction)
  • "Vrh" (top direction)
  • "Promo banner" (kept in English, standard for marketing terms)

If these translations haven't been reviewed by native Croatian speakers or professional translators, consider having them verified to ensure linguistic accuracy and naturalness, especially since this is part of a broader 22-locale update.

Also applies to: 299-299, 394-394, 611-611

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

195-195: LGTM! Accurate Spanish translation for container alignment.

The translation "Alineación de contenedores" correctly conveys "container alignment" in Spanish and is properly placed within the fields section.


298-298: LGTM! Direction translations are accurate.

Both "bottom_direction" → "Abajo" and "top_direction" → "Arriba" are correct Spanish translations and follow the consistent naming pattern established by other direction options in the file (center_direction, left_direction, right_direction).

Also applies to: 393-393


610-610: LGTM! Proper translation for promotional banner.

The translation "Banner promocional" is accurate and appropriately placed alongside related promotional content keys.

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

196-196: LGTM! Translation addition aligns with promo variants feature.

The containerAlignment field addition with translation "容器对齐" (Container alignment) is properly structured and consistent with the new promo variants functionality being introduced across all locale files.


299-299: LGTM! Directional option translation is accurate.

The bottom_direction translation "底部" is correct and appropriately placed within the options block for directional controls.


394-394: LGTM! Directional option translation is accurate.

The top_direction translation "顶部" is correct and complements the bottom_direction option for directional controls in the promo variants feature.


611-611: LGTM! Promotional banner translation is appropriate.

The promoBanner field addition with translation "促销横幅" (Promotional banner) is properly structured and aligns with the new promotional banner functionality in the promo variants feature.

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

197-197: Recommend verification of Swedish translations by a localization expert.

The new translation keys follow the existing structural patterns in the file, which is good. However, please ensure these Swedish translations are reviewed by a native speaker or localization expert for accuracy:

  • containerAlignment: "Behållaruppriktning"
  • bottom_direction: "Nederst"
  • top_direction: "Topp"
  • promoBanner: "Kampanjbanner"

Note: The term "containerAlignment" uses "uppriktning" while the existing "alignment" key uses "inriktning". If this distinction is intentional (different types of alignment), that's fine—but worth confirming.

Since this is part of a coordinated update across 22 languages, consider running a similar verification pass for all locale files in this PR.

Also applies to: 300-300, 395-395, 612-612

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

197-197: LGTM! Translation addition looks correct.

The containerAlignment field translation is properly structured and the Polish translation "Wyrównanie kontenera" is appropriate for "Container alignment."


300-300: LGTM! Directional translation is appropriate.

The bottom_direction option translation "Dół" correctly represents "Bottom" in Polish and is consistent with other directional options in the file.


395-395: LGTM! Directional translation is appropriate.

The top_direction option translation "Góra" correctly represents "Top" in Polish and pairs well with the bottom_direction addition.


612-612: LGTM! Promotional banner translation is correct.

The promoBanner translation "Baner promocyjny" is the standard Polish term for promotional banner and is properly positioned in the root-level keys.

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

196-196: LGTM! Translations are grammatically correct.

The Norwegian translations for the new keys are accurate:

  • "containerAlignment" → "Beholderjustering" (container alignment)
  • "bottom_direction" → "Nederst" (bottommost)
  • "top_direction" → "Topp" (top)
  • "promoBanner" → "Kampanjebanner" (campaign banner)

Also applies to: 299-299, 394-394, 611-611

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

195-195: LGTM! Clean localization additions across all locale files.

The new localization keys are properly formatted, grammatically correct, and appropriately placed within the JSON structure. All four keys (containerAlignment, bottom_direction, top_direction, promoBanner) have been consistently added across all 25 locale files.

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

196-196: LGTM! Container alignment translation added correctly.

The Dutch translation "Uitlijning van containers" is appropriate and the key is correctly positioned alphabetically within the fields section.


299-299: LGTM! Bottom direction translation added correctly.

The Dutch translation "Onder" accurately represents "bottom" direction and is positioned appropriately within the options section.


394-394: LGTM! Top direction translation added correctly.

The Dutch translation "Boven" accurately represents "top" direction and maintains consistency with the bottom_direction key pattern.


611-611: LGTM! Promo banner translation added correctly.

The Dutch translation "Promobanner" follows standard Dutch compound word formation and is correctly positioned alphabetically at the root level.


196-196: Consistency across all 25 locale files verified. All new keys (containerAlignment, bottom_direction, top_direction, promoBanner) are present in every language file, and removed keys (imageOrientation, mediaOrientation) have been cleanly removed from all locales without exceptions.

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

196-196: Verify translation consistency across all locale files.

The AI summary indicates that 22+ locale files were updated with these same keys. Given the empty string issue found in bottom_direction for the Romanian locale, please verify that all other locale files have proper translations for all four new keys (containerAlignment, bottom_direction, top_direction, promoBanner).

#!/bin/bash
# Description: Check all locale files for empty or missing translations in the new keys

echo "=== Checking for empty bottom_direction values ==="
fd -e json . packages/visual-editor/locales/ --exec grep -l '"bottom_direction": ""'

echo -e "\n=== Checking all locale files have the new keys ==="
for key in "containerAlignment" "bottom_direction" "top_direction" "promoBanner"; do
  echo -e "\nKey: $key"
  fd -e json . packages/visual-editor/locales/ --exec sh -c 'file="{}"; if grep -q "\"'"$key"'\"" "$file"; then echo "✓ $file"; else echo "✗ MISSING: $file"; fi'
done

echo -e "\n=== Sample translations for bottom_direction across locales ==="
fd -e json . packages/visual-editor/locales/ --exec sh -c 'file="{}"; result=$(grep "bottom_direction" "$file" 2>/dev/null); if [ -n "$result" ]; then echo "$file: $result"; fi'

Also applies to: 299-299, 394-394, 611-611

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

196-196: Remove unused locale keys or implement their references.

The locale keys containerAlignment and promoBanner are properly referenced in the promo section components. However, bottom_direction and top_direction are added to the Latvian locale file but have no corresponding references in the codebase. Remove these unused keys or implement their usage in the component code if intended for future features.

starter/src/dev.config.ts (1)

446-449: Expression entry for c_emptyPromo is consistent and correct

Adding c_emptyPromo with fullObject: true alongside c_examplePromo matches existing patterns and aligns with the schema block below. I don’t see any issues here.

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

1-748: LGTM: JSON structure is valid and changes align with PR objectives.

The localization updates correctly support the new PromoSection variant architecture by:

  • Adding containerAlignment support for the new alignment controls
  • Introducing directional labels (bottom_direction, top_direction) for layout options
  • Adding the promoBanner label for the promotional banner feature
  • Removing legacy orientation keys (per the AI summary)

The JSON structure is well-formed and the changes are consistent with the broader refactoring effort across all 22 locales.


196-196: Fix inconsistent Lithuanian translation for directional options.

The Lithuanian locale has an inconsistency in the new directional translations:

  • "bottom" and "bottom_direction" both use "Apačia" (consistent)
  • "top" uses "Viršus" (masculine) while "top_direction" uses "Viršutinė" (feminine adjective)

This inconsistency is unique to Lithuanian—other locales either use the same translation for both (English, Spanish, French, etc.) or use distinct noun/directional forms consistently. The current mixed approach suggests a translation error. Update "top_direction" to use "Viršus" to match the pattern established by "bottom" and "bottom_direction", unless the feminine form is intentional for a specific UI context.

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

196-196: LGTM: containerAlignment translation is consistent.

The Japanese translation "コンテナの位置合わせ" for containerAlignment aligns well with other alignment-related translations in the file.


611-611: Inconsistent with AI summary: only one promoBanner instance found.

The AI summary states that promoBanner was "added in two separate root-level sections related to product areas, duplicating insertion points," but only one instance at line 611 is visible in the provided code. Please verify whether:

  1. The duplicate exists in another file not included in this review
  2. The summary is incorrect
  3. One insertion was missed during the changes
packages/visual-editor/src/components/contentBlocks/Video.tsx (4)

9-11: LGTM! Internal className prop for flexibility.

The optional className prop marked as @internal provides flexibility for customizing the video styling while maintaining a sensible default. The JSDoc annotation clearly indicates this is not part of the public API.


40-40: LGTM! Consistent dragRef on editing placeholder.

The placeholder div correctly attaches dragRef for drag-and-drop support in editing mode when no video is configured. The h-20 mt-8 classes provide appropriate placeholder dimensions and spacing.


51-51: The inline: true flag removes the component's wrapping element and is used to create advanced CSS layouts. When inline is true, you must specify which item is draggable via the puck.dragRef prop.

Verify that the Video component's render function properly applies puck.dragRef to the appropriate element. If inline: true is set but dragRef is not used in the render output, this creates a configuration error and the component will not be draggable. Check the Video component's render method to ensure dragRef is applied where needed for the intended layout behavior.


28-28: Layout behavior is correct—no changes needed.

The dragRef wrapper pattern with h-full w-full is the documented approach for Puck's drag-and-drop functionality. The wrapper creates the drag target area without affecting the VideoAtom's responsive layout constraints. Child width classes like lg:w-4/5 and mx-auto centering work as intended inside this wrapper.

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

197-197: LGTM - New translation key added correctly.

The containerAlignment key is properly structured and alphabetically placed. The Slovak translation "Zarovnanie kontajnera" appears appropriate for "Container alignment".


300-300: LGTM - Directional translation keys appropriately distinguish from positional terms.

The new bottom_direction ("Dole" = downward) and top_direction ("Hore" = upward) translations correctly differentiate from the existing positional terms bottom ("Spodná časť" = bottom part) and top ("Vrchol" = top/peak). This semantic distinction aligns with the PR's architectural shift from orientation-based to position/direction-based controls.

Also applies to: 395-395

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

1-748: Migration of deprecated orientation properties is complete and properly implemented.

Verification confirms that the imageOrientation field has been successfully removed from the HeroStyles type definition and properly migrated. The migration file 0017_hero_variants.ts correctly transforms legacy data by mapping imageOrientation to the new desktopImagePosition field. No production code depends on the removed field—only migration logic and test fixtures reference it. The mediaOrientation field was not found in the codebase.

Note: The Hungarian locale file contains only UI translation strings and does not include component configuration keys. The actual deprecation was handled in the TypeScript type system and migration logic.

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

195-195: LGTM - Consistent localization updates.

The French locale additions (containerAlignment, bottom_direction, top_direction, promoBanner) align with the broader promo variants refactor across all locales and support the new PromoSection data model.

Also applies to: 298-298, 393-393, 611-611

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

196-196: LGTM - Consistent localization updates.

The Estonian locale additions mirror the pattern across all locales in this PR and support the new PromoSection variant architecture.

Also applies to: 299-299, 394-394, 611-611

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

202-202: LGTM - Consistent localization updates.

The Danish locale additions are consistent with the promo variants refactor across all 22 languages.

Also applies to: 306-306, 401-401, 618-618

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

200-200: LGTM - Consistent localization updates.

The German locale additions align with the broader localization expansion for promo variants.

Also applies to: 304-304, 399-399, 616-616

starter/localData/dev-location-stream__en__d7f52f17aa2a321e60e8c1a8fae53637.json (1)

447-447: LGTM - Test data cleanup.

The HTML content simplification removes inline styling while preserving the semantic content, aligning with the cleaner promo rendering approach.

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

30-30: LGTM - Module reorganization.

The import path update reflects the PromoSection refactor into a dedicated directory structure, consistent with the new variant architecture (ClassicPromo, ImmersivePromo, SpotlightPromo, CompactPromo).

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

50-50: LGTM - Correct migration registration.

The promoVariants migration is properly imported and added to the registry, following the established pattern for extending the migration pipeline.

Based on learnings, the migration index allocation is appropriate.

Also applies to: 106-106

packages/visual-editor/src/components/pageSections/index.ts (1)

53-57: LGTM!

The export path update correctly reflects the new PromoSection folder structure with variant components.

packages/visual-editor/src/components/pageSections/heroVariants/HeroContent.tsx (1)

17-18: LGTM!

The conditional width logic correctly omits w-full for the compact variant, allowing proper two-column layout behavior while maintaining full width for other variants.

packages/visual-editor/src/components/pageSections/HeroSection.tsx (2)

541-541: LGTM!

Simplified className for compact variant image correctly removes the responsive width breakpoint, aligning with the updated compact layout behavior.


668-678: LGTM!

The type widening to (string | undefined)[] and the falsy path guard enable cleaner conditional field updates, particularly useful when paths are conditionally included based on component state.

packages/visual-editor/src/components/pageSections/PromoSection/CompactPromo.tsx (2)

31-52: Verify duplicate content rendering impact.

PromoContent is rendered twice: once in the mobile container (lines 38) and once in the desktop container (line 51), with visibility toggled via CSS. This is a common responsive pattern but may have implications:

  1. Accessibility: Screen readers may read content twice
  2. SEO: Duplicate content in DOM
  3. Performance: Slot components render twice

Consider using a single PromoContent instance with responsive styling, or verify this pattern is intentional and acceptable for the use case.


1-67: Well-structured responsive layout.

The component correctly implements the compact variant with conditional image positioning for desktop/mobile viewports. The use of themeManagerCn and promoContentParentCn helpers maintains consistency with other promo variants.

packages/visual-editor/src/components/pageSections/PromoSection/SpotlightPromo.tsx (1)

13-59: Component implementation looks correct.

The SpotlightPromo variant properly resolves the background image, applies container alignment, and renders PromoContent within a styled Background wrapper. The use of minHeight with styles.imageHeight provides appropriate sizing control.

packages/visual-editor/src/components/pageSections/PromoSection/ClassicPromo.tsx (1)

7-38: LGTM!

Clean implementation of the classic promo variant. The responsive visibility logic for image positioning is consistent with the CompactPromo pattern, and the component properly composes PageSection, PromoMedia, and PromoContent.

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

591-628: Well-implemented variant rendering with empty state handling.

The render function cleanly handles variant selection and provides appropriate empty state feedback in editor mode when mapped entity fields are empty. The pattern mirrors the HeroSection approach effectively.

packages/visual-editor/src/docs/ai/components.d.ts (1)

698-733: LGTM! Type definitions are well-structured and consistent with HeroSection patterns.

The new PromoStyles interface correctly mirrors the HeroStyles variant architecture. One minor observation: imageHeight is required here (number) while in HeroStyles (line 656) it's optional (number?). This appears intentional since the default is documented as 500px.

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

64-2636: Comprehensive test coverage for all PromoSection variants and migration paths.

Excellent test coverage including:

  • All four variants (classic, immersive, spotlight, compact)
  • Multiple migration versions (0, 5, 16, 24, 30, 48, 49)
  • Entity values, constant values, and mixed value scenarios
  • Empty state handling
  • Video and image media types
  • Accessibility testing with axe

The tests will help ensure the migration from the old orientation-based system to the new variant system works correctly.

packages/visual-editor/src/components/pageSections/PromoSection/ImmersivePromo.tsx (1)

13-56: Well-structured immersive variant implementation.

The component properly:

  • Resolves background images with locale awareness
  • Applies a semi-transparent dark overlay for text readability when an image is present
  • Falls back gracefully to backgroundColors.background1 when no image exists
  • Uses aria-label for accessibility
  • Respects the imageHeight style for minimum height

The pattern aligns well with the HeroSection immersive variant approach.

packages/visual-editor/src/docs/components.md (1)

682-699: Documentation accurately reflects the new PromoSection API.

The updated tables correctly document:

  • New data.backgroundImage field
  • New style props: variant, containerAlignment, desktopImagePosition, mobileImagePosition, imageHeight
  • Appropriate default values matching the type definitions
packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx (1)

34-91: LGTM on the responsive layout restructure.

The two-column layout implementation with conditional visibility classes correctly handles all combinations of desktopImagePosition and mobileImagePosition. The dynamic padding calculation using CSS variables ensures proper alignment with content width constraints.

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

196-196: LGTM! Translation appears correct.

The Turkish translation "Konteyner Hizalaması" for containerAlignment follows the established pattern and appears linguistically appropriate for the UI context.


299-299: LGTM! Consistent with existing translations.

The translation "Alt" for bottom_direction correctly matches the existing bottom translation, maintaining consistency within the locale file.

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

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/pageSections/HeroSection.test.tsx (1)

2377-2381: Fix inconsistent logging in post-interaction branch.

Line 2380 logs the full results object, while Line 2365 correctly logs results.violations. These should be consistent.

🔎 Proposed fix
         if (isBrandColorTest(props) && results.violations.length) {
           console.warn(
             `IGNORING axe violations for brand color test: ${name}`,
-            results
+            results.violations
           );
♻️ Duplicate comments (4)
packages/visual-editor/src/components/migrations/0049_promo_variants.ts (1)

7-24: Mutation of original props and potential null reference still present.

The issues flagged in the previous review have not been addressed:

  1. Line 9 mutates the original props object via delete props.styles?.orientation
  2. The shallow copy on line 11 means newProps.styles === props.styles, so assignments on lines 13-22 also mutate the original
  3. If styles is undefined, lines 13-22 will throw a runtime error
🔎 Proposed fix with proper immutability
 export const promoVariants: Migration = {
   PromoSection: {
     action: "updated",
     propTransformation: (props) => {
       const orientation = props.styles?.orientation;
-      delete props.styles?.orientation;
-
-      const newProps = { ...props };
-
-      newProps.styles.variant = "classic";
-      newProps.styles.desktopImagePosition = orientation;
-      newProps.styles.mobileImagePosition = "top";
-      newProps.styles.imageHeight = 500;
-      newProps.styles.containerAlignment = "left";
-      newProps.styles.backgroundImage = {
-        field: "",
-        constantValue: getRandomPlaceholderImage({ width: 1440, height: 900 }),
-        constantValueEnabled: true,
-      };
+      const { orientation: _, ...restStyles } = props.styles || {};
+
+      const newProps = {
+        ...props,
+        styles: {
+          ...restStyles,
+          variant: "classic",
+          desktopImagePosition: orientation,
+          mobileImagePosition: "top",
+          imageHeight: 500,
+          containerAlignment: "left",
+        },
+        data: {
+          ...props.data,
+          backgroundImage: {
+            field: "",
+            constantValue: getRandomPlaceholderImage({ width: 1440, height: 900 }),
+            constantValueEnabled: true,
+          },
+        },
+      };

       return newProps;
     },
   },
 };

Note: backgroundImage should likely be under data rather than styles based on the PromoData interface in PromoSection.tsx (line 49).

packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx (2)

430-468: Add explicit fallthrough annotations to silence linter errors.

The switch statement uses intentional fallthrough (compact → classic, immersive → spotlight), which triggers Biome lint errors. Add /* falls through */ comments to make the intent explicit.

🔎 Proposed fix using explicit fallthrough comments
     switch (data.props.styles.variant) {
       case "compact": {
         // compact should remove the same fields removed by classic
       }
+      /* falls through */
       case "classic": {
         fields = updateFields(
           // ...
         );
         break;
       }
       case "immersive": {
         fields = updateFields(
           // ...
         );
         // immersive should also remove the fields removed by spotlight
       }
+      /* falls through */
       case "spotlight": {

532-536: Use a ternary conditional to handle empty field values properly.

When data.props?.data?.promo.field is an empty string, the current code produces ".image" instead of an empty string. The || "" fallback doesn't work because ".image" is a truthy value.

This issue appears in both line 533 and line 585.

🔎 Proposed fix
-      field: data.props?.data?.promo.field + ".image" || "",
+      field: data.props?.data?.promo.field ? data.props?.data?.promo.field + ".image" : "",

Apply this fix to both occurrences (lines 533 and 585).

Also applies to: 584-588

packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx (1)

46-46: Duplicate ID issue persists.

Both CompactHeroImage instances use id={id + "-image"}, creating duplicate DOM IDs. This was previously flagged and the suggested fix (using unique suffixes like -left and -right) has not yet been applied.

Also applies to: 82-82

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 07eaf79 and 70ddb39.

⛔ Files ignored due to path filters (32)
  • 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/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [compact] version 17 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [compact] version 31 props with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [compact] version 31 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] [classic] version 49 props with constant values and image.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] [classic] version 49 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] [compact] version 49 with constant values and image.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] [compact] version 49 with constant values and video.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] [compact] version 49 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] [immersive] version 49 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] default props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] default props with empty document.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] version 0 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] version 16 props using entity values with old CTA structure.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] version 16 props with missing ctaType.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] version 16 props with old CTA structure.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] version 24 with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] version 30 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] version 48 props with translatable image and CTA.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] version 5 props with constant value.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] [classic] version 49 props with constant values and image.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] [compact] version 49 with constant values and image.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] [compact] version 49 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [classic] version 49 props with constant values and image.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [compact] version 49 with constant values and image.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [compact] version 49 with constant values and video.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [compact] version 49 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [immersive] version 49 with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [spotlight] version 49 with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[xl] [compact] version 49 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
📒 Files selected for processing (7)
  • packages/visual-editor/src/components/migrations/0049_promo_variants.ts
  • packages/visual-editor/src/components/pageSections/HeroSection.test.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/CompactPromo.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/PromoContent.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.test.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx
  • packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/visual-editor/src/components/pageSections/PromoSection/PromoContent.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/CompactPromo.tsx
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-12-15T20:59:25.571Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 943
File: packages/visual-editor/src/components/contentBlocks/image/Image.tsx:175-191
Timestamp: 2025-12-15T20:59:25.571Z
Learning: In the ImageWrapper component at packages/visual-editor/src/components/contentBlocks/image/Image.tsx, when an Image is wrapped in a MaybeLink, the aria-label should be omitted because the image's alt text serves as the accessible name for the link. Adding an aria-label would override the image's alt text.

Applied to files:

  • packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx
  • packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.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/pageSections/PromoSection/PromoSection.tsx
📚 Learning: 2025-12-12T20:17:37.033Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 946
File: packages/visual-editor/src/components/migrations/migrationRegistry.ts:47-48
Timestamp: 2025-12-12T20:17:37.033Z
Learning: In the yext/visual-editor repository, empty object placeholders (`{}`) in the migrationRegistry array are sometimes intentionally used to reserve migration slots for parallel PRs to prevent merge conflicts and maintain consistent migration indices.

Applied to files:

  • packages/visual-editor/src/components/migrations/0049_promo_variants.ts
📚 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/pageSections/heroVariants/CompactHero.tsx
📚 Learning: 2025-12-12T19:26:15.855Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 942
File: packages/visual-editor/src/utils/schema/schemaBlocks.ts:29-35
Timestamp: 2025-12-12T19:26:15.855Z
Learning: In packages/visual-editor/src/utils/schema/schemaBlocks.ts, when siteDomain is missing (non-live/preview environments), schema id fields should use relative URLs rather than bare fragments, since Google will only crawl production sites where siteDomain is populated.

Applied to files:

  • packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx
🧬 Code graph analysis (1)
packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx (2)
packages/visual-editor/src/components/atoms/index.ts (1)
  • Background (16-16)
packages/visual-editor/src/components/pageSections/heroVariants/HeroContent.tsx (2)
  • heroContentParentCn (12-19)
  • HeroContent (21-89)
🪛 Biome (2.1.2)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx

[error] 431-433: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)


[error] 446-453: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)

⏰ 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). (2)
  • GitHub Check: call_unit_test / unit_tests (20.x)
  • GitHub Check: semgrep/ci
🔇 Additional comments (12)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.test.tsx (5)

1-18: LGTM! Well-organized test imports.

The imports cover all necessary testing utilities, component exports, and Puck integration for comprehensive component testing.


20-62: Good test fixture with realistic data.

The promoData fixture provides comprehensive mock data including CTA, rich text description, image with multiple thumbnail sizes, and title - all necessary for thorough testing of the PromoSection variants.


2486-2500: Reasonable approach for brand color accessibility exceptions.

The allowlist pattern for brand-color background tests that may have accessibility violations is a pragmatic solution. Brand colors are user-defined and may not always meet contrast requirements.


2502-2593: Comprehensive test harness with proper migration and rendering flow.

The test suite correctly:

  • Applies migrations with version tracking
  • Resolves all data before rendering
  • Waits for images to load
  • Adds delay for video content
  • Performs screenshot matching and accessibility testing
  • Handles post-interaction states when defined

64-975: Extensive version coverage for migration testing.

The test cases cover versions 0, 5, 16, 24, 30, 48, and 49, ensuring migration paths are properly validated. Each version has appropriate prop structures reflecting the component's evolution.

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

1-35: LGTM! Clean imports with new variant components.

The imports are well-organized, bringing in the new variant components (ClassicPromo, SpotlightPromo, ImmersivePromo, CompactPromo) and supporting utilities.


36-89: Well-documented interface extensions for variant system.

The PromoData and PromoStyles interfaces are properly documented with JSDoc comments explaining each new field's purpose and default values. The variant types and position options are clearly defined.


125-129: Good type extraction for variant props.

PromoVariantProps provides a clean public type for the variant components to consume. The commented-out conditionalRender suggests future extensibility.


597-648: Clean variant routing with empty state handling.

The render function properly:

  • Routes to the appropriate variant component based on styles.variant
  • Detects mapped fields vs constant values
  • Shows empty state in editor mode when a mapped field resolves to empty
  • Wraps output in analytics scope and visibility wrapper

292-426: Comprehensive default props with proper slot configuration.

The defaultProps provide sensible defaults for all new fields (variant, positions, alignment, imageHeight) and the slot configurations are well-structured with appropriate initial values.

packages/visual-editor/src/components/pageSections/HeroSection.test.tsx (2)

2362-2366: LGTM - Improved axe violation handling.

The conditional check for results.violations.length reduces noise by only logging when violations exist, and logging results.violations instead of the full results object provides cleaner output.


2206-2206: Clarify the scope and intent of XL viewport testing across variants.

Only the compact variant includes includeXLViewport: true in the test suite. Since other variants (classic, immersive, spotlight) also have responsive styling properties (e.g., desktop positioning, mobile alignment), clarify whether:

  • XL viewport testing is intentionally limited to compact variant only, or
  • Other variants should also be tested at XL viewport sizes for consistent coverage.

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

♻️ Duplicate comments (2)
packages/visual-editor/src/components/pageSections/PromoSection/PromoEmptyState.tsx (1)

11-15: Fix critical hook ordering violation.

useTemplateMetadata() is called after a conditional early return, violating the Rules of Hooks. All hooks must be called unconditionally and in the same order on every render.

🔎 Proposed fix
 export const PromoEmptyState: React.FC<{
   isEditing: boolean;
   backgroundStyle: BackgroundStyle | undefined;
 }> = (props) => {
   const { isEditing, backgroundStyle } = props;
+  const templateMetadata = useTemplateMetadata();
 
   if (!isEditing) {
     return <></>;
   }
-
-  const templateMetadata = useTemplateMetadata();
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx (1)

429-467: Add explicit fallthrough annotations to silence linter errors.

The switch statement uses intentional fallthrough (compact → classic, immersive → spotlight), which is a valid pattern but triggers Biome lint errors. Add /* falls through */ comments to make the intent explicit.

🔎 Proposed fix using explicit fallthrough comments
     switch (data.props.styles.variant) {
       case "compact": {
         // compact should remove the same fields removed by classic
       }
+      /* falls through */
       case "classic": {
         fields = updateFields(
           fields,
           [
             "data.objectFields.backgroundImage",
             "styles.objectFields.imageHeight",
           ],
           undefined
         );

         break;
       }
       case "immersive": {
         fields = updateFields(
           fields,
           ["slots.ImageSlot", "styles.objectFields.backgroundColor"],
           undefined
         );
         // immersive should also remove the fields removed by spotlight
       }
+      /* falls through */
       case "spotlight": {
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70ddb39 and c00ae4b.

⛔ Files ignored due to path filters (1)
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [compact] version 49 with constant values and video.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
📒 Files selected for processing (12)
  • packages/visual-editor/locales/fi/visual-editor.json
  • packages/visual-editor/locales/ja/visual-editor.json
  • packages/visual-editor/locales/ro/visual-editor.json
  • packages/visual-editor/locales/sk/visual-editor.json
  • packages/visual-editor/src/components/migrations/0049_promo_variants.ts
  • packages/visual-editor/src/components/pageSections/PromoSection/ClassicPromo.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/ImmersivePromo.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/PromoContent.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/PromoEmptyState.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/PromoMedia.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/SpotlightPromo.tsx
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/visual-editor/src/components/pageSections/PromoSection/ClassicPromo.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/ImmersivePromo.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/PromoContent.tsx
  • packages/visual-editor/locales/ja/visual-editor.json
  • packages/visual-editor/locales/fi/visual-editor.json
  • packages/visual-editor/src/components/migrations/0049_promo_variants.ts
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx:38-41
Timestamp: 2025-12-23T16:30:37.478Z
Learning: In CompactHero and CompactPromo components, duplicate IDs on image elements are intentional: conditional visibility classes (hidden/sm:block/sm:hidden) ensure only one image renders at a time based on responsive breakpoints and desktopImagePosition/mobileImagePosition settings.
📚 Learning: 2025-12-15T20:59:25.571Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 943
File: packages/visual-editor/src/components/contentBlocks/image/Image.tsx:175-191
Timestamp: 2025-12-15T20:59:25.571Z
Learning: In the ImageWrapper component at packages/visual-editor/src/components/contentBlocks/image/Image.tsx, when an Image is wrapped in a MaybeLink, the aria-label should be omitted because the image's alt text serves as the accessible name for the link. Adding an aria-label would override the image's alt text.

Applied to files:

  • packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx
📚 Learning: 2025-12-23T16:30:37.478Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx:38-41
Timestamp: 2025-12-23T16:30:37.478Z
Learning: In CompactHero and CompactPromo components, duplicate IDs on image elements are intentional: conditional visibility classes (hidden/sm:block/sm:hidden) ensure only one image renders at a time based on responsive breakpoints and desktopImagePosition/mobileImagePosition settings.

Applied to files:

  • packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx
🧬 Code graph analysis (3)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx (6)
packages/visual-editor/src/components/pageSections/HeroSection.tsx (1)
  • updateFields (668-700)
packages/visual-editor/src/components/pageSections/PromoSection/ClassicPromo.tsx (1)
  • ClassicPromo (7-38)
packages/visual-editor/src/components/pageSections/PromoSection/ImmersivePromo.tsx (1)
  • ImmersivePromo (13-56)
packages/visual-editor/src/components/pageSections/PromoSection/SpotlightPromo.tsx (1)
  • SpotlightPromo (13-59)
packages/visual-editor/src/components/pageSections/PromoSection/CompactPromo.tsx (1)
  • CompactPromo (7-70)
packages/visual-editor/src/components/pageSections/PromoSection/PromoEmptyState.tsx (1)
  • PromoEmptyState (5-53)
packages/visual-editor/src/components/pageSections/PromoSection/PromoMedia.tsx (2)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx (2)
  • PromoData (36-50)
  • PromoStyles (52-89)
packages/visual-editor/src/components/pageSections/index.ts (2)
  • PromoData (55-55)
  • PromoStyles (56-56)
packages/visual-editor/src/components/pageSections/PromoSection/PromoEmptyState.tsx (1)
packages/visual-editor/src/internal/hooks/useMessageReceivers.ts (1)
  • useTemplateMetadata (169-169)
🪛 Biome (2.1.2)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx

[error] 430-432: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)


[error] 445-452: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)

packages/visual-editor/src/components/pageSections/PromoSection/PromoEmptyState.tsx

[error] 15-15: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

⏰ 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: semgrep/ci
  • GitHub Check: create-dev-release
🔇 Additional comments (6)
packages/visual-editor/locales/sk/visual-editor.json (3)

197-197: LGTM!

The translation "Zarovnanie kontajnera" is accurate Slovak for "Container alignment".


300-300: LGTM!

Directional translations are correct: "Dole" (down/below) and "Hore" (up/above) are appropriate Slovak terms for bottom and top directions respectively.

Also applies to: 395-395


612-613: LGTM!

The Slovak translations are now properly localized. "Propagačný banner" and "Propagačné médiá" are consistent and linguistically correct, using the Slovak adjective "propagačný" (promotional) rather than the English "promo". This addresses the previous review feedback about maintaining consistency with other locales.

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

196-196: LGTM!

The Romanian translation "Alinierea containerului" (Container alignment) is grammatically correct and consistent with other alignment-related translations in the file.


299-299: LGTM! Previous issue resolved.

The empty bottom_direction value has been correctly set to "Fund", making it consistent with the "bottom": "Fund" translation on line 298.


611-611: LGTM!

The Romanian translation "Banner promoțional" (Promotional banner) is appropriate and follows common usage in Romanian UI contexts.

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

♻️ Duplicate comments (2)
packages/visual-editor/locales/tr/visual-editor.json (1)

197-197: LGTM! Turkish translations are complete and properly structured.

All required localization keys are present with appropriate Turkish translations. Directional key pairs were previously reviewed.

Also applies to: 300-300, 395-395, 613-613

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

198-198: LGTM! Polish translations are properly localized.

All required keys are present with appropriate Polish translations: containerAlignment (Wyrównanie kontenera), bottom_direction (Dół), top_direction (Góra), and promoBanner (Baner promocyjny).

Also applies to: 301-301, 396-396, 614-614

🧹 Nitpick comments (2)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.test.tsx (2)

2497-2497: Improve type safety for the test helper parameter.

The any type reduces type safety and could mask issues if the props structure changes.

🔎 Proposed fix for type safety
-const isBrandColorTest = (props: any) => {
+const isBrandColorTest = (props: { styles?: { backgroundColor?: { bgColor?: string } } }) => {
   const bg = props?.styles?.backgroundColor?.bgColor;
   return typeof bg === "string" && BRAND_COLOR_BG_ALLOWLIST.has(bg);
 };

2559-2561: Consider extracting the video delay to a named constant.

The hard-coded 1000ms delay is a magic number that could benefit from documentation.

🔎 Proposed refactor

Add near the top of the file (after imports):

+// Allow time for video thumbnails and embeds to load before screenshot
+const VIDEO_LOAD_DELAY_MS = 1000;
+
 const promoData = {

Then update the delay:

       if (props?.data?.media === "video") {
-        await delay(1000);
+        await delay(VIDEO_LOAD_DELAY_MS);
       }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c00ae4b and 637c70f.

⛔ Files ignored due to path filters (58)
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] [classic] version 50 props with constant values and image.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] [classic] version 50 with constant values and video.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] [classic] version 50 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] [compact] version 50 with constant values and image.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] [compact] version 50 with constant values and video.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] [compact] version 50 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] [immersive] version 50 with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] [immersive] version 50 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] [spotlight] version 50 with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] [spotlight] version 50 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] version 0 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] version 16 props using entity values with old CTA structure.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[desktop] version 30 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] [classic] version 50 props with constant values and image.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] [classic] version 50 with constant values and video.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] [classic] version 50 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] [compact] version 50 with constant values and image.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] [compact] version 50 with constant values and video.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] [compact] version 50 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] [immersive] version 50 with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] [immersive] version 50 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] [spotlight] version 50 with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] [spotlight] version 50 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] default props with document data.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] default props with empty document.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] version 0 props with constant value.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] version 0 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] version 16 props using entity values with old CTA structure.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] version 16 props with missing ctaType.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] version 16 props with old CTA structure.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] version 24 with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] version 30 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] version 30 props with mixed values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] version 48 props with translatable image and CTA.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[mobile] version 5 props with constant value.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [classic] version 50 props with constant values and image.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [classic] version 50 with constant values and video.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [classic] version 50 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [compact] version 50 with constant values and image.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [compact] version 50 with constant values and video.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [compact] version 50 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [immersive] version 50 with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [immersive] version 50 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [spotlight] version 50 with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] [spotlight] version 50 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[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/PromoSection/[tablet] default props with empty document.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] version 0 props with constant value.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] version 0 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] version 16 props using entity values with old CTA structure.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] version 16 props with missing ctaType.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] version 16 props with old CTA structure.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] version 24 with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] version 30 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] version 30 props with mixed values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] version 48 props with translatable image and CTA.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[tablet] version 5 props with constant value.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/PromoSection/[xl] [compact] version 50 with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
📒 Files selected for processing (31)
  • packages/visual-editor/locales/cs/visual-editor.json
  • packages/visual-editor/locales/da/visual-editor.json
  • packages/visual-editor/locales/de/visual-editor.json
  • packages/visual-editor/locales/en-GB/visual-editor.json
  • packages/visual-editor/locales/en/visual-editor.json
  • packages/visual-editor/locales/es/visual-editor.json
  • packages/visual-editor/locales/et/visual-editor.json
  • packages/visual-editor/locales/fi/visual-editor.json
  • packages/visual-editor/locales/fr/visual-editor.json
  • packages/visual-editor/locales/hr/visual-editor.json
  • packages/visual-editor/locales/hu/visual-editor.json
  • packages/visual-editor/locales/it/visual-editor.json
  • packages/visual-editor/locales/ja/visual-editor.json
  • packages/visual-editor/locales/lt/visual-editor.json
  • packages/visual-editor/locales/lv/visual-editor.json
  • packages/visual-editor/locales/nb/visual-editor.json
  • packages/visual-editor/locales/nl/visual-editor.json
  • packages/visual-editor/locales/pl/visual-editor.json
  • packages/visual-editor/locales/pt/visual-editor.json
  • packages/visual-editor/locales/ro/visual-editor.json
  • packages/visual-editor/locales/sk/visual-editor.json
  • packages/visual-editor/locales/sv/visual-editor.json
  • packages/visual-editor/locales/tr/visual-editor.json
  • packages/visual-editor/locales/zh-TW/visual-editor.json
  • packages/visual-editor/locales/zh/visual-editor.json
  • packages/visual-editor/src/components/migrations/0050_promo_variants.ts
  • packages/visual-editor/src/components/migrations/migrationRegistry.ts
  • packages/visual-editor/src/components/pageSections/HeroSection.test.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.test.tsx
  • packages/visual-editor/src/docs/ai/components.d.ts
  • packages/visual-editor/src/docs/components.md
🚧 Files skipped from review as they are similar to previous changes (15)
  • packages/visual-editor/locales/de/visual-editor.json
  • packages/visual-editor/locales/lt/visual-editor.json
  • packages/visual-editor/locales/ja/visual-editor.json
  • packages/visual-editor/locales/zh/visual-editor.json
  • packages/visual-editor/locales/sv/visual-editor.json
  • packages/visual-editor/locales/cs/visual-editor.json
  • packages/visual-editor/src/components/pageSections/HeroSection.test.tsx
  • packages/visual-editor/locales/it/visual-editor.json
  • packages/visual-editor/locales/fr/visual-editor.json
  • packages/visual-editor/locales/lv/visual-editor.json
  • packages/visual-editor/locales/fi/visual-editor.json
  • packages/visual-editor/locales/nb/visual-editor.json
  • packages/visual-editor/locales/es/visual-editor.json
  • packages/visual-editor/locales/hu/visual-editor.json
  • packages/visual-editor/locales/hr/visual-editor.json
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-12-12T20:17:37.033Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 946
File: packages/visual-editor/src/components/migrations/migrationRegistry.ts:47-48
Timestamp: 2025-12-12T20:17:37.033Z
Learning: In the yext/visual-editor repository, empty object placeholders (`{}`) in the migrationRegistry array are sometimes intentionally used to reserve migration slots for parallel PRs to prevent merge conflicts and maintain consistent migration indices.

Applied to files:

  • packages/visual-editor/src/components/migrations/migrationRegistry.ts
  • packages/visual-editor/src/components/migrations/0050_promo_variants.ts
📚 Learning: 2025-12-23T16:36:32.810Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/locales/ro/visual-editor.json:394-394
Timestamp: 2025-12-23T16:36:32.810Z
Learning: In Romanian locale files (ro), "Top" is an acceptable translation for directional terms like "top_direction" rather than the native Romanian "Sus". The maintainer benlife5 confirmed this is the preferred translation in packages/visual-editor/locales/ro/visual-editor.json.

Applied to files:

  • packages/visual-editor/locales/ro/visual-editor.json
  • packages/visual-editor/locales/en-GB/visual-editor.json
  • packages/visual-editor/locales/en/visual-editor.json
  • packages/visual-editor/locales/zh-TW/visual-editor.json
  • packages/visual-editor/locales/da/visual-editor.json
  • packages/visual-editor/locales/pl/visual-editor.json
  • packages/visual-editor/locales/et/visual-editor.json
  • packages/visual-editor/locales/nl/visual-editor.json
  • packages/visual-editor/locales/tr/visual-editor.json
  • packages/visual-editor/locales/sk/visual-editor.json
  • packages/visual-editor/locales/pt/visual-editor.json
📚 Learning: 2025-12-23T16:30:37.478Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx:38-41
Timestamp: 2025-12-23T16:30:37.478Z
Learning: In CompactHero and CompactPromo components, duplicate IDs on image elements are intentional: conditional visibility classes (hidden/sm:block/sm:hidden) ensure only one image renders at a time based on responsive breakpoints and desktopImagePosition/mobileImagePosition settings.

Applied to files:

  • packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.test.tsx
  • packages/visual-editor/src/components/migrations/0050_promo_variants.ts
  • packages/visual-editor/src/docs/components.md
🧬 Code graph analysis (2)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.test.tsx (4)
packages/visual-editor/src/components/testing/componentTests.setup.ts (4)
  • ComponentTest (109-121)
  • transformTests (92-107)
  • delay (126-127)
  • axe (77-81)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx (1)
  • PromoSection (291-652)
packages/visual-editor/src/components/migrations/migrationRegistry.ts (1)
  • migrationRegistry (58-109)
packages/visual-editor/src/components/categories/SlotsCategory.tsx (1)
  • SlotsCategoryComponents (190-263)
packages/visual-editor/src/docs/ai/components.d.ts (3)
packages/visual-editor/src/editor/YextEntityFieldSelector.tsx (1)
  • YextEntityField (53-70)
packages/visual-editor/src/types/images.ts (1)
  • AssetImageType (57-63)
packages/visual-editor/src/utils/themeConfigOptions.ts (1)
  • BackgroundStyle (36-43)
⏰ 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). (2)
  • GitHub Check: call_unit_test / unit_tests (20.x)
  • GitHub Check: semgrep/ci
🔇 Additional comments (14)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.test.tsx (2)

1-62: LGTM!

The imports and test fixture are well-organized. The promoData fixture provides comprehensive test data with realistic promo information including multiple image thumbnails, which is good for testing responsive image handling.


64-2484: Comprehensive test coverage across migration versions and variants.

The test suite thoroughly covers:

  • Migration paths from versions 0 through 50
  • All four promo variants (classic, immersive, spotlight, compact) introduced in this PR
  • Both entity and constant values
  • Image and video media types
  • Various slot configurations and positioning options

The explicit test fixtures, while lengthy, make each test case self-contained and easier to debug. The includeXLViewport flag on line 1992 is a good addition for testing responsive behavior at larger viewports.

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

196-196: LGTM! Localization keys align with PromoSection variant architecture.

The additions of containerAlignment, bottom_direction, top_direction, and promoBanner are properly structured and provide clear English labels for the new variant-based PromoSection features described in the PR.

Also applies to: 299-299, 394-394, 612-612

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

197-197: LGTM! Traditional Chinese translations are properly localized.

All new keys are translated appropriately: containerAlignment (容器對齊), bottom_direction (底部), top_direction (頂部), and promoBanner (促銷橫幅).

Also applies to: 300-300, 396-396, 614-614

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

196-196: LGTM! British English localization matches standard conventions.

All new keys are properly added with translations consistent with British English conventions.

Also applies to: 299-299, 394-394, 612-612

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

203-203: LGTM! Danish translations are complete and properly structured.

All new localization keys are present with appropriate Danish translations: containerAlignment (Beholderjustering), bottom_direction (Nederst), top_direction (Top), and promoBanner (Promo banner).

Also applies to: 307-307, 402-402, 620-620

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

197-197: LGTM! Dutch translations are properly localized.

All new keys are present with appropriate Dutch translations: containerAlignment (Uitlijning van containers), bottom_direction (Onder), top_direction (Boven), and promoBanner (Promobanner).

Also applies to: 300-300, 395-395, 613-613

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

198-198: LGTM! Slovak translations are complete and properly localized.

All new keys are present with appropriate Slovak translations. The promoBanner translation (Propagačný banner) properly uses Slovak language as previously requested, and promoMedia is consistently translated as Propagačné médiá.

Also applies to: 301-301, 396-396, 614-615

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

197-197: LGTM!

The new locale keys (containerAlignment, bottom_direction, top_direction, promoBanner) are properly translated to Portuguese and align with the variant-based PromoSection architecture introduced in this PR.

Also applies to: 300-300, 395-395, 613-613

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

197-197: LGTM!

The new locale keys are properly translated to Estonian and consistent with the broader localization updates across the PR.

Also applies to: 300-300, 395-395, 613-613

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

51-51: LGTM!

The promoVariants migration is correctly imported and appended to the registry. The migration index (50) aligns with the file naming convention, and placement at the end ensures proper execution order.

Also applies to: 108-108

packages/visual-editor/src/docs/components.md (1)

669-707: LGTM!

The PromoSection documentation accurately reflects the new variant-driven architecture with:

  • Updated screenshot path for the new variant-based rendering
  • New data.backgroundImage field
  • New style props (variant, desktopImagePosition, mobileImagePosition, containerAlignment, imageHeight) replacing the previous orientation field
  • Clear descriptions and default values
packages/visual-editor/src/docs/ai/components.d.ts (1)

717-763: LGTM!

The type definitions properly reflect the new variant-driven PromoSection architecture:

  • PromoData now includes backgroundImage consistent with HeroData
  • PromoStyles has appropriate variant options and positioning fields with clear JSDoc defaults
  • Types align with the migration logic and documentation updates
packages/visual-editor/locales/ro/visual-editor.json (1)

197-197: LGTM!

The Romanian locale updates are correct. Based on learnings, "Top" is confirmed as an acceptable translation for top_direction in Romanian, and all other translations (containerAlignment, bottom_direction, promoBanner) are properly localized.

Also applies to: 300-300, 395-395, 613-613

@benlife5 benlife5 requested a review from asanehisa December 23, 2025 17:33
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: 1

♻️ Duplicate comments (2)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx (2)

212-212: Fix build failure: ThemeOptions.VERTICAL_POSITION does not exist.

Same issue as in HeroSection.tsx line 277. The pipeline fails because ThemeOptions.VERTICAL_POSITION is not defined. You need to either add this property to ThemeOptions or use inline options.

🔎 Proposed fix using inline options (temporary)
       mobileImagePosition: YextField(
         msg("fields.mobileImagePosition", "Mobile Image Position"),
         {
           type: "radio",
-          options: ThemeOptions.VERTICAL_POSITION,
+          options: [
+            {
+              label: msg("fields.options.top", "Top", {
+                context: "bottom_direction",
+              }),
+              value: "top",
+            },
+            {
+              label: msg("fields.options.bottom", "Bottom", {
+                context: "bottom_direction",
+              }),
+              value: "bottom",
+            },
+          ],
         }
       ),

402-441: Add explicit fallthrough annotations to silence linter errors.

The switch statement uses intentional fallthrough (compact → classic at lines 403-405, and immersive → spotlight at lines 418-425). Add /* falls through */ comments after the fallthrough cases to satisfy the linter and make the intent explicit for future maintainers.

🔎 Proposed fix
     switch (data.props.styles.variant) {
       case "compact": {
         // compact should remove the same fields removed by classic
       }
+      /* falls through */
       case "classic": {
         fields = updateFields(
           fields,
           [
             "data.objectFields.backgroundImage",
             "styles.objectFields.imageHeight",
           ],
           undefined
         );

         break;
       }
       case "immersive": {
         fields = updateFields(
           fields,
           ["slots.ImageSlot", "styles.objectFields.backgroundColor"],
           undefined
         );
         // immersive should also remove the fields removed by spotlight
       }
+      /* falls through */
       case "spotlight": {
         fields = updateFields(
           fields,
           [
             "styles.objectFields.mobileImagePosition",
             "styles.objectFields.desktopImagePosition",
             "data.objectFields.media",
             data.props.data.promo.constantValueEnabled
               ? undefined
               : "data.objectFields.backgroundImage",
           ],
           undefined
         );
         break;
       }
     }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb19ea4 and b036a41.

📒 Files selected for processing (3)
  • packages/visual-editor/src/components/pageSections/HeroSection.tsx
  • packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx
  • packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx:38-41
Timestamp: 2025-12-23T16:30:37.478Z
Learning: In CompactHero and CompactPromo components, duplicate IDs on image elements are intentional: conditional visibility classes (hidden/sm:block/sm:hidden) ensure only one image renders at a time based on responsive breakpoints and desktopImagePosition/mobileImagePosition settings.
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx:38-41
Timestamp: 2025-12-23T16:30:37.478Z
Learning: In CompactHero and CompactPromo components, the 2xl breakpoint applies unconditional left padding because once the container reaches max-width (1440px), the design requires centering with consistent left padding regardless of desktopImagePosition.
📚 Learning: 2025-12-15T20:59:25.571Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 943
File: packages/visual-editor/src/components/contentBlocks/image/Image.tsx:175-191
Timestamp: 2025-12-15T20:59:25.571Z
Learning: In the ImageWrapper component at packages/visual-editor/src/components/contentBlocks/image/Image.tsx, when an Image is wrapped in a MaybeLink, the aria-label should be omitted because the image's alt text serves as the accessible name for the link. Adding an aria-label would override the image's alt text.

Applied to files:

  • packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx
  • packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx
📚 Learning: 2025-12-12T20:17:37.033Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 946
File: packages/visual-editor/src/components/migrations/migrationRegistry.ts:47-48
Timestamp: 2025-12-12T20:17:37.033Z
Learning: In the yext/visual-editor repository, empty object placeholders (`{}`) in the migrationRegistry array are sometimes intentionally used to reserve migration slots for parallel PRs to prevent merge conflicts and maintain consistent migration indices.

Applied to files:

  • packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx
📚 Learning: 2025-12-23T16:30:37.478Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx:38-41
Timestamp: 2025-12-23T16:30:37.478Z
Learning: In CompactHero and CompactPromo components, duplicate IDs on image elements are intentional: conditional visibility classes (hidden/sm:block/sm:hidden) ensure only one image renders at a time based on responsive breakpoints and desktopImagePosition/mobileImagePosition settings.

Applied to files:

  • packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx
📚 Learning: 2025-12-23T16:30:37.478Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx:38-41
Timestamp: 2025-12-23T16:30:37.478Z
Learning: In hero variant components (e.g., CompactHero.tsx and CompactPromo.tsx), duplicate IDs on image elements may be intentional because visibility is controlled with responsive utility classes (hidden/sm:block/sm:hidden), ensuring only one image is rendered at a time based on breakpoints and desktopImagePosition/mobileImagePosition settings. When reviewing similar files, treat duplicate IDs as acceptable only if these visibility rules are clearly documented in code or accompanying comments, and verify accessibility (alt text, aria-labels) and that unique semantics are preserved where required. If a single element is conditionally rendered, prefer stable IDs or remove duplicates if not needed.

Applied to files:

  • packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx
📚 Learning: 2025-12-23T16:30:37.478Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx:38-41
Timestamp: 2025-12-23T16:30:37.478Z
Learning: Enforce that at the 2xl breakpoint (max-width: 1440px), CompactHero and related hero variant components apply a consistent left padding, making the container centered with the same left padding regardless of desktopImagePosition. Do not conditionally vary left padding based on image position; ensure styles/components reflect a fixed left padding to maintain symmetrical centering. Apply this guideline to CompactHero and CompactPromo (and other heroVariants in the same directory if they share the same layout rules); update CSS/inline styles accordingly and adjust tests to verify padding behavior at 2xl.

Applied to files:

  • packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx
📚 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/pageSections/heroVariants/CompactHero.tsx
📚 Learning: 2025-12-12T19:26:15.855Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 942
File: packages/visual-editor/src/utils/schema/schemaBlocks.ts:29-35
Timestamp: 2025-12-12T19:26:15.855Z
Learning: In packages/visual-editor/src/utils/schema/schemaBlocks.ts, when siteDomain is missing (non-live/preview environments), schema id fields should use relative URLs rather than bare fragments, since Google will only crawl production sites where siteDomain is populated.

Applied to files:

  • packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx
🧬 Code graph analysis (1)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx (7)
packages/visual-editor/src/types/images.ts (1)
  • AssetImageType (57-63)
packages/visual-editor/src/components/pageSections/HeroSection.tsx (1)
  • updateFields (660-692)
packages/visual-editor/src/components/pageSections/PromoSection/ClassicPromo.tsx (1)
  • ClassicPromo (7-38)
packages/visual-editor/src/components/pageSections/PromoSection/ImmersivePromo.tsx (1)
  • ImmersivePromo (13-56)
packages/visual-editor/src/components/pageSections/PromoSection/SpotlightPromo.tsx (1)
  • SpotlightPromo (13-59)
packages/visual-editor/src/components/pageSections/PromoSection/CompactPromo.tsx (1)
  • CompactPromo (7-70)
packages/visual-editor/src/components/pageSections/PromoSection/PromoEmptyState.tsx (1)
  • PromoEmptyState (5-53)
🪛 Biome (2.1.2)
packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx

[error] 403-405: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)


[error] 418-425: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)

🪛 GitHub Actions: Create Dev Release
packages/visual-editor/src/components/pageSections/HeroSection.tsx

[error] 277-277: TypeScript error TS2339: Property 'VERTICAL_POSITION' does not exist on type '{ HEADING_LEVEL: { label: string; value: HeadingLevel; }[]; TEXT_TRANSFORM: { label: MsgString; value: string; }[]; LETTER_SPACING: { label: string; value: string; }[]; BACKGROUND_COLOR: ComboboxOptionGroup[]; ... 12 more ...; MAX_WIDTH: { ...; }[]; }'. Command: pnpm run build

🪛 GitHub Actions: main
packages/visual-editor/src/components/pageSections/HeroSection.tsx

[error] 277-277: TS2339: Property 'VERTICAL_POSITION' does not exist on type '{ HEADING_LEVEL: { label: string; value: HeadingLevel; }[]; TEXT_TRANSFORM: { label: MsgString; value: string; }[]; LETTER_SPACING: { label: string; value: string; }[]; BACKGROUND_COLOR: ComboboxOptionGroup[]; ... 12 more ...; MAX_WIDTH: { ...; }[]; }'.

⏰ 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: semgrep/ci
🔇 Additional comments (8)
packages/visual-editor/src/components/pageSections/HeroSection.tsx (2)

533-533: Image width behavior updated for compact variant.

The className change from "w-full sm:w-fit h-full" to "w-full h-full" removes responsive width fitting on small screens. This aligns with the layout refactor in CompactHero.tsx where images now use sm:w-[50%]. Ensure this produces the intended visual result across breakpoints.


662-670: LGTM! Defensive improvement to updateFields.

The signature change to (string | undefined)[] with a guard to skip falsy paths is a good defensive programming practice. This allows callers to conditionally pass undefined in the paths array (as seen in PromoSection.tsx line 433-435) without breaking the function.

packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx (2)

34-92: LGTM! Well-structured responsive layout refactor.

The restructured layout with separate mobile and desktop containers is clean and follows good responsive design patterns. The conditional padding and visibility classes appropriately handle the different image positions across breakpoints.

Based on learnings: The duplicate IDs on image elements (lines 46 and 82) are intentional since visibility classes ensure only one renders at a time.


17-17: Image width updated for responsive layout.

The change to "sm:w-[50%]" sets the image to 50% width on small screens and up, which aligns with the new two-column desktop layout structure. This works well with the conditional w-2/4 on the desktop content container (line 73).

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

451-481: LGTM! Variant-specific slot configuration.

The variant-specific className updates for ImageSlot and VideoSlot are well-structured and properly scoped to each variant. The alignment application to DescriptionSlot is also appropriately handled.


504-510: LGTM! Background image field construction handles empty strings correctly.

The ternary conditional properly handles empty field values. When data.props?.data?.promo.field is empty, it returns an empty string instead of ".image". This was correctly addressed per past review comments.

Also applies to: 558-564


573-624: LGTM! Clean variant-based rendering with empty state handling.

The render function is well-structured with proper variant selection and empty state handling for mapped fields in editor mode. The logic appropriately checks if a mapped entity field is empty and displays the PromoEmptyState component to inform the user.


219-219: No action required. ThemeOptions.ALIGNMENT is properly defined in packages/visual-editor/src/utils/themeConfigOptions.ts (line 430) and is being used correctly in this file.

mkilpatrick and others added 7 commits December 30, 2025 14:34
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Created starter branch and new site with entity page group, DM, and
locator. Navigated to the page source of the live site for each page
group and verified that only the main page group page had the BCF.

Test account: https://dev.yext.com/s/1000163760/yextsites/66475/pagesets

---------

Co-authored-by: coltondemetriou <coltondemetriou@gmail.com>
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 (2)
packages/visual-editor/src/docs/components.md (2)

681-685: Minor: Inconsistent formatting in default values.

The default values have inconsistent formatting:

  • Line 683: Uses a period at the end ("Placeholder image.")
  • Line 685: Uses single quotes around the value ('image')

Consider standardizing the format across all default values in the documentation for consistency.


696-696: Clarify the imageHeight description.

The description "Image Height for the promo image with Immersive or Spotlight variant Minimum height: content height + Page Section Top/Bottom Padding 500px" is unclear:

  1. Missing punctuation/proper sentence structure
  2. The relationship of "500px" to the formula is ambiguous - is 500px the minimum value, or is it part of the calculation?

Consider revising to something like:

  • "Minimum image height for immersive or spotlight variants. Must be at least 500px or content height + padding, whichever is greater."
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b036a41 and 30a3149.

📒 Files selected for processing (3)
  • packages/visual-editor/src/docs/ai/components.d.ts
  • packages/visual-editor/src/docs/components.md
  • packages/visual-editor/src/utils/themeConfigOptions.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/visual-editor/src/docs/ai/components.d.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx:38-41
Timestamp: 2025-12-23T16:30:55.497Z
Learning: In CompactHero and CompactPromo components, duplicate IDs on image elements are intentional: conditional visibility classes (hidden/sm:block/sm:hidden) ensure only one image renders at a time based on responsive breakpoints and desktopImagePosition/mobileImagePosition settings.
📚 Learning: 2025-12-23T16:30:55.497Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx:38-41
Timestamp: 2025-12-23T16:30:55.497Z
Learning: In CompactHero and CompactPromo components, duplicate IDs on image elements are intentional: conditional visibility classes (hidden/sm:block/sm:hidden) ensure only one image renders at a time based on responsive breakpoints and desktopImagePosition/mobileImagePosition settings.

Applied to files:

  • packages/visual-editor/src/docs/components.md
🧬 Code graph analysis (1)
packages/visual-editor/src/utils/themeConfigOptions.ts (2)
packages/visual-editor/src/utils/i18n/platform.ts (1)
  • msg (104-104)
packages/visual-editor/src/utils/i18n/index.ts (1)
  • msg (5-5)
⏰ 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). (5)
  • GitHub Check: call_unit_test / unit_tests (22.x)
  • GitHub Check: call_unit_test / unit_tests (20.x)
  • GitHub Check: call_unit_test / unit_tests (24.x)
  • GitHub Check: create-dev-release
  • GitHub Check: semgrep/ci
🔇 Additional comments (3)
packages/visual-editor/src/utils/themeConfigOptions.ts (2)

352-361: LGTM! Clean implementation of vertical positioning options.

The new verticalPositionOptions follows the established pattern for option definitions and correctly uses i18n with appropriate context.


442-442: LGTM! ThemeOptions export properly updated.

The new VERTICAL_POSITION export integrates cleanly with the existing ThemeOptions structure.

packages/visual-editor/src/docs/components.md (1)

691-698: LGTM! Comprehensive documentation of new variant props.

The expanded style props table clearly documents the new variant-driven architecture with appropriate descriptions distinguishing which variants each prop applies to.

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/pageSections/heroVariants/CompactHero.tsx (1)

40-50: Duplicate ID pattern is acceptable; consider enhancing comment.

The duplicate id={id + "-image"} on both image instances (lines 42 and 89) is intentional and valid because visibility classes (hidden, sm:block, sm:hidden) ensure only one image is rendered at any viewport.

To aid future maintainers, consider expanding the comment to explicitly note this pattern:

{/* Desktop left image / Mobile top image - shares ID with right/bottom image; only one visible at a time */}

Based on learnings, this pattern is acceptable for CompactHero and CompactPromo when visibility is properly controlled.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 30a3149 and d21e2a4.

⛔ Files ignored due to path filters (7)
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] [compact] version 17 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] [compact] version 31 props with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[desktop] [compact] version 31 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [compact] version 17 props using constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [compact] version 31 props with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[tablet] [compact] version 31 props with entity values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
  • packages/visual-editor/src/components/testing/screenshots/HeroSection/[xl] [compact] version 31 props with constant values.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
📒 Files selected for processing (1)
  • packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-12-23T16:30:37.478Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx:38-41
Timestamp: 2025-12-23T16:30:37.478Z
Learning: In hero variant components (e.g., CompactHero.tsx and CompactPromo.tsx), duplicate IDs on image elements may be intentional because visibility is controlled with responsive utility classes (hidden/sm:block/sm:hidden), ensuring only one image is rendered at a time based on breakpoints and desktopImagePosition/mobileImagePosition settings. When reviewing similar files, treat duplicate IDs as acceptable only if these visibility rules are clearly documented in code or accompanying comments, and verify accessibility (alt text, aria-labels) and that unique semantics are preserved where required. If a single element is conditionally rendered, prefer stable IDs or remove duplicates if not needed.

Applied to files:

  • packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx
📚 Learning: 2025-12-23T16:30:37.478Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 958
File: packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx:38-41
Timestamp: 2025-12-23T16:30:37.478Z
Learning: Enforce that at the 2xl breakpoint (max-width: 1440px), CompactHero and related hero variant components apply a consistent left padding, making the container centered with the same left padding regardless of desktopImagePosition. Do not conditionally vary left padding based on image position; ensure styles/components reflect a fixed left padding to maintain symmetrical centering. Apply this guideline to CompactHero and CompactPromo (and other heroVariants in the same directory if they share the same layout rules); update CSS/inline styles accordingly and adjust tests to verify padding behavior at 2xl.

Applied to files:

  • packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx
📚 Learning: 2025-12-15T20:59:25.571Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 943
File: packages/visual-editor/src/components/contentBlocks/image/Image.tsx:175-191
Timestamp: 2025-12-15T20:59:25.571Z
Learning: In the ImageWrapper component at packages/visual-editor/src/components/contentBlocks/image/Image.tsx, when an Image is wrapped in a MaybeLink, the aria-label should be omitted because the image's alt text serves as the accessible name for the link. Adding an aria-label would override the image's alt text.

Applied to files:

  • packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx
📚 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/pageSections/heroVariants/CompactHero.tsx
📚 Learning: 2025-12-12T19:26:15.855Z
Learnt from: benlife5
Repo: yext/visual-editor PR: 942
File: packages/visual-editor/src/utils/schema/schemaBlocks.ts:29-35
Timestamp: 2025-12-12T19:26:15.855Z
Learning: In packages/visual-editor/src/utils/schema/schemaBlocks.ts, when siteDomain is missing (non-live/preview environments), schema id fields should use relative URLs rather than bare fragments, since Google will only crawl production sites where siteDomain is populated.

Applied to files:

  • packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx
⏰ 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). (5)
  • GitHub Check: call_unit_test / unit_tests (24.x)
  • GitHub Check: call_unit_test / unit_tests (20.x)
  • GitHub Check: call_unit_test / unit_tests (22.x)
  • GitHub Check: semgrep/ci
  • GitHub Check: create-dev-release
🔇 Additional comments (5)
packages/visual-editor/src/components/pageSections/heroVariants/CompactHero.tsx (5)

7-28: LGTM! Width adjustment supports responsive layout.

The change from "w-full" to "sm:w-[50%]" (line 17) correctly implements the responsive behavior where the image occupies 50% width on desktop viewports, aligning with the restructured hero layout.


34-39: LGTM! Clean background and container structure.

The Background wrapper (line 34) and outer container with max-w-[1440px] (line 37) provide a solid foundation for the responsive hero layout. The flex direction switching (flex-col sm:flex-row) correctly handles mobile stacking.


52-60: LGTM! Mobile container properly isolated.

The mobile content container (lines 53-60) is correctly hidden on desktop (sm:hidden) and has appropriate width constraints and padding for mobile viewports.


87-97: Duplicate ID pattern is acceptable here as well.

Similar to the first image instance, the duplicate id={id + "-image"} (line 89) is valid because conditional visibility classes ensure only one image renders at a time. The visibility logic (hidden sm:block when mobile image is top, sm:hidden when desktop image is left) correctly controls rendering.


62-85: Conditional margins don't match documented design requirement for consistent centering.

The desktop container uses conditional padding and margins that vary based on desktopImagePosition (lines 68-82):

  • When image is left: applies sm:pl-8 lg:pl-16 + marginRight
  • When image is right: applies sm:pr-8 lg:pr-16 + marginLeft

According to documented learnings, the design should apply consistent left padding at the max-width breakpoint (1440px) regardless of image position. The current implementation diverges from this requirement by making padding and margins conditional on image placement. Clarify whether this conditional approach is intentional or if consistent left-side padding should be restored across both image position scenarios.

@colton-demetriou
Copy link
Contributor

colton-demetriou commented Jan 2, 2026

iImmersive/spotlight hero images not rendering in platform for v1.0.4, as well as with the updates in this pr, TAL but should probably fix it in this pr as well

site: https://dev.yext.com/s/1000163760/yextsites/66477/branches/6116/editor#pageSetId=test-hero&locale=en&themeId=smoke-test&entityId=8958214

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

Labels

create-dev-release Triggers dev release workflow

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants