From dcc9aecffd95461edf97abd69ad3dac3da5b982e Mon Sep 17 00:00:00 2001 From: nmanu1 <88398086+nmanu1@users.noreply.github.com> Date: Fri, 15 Sep 2023 13:33:27 -0400 Subject: [PATCH] Add HMR for layouts and remove top-level fragments (#373) This PR removes top-level fragments from the component tree of each layout in the layout slice so they won't be displayed in the UI when adding a layout to a new page. HMR is also updated to re-sync layouts in the store when a layout file is changed or a full sync is performed. J=SLAP-2937 TEST=manual In the test-site, saw that if `LocationLayout` had a top-level fragment, it was not present in the component tree in the `layouts` record. Also, checked that updating `LocationLayout` while running the test-site would correctly update it's component tree in the store. --- packages/studio/src/store/hotReloadStore.ts | 12 +++++++++++- .../studio/src/store/slices/createLayoutSlice.ts | 4 ++-- .../src/store/slices/{pages => }/createPageSlice.ts | 10 +++++----- packages/studio/src/store/useStudioStore.ts | 2 +- packages/studio/src/utils/removeTopLevelFragments.ts | 7 ++++--- 5 files changed, 23 insertions(+), 12 deletions(-) rename packages/studio/src/store/slices/{pages => }/createPageSlice.ts (94%) diff --git a/packages/studio/src/store/hotReloadStore.ts b/packages/studio/src/store/hotReloadStore.ts index 054401f10..3ae735749 100644 --- a/packages/studio/src/store/hotReloadStore.ts +++ b/packages/studio/src/store/hotReloadStore.ts @@ -16,7 +16,7 @@ export default async function hotReloadStore(payload: StudioHMRPayload) { await syncFileMetadata(studioData, payload.file); break; case "layouts": - // TODO SLAP-2930 + syncLayouts(studioData); break; case "pages": syncPages(studioData); @@ -32,6 +32,7 @@ export default async function hotReloadStore(payload: StudioHMRPayload) { async function fullSync(studioData: StudioData, file: string) { syncPages(studioData); + syncLayouts(studioData); await syncFileMetadata(studioData, file); syncSiteSettings(studioData); useStudioStore.setState((store) => { @@ -81,6 +82,15 @@ function syncPages(studioData: StudioData) { }); } +function syncLayouts(studioData: StudioData) { + const layoutNameToLayoutState = removeTopLevelFragments( + studioData.layoutNameToLayoutState + ); + useStudioStore.setState((store) => { + store.layouts.layouts = layoutNameToLayoutState; + }); +} + function syncSiteSettings(studioData: StudioData) { useStudioStore.setState((store) => { store.siteSettings.shape = studioData.siteSettings?.shape; diff --git a/packages/studio/src/store/slices/createLayoutSlice.ts b/packages/studio/src/store/slices/createLayoutSlice.ts index c2591afb0..94072bf66 100644 --- a/packages/studio/src/store/slices/createLayoutSlice.ts +++ b/packages/studio/src/store/slices/createLayoutSlice.ts @@ -1,10 +1,10 @@ import initialStudioData from "virtual_yext-studio"; import { LayoutSlice } from "../models/slices/LayoutSlice"; import { SliceCreator } from "../models/utils"; +import removeTopLevelFragments from "../../utils/removeTopLevelFragments"; const createLayoutSlice: SliceCreator = () => ({ - // TODO (SLAP-2930): Remove top-level fragments from layouts - layouts: initialStudioData.layoutNameToLayoutState, + layouts: removeTopLevelFragments(initialStudioData.layoutNameToLayoutState), }); export default createLayoutSlice; diff --git a/packages/studio/src/store/slices/pages/createPageSlice.ts b/packages/studio/src/store/slices/createPageSlice.ts similarity index 94% rename from packages/studio/src/store/slices/pages/createPageSlice.ts rename to packages/studio/src/store/slices/createPageSlice.ts index a286a7566..7482f159b 100644 --- a/packages/studio/src/store/slices/pages/createPageSlice.ts +++ b/packages/studio/src/store/slices/createPageSlice.ts @@ -6,11 +6,11 @@ import { } from "@yext/studio-plugin"; import { isEqual } from "lodash"; import initialStudioData from "virtual_yext-studio"; -import DOMRectProperties from "../../models/DOMRectProperties"; -import PageSlice, { PageSliceStates } from "../../models/slices/PageSlice"; -import { SliceCreator } from "../../models/utils"; -import removeTopLevelFragments from "../../../utils/removeTopLevelFragments"; -import PropValueHelpers from "../../../utils/PropValueHelpers"; +import DOMRectProperties from "../models/DOMRectProperties"; +import PageSlice, { PageSliceStates } from "../models/slices/PageSlice"; +import { SliceCreator } from "../models/utils"; +import removeTopLevelFragments from "../../utils/removeTopLevelFragments"; +import PropValueHelpers from "../../utils/PropValueHelpers"; const firstPageEntry = Object.entries( initialStudioData.pageNameToPageState diff --git a/packages/studio/src/store/useStudioStore.ts b/packages/studio/src/store/useStudioStore.ts index af8292379..ac523f687 100644 --- a/packages/studio/src/store/useStudioStore.ts +++ b/packages/studio/src/store/useStudioStore.ts @@ -5,7 +5,7 @@ import { enableMapSet } from "immer"; import { StudioStore } from "./models/StudioStore"; import createFileMetadataSlice from "./slices/createFileMetadataSlice"; -import createPageSlice from "./slices/pages/createPageSlice"; +import createPageSlice from "./slices/createPageSlice"; import createSiteSettingSlice from "./slices/createSiteSettingsSlice"; import createPagePreviewSlice from "./slices/createPagePreviewSlice"; import StudioActions from "./StudioActions"; diff --git a/packages/studio/src/utils/removeTopLevelFragments.ts b/packages/studio/src/utils/removeTopLevelFragments.ts index c3f6fc237..975797700 100644 --- a/packages/studio/src/utils/removeTopLevelFragments.ts +++ b/packages/studio/src/utils/removeTopLevelFragments.ts @@ -2,15 +2,16 @@ import { ComponentStateKind, ComponentState, PageState, + LayoutState, } from "@yext/studio-plugin"; /** * Iterates through a record of objects that contain a componentTree, * and removes all top level fragments. */ -export default function removeTopLevelFragments( - record: Record -): Record { +export default function removeTopLevelFragments< + T extends PageState | LayoutState +>(record: Record): Record { const entries = Object.entries(record).map( ([key, componentTreeContainer]) => { const updatedContainer = {