@prenta/admin 0.92.0 → 0.94.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +663 -0
- package/design-system/tokens.css +173 -71
- package/design-system/tokens.json +244 -63
- package/design-system/tokens.scss +203 -98
- package/design-system/tokens.tailwind.cjs +93 -38
- package/dist/AdminRoot.d.ts +27 -6
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/components/breakpoint-controls.render.test.d.ts +2 -0
- package/dist/__tests__/components/breakpoint-controls.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/breakpoint-controls.render.test.js +51 -0
- package/dist/__tests__/components/breakpoint-controls.render.test.js.map +1 -0
- package/dist/__tests__/components/canvas-edit-tip.render.test.d.ts +2 -0
- package/dist/__tests__/components/canvas-edit-tip.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/canvas-edit-tip.render.test.js +37 -0
- package/dist/__tests__/components/canvas-edit-tip.render.test.js.map +1 -0
- package/dist/__tests__/components/editor-rail.render.test.d.ts +2 -0
- package/dist/__tests__/components/editor-rail.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/editor-rail.render.test.js +138 -0
- package/dist/__tests__/components/editor-rail.render.test.js.map +1 -0
- package/dist/__tests__/components/media-block.render.test.d.ts +2 -0
- package/dist/__tests__/components/media-block.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/media-block.render.test.js +108 -0
- package/dist/__tests__/components/media-block.render.test.js.map +1 -0
- package/dist/__tests__/components/page-audit-panel.render.test.d.ts +2 -0
- package/dist/__tests__/components/page-audit-panel.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/page-audit-panel.render.test.js +59 -0
- package/dist/__tests__/components/page-audit-panel.render.test.js.map +1 -0
- package/dist/__tests__/components/reflow-meter.render.test.d.ts +2 -0
- package/dist/__tests__/components/reflow-meter.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/reflow-meter.render.test.js +94 -0
- package/dist/__tests__/components/reflow-meter.render.test.js.map +1 -0
- package/dist/__tests__/components/seo-char-count.test.js +24 -6
- package/dist/__tests__/components/seo-char-count.test.js.map +1 -1
- package/dist/__tests__/components/serp-preview.render.test.d.ts +2 -0
- package/dist/__tests__/components/serp-preview.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/serp-preview.render.test.js +25 -0
- package/dist/__tests__/components/serp-preview.render.test.js.map +1 -0
- package/dist/__tests__/lib/breakpoints.test.d.ts +2 -0
- package/dist/__tests__/lib/breakpoints.test.d.ts.map +1 -0
- package/dist/__tests__/lib/breakpoints.test.js +67 -0
- package/dist/__tests__/lib/breakpoints.test.js.map +1 -0
- package/dist/__tests__/lib/canvas-edit-chrome.test.d.ts +2 -0
- package/dist/__tests__/lib/canvas-edit-chrome.test.d.ts.map +1 -0
- package/dist/__tests__/lib/canvas-edit-chrome.test.js +44 -0
- package/dist/__tests__/lib/canvas-edit-chrome.test.js.map +1 -0
- package/dist/__tests__/lib/media-format.test.d.ts +2 -0
- package/dist/__tests__/lib/media-format.test.d.ts.map +1 -0
- package/dist/__tests__/lib/media-format.test.js +70 -0
- package/dist/__tests__/lib/media-format.test.js.map +1 -0
- package/dist/__tests__/lib/page-audit-input.test.d.ts +2 -0
- package/dist/__tests__/lib/page-audit-input.test.d.ts.map +1 -0
- package/dist/__tests__/lib/page-audit-input.test.js +122 -0
- package/dist/__tests__/lib/page-audit-input.test.js.map +1 -0
- package/dist/__tests__/lib/page-editor-service.test.js +2 -0
- package/dist/__tests__/lib/page-editor-service.test.js.map +1 -1
- package/dist/__tests__/lib/page-redirect.test.d.ts +2 -0
- package/dist/__tests__/lib/page-redirect.test.d.ts.map +1 -0
- package/dist/__tests__/lib/page-redirect.test.js +26 -0
- package/dist/__tests__/lib/page-redirect.test.js.map +1 -0
- package/dist/__tests__/lib/rail-width.test.d.ts +2 -0
- package/dist/__tests__/lib/rail-width.test.d.ts.map +1 -0
- package/dist/__tests__/lib/rail-width.test.js +69 -0
- package/dist/__tests__/lib/rail-width.test.js.map +1 -0
- package/dist/__tests__/lib/reflow-baseline.test.d.ts +2 -0
- package/dist/__tests__/lib/reflow-baseline.test.d.ts.map +1 -0
- package/dist/__tests__/lib/reflow-baseline.test.js +56 -0
- package/dist/__tests__/lib/reflow-baseline.test.js.map +1 -0
- package/dist/__tests__/lib/reflow-probe.test.d.ts +2 -0
- package/dist/__tests__/lib/reflow-probe.test.d.ts.map +1 -0
- package/dist/__tests__/lib/reflow-probe.test.js +73 -0
- package/dist/__tests__/lib/reflow-probe.test.js.map +1 -0
- package/dist/__tests__/lib/version-diff.test.d.ts +2 -0
- package/dist/__tests__/lib/version-diff.test.d.ts.map +1 -0
- package/dist/__tests__/lib/version-diff.test.js +230 -0
- package/dist/__tests__/lib/version-diff.test.js.map +1 -0
- package/dist/__tests__/lib/version-history.test.d.ts +2 -0
- package/dist/__tests__/lib/version-history.test.d.ts.map +1 -0
- package/dist/__tests__/lib/version-history.test.js +125 -0
- package/dist/__tests__/lib/version-history.test.js.map +1 -0
- package/dist/__tests__/styles/canvas-container-queries.test.d.ts +2 -0
- package/dist/__tests__/styles/canvas-container-queries.test.d.ts.map +1 -0
- package/dist/__tests__/styles/canvas-container-queries.test.js +87 -0
- package/dist/__tests__/styles/canvas-container-queries.test.js.map +1 -0
- package/dist/__tests__/styles/packaging.test.d.ts +2 -0
- package/dist/__tests__/styles/packaging.test.d.ts.map +1 -0
- package/dist/__tests__/styles/packaging.test.js +31 -0
- package/dist/__tests__/styles/packaging.test.js.map +1 -0
- package/dist/__tests__/styles/responsive-layer.test.d.ts +2 -0
- package/dist/__tests__/styles/responsive-layer.test.d.ts.map +1 -0
- package/dist/__tests__/styles/responsive-layer.test.js +127 -0
- package/dist/__tests__/styles/responsive-layer.test.js.map +1 -0
- package/dist/__tests__/styles/tokens-drift.test.d.ts +2 -0
- package/dist/__tests__/styles/tokens-drift.test.d.ts.map +1 -0
- package/dist/__tests__/styles/tokens-drift.test.js +92 -0
- package/dist/__tests__/styles/tokens-drift.test.js.map +1 -0
- package/dist/__tests__/views/admin-renderer-compat.test.d.ts +2 -0
- package/dist/__tests__/views/admin-renderer-compat.test.d.ts.map +1 -0
- package/dist/__tests__/views/admin-renderer-compat.test.js +43 -0
- package/dist/__tests__/views/admin-renderer-compat.test.js.map +1 -0
- package/dist/__tests__/views/admin-section-renderers.test.js +52 -4
- package/dist/__tests__/views/admin-section-renderers.test.js.map +1 -1
- package/dist/__tests__/views/canvas-review-mode.render.test.d.ts +2 -0
- package/dist/__tests__/views/canvas-review-mode.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/canvas-review-mode.render.test.js +70 -0
- package/dist/__tests__/views/canvas-review-mode.render.test.js.map +1 -0
- package/dist/__tests__/views/editor-top-bar.render.test.js +30 -1
- package/dist/__tests__/views/editor-top-bar.render.test.js.map +1 -1
- package/dist/__tests__/views/page-tab.render.test.d.ts +2 -0
- package/dist/__tests__/views/page-tab.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/page-tab.render.test.js +151 -0
- package/dist/__tests__/views/page-tab.render.test.js.map +1 -0
- package/dist/__tests__/views/section-class-parity.test.d.ts +2 -0
- package/dist/__tests__/views/section-class-parity.test.d.ts.map +1 -0
- package/dist/__tests__/views/section-class-parity.test.js +81 -0
- package/dist/__tests__/views/section-class-parity.test.js.map +1 -0
- package/dist/__tests__/views/sections-panel-design-owned.render.test.d.ts +2 -0
- package/dist/__tests__/views/sections-panel-design-owned.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/sections-panel-design-owned.render.test.js +86 -0
- package/dist/__tests__/views/sections-panel-design-owned.render.test.js.map +1 -0
- package/dist/__tests__/views/version-history-panel.render.test.d.ts +2 -0
- package/dist/__tests__/views/version-history-panel.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/version-history-panel.render.test.js +153 -0
- package/dist/__tests__/views/version-history-panel.render.test.js.map +1 -0
- package/dist/components/BreakpointPicker.d.ts +18 -0
- package/dist/components/BreakpointPicker.d.ts.map +1 -0
- package/dist/components/BreakpointPicker.js +31 -0
- package/dist/components/BreakpointPicker.js.map +1 -0
- package/dist/components/CanvasEditTip.d.ts +6 -0
- package/dist/components/CanvasEditTip.d.ts.map +1 -0
- package/dist/components/CanvasEditTip.js +22 -0
- package/dist/components/CanvasEditTip.js.map +1 -0
- package/dist/components/EditorRail.d.ts +36 -0
- package/dist/components/EditorRail.d.ts.map +1 -0
- package/dist/components/EditorRail.js +104 -0
- package/dist/components/EditorRail.js.map +1 -0
- package/dist/components/EditorRailTabs.d.ts +32 -0
- package/dist/components/EditorRailTabs.d.ts.map +1 -0
- package/dist/components/EditorRailTabs.js +45 -0
- package/dist/components/EditorRailTabs.js.map +1 -0
- package/dist/components/MediaBlock.d.ts +49 -0
- package/dist/components/MediaBlock.d.ts.map +1 -0
- package/dist/components/MediaBlock.js +50 -0
- package/dist/components/MediaBlock.js.map +1 -0
- package/dist/components/ReflowMeter.d.ts +28 -0
- package/dist/components/ReflowMeter.d.ts.map +1 -0
- package/dist/components/ReflowMeter.js +42 -0
- package/dist/components/ReflowMeter.js.map +1 -0
- package/dist/components/ZoomChip.d.ts +17 -0
- package/dist/components/ZoomChip.d.ts.map +1 -0
- package/dist/components/ZoomChip.js +17 -0
- package/dist/components/ZoomChip.js.map +1 -0
- package/dist/components/breakpoint-icons.d.ts +3 -0
- package/dist/components/breakpoint-icons.d.ts.map +1 -0
- package/dist/components/breakpoint-icons.js +16 -0
- package/dist/components/breakpoint-icons.js.map +1 -0
- package/dist/components/page-tab/AuthorPicker.d.ts +31 -0
- package/dist/components/page-tab/AuthorPicker.d.ts.map +1 -0
- package/dist/components/page-tab/AuthorPicker.js +95 -0
- package/dist/components/page-tab/AuthorPicker.js.map +1 -0
- package/dist/components/page-tab/DirectiveSwitch.d.ts +38 -0
- package/dist/components/page-tab/DirectiveSwitch.d.ts.map +1 -0
- package/dist/components/page-tab/DirectiveSwitch.js +33 -0
- package/dist/components/page-tab/DirectiveSwitch.js.map +1 -0
- package/dist/components/seo/EditorSeoScoreBadge.d.ts +8 -1
- package/dist/components/seo/EditorSeoScoreBadge.d.ts.map +1 -1
- package/dist/components/seo/EditorSeoScoreBadge.js +2 -2
- package/dist/components/seo/EditorSeoScoreBadge.js.map +1 -1
- package/dist/components/seo/PageAuditPanel.d.ts +14 -0
- package/dist/components/seo/PageAuditPanel.d.ts.map +1 -0
- package/dist/components/seo/PageAuditPanel.js +59 -0
- package/dist/components/seo/PageAuditPanel.js.map +1 -0
- package/dist/components/seo/SeoCharCount.d.ts +19 -7
- package/dist/components/seo/SeoCharCount.d.ts.map +1 -1
- package/dist/components/seo/SeoCharCount.js +30 -16
- package/dist/components/seo/SeoCharCount.js.map +1 -1
- package/dist/components/seo/SeoEditorPane.d.ts +15 -1
- package/dist/components/seo/SeoEditorPane.d.ts.map +1 -1
- package/dist/components/seo/SeoEditorPane.js +118 -21
- package/dist/components/seo/SeoEditorPane.js.map +1 -1
- package/dist/components/seo/SerpPreview.d.ts +20 -0
- package/dist/components/seo/SerpPreview.d.ts.map +1 -0
- package/dist/components/seo/SerpPreview.js +6 -0
- package/dist/components/seo/SerpPreview.js.map +1 -0
- package/dist/hooks/usePageAudit.d.ts +41 -0
- package/dist/hooks/usePageAudit.d.ts.map +1 -0
- package/dist/hooks/usePageAudit.js +68 -0
- package/dist/hooks/usePageAudit.js.map +1 -0
- package/dist/hooks/useReflowGuard.d.ts +47 -0
- package/dist/hooks/useReflowGuard.d.ts.map +1 -0
- package/dist/hooks/useReflowGuard.js +52 -0
- package/dist/hooks/useReflowGuard.js.map +1 -0
- package/dist/layout/primitives/AdminShell.d.ts.map +1 -1
- package/dist/layout/primitives/AdminShell.js +2 -2
- package/dist/layout/primitives/AdminShell.js.map +1 -1
- package/dist/lib/breakpoints.d.ts +51 -0
- package/dist/lib/breakpoints.d.ts.map +1 -0
- package/dist/lib/breakpoints.js +84 -0
- package/dist/lib/breakpoints.js.map +1 -0
- package/dist/lib/canvas-edit-chrome.d.ts +22 -0
- package/dist/lib/canvas-edit-chrome.d.ts.map +1 -0
- package/dist/lib/canvas-edit-chrome.js +81 -0
- package/dist/lib/canvas-edit-chrome.js.map +1 -0
- package/dist/lib/media-format.d.ts +55 -0
- package/dist/lib/media-format.d.ts.map +1 -0
- package/dist/lib/media-format.js +93 -0
- package/dist/lib/media-format.js.map +1 -0
- package/dist/lib/page-audit-input.d.ts +45 -0
- package/dist/lib/page-audit-input.d.ts.map +1 -0
- package/dist/lib/page-audit-input.js +53 -0
- package/dist/lib/page-audit-input.js.map +1 -0
- package/dist/lib/page-editor-service.d.ts +12 -0
- package/dist/lib/page-editor-service.d.ts.map +1 -1
- package/dist/lib/page-editor-service.js +6 -0
- package/dist/lib/page-editor-service.js.map +1 -1
- package/dist/lib/page-redirect.d.ts +39 -0
- package/dist/lib/page-redirect.d.ts.map +1 -0
- package/dist/lib/page-redirect.js +38 -0
- package/dist/lib/page-redirect.js.map +1 -0
- package/dist/lib/rail-width.d.ts +23 -0
- package/dist/lib/rail-width.d.ts.map +1 -0
- package/dist/lib/rail-width.js +59 -0
- package/dist/lib/rail-width.js.map +1 -0
- package/dist/lib/reflow-baseline.d.ts +45 -0
- package/dist/lib/reflow-baseline.d.ts.map +1 -0
- package/dist/lib/reflow-baseline.js +38 -0
- package/dist/lib/reflow-baseline.js.map +1 -0
- package/dist/lib/reflow-probe.d.ts +159 -0
- package/dist/lib/reflow-probe.d.ts.map +1 -0
- package/dist/lib/reflow-probe.js +228 -0
- package/dist/lib/reflow-probe.js.map +1 -0
- package/dist/lib/seo-service.d.ts +10 -0
- package/dist/lib/seo-service.d.ts.map +1 -1
- package/dist/lib/seo-service.js.map +1 -1
- package/dist/lib/version-diff.d.ts +50 -0
- package/dist/lib/version-diff.d.ts.map +1 -0
- package/dist/lib/version-diff.js +294 -0
- package/dist/lib/version-diff.js.map +1 -0
- package/dist/lib/version-history.d.ts +73 -0
- package/dist/lib/version-history.d.ts.map +1 -0
- package/dist/lib/version-history.js +126 -0
- package/dist/lib/version-history.js.map +1 -0
- package/dist/prenta-admin.css +1 -1
- package/dist/views/page-editor/CanvasMetaLine.d.ts +19 -0
- package/dist/views/page-editor/CanvasMetaLine.d.ts.map +1 -0
- package/dist/views/page-editor/CanvasMetaLine.js +23 -0
- package/dist/views/page-editor/CanvasMetaLine.js.map +1 -0
- package/dist/views/page-editor/EditorCanvas.d.ts +47 -8
- package/dist/views/page-editor/EditorCanvas.d.ts.map +1 -1
- package/dist/views/page-editor/EditorCanvas.js +68 -34
- package/dist/views/page-editor/EditorCanvas.js.map +1 -1
- package/dist/views/page-editor/EditorTopBar.d.ts +21 -2
- package/dist/views/page-editor/EditorTopBar.d.ts.map +1 -1
- package/dist/views/page-editor/EditorTopBar.js +13 -20
- package/dist/views/page-editor/EditorTopBar.js.map +1 -1
- package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -1
- package/dist/views/page-editor/PageSectionEditor.js +383 -78
- package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
- package/dist/views/page-editor/PageTab.d.ts +36 -0
- package/dist/views/page-editor/PageTab.d.ts.map +1 -0
- package/dist/views/page-editor/PageTab.js +65 -0
- package/dist/views/page-editor/PageTab.js.map +1 -0
- package/dist/views/page-editor/ReviewBar.d.ts +21 -0
- package/dist/views/page-editor/ReviewBar.d.ts.map +1 -0
- package/dist/views/page-editor/ReviewBar.js +22 -0
- package/dist/views/page-editor/ReviewBar.js.map +1 -0
- package/dist/views/page-editor/SectionInspector.d.ts.map +1 -1
- package/dist/views/page-editor/SectionInspector.js +64 -20
- package/dist/views/page-editor/SectionInspector.js.map +1 -1
- package/dist/views/page-editor/SectionsPanel.d.ts +30 -5
- package/dist/views/page-editor/SectionsPanel.d.ts.map +1 -1
- package/dist/views/page-editor/SectionsPanel.js +38 -7
- package/dist/views/page-editor/SectionsPanel.js.map +1 -1
- package/dist/views/page-editor/SitePreviewFrame.d.ts.map +1 -1
- package/dist/views/page-editor/SitePreviewFrame.js +3 -0
- package/dist/views/page-editor/SitePreviewFrame.js.map +1 -1
- package/dist/views/page-editor/VersionHistoryPanel.d.ts +29 -0
- package/dist/views/page-editor/VersionHistoryPanel.d.ts.map +1 -0
- package/dist/views/page-editor/VersionHistoryPanel.js +81 -0
- package/dist/views/page-editor/VersionHistoryPanel.js.map +1 -0
- package/dist/views/page-editor/reflow-context.d.ts +28 -0
- package/dist/views/page-editor/reflow-context.d.ts.map +1 -0
- package/dist/views/page-editor/reflow-context.js +14 -0
- package/dist/views/page-editor/reflow-context.js.map +1 -0
- package/dist/views/page-editor/sections/ByTheNumbersSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/ByTheNumbersSection.js +1 -1
- package/dist/views/page-editor/sections/ByTheNumbersSection.js.map +1 -1
- package/dist/views/page-editor/sections/CtaBannerSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/CtaBannerSection.js +1 -1
- package/dist/views/page-editor/sections/CtaBannerSection.js.map +1 -1
- package/dist/views/page-editor/sections/FeatureHighlightSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/FeatureHighlightSection.js +2 -2
- package/dist/views/page-editor/sections/FeatureHighlightSection.js.map +1 -1
- package/dist/views/page-editor/sections/GallerySection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/GallerySection.js +7 -3
- package/dist/views/page-editor/sections/GallerySection.js.map +1 -1
- package/dist/views/page-editor/sections/HeroBannerSection.js +1 -1
- package/dist/views/page-editor/sections/HeroBannerSection.js.map +1 -1
- package/dist/views/page-editor/sections/MissionSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/MissionSection.js +1 -1
- package/dist/views/page-editor/sections/MissionSection.js.map +1 -1
- package/dist/views/page-editor/sections/PullQuoteSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/PullQuoteSection.js +1 -1
- package/dist/views/page-editor/sections/PullQuoteSection.js.map +1 -1
- package/dist/views/page-editor/sections/RelatedPostsSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/RelatedPostsSection.js +1 -1
- package/dist/views/page-editor/sections/RelatedPostsSection.js.map +1 -1
- package/dist/views/page-editor/sections/SectionRenderer.d.ts +13 -1
- package/dist/views/page-editor/sections/SectionRenderer.d.ts.map +1 -1
- package/dist/views/page-editor/sections/SectionRenderer.js +66 -26
- package/dist/views/page-editor/sections/SectionRenderer.js.map +1 -1
- package/dist/views/page-editor/sections/admin-renderers.d.ts +47 -15
- package/dist/views/page-editor/sections/admin-renderers.d.ts.map +1 -1
- package/dist/views/page-editor/sections/admin-renderers.js +27 -15
- package/dist/views/page-editor/sections/admin-renderers.js.map +1 -1
- package/dist/views/page-editor/sections/parts.d.ts +0 -1
- package/dist/views/page-editor/sections/parts.d.ts.map +1 -1
- package/dist/views/page-editor/sections/parts.js +41 -16
- package/dist/views/page-editor/sections/parts.js.map +1 -1
- package/dist/views/page-editor/viewports.d.ts +7 -14
- package/dist/views/page-editor/viewports.d.ts.map +1 -1
- package/dist/views/page-editor/viewports.js +7 -12
- package/dist/views/page-editor/viewports.js.map +1 -1
- package/dist/views/post-editor/PostEditorCanvas.d.ts +2 -1
- package/dist/views/post-editor/PostEditorCanvas.d.ts.map +1 -1
- package/dist/views/post-editor/PostEditorCanvas.js +5 -2
- package/dist/views/post-editor/PostEditorCanvas.js.map +1 -1
- package/dist/views/post-editor/PostSectionEditor.d.ts.map +1 -1
- package/dist/views/post-editor/PostSectionEditor.js +2 -20
- package/dist/views/post-editor/PostSectionEditor.js.map +1 -1
- package/dist/views/post-editor/PostTemplateEditor.d.ts.map +1 -1
- package/dist/views/post-editor/PostTemplateEditor.js +4 -1
- package/dist/views/post-editor/PostTemplateEditor.js.map +1 -1
- package/package.json +5 -3
- package/src/AdminRoot.tsx +27 -6
- package/src/__tests__/components/breakpoint-controls.render.test.tsx +57 -0
- package/src/__tests__/components/canvas-edit-tip.render.test.tsx +41 -0
- package/src/__tests__/components/editor-rail.render.test.tsx +192 -0
- package/src/__tests__/components/media-block.render.test.tsx +126 -0
- package/src/__tests__/components/page-audit-panel.render.test.tsx +64 -0
- package/src/__tests__/components/reflow-meter.render.test.tsx +144 -0
- package/src/__tests__/components/seo-char-count.test.tsx +26 -6
- package/src/__tests__/components/serp-preview.render.test.tsx +32 -0
- package/src/__tests__/lib/breakpoints.test.ts +84 -0
- package/src/__tests__/lib/canvas-edit-chrome.test.ts +56 -0
- package/src/__tests__/lib/media-format.test.ts +89 -0
- package/src/__tests__/lib/page-audit-input.test.ts +144 -0
- package/src/__tests__/lib/page-editor-service.test.ts +2 -0
- package/src/__tests__/lib/page-redirect.test.ts +29 -0
- package/src/__tests__/lib/rail-width.test.ts +86 -0
- package/src/__tests__/lib/reflow-baseline.test.ts +64 -0
- package/src/__tests__/lib/reflow-probe.test.ts +95 -0
- package/src/__tests__/lib/version-diff.test.ts +278 -0
- package/src/__tests__/lib/version-history.test.ts +166 -0
- package/src/__tests__/styles/canvas-container-queries.test.ts +103 -0
- package/src/__tests__/styles/packaging.test.ts +38 -0
- package/src/__tests__/styles/responsive-layer.test.ts +152 -0
- package/src/__tests__/styles/tokens-drift.test.ts +110 -0
- package/src/__tests__/views/admin-renderer-compat.test.ts +48 -0
- package/src/__tests__/views/admin-section-renderers.test.tsx +70 -4
- package/src/__tests__/views/canvas-review-mode.render.test.tsx +94 -0
- package/src/__tests__/views/editor-top-bar.render.test.tsx +43 -1
- package/src/__tests__/views/page-tab.render.test.tsx +226 -0
- package/src/__tests__/views/section-class-parity.test.ts +98 -0
- package/src/__tests__/views/sections-panel-design-owned.render.test.tsx +104 -0
- package/src/__tests__/views/version-history-panel.render.test.tsx +195 -0
- package/src/components/BreakpointPicker.tsx +114 -0
- package/src/components/CanvasEditTip.tsx +46 -0
- package/src/components/EditorRail.tsx +219 -0
- package/src/components/EditorRailTabs.tsx +117 -0
- package/src/components/MediaBlock.tsx +223 -0
- package/src/components/ReflowMeter.tsx +125 -0
- package/src/components/ZoomChip.tsx +39 -0
- package/src/components/breakpoint-icons.ts +17 -0
- package/src/components/page-tab/AuthorPicker.tsx +240 -0
- package/src/components/page-tab/DirectiveSwitch.tsx +89 -0
- package/src/components/seo/EditorSeoScoreBadge.tsx +9 -1
- package/src/components/seo/PageAuditPanel.tsx +192 -0
- package/src/components/seo/SeoCharCount.tsx +55 -24
- package/src/components/seo/SeoEditorPane.tsx +213 -31
- package/src/components/seo/SerpPreview.tsx +41 -0
- package/src/hooks/usePageAudit.ts +98 -0
- package/src/hooks/useReflowGuard.ts +96 -0
- package/src/layout/primitives/AdminShell.tsx +13 -1
- package/src/lib/breakpoints.ts +107 -0
- package/src/lib/canvas-edit-chrome.ts +82 -0
- package/src/lib/media-format.ts +99 -0
- package/src/lib/page-audit-input.ts +104 -0
- package/src/lib/page-editor-service.ts +18 -0
- package/src/lib/page-redirect.ts +60 -0
- package/src/lib/rail-width.ts +58 -0
- package/src/lib/reflow-baseline.ts +70 -0
- package/src/lib/reflow-probe.ts +299 -0
- package/src/lib/seo-service.ts +10 -0
- package/src/lib/version-diff.ts +376 -0
- package/src/lib/version-history.ts +192 -0
- package/src/styles/prenta-tokens.css +120 -42
- package/src/styles/responsive.css +281 -0
- package/src/styles/theme.css +138 -0
- package/src/views/page-editor/CanvasMetaLine.tsx +50 -0
- package/src/views/page-editor/EditorCanvas.tsx +140 -54
- package/src/views/page-editor/EditorTopBar.tsx +55 -51
- package/src/views/page-editor/PageSectionEditor.tsx +575 -185
- package/src/views/page-editor/PageTab.tsx +433 -0
- package/src/views/page-editor/ReviewBar.tsx +76 -0
- package/src/views/page-editor/SectionInspector.tsx +84 -42
- package/src/views/page-editor/SectionsPanel.tsx +282 -141
- package/src/views/page-editor/SitePreviewFrame.tsx +3 -0
- package/src/views/page-editor/VersionHistoryPanel.tsx +302 -0
- package/src/views/page-editor/reflow-context.ts +43 -0
- package/src/views/page-editor/sections/ByTheNumbersSection.tsx +3 -2
- package/src/views/page-editor/sections/CtaBannerSection.tsx +3 -1
- package/src/views/page-editor/sections/FeatureHighlightSection.tsx +3 -2
- package/src/views/page-editor/sections/GallerySection.tsx +7 -3
- package/src/views/page-editor/sections/HeroBannerSection.tsx +2 -2
- package/src/views/page-editor/sections/MissionSection.tsx +4 -2
- package/src/views/page-editor/sections/PullQuoteSection.tsx +3 -1
- package/src/views/page-editor/sections/RelatedPostsSection.tsx +2 -1
- package/src/views/page-editor/sections/SectionRenderer.tsx +115 -34
- package/src/views/page-editor/sections/admin-renderers.tsx +54 -15
- package/src/views/page-editor/sections/parts.tsx +46 -18
- package/src/views/page-editor/viewports.ts +13 -26
- package/src/views/post-editor/PostEditorCanvas.tsx +18 -11
- package/src/views/post-editor/PostSectionEditor.tsx +1 -56
- package/src/views/post-editor/PostTemplateEditor.tsx +3 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { RAIL_DEFAULT_WIDTH, RAIL_MAX_WIDTH, RAIL_MIN_WIDTH, clampRailWidth, readRailWidth, writeRailWidth, } from '../../lib/rail-width.js';
|
|
3
|
+
function fakeStorage(initial = {}) {
|
|
4
|
+
const map = new Map(Object.entries(initial));
|
|
5
|
+
return {
|
|
6
|
+
getItem: (k) => map.get(k) ?? null,
|
|
7
|
+
setItem: (k, v) => void map.set(k, v),
|
|
8
|
+
read: (k) => map.get(k) ?? null,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
describe('clampRailWidth', () => {
|
|
12
|
+
it('holds the rail inside its usable range', () => {
|
|
13
|
+
expect(clampRailWidth(200)).toBe(RAIL_MIN_WIDTH);
|
|
14
|
+
expect(clampRailWidth(900)).toBe(RAIL_MAX_WIDTH);
|
|
15
|
+
expect(clampRailWidth(420)).toBe(420);
|
|
16
|
+
});
|
|
17
|
+
it('rounds to whole pixels', () => {
|
|
18
|
+
// Pointer deltas are fractional on scaled displays; a fractional width
|
|
19
|
+
// makes the rail's border land between device pixels and shimmer.
|
|
20
|
+
expect(clampRailWidth(420.6)).toBe(421);
|
|
21
|
+
});
|
|
22
|
+
it('falls back to the default on junk rather than producing NaN width', () => {
|
|
23
|
+
expect(clampRailWidth(Number.NaN)).toBe(RAIL_DEFAULT_WIDTH);
|
|
24
|
+
expect(clampRailWidth(Number.POSITIVE_INFINITY)).toBe(RAIL_DEFAULT_WIDTH);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe('readRailWidth', () => {
|
|
28
|
+
it('returns the default when nothing is stored', () => {
|
|
29
|
+
expect(readRailWidth(fakeStorage())).toBe(RAIL_DEFAULT_WIDTH);
|
|
30
|
+
});
|
|
31
|
+
it('reads a stored width', () => {
|
|
32
|
+
expect(readRailWidth(fakeStorage({ 'pb.panelW': '480' }))).toBe(480);
|
|
33
|
+
});
|
|
34
|
+
it('clamps a stored width that is out of range', () => {
|
|
35
|
+
// A width persisted before the range changed, or hand-edited in devtools,
|
|
36
|
+
// must not be able to render an unusable rail.
|
|
37
|
+
expect(readRailWidth(fakeStorage({ 'pb.panelW': '5000' }))).toBe(RAIL_MAX_WIDTH);
|
|
38
|
+
expect(readRailWidth(fakeStorage({ 'pb.panelW': '10' }))).toBe(RAIL_MIN_WIDTH);
|
|
39
|
+
});
|
|
40
|
+
it('survives a non-numeric stored value', () => {
|
|
41
|
+
expect(readRailWidth(fakeStorage({ 'pb.panelW': 'wide' }))).toBe(RAIL_DEFAULT_WIDTH);
|
|
42
|
+
});
|
|
43
|
+
it('survives storage that throws', () => {
|
|
44
|
+
// Safari in private mode throws on getItem. A rail that cannot size itself
|
|
45
|
+
// must not take down the editor.
|
|
46
|
+
const hostile = {
|
|
47
|
+
getItem: () => {
|
|
48
|
+
throw new Error('SecurityError');
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
expect(readRailWidth(hostile)).toBe(RAIL_DEFAULT_WIDTH);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('writeRailWidth', () => {
|
|
55
|
+
it('persists the clamped value, not the raw one', () => {
|
|
56
|
+
const store = fakeStorage();
|
|
57
|
+
writeRailWidth(9000, store);
|
|
58
|
+
expect(store.read('pb.panelW')).toBe(String(RAIL_MAX_WIDTH));
|
|
59
|
+
});
|
|
60
|
+
it('swallows a storage failure', () => {
|
|
61
|
+
const hostile = {
|
|
62
|
+
setItem: () => {
|
|
63
|
+
throw new Error('QuotaExceededError');
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
expect(() => writeRailWidth(400, hostile)).not.toThrow();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=rail-width.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rail-width.test.js","sourceRoot":"","sources":["../../../src/__tests__/lib/rail-width.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reflow-baseline.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/lib/reflow-baseline.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { baselineFieldText, snapshotBaseline } from '../../lib/reflow-baseline.js';
|
|
3
|
+
const sections = [
|
|
4
|
+
{
|
|
5
|
+
id: 's1',
|
|
6
|
+
sectionType: 'hero',
|
|
7
|
+
name: 'Hero',
|
|
8
|
+
visible: true,
|
|
9
|
+
content: {
|
|
10
|
+
heading: 'Ship content without layout drift',
|
|
11
|
+
body: 'A design-owned authoring surface.',
|
|
12
|
+
stats: [{ label: 'Uptime', value: '99.9%' }],
|
|
13
|
+
count: 3,
|
|
14
|
+
},
|
|
15
|
+
settings: {},
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
describe('reflow baseline', () => {
|
|
19
|
+
it('captures nothing for a page that is not live', () => {
|
|
20
|
+
// A draft has no published layout to break. An empty snapshot would be
|
|
21
|
+
// worse than none: it reads as "every field was blank", so every line of
|
|
22
|
+
// existing copy is reported as newly added.
|
|
23
|
+
expect(snapshotBaseline(sections, 'DRAFT')).toBeNull();
|
|
24
|
+
expect(snapshotBaseline(sections, 'SCHEDULED')).toBeNull();
|
|
25
|
+
expect(snapshotBaseline(sections, 'PUBLISHED')).not.toBeNull();
|
|
26
|
+
});
|
|
27
|
+
it('is a copy, so later edits to the draft do not move it', () => {
|
|
28
|
+
// The baseline is what is live. If it aliased the section's content object,
|
|
29
|
+
// editing the draft would silently drag the baseline along and the guard
|
|
30
|
+
// would report that nothing ever changed.
|
|
31
|
+
const baseline = snapshotBaseline(sections, 'PUBLISHED');
|
|
32
|
+
sections[0].content.heading = 'Edited in the draft';
|
|
33
|
+
expect(baselineFieldText(baseline, 's1', 'heading')).toBe('Ship content without layout drift');
|
|
34
|
+
sections[0].content.heading = 'Ship content without layout drift';
|
|
35
|
+
});
|
|
36
|
+
it('resolves repeater paths the renderers emit', () => {
|
|
37
|
+
const baseline = snapshotBaseline(sections, 'PUBLISHED');
|
|
38
|
+
expect(baselineFieldText(baseline, 's1', 'stats.0.label')).toBe('Uptime');
|
|
39
|
+
});
|
|
40
|
+
it('returns undefined for anything that is not baseline text', () => {
|
|
41
|
+
const baseline = snapshotBaseline(sections, 'PUBLISHED');
|
|
42
|
+
// No snapshot at all.
|
|
43
|
+
expect(baselineFieldText(null, 's1', 'heading')).toBeUndefined();
|
|
44
|
+
// Section added since publish.
|
|
45
|
+
expect(baselineFieldText(baseline, 'brand-new', 'heading')).toBeUndefined();
|
|
46
|
+
// Field that did not exist.
|
|
47
|
+
expect(baselineFieldText(baseline, 's1', 'subheading')).toBeUndefined();
|
|
48
|
+
// Path that runs off the end of a scalar.
|
|
49
|
+
expect(baselineFieldText(baseline, 's1', 'heading.nope')).toBeUndefined();
|
|
50
|
+
// Present but not a string — a count has no wrapping to compare.
|
|
51
|
+
expect(baselineFieldText(baseline, 's1', 'count')).toBeUndefined();
|
|
52
|
+
// Index past the end of a repeater.
|
|
53
|
+
expect(baselineFieldText(baseline, 's1', 'stats.7.label')).toBeUndefined();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
//# sourceMappingURL=reflow-baseline.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reflow-baseline.test.js","sourceRoot":"","sources":["../../../src/__tests__/lib/reflow-baseline.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reflow-probe.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/lib/reflow-probe.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { linesFromMetrics, reflowSeverity, worstBreakpoint, } from '../../lib/reflow-probe.js';
|
|
3
|
+
describe('linesFromMetrics', () => {
|
|
4
|
+
it('divides height by line height', () => {
|
|
5
|
+
expect(linesFromMetrics(72, 24, 16)).toBe(3);
|
|
6
|
+
});
|
|
7
|
+
it('rounds rather than floors', () => {
|
|
8
|
+
// Sub-pixel line heights accumulate; 2.97 lines is three lines on screen,
|
|
9
|
+
// and flooring would under-report exactly the overflow being guarded.
|
|
10
|
+
expect(linesFromMetrics(71.3, 24, 16)).toBe(3);
|
|
11
|
+
});
|
|
12
|
+
it('falls back to a ratio when line-height computes to normal', () => {
|
|
13
|
+
// `parseFloat('normal')` is NaN — every element that never set a
|
|
14
|
+
// line-height would otherwise measure as one line forever.
|
|
15
|
+
expect(linesFromMetrics(60, Number.NaN, 16)).toBe(3);
|
|
16
|
+
});
|
|
17
|
+
it('never reports zero lines', () => {
|
|
18
|
+
// An empty field still occupies a line's worth of intent, and zero would
|
|
19
|
+
// read as "this shrank" against any baseline.
|
|
20
|
+
expect(linesFromMetrics(0, 24, 16)).toBe(1);
|
|
21
|
+
expect(linesFromMetrics(0, 0, 0)).toBe(1);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
describe('reflowSeverity', () => {
|
|
25
|
+
it('flags one extra line as growth and two as bad', () => {
|
|
26
|
+
expect(reflowSeverity(3, 2)).toBe('grew');
|
|
27
|
+
expect(reflowSeverity(4, 2)).toBe('grew-badly');
|
|
28
|
+
});
|
|
29
|
+
it('stays quiet when copy shrinks', () => {
|
|
30
|
+
// Shorter copy cannot break a layout that already held the longer version.
|
|
31
|
+
// Flagging it would put a warning on the edit the editor was asked to make.
|
|
32
|
+
expect(reflowSeverity(1, 3)).toBe('same');
|
|
33
|
+
expect(reflowSeverity(2, 2)).toBe('same');
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe('worstBreakpoint', () => {
|
|
37
|
+
const at = (pairs) => pairs.map(([id, lines]) => ({ id, lines }));
|
|
38
|
+
it('picks the biggest jump', () => {
|
|
39
|
+
const worst = worstBreakpoint(at([
|
|
40
|
+
['wide', 3],
|
|
41
|
+
['laptop', 5],
|
|
42
|
+
['mobile', 4],
|
|
43
|
+
]), at([
|
|
44
|
+
['wide', 3],
|
|
45
|
+
['laptop', 3],
|
|
46
|
+
['mobile', 3],
|
|
47
|
+
]));
|
|
48
|
+
expect(worst).toBe('laptop');
|
|
49
|
+
});
|
|
50
|
+
it('breaks a tie toward the narrowest width', () => {
|
|
51
|
+
// The same damage at several sizes is most likely to look broken where
|
|
52
|
+
// there is least room.
|
|
53
|
+
const worst = worstBreakpoint(at([
|
|
54
|
+
['4k', 4],
|
|
55
|
+
['laptop', 4],
|
|
56
|
+
['mobile', 4],
|
|
57
|
+
]), at([
|
|
58
|
+
['4k', 3],
|
|
59
|
+
['laptop', 3],
|
|
60
|
+
['mobile', 3],
|
|
61
|
+
]));
|
|
62
|
+
expect(worst).toBe('mobile');
|
|
63
|
+
});
|
|
64
|
+
it('returns nothing when no breakpoint grew', () => {
|
|
65
|
+
expect(worstBreakpoint(at([['wide', 2]]), at([['wide', 3]]))).toBeNull();
|
|
66
|
+
});
|
|
67
|
+
it('ignores breakpoints missing from the baseline', () => {
|
|
68
|
+
// A baseline captured before a preset existed must not make that preset
|
|
69
|
+
// look like a regression.
|
|
70
|
+
expect(worstBreakpoint(at([['4k', 9]]), at([['wide', 1]]))).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=reflow-probe.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reflow-probe.test.js","sourceRoot":"","sources":["../../../src/__tests__/lib/reflow-probe.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version-diff.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/lib/version-diff.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { changedSectionIds, diffVersions, formatValue, groupChangesBySection, } from '../../lib/version-diff.js';
|
|
3
|
+
function section(overrides) {
|
|
4
|
+
return {
|
|
5
|
+
sectionType: 'hero',
|
|
6
|
+
name: 'Hero Banner',
|
|
7
|
+
visible: true,
|
|
8
|
+
content: {},
|
|
9
|
+
settings: {},
|
|
10
|
+
...overrides,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
describe('formatValue', () => {
|
|
14
|
+
it('renders absent and blank values as an em dash so an empty diff cell is never blank', () => {
|
|
15
|
+
expect(formatValue(null)).toBe('—');
|
|
16
|
+
expect(formatValue(undefined)).toBe('—');
|
|
17
|
+
expect(formatValue('')).toBe('—');
|
|
18
|
+
expect(formatValue(' ')).toBe('—');
|
|
19
|
+
expect(formatValue([])).toBe('—');
|
|
20
|
+
expect(formatValue({})).toBe('—');
|
|
21
|
+
});
|
|
22
|
+
it('summarises structured values rather than dumping them', () => {
|
|
23
|
+
expect(formatValue([{ value: '1' }, { value: '2' }])).toBe('2 items');
|
|
24
|
+
expect(formatValue([{ value: '1' }])).toBe('1 item');
|
|
25
|
+
expect(formatValue({ name: 'hero.jpg', url: '/m/hero.jpg' })).toBe('hero.jpg');
|
|
26
|
+
// No name-ish key: fall back to a shape description, not "[object Object]".
|
|
27
|
+
expect(formatValue({ x: 1, y: 2 })).toBe('2 fields');
|
|
28
|
+
});
|
|
29
|
+
it('reads booleans and numbers as words the panel can show', () => {
|
|
30
|
+
expect(formatValue(true)).toBe('Yes');
|
|
31
|
+
expect(formatValue(false)).toBe('No');
|
|
32
|
+
expect(formatValue(0)).toBe('0');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe('diffVersions', () => {
|
|
36
|
+
it('labels content fields through the section registry, not by raw key', () => {
|
|
37
|
+
const before = [section({ id: 's1', content: { heading: 'Old headline' } })];
|
|
38
|
+
const after = [section({ id: 's1', content: { heading: 'New headline' } })];
|
|
39
|
+
const changes = diffVersions(after, before);
|
|
40
|
+
expect(changes).toEqual([
|
|
41
|
+
{
|
|
42
|
+
sectionId: 's1',
|
|
43
|
+
sectionName: 'Hero Banner',
|
|
44
|
+
// 'heading' is `label: 'Heading'` on the hero type in the registry.
|
|
45
|
+
field: 'Heading',
|
|
46
|
+
from: 'Old headline',
|
|
47
|
+
to: 'New headline',
|
|
48
|
+
},
|
|
49
|
+
]);
|
|
50
|
+
});
|
|
51
|
+
it('falls back to the raw key for a type the registry does not describe', () => {
|
|
52
|
+
const before = [section({ id: 's1', sectionType: 'opal-pricing', content: { tiers: 'a' } })];
|
|
53
|
+
const after = [section({ id: 's1', sectionType: 'opal-pricing', content: { tiers: 'b' } })];
|
|
54
|
+
expect(diffVersions(after, before)[0]?.field).toBe('tiers');
|
|
55
|
+
});
|
|
56
|
+
it('reports a field cleared to nothing — the key exists only in the older snapshot', () => {
|
|
57
|
+
const before = [section({ id: 's1', content: { heading: 'Something' } })];
|
|
58
|
+
const after = [section({ id: 's1', content: {} })];
|
|
59
|
+
expect(diffVersions(after, before)).toEqual([
|
|
60
|
+
{ sectionId: 's1', sectionName: 'Hero Banner', field: 'Heading', from: 'Something', to: '—' },
|
|
61
|
+
]);
|
|
62
|
+
});
|
|
63
|
+
it('reports visibility as a state, because hiding a section is the one structural edit there is', () => {
|
|
64
|
+
const before = [section({ id: 's1', visible: true })];
|
|
65
|
+
const after = [section({ id: 's1', visible: false })];
|
|
66
|
+
expect(diffVersions(after, before)).toEqual([
|
|
67
|
+
{
|
|
68
|
+
sectionId: 's1',
|
|
69
|
+
sectionName: 'Hero Banner',
|
|
70
|
+
field: 'Visibility',
|
|
71
|
+
from: 'Shown',
|
|
72
|
+
to: 'Hidden',
|
|
73
|
+
},
|
|
74
|
+
]);
|
|
75
|
+
});
|
|
76
|
+
it('reports added and removed sections', () => {
|
|
77
|
+
const before = [section({ id: 's1' })];
|
|
78
|
+
const after = [section({ id: 's1' }), section({ id: 's2', name: 'CTA', sectionType: 'cta' })];
|
|
79
|
+
expect(diffVersions(after, before)).toEqual([
|
|
80
|
+
{ sectionId: 's2', sectionName: 'CTA', field: 'Section', from: '—', to: 'Added' },
|
|
81
|
+
]);
|
|
82
|
+
expect(diffVersions(before, after)).toEqual([
|
|
83
|
+
{ sectionId: 's2', sectionName: 'CTA', field: 'Section', from: 'Present', to: 'Removed' },
|
|
84
|
+
]);
|
|
85
|
+
});
|
|
86
|
+
it('reports nothing when the snapshots match', () => {
|
|
87
|
+
const same = [section({ id: 's1', content: { heading: 'Same' } })];
|
|
88
|
+
expect(diffVersions(same, [section({ id: 's1', content: { heading: 'Same' } })])).toEqual([]);
|
|
89
|
+
});
|
|
90
|
+
it('reports nothing against a missing predecessor', () => {
|
|
91
|
+
// The oldest version we hold is not the moment the page was created, so
|
|
92
|
+
// reporting every field as "added" would state something false.
|
|
93
|
+
expect(diffVersions([section({ id: 's1', content: { heading: 'x' } })], undefined)).toEqual([]);
|
|
94
|
+
expect(diffVersions([section({ id: 's1' })], null)).toEqual([]);
|
|
95
|
+
});
|
|
96
|
+
it('does not report a settings key that was merely absent versus explicitly default', () => {
|
|
97
|
+
// `{}` and `{ backgroundType: undefined }` format identically, so neither
|
|
98
|
+
// produces a row. Without this the first save after a schema addition
|
|
99
|
+
// would light up every section on the canvas.
|
|
100
|
+
const before = [section({ id: 's1', settings: {} })];
|
|
101
|
+
const after = [section({ id: 's1', settings: { backgroundType: undefined } })];
|
|
102
|
+
expect(diffVersions(after, before)).toEqual([]);
|
|
103
|
+
});
|
|
104
|
+
it('labels known settings and reports a real background change', () => {
|
|
105
|
+
const before = [section({ id: 's1', settings: { backgroundType: 'none' } })];
|
|
106
|
+
const after = [section({ id: 's1', settings: { backgroundType: 'solid' } })];
|
|
107
|
+
expect(diffVersions(after, before)).toEqual([
|
|
108
|
+
{
|
|
109
|
+
sectionId: 's1',
|
|
110
|
+
sectionName: 'Hero Banner',
|
|
111
|
+
field: 'Background type',
|
|
112
|
+
from: 'none',
|
|
113
|
+
to: 'solid',
|
|
114
|
+
},
|
|
115
|
+
]);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe('diffVersions — structured fields', () => {
|
|
119
|
+
const stats = (rows) => section({ id: 's1', sectionType: 'stats', name: 'By The Numbers', content: { stats: rows } });
|
|
120
|
+
it('catches an edit that keeps the row count', () => {
|
|
121
|
+
// The bug this exists for: deciding "did it change" from the DISPLAY
|
|
122
|
+
// summary collapses an array to "2 items", so retyping a row while keeping
|
|
123
|
+
// the count formats identically on both sides and the field reports no
|
|
124
|
+
// change — no panel row AND no outline on the canvas, which is exactly the
|
|
125
|
+
// edit a reviewer opened history to find.
|
|
126
|
+
const before = [
|
|
127
|
+
stats([
|
|
128
|
+
{ value: '10k', label: 'Users' },
|
|
129
|
+
{ value: '99%', label: 'Uptime' },
|
|
130
|
+
]),
|
|
131
|
+
];
|
|
132
|
+
const after = [
|
|
133
|
+
stats([
|
|
134
|
+
{ value: '20k', label: 'Users' },
|
|
135
|
+
{ value: '99%', label: 'Uptime' },
|
|
136
|
+
]),
|
|
137
|
+
];
|
|
138
|
+
const changes = diffVersions(after, before);
|
|
139
|
+
expect(changes).toHaveLength(1);
|
|
140
|
+
expect(changes[0]?.from).toBe('10k');
|
|
141
|
+
expect(changes[0]?.to).toBe('20k');
|
|
142
|
+
// Named down to the row and the sub-field, so "which one" has an answer.
|
|
143
|
+
expect(changes[0]?.field).toBe('Stats · Stat 1 · Value');
|
|
144
|
+
expect(changedSectionIds(changes).has('s1')).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
it('never emits a row whose before and after both read as a bare count', () => {
|
|
147
|
+
const before = [
|
|
148
|
+
stats([
|
|
149
|
+
{ value: 'a', label: 'x' },
|
|
150
|
+
{ value: 'b', label: 'y' },
|
|
151
|
+
]),
|
|
152
|
+
];
|
|
153
|
+
const after = [
|
|
154
|
+
stats([
|
|
155
|
+
{ value: 'c', label: 'x' },
|
|
156
|
+
{ value: 'd', label: 'y' },
|
|
157
|
+
]),
|
|
158
|
+
];
|
|
159
|
+
for (const change of diffVersions(after, before)) {
|
|
160
|
+
expect(change.from).not.toBe(change.to);
|
|
161
|
+
expect(change.from).not.toMatch(/^\d+ items?$/);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
it('reports added and removed rows', () => {
|
|
165
|
+
const before = [stats([{ value: '10k', label: 'Users' }])];
|
|
166
|
+
const after = [
|
|
167
|
+
stats([
|
|
168
|
+
{ value: '10k', label: 'Users' },
|
|
169
|
+
{ value: '99%', label: 'Uptime' },
|
|
170
|
+
]),
|
|
171
|
+
];
|
|
172
|
+
expect(diffVersions(after, before)).toEqual([
|
|
173
|
+
expect.objectContaining({ field: 'Stats · Stat 2', from: '—', to: 'Added' }),
|
|
174
|
+
]);
|
|
175
|
+
expect(diffVersions(before, after)).toEqual([
|
|
176
|
+
expect.objectContaining({ field: 'Stats · Stat 2', from: 'Present', to: 'Removed' }),
|
|
177
|
+
]);
|
|
178
|
+
});
|
|
179
|
+
it('catches a nested change inside a media object', () => {
|
|
180
|
+
// Same failure one level down: two media objects with the same `name` used
|
|
181
|
+
// to format identically while pointing at different files.
|
|
182
|
+
const media = (url) => ({ name: 'hero.jpg', url });
|
|
183
|
+
const before = [section({ id: 's1', content: { imageUrl: media('/a.jpg') } })];
|
|
184
|
+
const after = [section({ id: 's1', content: { imageUrl: media('/b.jpg') } })];
|
|
185
|
+
expect(diffVersions(after, before)).toHaveLength(1);
|
|
186
|
+
});
|
|
187
|
+
it('treats every spelling of blank as unchanged', () => {
|
|
188
|
+
// Tightening the comparison must not start reporting "— → —".
|
|
189
|
+
for (const [before, after] of [
|
|
190
|
+
['', null],
|
|
191
|
+
[null, undefined],
|
|
192
|
+
[undefined, ' '],
|
|
193
|
+
[[], undefined],
|
|
194
|
+
[{}, null],
|
|
195
|
+
]) {
|
|
196
|
+
const changes = diffVersions([section({ id: 's1', content: { heading: after } })], [section({ id: 's1', content: { heading: before } })]);
|
|
197
|
+
expect(changes, `${JSON.stringify(before)} → ${JSON.stringify(after)}`).toEqual([]);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
it('reports an unchanged repeater as no change at all', () => {
|
|
201
|
+
const rows = [{ value: '10k', label: 'Users' }];
|
|
202
|
+
expect(diffVersions([stats(rows)], [stats([...rows.map((r) => ({ ...r }))])])).toEqual([]);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
describe('groupChangesBySection', () => {
|
|
206
|
+
it('groups by section in first-seen order', () => {
|
|
207
|
+
const before = [
|
|
208
|
+
section({ id: 's1', content: { heading: 'a', body: 'a' } }),
|
|
209
|
+
section({ id: 's2', name: 'CTA', sectionType: 'cta', content: { heading: 'a' } }),
|
|
210
|
+
];
|
|
211
|
+
const after = [
|
|
212
|
+
section({ id: 's1', content: { heading: 'b', body: 'b' } }),
|
|
213
|
+
section({ id: 's2', name: 'CTA', sectionType: 'cta', content: { heading: 'b' } }),
|
|
214
|
+
];
|
|
215
|
+
const groups = groupChangesBySection(diffVersions(after, before));
|
|
216
|
+
expect(groups.map((g) => g.sectionId)).toEqual(['s1', 's2']);
|
|
217
|
+
expect(groups[0]?.changes).toHaveLength(2);
|
|
218
|
+
expect(groups[1]?.changes).toHaveLength(1);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
describe('changedSectionIds', () => {
|
|
222
|
+
it('collapses several changes on one section to a single outline', () => {
|
|
223
|
+
const before = [section({ id: 's1', content: { heading: 'a', body: 'a' } })];
|
|
224
|
+
const after = [section({ id: 's1', content: { heading: 'b', body: 'b' } })];
|
|
225
|
+
const ids = changedSectionIds(diffVersions(after, before));
|
|
226
|
+
expect(ids.size).toBe(1);
|
|
227
|
+
expect(ids.has('s1')).toBe(true);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
//# sourceMappingURL=version-diff.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version-diff.test.js","sourceRoot":"","sources":["../../../src/__tests__/lib/version-diff.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,qBAAqB,GACtB,MAAM,2BAA2B,CAAA;AAGlC,SAAS,OAAO,CAAC,SAAgD;IAC/D,OAAO;QACL,WAAW,EAAE,MAAM;QACnB,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,EAAE;QACX,QAAQ,EAAE,EAAE;QACZ,GAAG,SAAS;KACb,CAAA;AACH,CAAC;AAED,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,oFAAoF,EAAE,GAAG,EAAE;QAC5F,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACnC,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACxC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACjC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACpC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACjC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACnC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACrE,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACpD,MAAM,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAC9E,4EAA4E;QAC5E,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACtD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACrC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACrC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAClC,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,EAAE,CAAC,CAAC,CAAA;QAC5E,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,EAAE,CAAC,CAAC,CAAA;QAE3E,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;QAE3C,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC;YACtB;gBACE,SAAS,EAAE,IAAI;gBACf,WAAW,EAAE,aAAa;gBAC1B,oEAAoE;gBACpE,KAAK,EAAE,SAAS;gBAChB,IAAI,EAAE,cAAc;gBACpB,EAAE,EAAE,cAAc;aACnB;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,cAAc,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAA;QAC5F,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,cAAc,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAA;QAE3F,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC7D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gFAAgF,EAAE,GAAG,EAAE;QACxF,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC,CAAA;QACzE,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,CAAA;QAElD,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;YAC1C,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,GAAG,EAAE;SAC9F,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,6FAA6F,EAAE,GAAG,EAAE;QACrG,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;QACrD,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;QAErD,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;YAC1C;gBACE,SAAS,EAAE,IAAI;gBACf,WAAW,EAAE,aAAa;gBAC1B,KAAK,EAAE,YAAY;gBACnB,IAAI,EAAE,OAAO;gBACb,EAAE,EAAE,QAAQ;aACb;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;QACtC,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;QAE7F,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;YAC1C,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE;SAClF,CAAC,CAAA;QAEF,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;YAC1C,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE;SAC1F,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAA;QAClE,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IAC/F,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,wEAAwE;QACxE,gEAAgE;QAChE,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAC/F,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACjE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iFAAiF,EAAE,GAAG,EAAE;QACzF,0EAA0E;QAC1E,sEAAsE;QACtE,8CAA8C;QAC9C,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,CAAA;QACpD,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,cAAc,EAAE,SAAS,EAAW,EAAE,CAAC,CAAC,CAAA;QACvF,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACjD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAA;QAC5E,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,CAAA;QAE5E,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;YAC1C;gBACE,SAAS,EAAE,IAAI;gBACf,WAAW,EAAE,aAAa;gBAC1B,KAAK,EAAE,iBAAiB;gBACxB,IAAI,EAAE,MAAM;gBACZ,EAAE,EAAE,OAAO;aACZ;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,MAAM,KAAK,GAAG,CAAC,IAA6C,EAAE,EAAE,CAC9D,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;IAE/F,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,qEAAqE;QACrE,2EAA2E;QAC3E,uEAAuE;QACvE,2EAA2E;QAC3E,0CAA0C;QAC1C,MAAM,MAAM,GAAG;YACb,KAAK,CAAC;gBACJ,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE;gBAChC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE;aAClC,CAAC;SACH,CAAA;QACD,MAAM,KAAK,GAAG;YACZ,KAAK,CAAC;gBACJ,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE;gBAChC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE;aAClC,CAAC;SACH,CAAA;QAED,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;QAE3C,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;QAC/B,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACpC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAClC,yEAAyE;QACzE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAA;QACxD,MAAM,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,MAAM,GAAG;YACb,KAAK,CAAC;gBACJ,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE;gBAC1B,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE;aAC3B,CAAC;SACH,CAAA;QACD,MAAM,KAAK,GAAG;YACZ,KAAK,CAAC;gBACJ,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE;gBAC1B,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE;aAC3B,CAAC;SACH,CAAA;QAED,KAAK,MAAM,MAAM,IAAI,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;YACjD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;YACvC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAA;QACjD,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;QAC1D,MAAM,KAAK,GAAG;YACZ,KAAK,CAAC;gBACJ,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE;gBAChC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE;aAClC,CAAC;SACH,CAAA;QAED,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;YAC1C,MAAM,CAAC,gBAAgB,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC;SAC7E,CAAC,CAAA;QACF,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;YAC1C,MAAM,CAAC,gBAAgB,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC;SACrF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,2EAA2E;QAC3E,2DAA2D;QAC3D,MAAM,KAAK,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAA;QAC1D,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;QAC9E,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;QAE7E,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;IACrD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,8DAA8D;QAC9D,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI;YAC5B,CAAC,EAAE,EAAE,IAAI,CAAC;YACV,CAAC,IAAI,EAAE,SAAS,CAAC;YACjB,CAAC,SAAS,EAAE,KAAK,CAAC;YAClB,CAAC,EAAE,EAAE,SAAS,CAAC;YACf,CAAC,EAAE,EAAE,IAAI,CAAC;SACkB,EAAE,CAAC;YAC/B,MAAM,OAAO,GAAG,YAAY,CAC1B,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EACpD,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CACtD,CAAA;YACD,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QACrF,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,IAAI,GAAG,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;QAC/C,MAAM,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IAC5F,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,MAAM,GAAG;YACb,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;YAC3D,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,CAAC;SAClF,CAAA;QACD,MAAM,KAAK,GAAG;YACZ,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;YAC3D,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,CAAC;SAClF,CAAA;QAED,MAAM,MAAM,GAAG,qBAAqB,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;QAEjE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;QAC5D,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;QAC1C,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;IAC5C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAA;QAC5E,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAA;QAE3E,MAAM,GAAG,GAAG,iBAAiB,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;QAE1D,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACxB,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAClC,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version-history.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/lib/version-history.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { dayLabel, groupVersionsByDay, initials, toPageVersion, versionKind, } from '../../lib/version-history.js';
|
|
3
|
+
const NOW = new Date('2026-08-02T17:00:00Z');
|
|
4
|
+
function version(id, createdAt) {
|
|
5
|
+
return {
|
|
6
|
+
id,
|
|
7
|
+
changeType: 'UPDATE',
|
|
8
|
+
createdAt,
|
|
9
|
+
author: { id: 'u1', name: 'Sarah Mills' },
|
|
10
|
+
sections: [],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
describe('toPageVersion', () => {
|
|
14
|
+
it('lifts the section snapshot out of the version payload', () => {
|
|
15
|
+
const mapped = toPageVersion({
|
|
16
|
+
id: 'v1',
|
|
17
|
+
changeType: 'PUBLISH',
|
|
18
|
+
createdAt: '2026-08-02T12:00:00Z',
|
|
19
|
+
data: {
|
|
20
|
+
sections: [
|
|
21
|
+
{ id: 's1', sectionType: 'hero', name: 'Hero', visible: true, content: {}, settings: {} },
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
changedBy: { id: 'u1', name: 'Sarah Mills', email: 's@example.com' },
|
|
25
|
+
});
|
|
26
|
+
expect(mapped.sections).toHaveLength(1);
|
|
27
|
+
expect(mapped.sections[0]?.id).toBe('s1');
|
|
28
|
+
expect(mapped.changeType).toBe('PUBLISH');
|
|
29
|
+
expect(mapped.author.name).toBe('Sarah Mills');
|
|
30
|
+
});
|
|
31
|
+
it('preserves an unmanaged section rather than dropping it from the snapshot', () => {
|
|
32
|
+
// Coercing a snapshot more strictly than the live document reads would
|
|
33
|
+
// make an externally-managed section look deleted in every version.
|
|
34
|
+
const mapped = toPageVersion({
|
|
35
|
+
id: 'v1',
|
|
36
|
+
changeType: 'UPDATE',
|
|
37
|
+
createdAt: '2026-08-02T12:00:00Z',
|
|
38
|
+
data: {
|
|
39
|
+
sections: [
|
|
40
|
+
{ id: 's1', sectionType: 'opal-pricing', name: 'Pricing', visible: true, content: {} },
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
expect(mapped.sections).toHaveLength(1);
|
|
45
|
+
expect(mapped.sections[0]?.sectionType).toBe('opal-pricing');
|
|
46
|
+
});
|
|
47
|
+
it('falls back through name → email → a real word for the avatar', () => {
|
|
48
|
+
expect(toPageVersion({ id: 'v', changeType: 'UPDATE', createdAt: '', data: null }).author.name).toBe('Unknown');
|
|
49
|
+
expect(toPageVersion({
|
|
50
|
+
id: 'v',
|
|
51
|
+
changeType: 'UPDATE',
|
|
52
|
+
createdAt: '',
|
|
53
|
+
data: null,
|
|
54
|
+
changedBy: { id: 'u', name: ' ', email: 'a@b.com' },
|
|
55
|
+
}).author.name).toBe('a@b.com');
|
|
56
|
+
});
|
|
57
|
+
it('yields no sections for a version whose payload has none', () => {
|
|
58
|
+
expect(toPageVersion({ id: 'v', changeType: 'DELETE', createdAt: '', data: {} }).sections).toEqual([]);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('versionKind', () => {
|
|
62
|
+
it('marks a published snapshot as the one a visitor could have seen', () => {
|
|
63
|
+
expect(versionKind('PUBLISH')).toEqual({ label: 'Published', tone: 'live' });
|
|
64
|
+
});
|
|
65
|
+
it('calls UPDATE "Saved", never "Draft"', () => {
|
|
66
|
+
// Every row written before cms-core started recording PUBLISH is an UPDATE
|
|
67
|
+
// whether or not it went live, so "Draft" would be false for those.
|
|
68
|
+
expect(versionKind('UPDATE').label).toBe('Saved');
|
|
69
|
+
});
|
|
70
|
+
it('distinguishes a rollback from authored content', () => {
|
|
71
|
+
expect(versionKind('RESTORE').label).toBe('Restored');
|
|
72
|
+
});
|
|
73
|
+
it('degrades to a neutral label for an unrecognised type', () => {
|
|
74
|
+
expect(versionKind('SOMETHING_NEW').label).toBe('Saved');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe('dayLabel', () => {
|
|
78
|
+
it('names today and yesterday', () => {
|
|
79
|
+
expect(dayLabel('2026-08-02T09:00:00', NOW)).toBe('Today');
|
|
80
|
+
expect(dayLabel('2026-08-01T23:59:00', NOW)).toBe('Yesterday');
|
|
81
|
+
});
|
|
82
|
+
it('drops the year within the current one and keeps it outside', () => {
|
|
83
|
+
expect(dayLabel('2026-07-27T09:00:00', NOW)).not.toMatch(/2026/);
|
|
84
|
+
expect(dayLabel('2025-07-27T09:00:00', NOW)).toMatch(/2025/);
|
|
85
|
+
});
|
|
86
|
+
it('does not throw on an unparseable timestamp', () => {
|
|
87
|
+
expect(dayLabel('not-a-date', NOW)).toBe('Unknown date');
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe('groupVersionsByDay', () => {
|
|
91
|
+
it('groups consecutive runs and keeps newest-first order', () => {
|
|
92
|
+
const groups = groupVersionsByDay([
|
|
93
|
+
version('v5', '2026-08-02T16:12:00'),
|
|
94
|
+
version('v4', '2026-08-02T11:03:00'),
|
|
95
|
+
version('v3', '2026-08-01T17:47:00'),
|
|
96
|
+
version('v1', '2026-07-27T09:30:00'),
|
|
97
|
+
], NOW);
|
|
98
|
+
expect(groups.map((g) => g.label)).toEqual(['Today', 'Yesterday', expect.stringMatching(/Jul/)]);
|
|
99
|
+
expect(groups[0]?.versions.map((v) => v.id)).toEqual(['v5', 'v4']);
|
|
100
|
+
});
|
|
101
|
+
it('does not merge two non-adjacent runs that share a label', () => {
|
|
102
|
+
// A Map keyed on the label would silently reorder an unsorted page into
|
|
103
|
+
// something that reads as chronological but is not.
|
|
104
|
+
const groups = groupVersionsByDay([
|
|
105
|
+
version('a', '2026-08-02T16:00:00'),
|
|
106
|
+
version('b', '2026-08-01T16:00:00'),
|
|
107
|
+
version('c', '2026-08-02T09:00:00'),
|
|
108
|
+
], NOW);
|
|
109
|
+
expect(groups.map((g) => g.label)).toEqual(['Today', 'Yesterday', 'Today']);
|
|
110
|
+
});
|
|
111
|
+
it('returns nothing for an empty list', () => {
|
|
112
|
+
expect(groupVersionsByDay([], NOW)).toEqual([]);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
describe('initials', () => {
|
|
116
|
+
it('takes first and last for a full name, two letters for one word', () => {
|
|
117
|
+
expect(initials('Sarah Mills')).toBe('SM');
|
|
118
|
+
expect(initials('Sarah Jane Mills')).toBe('SM');
|
|
119
|
+
expect(initials('sarah')).toBe('SA');
|
|
120
|
+
});
|
|
121
|
+
it('never renders an empty monogram', () => {
|
|
122
|
+
expect(initials(' ')).toBe('?');
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
//# sourceMappingURL=version-history.test.js.map
|