-
Notifications
You must be signed in to change notification settings - Fork 0
feat: empty states for promo and banner sections #874
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
colton-demetriou
commented
Nov 6, 2025
|
Warning: Component files have been updated but no migrations have been added. See https://github.com/yext/visual-editor/blob/main/packages/visual-editor/src/components/migrations/README.md for more information. |
WalkthroughAdds two new localization keys ( Sequence Diagram(s)sequenceDiagram
participant Editor as Editor (User)
participant Component as Banner / PromoSection
participant Doc as Document Context (useDocument)
participant Resolver as resolveYextEntityField
participant Meta as useTemplateMetadata
participant Locale as Localization
Editor->>Component: mount / render section
Component->>Resolver: resolve mapped field (if mapped)
Resolver-->>Component: rawValue
Component->>Component: compute isMappedField, isEmpty
alt mapped & empty & editor mode (puck.isEditing)
Component->>Meta: get entityType display name
Meta-->>Component: entityType
Component->>Locale: fetch emptyStateFieldEmpty & emptyStateSectionHidden
Locale-->>Component: localized strings
Component->>Editor: render empty-state placeholder (icon + messages)
else mapped & empty & not editing
Component->>Editor: render nothing
else not (mapped & empty)
Component->>Editor: render normal content
end
Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (27)
packages/visual-editor/locales/cs/visual-editor.json(1 hunks)packages/visual-editor/locales/da/visual-editor.json(1 hunks)packages/visual-editor/locales/de/visual-editor.json(1 hunks)packages/visual-editor/locales/en-GB/visual-editor.json(1 hunks)packages/visual-editor/locales/en/visual-editor.json(1 hunks)packages/visual-editor/locales/es/visual-editor.json(1 hunks)packages/visual-editor/locales/et/visual-editor.json(1 hunks)packages/visual-editor/locales/fi/visual-editor.json(1 hunks)packages/visual-editor/locales/fr/visual-editor.json(1 hunks)packages/visual-editor/locales/hr/visual-editor.json(1 hunks)packages/visual-editor/locales/hu/visual-editor.json(1 hunks)packages/visual-editor/locales/it/visual-editor.json(1 hunks)packages/visual-editor/locales/ja/visual-editor.json(1 hunks)packages/visual-editor/locales/lt/visual-editor.json(1 hunks)packages/visual-editor/locales/lv/visual-editor.json(1 hunks)packages/visual-editor/locales/nb/visual-editor.json(1 hunks)packages/visual-editor/locales/nl/visual-editor.json(1 hunks)packages/visual-editor/locales/pl/visual-editor.json(1 hunks)packages/visual-editor/locales/pt/visual-editor.json(1 hunks)packages/visual-editor/locales/ro/visual-editor.json(1 hunks)packages/visual-editor/locales/sk/visual-editor.json(1 hunks)packages/visual-editor/locales/sv/visual-editor.json(1 hunks)packages/visual-editor/locales/tr/visual-editor.json(1 hunks)packages/visual-editor/locales/zh-TW/visual-editor.json(1 hunks)packages/visual-editor/locales/zh/visual-editor.json(1 hunks)packages/visual-editor/src/components/pageSections/Banner.tsx(3 hunks)packages/visual-editor/src/components/pageSections/PromoSection.tsx(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/visual-editor/src/components/pageSections/PromoSection.tsx (3)
packages/visual-editor/src/utils/resolveYextEntityField.ts (1)
resolveYextEntityField(5-37)packages/visual-editor/src/internal/hooks/useMessageReceivers.ts (1)
TemplateMetadataContext(169-169)packages/visual-editor/src/utils/themeConfigOptions.ts (1)
backgroundColors(50-125)
🪛 Biome (2.1.2)
packages/visual-editor/src/components/pageSections/PromoSection.tsx
[error] 240-240: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
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/Banner.tsx
[error] 135-135: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
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). (3)
- GitHub Check: call_unit_test / unit_tests (18.x)
- GitHub Check: call_unit_test / unit_tests (20.x)
- GitHub Check: semgrep/ci
🔇 Additional comments (24)
packages/visual-editor/locales/sv/visual-editor.json (1)
158-159: Localization keys look good.Both new Swedish translations are grammatically correct, use consistent placeholder syntax (
{{entityType}}), and follow the existing alphabetical ordering in the file.packages/visual-editor/locales/ro/visual-editor.json (1)
157-158: Localization keys properly added with correct formatting.The two new keys follow the established patterns in the file: camelCase naming, proper JSON structure, and consistent use of the
{{entityType}}template variable (aligned with similar templated keys like{{fieldLabel}}on line 27). The Romanian translations appear semantically sound for empty-state messaging.packages/visual-editor/locales/lv/visual-editor.json (1)
157-158: ---All 25 locale files successfully include the new empty-state keys.
The verification script confirms that both
emptyStateFieldEmptyandemptyStateSectionHiddenare present in all locale files. No missing keys were detected. The Latvian translations are properly formatted with consistent template variables and correct alphabetical ordering.packages/visual-editor/locales/en/visual-editor.json (1)
156-157: Consistent localization additions for empty-state messaging.The new keys are well-positioned, properly formatted, and follow established naming conventions. Templates use appropriate placeholder syntax and convey clear messaging for editor UI.
packages/visual-editor/locales/sk/visual-editor.json (1)
158-159: Translation quality and consistency verified.Slovak translations properly convey the intended meaning and maintain consistency with the localization pattern across other locales. Formatting and JSON structure are correct.
packages/visual-editor/locales/de/visual-editor.json (1)
156-157: German translations properly localized.Translations correctly apply German grammar conventions and maintain semantic alignment with source text. Structure and formatting consistent with other locale files.
packages/visual-editor/locales/nl/visual-editor.json (1)
157-158: Dutch locale additions follow established pattern.Translations are fluent and idiomatic. Structure and formatting maintain consistency with other locales in this PR.
packages/visual-editor/locales/fr/visual-editor.json (1)
156-157: French translations properly contextualized.Grammar and phrasing are correct for French. Translations maintain semantic equivalence and follow the consistent structure used across all locale additions.
packages/visual-editor/locales/cs/visual-editor.json (1)
157-158: Czech translations follow established localization pattern.Translations are grammatically correct and semantically appropriate. Structure and formatting maintain consistency with other locale files in this PR.
packages/visual-editor/locales/pl/visual-editor.json (1)
158-159: Localization keys are properly integrated and consumed by components.The two new Polish translation keys follow the file's naming conventions and placeholder patterns (matching existing
{{fieldLabel}}and{{number}}patterns). JSON structure is valid with no syntax issues. Verification confirms bothemptyStateFieldEmptyandemptyStateSectionHiddenare actively referenced in Banner.tsx and PromoSection.tsx components where they are expected.packages/visual-editor/locales/lt/visual-editor.json (1)
157-158: Localization keys are properly integrated.The new keys
emptyStateFieldEmptyandemptyStateSectionHiddenare actively referenced by both Banner.tsx and PromoSection.tsx components with the correct{{entityType}}placeholder variable, which the components properly populate based on context.packages/visual-editor/locales/en-GB/visual-editor.json (1)
156-157: Localization entries are properly formatted and correctly wired.Both new keys are actively referenced in the Banner and PromoSection components (
packages/visual-editor/src/components/pageSections/Banner.tsxlines 149 and 160;PromoSection.tsxlines 253 and 264). The keys use standard i18next interpolation syntax ({{entityType}}), follow camelCase naming conventions consistent with existing entries, and the fallback strings in components match the JSON localization file exactly. No issues found.packages/visual-editor/locales/it/visual-editor.json (1)
157-158: Localization additions verified and approved.The new keys are correctly implemented across the entire codebase:
- Component integration confirmed: Both keys (
emptyStateFieldEmpty,emptyStateSectionHidden) are actively referenced in Banner.tsx and PromoSection.tsx using thept()translation function with proper fallback strings.- Cross-locale consistency verified: All 25 locale files (nb, zh, tr, zh-TW, sv, sk, ro, pt, pl, nl, da, lv, lt, ja, it, hu, hr, et, fi, fr, en-GB, es, en, cs, de) have been updated with these keys at consistent line positions.
- Template variable consistency confirmed: All translations use the
{{entityType}}placeholder uniformly.- Italian translations: Grammatically correct and properly formatted within the JSON structure.
packages/visual-editor/locales/hr/visual-editor.json (1)
157-158: JSON structure, locale consistency, and component integration all verified.The two new localization keys are correctly positioned, syntactically valid, and properly integrated:
- ✅ Locale consistency: Both keys confirmed present in Croatian (hr), English (en), German (de), and French (fr) locales
- ✅ Component integration: Banner.tsx and PromoSection.tsx correctly use the keys with proper
{{entityType}}placeholder interpolation, extracting the actual entity type fromTemplateMetadataContextwith sensible fallbacks ("page" and "Entity")- ✅ Runtime safety: Placeholder substitution is handled correctly; no translation errors expected
packages/visual-editor/locales/fi/visual-editor.json (1)
157-158: Localization keys consistently deployed across all 25 language locales.Both
emptyStateFieldEmptyandemptyStateSectionHiddenkeys are present in all 25 locale files with proper JSON syntax, camelCase naming, and correct template variable formatting ({{entityType}}). Verification confirms complete consistency across the entire localization suite—no locales are missing either key.packages/visual-editor/locales/zh/visual-editor.json (1)
157-158: JSON formatting and localization key additions are correct and actively used.Both new keys follow proper JSON syntax, are placed in alphabetical order, and use consistent template-variable patterns (
{{entityType}}). The keys are confirmed to be actively referenced in the Banner and PromoSection components as expected:
emptyStateFieldEmptyused inPromoSection.tsx:264andBanner.tsx:160emptyStateSectionHiddenused inPromoSection.tsx:253andBanner.tsx:149packages/visual-editor/locales/es/visual-editor.json (1)
156-157: ✓ Spanish locale additions approved.The two empty-state keys are correctly placed, properly formatted, and use appropriate Spanish translations with consistent {{entityType}} placeholder usage.
packages/visual-editor/locales/tr/visual-editor.json (1)
157-158: ✓ Turkish locale additions approved.Both keys are correctly positioned and use proper Turkish grammar with the possessive marker. Placeholder variable is consistently applied.
packages/visual-editor/locales/pt/visual-editor.json (1)
157-158: ✓ Portuguese locale additions approved.Correctly positioned entries with idiomatic Portuguese translations using appropriate articles and prepositions.
packages/visual-editor/locales/et/visual-editor.json (1)
157-158: ✓ Estonian locale additions approved.Entries correctly formatted with appropriate Estonian grammatical structure and consistent placeholder usage.
packages/visual-editor/locales/hu/visual-editor.json (1)
157-158: ✓ Hungarian locale additions approved.Entries correctly handle Hungarian vowel harmony conventions using A(z) notation and maintain consistent {{entityType}} placeholder usage.
packages/visual-editor/locales/ja/visual-editor.json (1)
157-158: ✓ Japanese locale additions approved.Entries correctly formatted with appropriate Japanese particles and grammar; placeholder usage is consistent.
packages/visual-editor/locales/da/visual-editor.json (1)
158-159: ✓ Danish locale additions approved.Correctly positioned with idiomatic Danish translations and consistent placeholder usage.
packages/visual-editor/locales/zh-TW/visual-editor.json (1)
157-158: ✓ Traditional Chinese locale additions approved.Both entries use correct Traditional Chinese grammar with appropriate possessive markers (的) and consistent {{entityType}} placeholder usage.
There was a problem hiding this 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
🧹 Nitpick comments (1)
packages/visual-editor/src/components/pageSections/Banner.tsx (1)
134-176: Empty state implementation is correct, but consider extracting shared UI.The editor placeholder logic correctly handles the empty mapped field case. However, the empty state UI is nearly identical to
PromoSection.tsx(lines 239-280), differing only in layout and dimensions.Consider extracting a shared
EmptyStateMessagecomponent to reduce duplication:// packages/visual-editor/src/components/atoms/EmptyStateMessage.tsx interface EmptyStateMessageProps { entityTypeDisplayName?: string; layout?: "horizontal" | "vertical"; height?: string; } export const EmptyStateMessage: React.FC<EmptyStateMessageProps> = ({ entityTypeDisplayName, layout = "vertical", height = "h-[300px]", }) => { const flexDirection = layout === "horizontal" ? "flex-row" : "flex-col"; return ( <div className={`relative ${height} w-full bg-gray-100 rounded-lg border border-gray-200 flex ${flexDirection} items-center justify-center gap-${layout === "horizontal" ? "3 px-4" : "2.5 py-8"}`}> <CircleSlash2 className={`${layout === "horizontal" ? "w-10 h-10" : "w-12 h-12"} text-gray-400 ${layout === "horizontal" ? "flex-shrink-0" : ""}`} /> <div className={`flex flex-col items-${layout === "horizontal" ? "start" : "center"} gap-0`}> {/* message bodies */} </div> </div> ); };Then use in both components:
<PageSection background={...} className="flex items-center justify-center"> <EmptyStateMessage entityTypeDisplayName={entityTypeDisplayName} layout={/* "horizontal" for Banner, "vertical" for PromoSection */} height={/* "h-20" for Banner, "h-[300px]" for PromoSection */} /> </PageSection>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/visual-editor/src/components/pageSections/Banner.tsx(2 hunks)packages/visual-editor/src/components/pageSections/PromoSection.tsx(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/visual-editor/src/components/pageSections/PromoSection.tsx (5)
packages/visual-editor/src/hooks/useDocument.tsx (1)
useDocument(31-31)packages/visual-editor/src/internal/hooks/useMessageReceivers.ts (1)
useTemplateMetadata(169-169)packages/visual-editor/src/utils/resolveYextEntityField.ts (1)
resolveYextEntityField(5-37)packages/visual-editor/src/components/atoms/index.ts (2)
PageSection(14-14)Body(1-1)packages/visual-editor/src/utils/themeConfigOptions.ts (1)
backgroundColors(50-125)
packages/visual-editor/src/components/pageSections/Banner.tsx (2)
packages/visual-editor/src/utils/resolveComponentData.tsx (1)
resolveComponentData(52-99)packages/visual-editor/src/utils/themeConfigOptions.ts (1)
backgroundColors(50-125)
🔇 Additional comments (4)
packages/visual-editor/src/components/pageSections/PromoSection.tsx (3)
34-40: Hook ordering issue resolved.The previous review flagged a conditional
useContextcall. This has been correctly fixed—useTemplateMetadata()is now called unconditionally at the top level (line 227) before any conditional logic, ensuring React's hook ordering guarantees are preserved.Also applies to: 222-227
230-236: Verify empty detection for structured promo objects.The
isEmptycheck usesObject.keys().length === 0, which only detects completely empty objects ({}). APromoSectionTypeobject with empty field values (e.g.,{title: "", description: {}, cta: {}, image: {}}) would still have keys and pass as non-empty, potentially rendering blank content instead of the empty state.Consider enhancing the check to validate content within the resolved promo structure:
const isEmpty = isMappedField && - (!resolvedPromo || Object.keys(resolvedPromo || {}).length === 0); + (!resolvedPromo || + Object.keys(resolvedPromo || {}).length === 0 || + !("title" in resolvedPromo) || + (!resolvedPromo.title && !resolvedPromo.description && !resolvedPromo.cta && !resolvedPromo.image));Or verify that the backend/resolver always returns
null/undefinedfor truly empty promos rather than objects with empty fields.
239-280: Empty state rendering looks good.The editor-only placeholder correctly provides clear feedback when a mapped field is empty, with appropriate localization and entity-type context. The empty fragment return for non-editing mode is the right choice.
packages/visual-editor/src/components/pageSections/Banner.tsx (1)
14-22: Hook ordering issue resolved.The conditional
useContextcall flagged in the previous review has been properly fixed.useTemplateMetadata()is now invoked unconditionally at the component's top level (line 120), ensuring consistent hook execution order across all renders.Also applies to: 111-120
a6e7212
There was a problem hiding this 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
🧹 Nitpick comments (1)
packages/visual-editor/src/components/pageSections/Banner.tsx (1)
162-197: Consider using theme system for empty-state colors.The empty-state UI uses hardcoded Tailwind classes (
bg-gray-100,text-gray-400, etc.) andbackgroundColors.background1.value, while the rest of the component usesstyles.backgroundColorfrom the theme system. This creates visual inconsistency—the placeholder won't match the banner's configured background.For consistency, consider:
<PageSection - background={backgroundColors.background1.value} + background={styles.backgroundColor} verticalPadding="sm" className="flex items-center justify-center" >And if theme-compatible grays exist (e.g.,
text-muted,bg-surface-secondary), replace the hardcoded grays to align with the design system.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/visual-editor/src/components/pageSections/Banner.tsx(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/visual-editor/src/components/pageSections/Banner.tsx (2)
packages/visual-editor/src/internal/hooks/useMessageReceivers.ts (1)
useTemplateMetadata(169-169)packages/visual-editor/src/utils/resolveYextEntityField.ts (1)
resolveYextEntityField(5-37)
🔇 Additional comments (3)
packages/visual-editor/src/components/pageSections/Banner.tsx (3)
141-141: Hook now called unconditionally—addresses past critical issue.Moving
useTemplateMetadata()to the top level ensures the hook executes in the same order on every render, regardless of editor mode. This correctly resolves the previous concern about conditional hook calls breaking React's rules.
143-147: Empty detection correctly checks raw value before wrapping.The implementation resolves
rawValueviaresolveYextEntityField(line 145) and checks it for emptiness (line 147) beforeresolveComponentDatawraps it into a React element (line 149). This approach correctly detects empty RTF content before any MaybeRTF wrapping occurs, addressing the concern from the previous review about wrapped elements bypassing the empty check.
158-200: Empty-state logic correctly handles editor vs. live rendering.The implementation appropriately differentiates behavior:
- In editor mode with an empty mapped field: renders an informative placeholder with entity-type context
- On the live page with an empty mapped field: renders nothing (empty fragment)
- With populated or constant-value fields: proceeds to normal rendering
This pattern aligns with the PromoSection implementation and provides clear feedback to content editors.
| function isRichTextEmpty(value: any): boolean { | ||
| if (!value) { | ||
| return true; | ||
| } | ||
| if (typeof value === "string") { | ||
| return value.trim() === ""; | ||
| } | ||
| if (typeof value === "object") { | ||
| if ("html" in value) { | ||
| return !value.html || value.html.trim() === ""; | ||
| } | ||
| if ("json" in value) { | ||
| return ( | ||
| !value.json || | ||
| (typeof value.json === "string" && value.json.trim() === "") | ||
| ); | ||
| } | ||
| } | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider handling object-type JSON in rich text emptiness check.
The isRichTextEmpty function checks value.json only when it's a string (line 126). If json is an object—common with Lexical or Draft.js structures—an empty structure like {root: {children: []}} would bypass this check and potentially render an empty styled container instead of the empty-state placeholder.
Consider adding a branch to handle object-type JSON:
if ("json" in value) {
- return (
- !value.json ||
- (typeof value.json === "string" && value.json.trim() === "")
- );
+ if (!value.json) return true;
+ if (typeof value.json === "string") return value.json.trim() === "";
+ if (typeof value.json === "object") {
+ // Check for empty Lexical/Draft.js structure
+ const jsonStr = JSON.stringify(value.json);
+ return jsonStr === "{}" || jsonStr === '{"root":{"children":[]}}' || jsonStr.length < 30;
+ }
+ return false;
}Adjust the object-emptiness heuristic to match your actual RTF structure.
🤖 Prompt for AI Agents
In packages/visual-editor/src/components/pageSections/Banner.tsx around lines
112 to 131, the isRichTextEmpty helper only treats value.json as a string and
will miss empty JSON-rich-text objects (e.g., {root:{children:[]}}); update the
function to detect object-type JSON by adding a branch that checks for empty
structures — for example, if value.json is an object, treat it as empty when it
has no meaningful nodes/children (empty arrays or only empty text nodes) or when
its root children array is empty; implement a small safe traversal/heuristic
(check for root/children length, recursively ensure non-whitespace text nodes)
and return true when no real content is found.
759c0ef
into
fall-2025-slot-ify-components