feat: support full preview in local-editor#1224
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (3)
WalkthroughThis PR adds client-side preview functionality to the local editor. It introduces utilities in selection.ts for generating preview URLs and reconstructing layout state from compressed localStorage history, adds an "Open Preview" button to the editor shell that opens previews in a new tab, and implements a Preview component in the page template that fetches, migrates, and renders preview data. Sequence Diagram(s)sequenceDiagram
participant User
participant LocalEditorShell
participant buildLocalEditorPreviewUrl
participant Browser
participant PreviewComponent
User->>LocalEditorShell: Click "Open Preview"
LocalEditorShell->>buildLocalEditorPreviewUrl: Build URL (origin, templateId, entityId, locale)
buildLocalEditorPreviewUrl-->>LocalEditorShell: Preview URL
LocalEditorShell->>Browser: window.open(preview URL, noopener,noreferrer)
Browser->>PreviewComponent: Request /edit/<templateId>?preview=1&previewStorageKey=...
PreviewComponent->>PreviewComponent: readLocalEditorPreviewLayoutData -> migrate -> resolveAllData
PreviewComponent-->>Browser: Render preview
Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 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/vite-plugin/templates/edit.tsx (1)
196-233:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winRules of Hooks violation: conditional hook calls after early returns will crash
Edit.
usePlatformBridgeDocument()andusePlatformBridgeEntityFields()are called after theif (!isClientReady) return null;andif (isPreviewMode) return <Preview />;early returns. The number of hooks invoked therefore changes across renders (first render: onlyuseState/useEffect; later renders: also the bridge hooks; preview mode: never), which React rejects with "Rendered more hooks than during the previous render." Additionally,Editis typed() => JSX.Elementbutreturn nullis not assignable toJSX.Element.Extract the non-preview branch into its own component so each component calls a stable set of hooks, and widen the return type.
Based on learnings: "Do not use React hooks inside arbitrary callback functions. Hooks must be called only at the top level of React function components or custom hooks."🐛 Proposed fix
-const Edit: () => JSX.Element = () => { +const EditorView = () => { + const entityDocument = usePlatformBridgeDocument(); + const entityFields = usePlatformBridgeEntityFields(); + + return ( + <VisualEditorProvider + templateProps={{ + document: entityDocument, + }} + entityFields={entityFields} + tailwindConfig={tailwindConfig} + > + <Editor + document={entityDocument} + componentRegistry={componentRegistry} + themeConfig={defaultThemeConfig} + /> + </VisualEditorProvider> + ); +}; + +const Edit: () => JSX.Element | null = () => { // Wait for URLSearchParams to be available const [isClientReady, setIsClientReady] = React.useState(false); React.useEffect(() => { setIsClientReady(true); }, []); if (!isClientReady) { return null; } const searchParams = new URLSearchParams(window.location.search); const isPreviewMode = searchParams.get(previewModeParam) === "1"; if (isPreviewMode) { return <Preview />; } - const entityDocument = usePlatformBridgeDocument(); - const entityFields = usePlatformBridgeEntityFields(); - - return ( - <VisualEditorProvider - templateProps={{ - document: entityDocument, - }} - entityFields={entityFields} - tailwindConfig={tailwindConfig} - > - <Editor - document={entityDocument} - componentRegistry={componentRegistry} - themeConfig={defaultThemeConfig} - /> - </VisualEditorProvider> - ); + return <EditorView />; };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/visual-editor/src/vite-plugin/templates/edit.tsx` around lines 196 - 233, The Edit component violates Rules of Hooks by calling usePlatformBridgeDocument() and usePlatformBridgeEntityFields() conditionally; extract the non-preview rendering into a new component (e.g., EditInner or VisualEditorInner) that contains the hooks and returns the VisualEditorProvider + Editor tree, and have Edit only handle client-ready and preview branching to return either <Preview /> or <EditInner />; also widen Edit's return type to allow null (e.g., JSX.Element | null) so the initial client-ready null return is valid. Ensure the new component owns the hooks (usePlatformBridgeDocument, usePlatformBridgeEntityFields) and the original names VisualEditorProvider and Editor are used unchanged when rendering.
🧹 Nitpick comments (1)
packages/visual-editor/src/vite-plugin/templates/edit.tsx (1)
237-320: ⚖️ Poor tradeoffAvoid duplicating preview layout helpers in the autogenerated template
packages/visual-editor/src/vite-plugin/templates/edit.tsxinlinesbuildLocalEditorDocumentRequestPath/readLocalEditorPreviewLayoutData/readDocumentLayoutData, duplicating logic frompackages/visual-editor/src/local-editor/selection.ts. However, these aren’t byte-for-byte copies (e.g.,readDocumentLayoutDatausescreateEmptyLayoutData()inselection.ts, while the template returns the object inline), and only the first two helpers are exported fromselection.ts—readDocumentLayoutDatais private.Because
src/index.tsdoesn’t re-export these helpers, the template can’t simply import them from@yext/visual-editor; consider re-exporting the needed helpers (or refactoring template generation to share the implementation) so the storage-key/decompression contract can’t drift.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/visual-editor/src/vite-plugin/templates/edit.tsx` around lines 237 - 320, The template duplicates and drifts from the shared helpers: buildLocalEditorDocumentRequestPath, readLocalEditorPreviewLayoutData and readDocumentLayoutData are re-implemented in edit.tsx instead of reusing the versions in local-editor/selection.ts (which uses createEmptyLayoutData and has the authoritative storage-key/decompression contract); fix by refactoring so the template imports the canonical implementations from `@yext/visual-editor` (or change template generation to reference the shared module), add the missing export(s) in src/index.ts for any helpers needed by the template (export readLocalEditorPreviewLayoutData and/or readDocumentLayoutData or a single wrapper that returns createEmptyLayoutData-consistent defaults), and ensure the template uses those imports so behavior (storage key, decompression, and empty-layout defaults) stays identical to selection.ts.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/visual-editor/src/local-editor/selection.ts`:
- Line 165: The preview URL currently hardcodes `/edit/${templateId}` which
breaks when the router registers only `edit` (no param) when "main" is present;
update the previewUrl construction in selection.ts (the const previewUrl
variable referencing templateId) to use the routed editor path `/edit` and pass
templateId via a query parameter (e.g.,
previewUrl.searchParams.set('templateId', templateId)) or another non-path
mechanism consistent with editorRoute.ts returning `"edit"` when "main" exists,
so the URL matches the registered route instead of 404ing.
---
Outside diff comments:
In `@packages/visual-editor/src/vite-plugin/templates/edit.tsx`:
- Around line 196-233: The Edit component violates Rules of Hooks by calling
usePlatformBridgeDocument() and usePlatformBridgeEntityFields() conditionally;
extract the non-preview rendering into a new component (e.g., EditInner or
VisualEditorInner) that contains the hooks and returns the VisualEditorProvider
+ Editor tree, and have Edit only handle client-ready and preview branching to
return either <Preview /> or <EditInner />; also widen Edit's return type to
allow null (e.g., JSX.Element | null) so the initial client-ready null return is
valid. Ensure the new component owns the hooks (usePlatformBridgeDocument,
usePlatformBridgeEntityFields) and the original names VisualEditorProvider and
Editor are used unchanged when rendering.
---
Nitpick comments:
In `@packages/visual-editor/src/vite-plugin/templates/edit.tsx`:
- Around line 237-320: The template duplicates and drifts from the shared
helpers: buildLocalEditorDocumentRequestPath, readLocalEditorPreviewLayoutData
and readDocumentLayoutData are re-implemented in edit.tsx instead of reusing the
versions in local-editor/selection.ts (which uses createEmptyLayoutData and has
the authoritative storage-key/decompression contract); fix by refactoring so the
template imports the canonical implementations from `@yext/visual-editor` (or
change template generation to reference the shared module), add the missing
export(s) in src/index.ts for any helpers needed by the template (export
readLocalEditorPreviewLayoutData and/or readDocumentLayoutData or a single
wrapper that returns createEmptyLayoutData-consistent defaults), and ensure the
template uses those imports so behavior (storage key, decompression, and
empty-layout defaults) stays identical to selection.ts.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: bbef837b-f599-4c36-bfb2-aeabcede91d8
📒 Files selected for processing (3)
packages/visual-editor/src/local-editor/LocalEditorShell.tsxpackages/visual-editor/src/local-editor/selection.tspackages/visual-editor/src/vite-plugin/templates/edit.tsx
auto-screenshot-update: true
Opens a new tab with the contents of the editor run through
<Render>to mimic the live page for better QA and the potential to use an accessibility testing browser extensionScreen.Recording.2026-06-01.at.3.19.36.PM.mov