@@ -83,7 +83,7 @@ export default function DialogModal({
return (
diff --git a/packages/studio/src/components/common/Divider.tsx b/packages/studio-ui/src/components/common/Divider.tsx
similarity index 100%
rename from packages/studio/src/components/common/Divider.tsx
rename to packages/studio-ui/src/components/common/Divider.tsx
diff --git a/packages/studio/src/components/common/ErrorBoundary.tsx b/packages/studio-ui/src/components/common/ErrorBoundary.tsx
similarity index 100%
rename from packages/studio/src/components/common/ErrorBoundary.tsx
rename to packages/studio-ui/src/components/common/ErrorBoundary.tsx
diff --git a/packages/studio/src/components/common/FormModal.tsx b/packages/studio-ui/src/components/common/FormModal.tsx
similarity index 100%
rename from packages/studio/src/components/common/FormModal.tsx
rename to packages/studio-ui/src/components/common/FormModal.tsx
diff --git a/packages/studio/src/components/common/MessageBubble.tsx b/packages/studio-ui/src/components/common/MessageBubble.tsx
similarity index 100%
rename from packages/studio/src/components/common/MessageBubble.tsx
rename to packages/studio-ui/src/components/common/MessageBubble.tsx
diff --git a/packages/studio/src/components/common/Modal.tsx b/packages/studio-ui/src/components/common/Modal.tsx
similarity index 100%
rename from packages/studio/src/components/common/Modal.tsx
rename to packages/studio-ui/src/components/common/Modal.tsx
diff --git a/packages/studio/src/components/common/OptionPicker.tsx b/packages/studio-ui/src/components/common/OptionPicker.tsx
similarity index 100%
rename from packages/studio/src/components/common/OptionPicker.tsx
rename to packages/studio-ui/src/components/common/OptionPicker.tsx
diff --git a/packages/studio/src/components/common/StreamScopeField.tsx b/packages/studio-ui/src/components/common/StreamScopeField.tsx
similarity index 100%
rename from packages/studio/src/components/common/StreamScopeField.tsx
rename to packages/studio-ui/src/components/common/StreamScopeField.tsx
diff --git a/packages/studio/src/components/common/StreamScopeFieldLabel.tsx b/packages/studio-ui/src/components/common/StreamScopeFieldLabel.tsx
similarity index 100%
rename from packages/studio/src/components/common/StreamScopeFieldLabel.tsx
rename to packages/studio-ui/src/components/common/StreamScopeFieldLabel.tsx
diff --git a/packages/studio/src/components/common/Toggle.tsx b/packages/studio-ui/src/components/common/Toggle.tsx
similarity index 100%
rename from packages/studio/src/components/common/Toggle.tsx
rename to packages/studio-ui/src/components/common/Toggle.tsx
diff --git a/packages/studio/src/components/common/renderIconForType.tsx b/packages/studio-ui/src/components/common/renderIconForType.tsx
similarity index 81%
rename from packages/studio/src/components/common/renderIconForType.tsx
rename to packages/studio-ui/src/components/common/renderIconForType.tsx
index 8f5c74b54..9452add93 100644
--- a/packages/studio/src/components/common/renderIconForType.tsx
+++ b/packages/studio-ui/src/components/common/renderIconForType.tsx
@@ -1,4 +1,3 @@
-import { ReactComponent as Hexagon } from "../../icons/hexagon.svg";
import { ReactComponent as Box } from "../../icons/box.svg";
import { ReactComponent as Container } from "../../icons/container.svg";
import { ElementType } from "../AddElementMenu/AddElementMenu";
@@ -11,9 +10,8 @@ export default function renderIconForType(type: ElementType) {
case ElementType.Components:
return
;
case ElementType.Containers:
+ case ElementType.Layouts:
return
;
- case ElementType.Modules:
- return
;
default:
console.error(`Could not find Icon for type ${type}`);
return null;
diff --git a/packages/studio/src/index.d.ts b/packages/studio-ui/src/global.d.ts
similarity index 100%
rename from packages/studio/src/index.d.ts
rename to packages/studio-ui/src/global.d.ts
diff --git a/packages/studio/src/hooks/useActiveComponent.tsx b/packages/studio-ui/src/hooks/useActiveComponent.tsx
similarity index 70%
rename from packages/studio/src/hooks/useActiveComponent.tsx
rename to packages/studio-ui/src/hooks/useActiveComponent.tsx
index 5a3dcdb0a..b25e81af8 100644
--- a/packages/studio/src/hooks/useActiveComponent.tsx
+++ b/packages/studio-ui/src/hooks/useActiveComponent.tsx
@@ -2,7 +2,6 @@ import {
ComponentState,
ComponentStateKind,
FileMetadata,
- TypeGuards,
} from "@yext/studio-plugin";
import useStudioStore from "../store/useStudioStore";
@@ -16,13 +15,8 @@ export default function useActiveComponent(): {
return useStudioStore((store) => {
const activeComponentState = store.actions.getActiveComponentState();
const activeComponentMetadata =
- activeComponentState &&
- TypeGuards.isStandardOrModuleComponentState(activeComponentState)
+ activeComponentState?.kind === ComponentStateKind.Standard
? store.fileMetadatas.getFileMetadata(activeComponentState.metadataUUID)
- : activeComponentState?.kind === ComponentStateKind.Repeater
- ? store.fileMetadatas.getFileMetadata(
- activeComponentState.repeatedComponent.metadataUUID
- )
: undefined;
return {
activeComponentMetadata,
diff --git a/packages/studio/src/hooks/useActiveComponentName.tsx b/packages/studio-ui/src/hooks/useActiveComponentName.tsx
similarity index 72%
rename from packages/studio/src/hooks/useActiveComponentName.tsx
rename to packages/studio-ui/src/hooks/useActiveComponentName.tsx
index c25a30f6e..4b58d3824 100644
--- a/packages/studio/src/hooks/useActiveComponentName.tsx
+++ b/packages/studio-ui/src/hooks/useActiveComponentName.tsx
@@ -1,8 +1,4 @@
-import {
- ComponentState,
- ComponentStateKind,
- TypeGuards,
-} from "@yext/studio-plugin";
+import { ComponentState, ComponentStateKind } from "@yext/studio-plugin";
import useStudioStore from "../store/useStudioStore";
export default function useActiveComponentName(): string | undefined {
@@ -19,8 +15,6 @@ export default function useActiveComponentName(): string | undefined {
export function getComponentDisplayName(componentState: ComponentState) {
if (componentState.kind === ComponentStateKind.Fragment) {
return "Fragment";
- } else if (TypeGuards.isRepeaterState(componentState)) {
- return `List (${componentState.repeatedComponent.componentName})`;
} else {
return componentState.componentName;
}
diff --git a/packages/studio/src/hooks/useActiveComponentWithProps.tsx b/packages/studio-ui/src/hooks/useActiveComponentWithProps.tsx
similarity index 54%
rename from packages/studio/src/hooks/useActiveComponentWithProps.tsx
rename to packages/studio-ui/src/hooks/useActiveComponentWithProps.tsx
index 3158e89b6..f1d07c945 100644
--- a/packages/studio/src/hooks/useActiveComponentWithProps.tsx
+++ b/packages/studio-ui/src/hooks/useActiveComponentWithProps.tsx
@@ -1,12 +1,17 @@
import {
- ComponentStateHelpers,
ComponentStateKind,
+ FileMetadata,
FileMetadataKind,
- TypeGuards,
+ PropShape,
+ StandardComponentState,
} from "@yext/studio-plugin";
import useActiveComponent from "./useActiveComponent";
-export default function useActiveComponentWithProps() {
+export default function useActiveComponentWithProps(): {
+ activeComponentMetadata: FileMetadata;
+ propShape: PropShape;
+ activeComponentState: StandardComponentState;
+} | null {
const { activeComponentMetadata, activeComponentState } =
useActiveComponent();
@@ -17,17 +22,7 @@ export default function useActiveComponentWithProps() {
return null;
}
- if (
- !activeComponentState ||
- !TypeGuards.isEditableComponentState(activeComponentState)
- ) {
- return null;
- }
-
- const extractedComponentState =
- ComponentStateHelpers.extractRepeatedState(activeComponentState);
-
- if (extractedComponentState.kind === ComponentStateKind.Error) {
+ if (activeComponentState?.kind !== ComponentStateKind.Standard) {
return null;
}
@@ -35,6 +30,5 @@ export default function useActiveComponentWithProps() {
activeComponentMetadata,
propShape: activeComponentMetadata.propShape,
activeComponentState,
- extractedComponentState,
};
}
diff --git a/packages/studio/src/hooks/useComposedCssClasses.tsx b/packages/studio-ui/src/hooks/useComposedCssClasses.tsx
similarity index 100%
rename from packages/studio/src/hooks/useComposedCssClasses.tsx
rename to packages/studio-ui/src/hooks/useComposedCssClasses.tsx
diff --git a/packages/studio/src/hooks/useFuncWithZundoBatching.tsx b/packages/studio-ui/src/hooks/useFuncWithZundoBatching.tsx
similarity index 95%
rename from packages/studio/src/hooks/useFuncWithZundoBatching.tsx
rename to packages/studio-ui/src/hooks/useFuncWithZundoBatching.tsx
index f4bb997f2..40bc8e7ac 100644
--- a/packages/studio/src/hooks/useFuncWithZundoBatching.tsx
+++ b/packages/studio-ui/src/hooks/useFuncWithZundoBatching.tsx
@@ -1,6 +1,6 @@
import { useCallback, useMemo } from "react";
import useTemporalStore from "../store/useTemporalStore";
-import { debounce } from "lodash";
+import debounce from "lodash/debounce";
/**
* Updates a function so it doesn't trigger Zundo store updates until after a
diff --git a/packages/studio/src/hooks/useHasChanges.ts b/packages/studio-ui/src/hooks/useHasChanges.ts
similarity index 62%
rename from packages/studio/src/hooks/useHasChanges.ts
rename to packages/studio-ui/src/hooks/useHasChanges.ts
index 0cd7c3ac6..22811178b 100644
--- a/packages/studio/src/hooks/useHasChanges.ts
+++ b/packages/studio-ui/src/hooks/useHasChanges.ts
@@ -1,5 +1,5 @@
import useStudioStore from "../store/useStudioStore";
-import { isEqual } from "lodash";
+import isEqual from "lodash/isEqual";
export default function useHasChanges() {
// TODO(SLAP-2556) Refactor pendingChanges to use PreviousSaveSlice
@@ -7,9 +7,7 @@ export default function useHasChanges() {
store.pages.pendingChanges.pagesToRemove,
store.pages.pendingChanges.pagesToUpdate,
]);
- const UUIDToFileMetadata = useStudioStore(
- (store) => store.fileMetadatas.UUIDToFileMetadata
- );
+
const previousSave = useStudioStore((store) => store.previousSave);
const siteSettingsValues = useStudioStore(
(store) => store.siteSettings.values
@@ -19,15 +17,8 @@ export default function useHasChanges() {
previousSave.siteSettings.values,
siteSettingsValues
);
- const hasFileMetadataChanges = !isEqual(
- previousSave.fileMetadatas.UUIDToFileMetadata,
- UUIDToFileMetadata
- );
return (
- pagesToRemove.size > 0 ||
- pagesToUpdate.size > 0 ||
- siteSettingsHaveChanged ||
- hasFileMetadataChanges
+ pagesToRemove.size > 0 || pagesToUpdate.size > 0 || siteSettingsHaveChanged
);
}
diff --git a/packages/studio/src/hooks/useImportedComponents.tsx b/packages/studio-ui/src/hooks/useImportedComponents.tsx
similarity index 80%
rename from packages/studio/src/hooks/useImportedComponents.tsx
rename to packages/studio-ui/src/hooks/useImportedComponents.tsx
index 03ab78467..a2d597c2f 100644
--- a/packages/studio/src/hooks/useImportedComponents.tsx
+++ b/packages/studio-ui/src/hooks/useImportedComponents.tsx
@@ -3,8 +3,8 @@ import useStudioStore from "../store/useStudioStore";
import { ComponentState } from "@yext/studio-plugin";
/**
- * Load all functional component methods correspond to the components
- * and modules use in the provided page state's component tree.
+ * Load all functional component methods corresponding to the components
+ * used in the provided page state's component tree.
*/
export default async function useImportedComponents(
componentTree?: ComponentState[]
diff --git a/packages/studio/src/hooks/useOnPropChange.tsx b/packages/studio-ui/src/hooks/useOnPropChange.tsx
similarity index 100%
rename from packages/studio/src/hooks/useOnPropChange.tsx
rename to packages/studio-ui/src/hooks/useOnPropChange.tsx
diff --git a/packages/studio/src/hooks/usePreviewProps.tsx b/packages/studio-ui/src/hooks/usePreviewProps.tsx
similarity index 73%
rename from packages/studio/src/hooks/usePreviewProps.tsx
rename to packages/studio-ui/src/hooks/usePreviewProps.tsx
index caa18f01b..855aef8e1 100644
--- a/packages/studio/src/hooks/usePreviewProps.tsx
+++ b/packages/studio-ui/src/hooks/usePreviewProps.tsx
@@ -1,5 +1,4 @@
import {
- TypeGuards,
ComponentState,
FileMetadataKind,
ComponentStateKind,
@@ -13,12 +12,11 @@ import {
/**
* Gets the previewable props for the specified component, with any expressions
- * hydrated from the expression sources and parent item.
+ * hydrated from the expression sources.
*/
export default function usePreviewProps(
c: ComponentState | undefined,
- expressionSources: ExpressionSources,
- parentItem?: unknown
+ expressionSources: ExpressionSources
): Record
{
const getFileMetadata = useStudioStore(
(store) => store.fileMetadatas.getFileMetadata
@@ -31,12 +29,12 @@ export default function usePreviewProps(
}, {});
}
- if (!c || !TypeGuards.isStandardOrModuleComponentState(c)) {
+ if (c?.kind !== ComponentStateKind.Standard) {
return {};
}
const fileMetadata = getFileMetadata(c.metadataUUID);
- if (!fileMetadata || fileMetadata.kind === FileMetadataKind.Error) {
+ if (fileMetadata?.kind === FileMetadataKind.Error) {
throw new Error(
`Cannot get propShape for FileMetadata of ${c.componentName}`
);
@@ -44,7 +42,6 @@ export default function usePreviewProps(
return getPropsForPreview(c.props, fileMetadata?.propShape ?? {}, {
...expressionSources,
- ...(parentItem !== undefined && { item: parentItem }),
});
- }, [c, expressionSources, parentItem, getFileMetadata]);
+ }, [c, expressionSources, getFileMetadata]);
}
diff --git a/packages/studio/src/hooks/useRawSiteSettings.tsx b/packages/studio-ui/src/hooks/useRawSiteSettings.tsx
similarity index 100%
rename from packages/studio/src/hooks/useRawSiteSettings.tsx
rename to packages/studio-ui/src/hooks/useRawSiteSettings.tsx
diff --git a/packages/studio/src/icons/addcomponent.svg b/packages/studio-ui/src/icons/addcomponent.svg
similarity index 100%
rename from packages/studio/src/icons/addcomponent.svg
rename to packages/studio-ui/src/icons/addcomponent.svg
diff --git a/packages/studio/src/icons/box.svg b/packages/studio-ui/src/icons/box.svg
similarity index 100%
rename from packages/studio/src/icons/box.svg
rename to packages/studio-ui/src/icons/box.svg
diff --git a/packages/studio/src/icons/check.svg b/packages/studio-ui/src/icons/check.svg
similarity index 100%
rename from packages/studio/src/icons/check.svg
rename to packages/studio-ui/src/icons/check.svg
diff --git a/packages/studio/src/icons/container.svg b/packages/studio-ui/src/icons/container.svg
similarity index 100%
rename from packages/studio/src/icons/container.svg
rename to packages/studio-ui/src/icons/container.svg
diff --git a/packages/studio/src/icons/content.svg b/packages/studio-ui/src/icons/content.svg
similarity index 100%
rename from packages/studio/src/icons/content.svg
rename to packages/studio-ui/src/icons/content.svg
diff --git a/packages/studio/src/icons/deletemodule.svg b/packages/studio-ui/src/icons/deletemodule.svg
similarity index 100%
rename from packages/studio/src/icons/deletemodule.svg
rename to packages/studio-ui/src/icons/deletemodule.svg
diff --git a/packages/studio/src/icons/detachmodule.svg b/packages/studio-ui/src/icons/detachmodule.svg
similarity index 100%
rename from packages/studio/src/icons/detachmodule.svg
rename to packages/studio-ui/src/icons/detachmodule.svg
diff --git a/packages/studio/src/icons/editmodule.svg b/packages/studio-ui/src/icons/editmodule.svg
similarity index 100%
rename from packages/studio/src/icons/editmodule.svg
rename to packages/studio-ui/src/icons/editmodule.svg
diff --git a/packages/studio/src/icons/ellipses.svg b/packages/studio-ui/src/icons/ellipses.svg
similarity index 100%
rename from packages/studio/src/icons/ellipses.svg
rename to packages/studio-ui/src/icons/ellipses.svg
diff --git a/packages/studio/src/icons/embed.svg b/packages/studio-ui/src/icons/embed.svg
similarity index 100%
rename from packages/studio/src/icons/embed.svg
rename to packages/studio-ui/src/icons/embed.svg
diff --git a/packages/studio/src/icons/gear.svg b/packages/studio-ui/src/icons/gear.svg
similarity index 100%
rename from packages/studio/src/icons/gear.svg
rename to packages/studio-ui/src/icons/gear.svg
diff --git a/packages/studio/src/icons/globe.svg b/packages/studio-ui/src/icons/globe.svg
similarity index 100%
rename from packages/studio/src/icons/globe.svg
rename to packages/studio-ui/src/icons/globe.svg
diff --git a/packages/studio/src/icons/info.svg b/packages/studio-ui/src/icons/info.svg
similarity index 100%
rename from packages/studio/src/icons/info.svg
rename to packages/studio-ui/src/icons/info.svg
diff --git a/packages/studio/src/icons/plus.svg b/packages/studio-ui/src/icons/plus.svg
similarity index 100%
rename from packages/studio/src/icons/plus.svg
rename to packages/studio-ui/src/icons/plus.svg
diff --git a/packages/studio/src/icons/scope.svg b/packages/studio-ui/src/icons/scope.svg
similarity index 100%
rename from packages/studio/src/icons/scope.svg
rename to packages/studio-ui/src/icons/scope.svg
diff --git a/packages/studio/src/icons/sliders.svg b/packages/studio-ui/src/icons/sliders.svg
similarity index 100%
rename from packages/studio/src/icons/sliders.svg
rename to packages/studio-ui/src/icons/sliders.svg
diff --git a/packages/studio/src/icons/undo.svg b/packages/studio-ui/src/icons/undo.svg
similarity index 100%
rename from packages/studio/src/icons/undo.svg
rename to packages/studio-ui/src/icons/undo.svg
diff --git a/packages/studio/src/icons/vector.svg b/packages/studio-ui/src/icons/vector.svg
similarity index 100%
rename from packages/studio/src/icons/vector.svg
rename to packages/studio-ui/src/icons/vector.svg
diff --git a/packages/studio/src/icons/viewport.svg b/packages/studio-ui/src/icons/viewport.svg
similarity index 100%
rename from packages/studio/src/icons/viewport.svg
rename to packages/studio-ui/src/icons/viewport.svg
diff --git a/packages/studio/src/icons/x.svg b/packages/studio-ui/src/icons/x.svg
similarity index 100%
rename from packages/studio/src/icons/x.svg
rename to packages/studio-ui/src/icons/x.svg
diff --git a/packages/studio/src/icons/yextfavicon.svg b/packages/studio-ui/src/icons/yextfavicon.svg
similarity index 100%
rename from packages/studio/src/icons/yextfavicon.svg
rename to packages/studio-ui/src/icons/yextfavicon.svg
diff --git a/packages/studio-ui/src/index.ts b/packages/studio-ui/src/index.ts
new file mode 100644
index 000000000..3fe76c509
--- /dev/null
+++ b/packages/studio-ui/src/index.ts
@@ -0,0 +1,3 @@
+export { default as App } from "./App";
+export { default as hotReloadStore } from "./store/hotReloadStore";
+export { StudioHMRUpdateID } from "@yext/studio-plugin";
diff --git a/packages/studio/src/messaging/sendMessage.ts b/packages/studio-ui/src/messaging/sendMessage.ts
similarity index 100%
rename from packages/studio/src/messaging/sendMessage.ts
rename to packages/studio-ui/src/messaging/sendMessage.ts
diff --git a/packages/studio/src/store/StudioActions.ts b/packages/studio-ui/src/store/StudioActions.ts
similarity index 74%
rename from packages/studio/src/store/StudioActions.ts
rename to packages/studio-ui/src/store/StudioActions.ts
index 1e2623593..11f832796 100644
--- a/packages/studio/src/store/StudioActions.ts
+++ b/packages/studio-ui/src/store/StudioActions.ts
@@ -2,17 +2,14 @@ import {
ComponentState,
ComponentStateKind,
ComponentTreeHelpers,
+ LayoutState,
MessageID,
- ModuleMetadata,
- ModuleState,
PropValues,
- TypeGuards,
} from "@yext/studio-plugin";
import FileMetadataSlice from "./models/slices/FileMetadataSlice";
import PageSlice from "./models/slices/PageSlice";
-import { v4 } from "uuid";
import sendMessage from "../messaging/sendMessage";
-import { cloneDeep } from "lodash";
+import cloneDeep from "lodash/cloneDeep";
import SiteSettingsSlice from "./models/slices/SiteSettingsSlice";
import PreviousSaveSlice from "./models/slices/PreviousSaveSlice";
import StudioConfigSlice from "./models/slices/StudioConfigSlice";
@@ -24,6 +21,7 @@ import GenerateTestDataAction from "./StudioActions/GenerateTestDataAction";
import CreatePageAction from "./StudioActions/CreatePageAction";
import dynamicImportFromBrowser from "../utils/dynamicImportFromBrowser";
import path from "path-browserify";
+import { v4 } from "uuid";
export default class StudioActions {
public addComponent: AddComponentAction["addComponent"];
@@ -62,10 +60,7 @@ export default class StudioActions {
}
getComponentTree = () => {
- const moduleMetadataBeingEdited = this.getModuleMetadataBeingEdited();
- return moduleMetadataBeingEdited
- ? moduleMetadataBeingEdited.componentTree
- : this.getPages().getActivePageState()?.componentTree;
+ return this.getPages().getActivePageState()?.componentTree;
};
getComponentState = (componentTree?: ComponentState[], uuid?: string) => {
@@ -90,18 +85,6 @@ export default class StudioActions {
);
};
- getModuleMetadataBeingEdited = () => {
- const { moduleUUIDBeingEdited, getActivePageState } = this.getPages();
- const state = this.getComponentState(
- getActivePageState()?.componentTree,
- moduleUUIDBeingEdited
- );
- if (!state || !TypeGuards.isModuleState(state)) {
- return undefined;
- }
- return this.getFileMetadatas().getModuleMetadata(state.metadataUUID);
- };
-
updateActiveComponentProps = (props: PropValues) => {
const activeComponentState = this.getActiveComponentState();
if (!activeComponentState) {
@@ -119,17 +102,7 @@ export default class StudioActions {
);
}
- const updatedComponentState = TypeGuards.isRepeaterState(
- activeComponentState
- )
- ? {
- ...activeComponentState,
- repeatedComponent: {
- ...activeComponentState.repeatedComponent,
- props,
- },
- }
- : { ...activeComponentState, props };
+ const updatedComponentState = { ...activeComponentState, props };
this.replaceComponentState(
activeComponentState.uuid,
@@ -138,15 +111,8 @@ export default class StudioActions {
};
updateComponentTree = (componentTree: ComponentState[]) => {
- const moduleMetadataBeingEdited = this.getModuleMetadataBeingEdited();
const activePageName = this.getPages().activePageName;
-
- if (moduleMetadataBeingEdited) {
- this.getFileMetadatas().setComponentTreeInModule(
- moduleMetadataBeingEdited.metadataUUID,
- componentTree
- );
- } else if (activePageName) {
+ if (activePageName) {
this.getPages().setComponentTreeInPage(activePageName, componentTree);
}
};
@@ -186,38 +152,6 @@ export default class StudioActions {
}
};
- /**
- * @param moduleMetadata - the {@link ModuleMetadata} of the module to detach.
- * @param instanceUUID - the instance to detach.
- */
- detachModuleInstance = (
- moduleMetadata: ModuleMetadata,
- moduleState: ModuleState
- ) => {
- const currentComponentTree = this.getComponentTree();
- if (!currentComponentTree) {
- return;
- }
- const updatedComponentTree = currentComponentTree.flatMap(
- (componentState) => {
- if (componentState.uuid !== moduleState.uuid) {
- return componentState;
- }
- return ComponentTreeHelpers.mapComponentTreeParentsFirst(
- moduleMetadata.componentTree,
- (child, parentValue) => {
- return {
- ...child,
- uuid: v4(),
- parentUUID: parentValue?.uuid ?? componentState.parentUUID,
- };
- }
- );
- }
- );
- this.updateComponentTree(updatedComponentTree);
- };
-
deploy = async () => {
await sendMessage(MessageID.Deploy, this.getSaveData());
this.updatePreviousSave();
@@ -251,16 +185,32 @@ export default class StudioActions {
this.getPages().setActivePageEntities(entitiesRecord);
};
+ addLayout = (layoutState: LayoutState) => {
+ const tree = this.getComponentTree() ?? [];
+ const layoutTreeWithNewUUIDs: ComponentState[] =
+ ComponentTreeHelpers.mapComponentTreeParentsFirst(
+ layoutState.componentTree,
+ (componentState, parentState) => {
+ const updatedComponentState = {
+ ...componentState,
+ uuid: v4(),
+ };
+ if (parentState) {
+ updatedComponentState.parentUUID = parentState.uuid;
+ }
+ return updatedComponentState;
+ }
+ );
+ const updatedTree = layoutTreeWithNewUUIDs.concat(tree);
+ this.updateComponentTree(updatedTree);
+ };
+
private updatePreviousSave = () => {
- const { UUIDToFileMetadata } = this.getFileMetadatas();
const { values } = this.getSiteSettings();
const previousSaveState = cloneDeep({
siteSettings: {
values,
},
- fileMetadatas: {
- UUIDToFileMetadata,
- },
});
this.getPreviousSave().setPreviousSave(previousSaveState);
};
@@ -268,11 +218,9 @@ export default class StudioActions {
private getSaveData = () => {
const { pages, pendingChanges: pendingPageChanges } = this.getPages();
const { pagesToRemove, pagesToUpdate } = pendingPageChanges;
- const { UUIDToFileMetadata } = this.getFileMetadatas();
const { values } = this.getSiteSettings();
return {
pageNameToPageState: pages,
- UUIDToFileMetadata,
pendingChanges: {
pagesToRemove: [...pagesToRemove.keys()],
pagesToUpdate: [...pagesToUpdate],
diff --git a/packages/studio/src/store/StudioActions/AddComponentAction.ts b/packages/studio-ui/src/store/StudioActions/AddComponentAction.ts
similarity index 95%
rename from packages/studio/src/store/StudioActions/AddComponentAction.ts
rename to packages/studio-ui/src/store/StudioActions/AddComponentAction.ts
index a93ab4c61..2c23b6e80 100644
--- a/packages/studio/src/store/StudioActions/AddComponentAction.ts
+++ b/packages/studio-ui/src/store/StudioActions/AddComponentAction.ts
@@ -1,7 +1,7 @@
import {
ComponentState,
TypeGuards,
- ValidFileMetadata,
+ ComponentMetadata,
} from "@yext/studio-plugin";
import StudioActions from "../StudioActions";
@@ -11,7 +11,7 @@ export default class AddComponentAction {
/**
* Adds the component to the current active component tree.
*/
- addComponent = (metadata: ValidFileMetadata) => {
+ addComponent = (metadata: ComponentMetadata) => {
const componentState = this.studioActions.createComponentState(metadata);
return this.insertComponentState(componentState);
};
diff --git a/packages/studio/src/store/StudioActions/CreateComponentStateAction.ts b/packages/studio-ui/src/store/StudioActions/CreateComponentStateAction.ts
similarity index 62%
rename from packages/studio/src/store/StudioActions/CreateComponentStateAction.ts
rename to packages/studio-ui/src/store/StudioActions/CreateComponentStateAction.ts
index 90d11de82..4b33b4b5a 100644
--- a/packages/studio/src/store/StudioActions/CreateComponentStateAction.ts
+++ b/packages/studio-ui/src/store/StudioActions/CreateComponentStateAction.ts
@@ -1,8 +1,7 @@
import {
ComponentStateKind,
- FileMetadataKind,
- StandardOrModuleComponentState,
- ValidFileMetadata,
+ StandardComponentState,
+ ComponentMetadata,
} from "@yext/studio-plugin";
import path from "path-browserify";
import { v4 } from "uuid";
@@ -10,14 +9,11 @@ import PropValueHelpers from "../../utils/PropValueHelpers";
export default class CreateComponentStateAction {
createComponentState = (
- metadata: ValidFileMetadata
- ): StandardOrModuleComponentState => {
+ metadata: ComponentMetadata
+ ): StandardComponentState => {
const componentName = path.basename(metadata.filepath, ".tsx");
- const componentState: StandardOrModuleComponentState = {
- kind:
- metadata.kind === FileMetadataKind.Module
- ? ComponentStateKind.Module
- : ComponentStateKind.Standard,
+ const componentState: StandardComponentState = {
+ kind: ComponentStateKind.Standard,
componentName,
props: {
...PropValueHelpers.getDefaultPropValues(metadata.propShape ?? {}),
diff --git a/packages/studio/src/store/StudioActions/CreatePageAction.ts b/packages/studio-ui/src/store/StudioActions/CreatePageAction.ts
similarity index 100%
rename from packages/studio/src/store/StudioActions/CreatePageAction.ts
rename to packages/studio-ui/src/store/StudioActions/CreatePageAction.ts
diff --git a/packages/studio/src/store/StudioActions/GenerateTestDataAction.ts b/packages/studio-ui/src/store/StudioActions/GenerateTestDataAction.ts
similarity index 98%
rename from packages/studio/src/store/StudioActions/GenerateTestDataAction.ts
rename to packages/studio-ui/src/store/StudioActions/GenerateTestDataAction.ts
index a620de853..98dc1eeaa 100644
--- a/packages/studio/src/store/StudioActions/GenerateTestDataAction.ts
+++ b/packages/studio-ui/src/store/StudioActions/GenerateTestDataAction.ts
@@ -10,7 +10,7 @@ import {
import { Stream } from "@yext/pages";
import PageSlice from "../models/slices/PageSlice";
import sendMessage from "../../messaging/sendMessage";
-import { isEqual } from "lodash";
+import isEqual from "lodash/isEqual";
import StudioActions from "../StudioActions";
export default class GenerateTestDataAction {
diff --git a/packages/studio/src/store/StudioActions/ImportComponentAction.ts b/packages/studio-ui/src/store/StudioActions/ImportComponentAction.ts
similarity index 62%
rename from packages/studio/src/store/StudioActions/ImportComponentAction.ts
rename to packages/studio-ui/src/store/StudioActions/ImportComponentAction.ts
index e4210edc7..e16621426 100644
--- a/packages/studio/src/store/StudioActions/ImportComponentAction.ts
+++ b/packages/studio-ui/src/store/StudioActions/ImportComponentAction.ts
@@ -1,13 +1,9 @@
import {
ComponentState,
- ComponentStateHelpers,
ComponentStateKind,
ErrorComponentState,
FileMetadata,
- FileMetadataKind,
- ModuleMetadata,
- StandardOrModuleComponentState,
- TypeGuards,
+ StandardComponentState,
} from "@yext/studio-plugin";
import FileMetadataSlice from "../models/slices/FileMetadataSlice";
import dynamicImportFromBrowser from "../../utils/dynamicImportFromBrowser";
@@ -15,27 +11,23 @@ import getFunctionComponent from "../../utils/getFunctionComponent";
/**
* Imports a component into the global store.
- *
- * Modules are not directly imported, but instead have all their constituents
- * imported instead, similar to a Page.
*/
export default class ImportComponentAction {
constructor(private getFileMetadataSlice: () => FileMetadataSlice) {}
importComponent = async (c: ComponentState): Promise => {
if (
- !TypeGuards.isEditableComponentState(c) &&
+ c.kind !== ComponentStateKind.Standard &&
c.kind !== ComponentStateKind.Error
) {
return;
}
- const componentState = ComponentStateHelpers.extractRepeatedState(c);
- await this.importStandardOrModuleComponentState(componentState);
+ await this.importStandardOrErrorComponentState(c);
};
- private importStandardOrModuleComponentState = async (
- componentState: StandardOrModuleComponentState | ErrorComponentState
+ private importStandardOrErrorComponentState = async (
+ componentState: StandardComponentState | ErrorComponentState
) => {
const { metadataUUID, componentName } = componentState;
const { getFileMetadata, UUIDToImportedComponent } =
@@ -49,10 +41,6 @@ export default class ImportComponentAction {
return;
}
- if (metadata.kind === FileMetadataKind.Module) {
- return this.importModule(metadata);
- }
-
const importedValue = await dynamicImportFromBrowser(metadata.filepath);
const functionComponent = getFunctionComponent(
importedValue,
@@ -66,10 +54,4 @@ export default class ImportComponentAction {
);
}
};
-
- importModule = async (metadata: ModuleMetadata) => {
- return Promise.all(
- metadata.componentTree.map((c) => this.importComponent(c))
- );
- };
}
diff --git a/packages/studio/src/store/StudioActions/UpdateActivePageAction.ts b/packages/studio-ui/src/store/StudioActions/UpdateActivePageAction.ts
similarity index 93%
rename from packages/studio/src/store/StudioActions/UpdateActivePageAction.ts
rename to packages/studio-ui/src/store/StudioActions/UpdateActivePageAction.ts
index fca2c545e..4039e4b43 100644
--- a/packages/studio/src/store/StudioActions/UpdateActivePageAction.ts
+++ b/packages/studio-ui/src/store/StudioActions/UpdateActivePageAction.ts
@@ -13,7 +13,6 @@ export default class UpdateActivePageAction {
updateActivePage = async (activePageName?: string): Promise => {
this.getPageSlice().setActivePage(activePageName);
- this.getPageSlice().setModuleUUIDBeingEdited(undefined);
const activePageState = this.getPageSlice().getActivePageState();
// Any file is fine so pick the first one.
const anyAcceptedEntityFile = activePageState?.pagesJS?.entityFiles?.[0];
diff --git a/packages/studio/src/store/hotReloadStore.ts b/packages/studio-ui/src/store/hotReloadStore.ts
similarity index 86%
rename from packages/studio/src/store/hotReloadStore.ts
rename to packages/studio-ui/src/store/hotReloadStore.ts
index c40e668a0..806b49116 100644
--- a/packages/studio/src/store/hotReloadStore.ts
+++ b/packages/studio-ui/src/store/hotReloadStore.ts
@@ -13,9 +13,11 @@ export default async function hotReloadStore(payload: StudioHMRPayload) {
const { updateType, studioData } = payload;
switch (updateType) {
case "components":
- case "modules":
await syncFileMetadata(studioData, payload.file);
break;
+ case "layouts":
+ syncLayouts(studioData);
+ break;
case "pages":
syncPages(studioData);
break;
@@ -30,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) => {
@@ -38,12 +41,9 @@ async function fullSync(studioData: StudioData, file: string) {
}
async function syncFileMetadata(studioData: StudioData, file: string) {
- const UUIDToFileMetadata = removeTopLevelFragments(
- studioData.UUIDToFileMetadata
- );
+ const UUIDToFileMetadata = studioData.UUIDToFileMetadata;
useStudioStore.setState((store) => {
store.fileMetadatas.UUIDToFileMetadata = UUIDToFileMetadata;
- store.previousSave.fileMetadatas.UUIDToFileMetadata = UUIDToFileMetadata;
});
const fileMetadata = Object.values(UUIDToFileMetadata).find(
(metadata) => metadata.filepath === file
@@ -82,6 +82,15 @@ function syncPages(studioData: StudioData) {
});
}
+function syncLayouts(studioData: StudioData) {
+ const layoutNameToLayoutState = removeTopLevelFragments(
+ studioData.layoutNameToLayoutState
+ );
+ useStudioStore.setState((store) => {
+ store.layouts.layoutNameToLayoutState = layoutNameToLayoutState;
+ });
+}
+
function syncSiteSettings(studioData: StudioData) {
useStudioStore.setState((store) => {
store.siteSettings.shape = studioData.siteSettings?.shape;
diff --git a/packages/studio/src/store/models/DOMRectProperties.ts b/packages/studio-ui/src/store/models/DOMRectProperties.ts
similarity index 100%
rename from packages/studio/src/store/models/DOMRectProperties.ts
rename to packages/studio-ui/src/store/models/DOMRectProperties.ts
diff --git a/packages/studio/src/store/models/ImportType.ts b/packages/studio-ui/src/store/models/ImportType.ts
similarity index 82%
rename from packages/studio/src/store/models/ImportType.ts
rename to packages/studio-ui/src/store/models/ImportType.ts
index a640cca03..0d9c3c297 100644
--- a/packages/studio/src/store/models/ImportType.ts
+++ b/packages/studio-ui/src/store/models/ImportType.ts
@@ -2,6 +2,6 @@ import { FunctionComponent } from "react";
/**
* Describe the import shape of a Studio's React source file
- * (e.g. Module, Component, and Page).
+ * (e.g. Component or Page).
*/
export type ImportType = FunctionComponent>;
diff --git a/packages/studio/src/store/models/StudioStore.ts b/packages/studio-ui/src/store/models/StudioStore.ts
similarity index 93%
rename from packages/studio/src/store/models/StudioStore.ts
rename to packages/studio-ui/src/store/models/StudioStore.ts
index 55a04d056..5b38fda06 100644
--- a/packages/studio/src/store/models/StudioStore.ts
+++ b/packages/studio-ui/src/store/models/StudioStore.ts
@@ -1,6 +1,7 @@
import StudioActions from "../StudioActions";
import AccountContentSlice from "./slices/AccountContentSlice";
import FileMetadataSlice from "./slices/FileMetadataSlice";
+import { LayoutSlice } from "./slices/LayoutSlice";
import PagePreviewSlice from "./slices/PagePreviewSlice";
import PageSlice from "./slices/PageSlice";
import PreviousSaveSlice from "./slices/PreviousSaveSlice";
@@ -16,10 +17,10 @@ import StudioEnvDataSlice from "./slices/StudioEnvDataSlice";
export type StudioStore = {
fileMetadatas: FileMetadataSlice;
pages: PageSlice;
+ layouts: LayoutSlice;
siteSettings: SiteSettingSlice;
pagePreview: PagePreviewSlice;
previousSave: PreviousSaveSlice;
- createModule: (modulePath: string) => void;
actions: StudioActions;
studioConfig: StudioConfigSlice;
studioEnvData: StudioEnvDataSlice;
diff --git a/packages/studio/src/store/models/slices/AccountContentSlice.ts b/packages/studio-ui/src/store/models/slices/AccountContentSlice.ts
similarity index 100%
rename from packages/studio/src/store/models/slices/AccountContentSlice.ts
rename to packages/studio-ui/src/store/models/slices/AccountContentSlice.ts
diff --git a/packages/studio/src/store/models/slices/FileMetadataSlice.ts b/packages/studio-ui/src/store/models/slices/FileMetadataSlice.ts
similarity index 53%
rename from packages/studio/src/store/models/slices/FileMetadataSlice.ts
rename to packages/studio-ui/src/store/models/slices/FileMetadataSlice.ts
index 48546362a..71e7e9914 100644
--- a/packages/studio/src/store/models/slices/FileMetadataSlice.ts
+++ b/packages/studio-ui/src/store/models/slices/FileMetadataSlice.ts
@@ -1,37 +1,21 @@
-import {
- ComponentMetadata,
- ComponentState,
- FileMetadata,
- ModuleMetadata,
- ValidFileMetadata,
-} from "@yext/studio-plugin";
+import { ComponentMetadata, FileMetadata } from "@yext/studio-plugin";
import { ImportType } from "../ImportType";
export interface FileMetadataSliceStates {
- /** Metadata of all components and modules that can be used in Studio. */
+ /** Metadata of all components that can be used in Studio. */
UUIDToFileMetadata: Record;
/** Component's metadata uuid and its functional component method. */
UUIDToImportedComponent: Record;
}
export interface FileMetadataSliceActions {
- setFileMetadata: (
- metadataUUID: string,
- fileMetadata: ValidFileMetadata
- ) => void;
getFileMetadata: (metadataUUID: string) => FileMetadata | undefined;
- getModuleMetadata: (metadataUUID: string) => ModuleMetadata;
- removeFileMetadata: (metadataUUID: string) => void;
getComponentMetadata: (metadataUUID: string) => ComponentMetadata;
setImportedComponent: (uuid: string, importedComponent: ImportType) => void;
- setComponentTreeInModule: (
- metadataUUID: string,
- componentTree: ComponentState[]
- ) => void;
}
/**
- * Maintains metadata for Component and Module, available for users
+ * Maintains metadata for Components available for users
* to import and preview in Studio.
*/
type FileMetadataSlice = FileMetadataSliceStates & FileMetadataSliceActions;
diff --git a/packages/studio-ui/src/store/models/slices/LayoutSlice.ts b/packages/studio-ui/src/store/models/slices/LayoutSlice.ts
new file mode 100644
index 000000000..0aae78f10
--- /dev/null
+++ b/packages/studio-ui/src/store/models/slices/LayoutSlice.ts
@@ -0,0 +1,8 @@
+import { LayoutState } from "@yext/studio-plugin";
+
+export interface LayoutSlice {
+ /**
+ * A mapping of name to LayoutState for layouts that can be applied via Studio.
+ */
+ layoutNameToLayoutState: Record;
+}
diff --git a/packages/studio/src/store/models/slices/PagePreviewSlice.ts b/packages/studio-ui/src/store/models/slices/PagePreviewSlice.ts
similarity index 100%
rename from packages/studio/src/store/models/slices/PagePreviewSlice.ts
rename to packages/studio-ui/src/store/models/slices/PagePreviewSlice.ts
diff --git a/packages/studio/src/store/models/slices/PageSlice.ts b/packages/studio-ui/src/store/models/slices/PageSlice.ts
similarity index 90%
rename from packages/studio/src/store/models/slices/PageSlice.ts
rename to packages/studio-ui/src/store/models/slices/PageSlice.ts
index a690f2254..9463240b6 100644
--- a/packages/studio/src/store/models/slices/PageSlice.ts
+++ b/packages/studio-ui/src/store/models/slices/PageSlice.ts
@@ -2,7 +2,6 @@ import {
ComponentState,
ErrorPageState,
GetPathVal,
- ModuleMetadata,
PageState,
StreamScope,
} from "@yext/studio-plugin";
@@ -43,10 +42,6 @@ export interface PageSliceStates {
*/
pagesToUpdate: Set;
};
- /**
- * The {@link ComponentState.uuid} for the module currently being edited, if it exists.
- */
- moduleUUIDBeingEdited?: string;
}
interface PageSliceActions {
@@ -65,7 +60,6 @@ interface PageSliceActions {
setActiveComponentUUID: (activeComponentUUID: string | undefined) => void;
setActiveComponentRect: (rect: DOMRectProperties | undefined) => void;
- setModuleUUIDBeingEdited: (moduleStateUUID: string | undefined) => void;
setActiveEntityFile: (activeEntityFile?: string) => void;
setActivePageEntities: (
@@ -74,7 +68,6 @@ interface PageSliceActions {
getActiveEntityData: () => Record | undefined;
clearPendingChanges: () => void;
- detachAllModuleInstances: (metadata: ModuleMetadata) => void;
}
/**
diff --git a/packages/studio/src/store/models/slices/PreviousSaveSlice.ts b/packages/studio-ui/src/store/models/slices/PreviousSaveSlice.ts
similarity index 79%
rename from packages/studio/src/store/models/slices/PreviousSaveSlice.ts
rename to packages/studio-ui/src/store/models/slices/PreviousSaveSlice.ts
index 8aaa80786..e8be737dd 100644
--- a/packages/studio/src/store/models/slices/PreviousSaveSlice.ts
+++ b/packages/studio-ui/src/store/models/slices/PreviousSaveSlice.ts
@@ -1,4 +1,3 @@
-import FileMetadataSlice from "./FileMetadataSlice";
import SiteSettingSlice from "./SiteSettingsSlice";
/**
@@ -6,7 +5,6 @@ import SiteSettingSlice from "./SiteSettingsSlice";
*/
export interface PreviousSaveSliceState {
siteSettings: Pick;
- fileMetadatas: Pick;
}
export interface PreviousSaveSliceActions {
diff --git a/packages/studio/src/store/models/slices/SiteSettingsSlice.ts b/packages/studio-ui/src/store/models/slices/SiteSettingsSlice.ts
similarity index 100%
rename from packages/studio/src/store/models/slices/SiteSettingsSlice.ts
rename to packages/studio-ui/src/store/models/slices/SiteSettingsSlice.ts
diff --git a/packages/studio/src/store/models/slices/StudioConfigSlice.ts b/packages/studio-ui/src/store/models/slices/StudioConfigSlice.ts
similarity index 100%
rename from packages/studio/src/store/models/slices/StudioConfigSlice.ts
rename to packages/studio-ui/src/store/models/slices/StudioConfigSlice.ts
diff --git a/packages/studio/src/store/models/slices/StudioEnvDataSlice.ts b/packages/studio-ui/src/store/models/slices/StudioEnvDataSlice.ts
similarity index 100%
rename from packages/studio/src/store/models/slices/StudioEnvDataSlice.ts
rename to packages/studio-ui/src/store/models/slices/StudioEnvDataSlice.ts
diff --git a/packages/studio/src/store/models/utils.ts b/packages/studio-ui/src/store/models/utils.ts
similarity index 100%
rename from packages/studio/src/store/models/utils.ts
rename to packages/studio-ui/src/store/models/utils.ts
diff --git a/packages/studio/src/store/slices/accountContent/createAccountContentSlice.ts b/packages/studio-ui/src/store/slices/accountContent/createAccountContentSlice.ts
similarity index 100%
rename from packages/studio/src/store/slices/accountContent/createAccountContentSlice.ts
rename to packages/studio-ui/src/store/slices/accountContent/createAccountContentSlice.ts
diff --git a/packages/studio/src/store/slices/accountContent/utils.ts b/packages/studio-ui/src/store/slices/accountContent/utils.ts
similarity index 100%
rename from packages/studio/src/store/slices/accountContent/utils.ts
rename to packages/studio-ui/src/store/slices/accountContent/utils.ts
diff --git a/packages/studio-ui/src/store/slices/createFileMetadataSlice.ts b/packages/studio-ui/src/store/slices/createFileMetadataSlice.ts
new file mode 100644
index 000000000..2f47bd960
--- /dev/null
+++ b/packages/studio-ui/src/store/slices/createFileMetadataSlice.ts
@@ -0,0 +1,44 @@
+import {
+ ComponentMetadata,
+ FileMetadata,
+ FileMetadataKind,
+} from "@yext/studio-plugin";
+import initialStudioData from "virtual_yext-studio";
+import FileMetadataSlice from "../models/slices/FileMetadataSlice";
+import { SliceCreator } from "../models/utils";
+
+const createFileMetadataSlice: SliceCreator = (
+ set,
+ get
+) => ({
+ UUIDToFileMetadata: initialStudioData.UUIDToFileMetadata,
+ UUIDToImportedComponent: {},
+ getFileMetadata: (metadataUUID: string) =>
+ get().UUIDToFileMetadata[metadataUUID],
+ getComponentMetadata: (metadataUUID) => {
+ const fileMetadata = get().getFileMetadata(metadataUUID);
+ assertIsComponentMetadata(fileMetadata);
+ return fileMetadata;
+ },
+ setImportedComponent(uuid, importedComponent) {
+ set((store) => {
+ store.UUIDToImportedComponent[uuid] = importedComponent;
+ });
+ },
+});
+
+function assertIsComponentMetadata(
+ fileMetadata?: FileMetadata
+): asserts fileMetadata is ComponentMetadata {
+ if (fileMetadata?.kind !== FileMetadataKind.Component) {
+ throw new Error(
+ `Expected a ComponentMetadata, instead received ${JSON.stringify(
+ fileMetadata,
+ null,
+ 2
+ )}.`
+ );
+ }
+}
+
+export default createFileMetadataSlice;
diff --git a/packages/studio-ui/src/store/slices/createLayoutSlice.ts b/packages/studio-ui/src/store/slices/createLayoutSlice.ts
new file mode 100644
index 000000000..b10ebcad1
--- /dev/null
+++ b/packages/studio-ui/src/store/slices/createLayoutSlice.ts
@@ -0,0 +1,12 @@
+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 = () => ({
+ layoutNameToLayoutState: removeTopLevelFragments(
+ initialStudioData.layoutNameToLayoutState
+ ),
+});
+
+export default createLayoutSlice;
diff --git a/packages/studio/src/store/slices/createPagePreviewSlice.ts b/packages/studio-ui/src/store/slices/createPagePreviewSlice.ts
similarity index 100%
rename from packages/studio/src/store/slices/createPagePreviewSlice.ts
rename to packages/studio-ui/src/store/slices/createPagePreviewSlice.ts
diff --git a/packages/studio/src/store/slices/pages/createPageSlice.ts b/packages/studio-ui/src/store/slices/createPageSlice.ts
similarity index 88%
rename from packages/studio/src/store/slices/pages/createPageSlice.ts
rename to packages/studio-ui/src/store/slices/createPageSlice.ts
index 2ce693c40..872a242b6 100644
--- a/packages/studio/src/store/slices/pages/createPageSlice.ts
+++ b/packages/studio-ui/src/store/slices/createPageSlice.ts
@@ -4,14 +4,13 @@ import {
PageState,
StreamScope,
} from "@yext/studio-plugin";
-import { isEqual } from "lodash";
+import isEqual from "lodash/isEqual";
import initialStudioData from "virtual_yext-studio";
-import DOMRectProperties from "../../models/DOMRectProperties";
-import PageSlice, { PageSliceStates } from "../../models/slices/PageSlice";
-import { SliceCreator } from "../../models/utils";
-import createDetachAllModuleInstances from "./detachAllModuleInstances";
-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
@@ -28,7 +27,6 @@ const initialStates: PageSliceStates = {
pagesToRemove: new Set(),
pagesToUpdate: new Set(),
},
- moduleUUIDBeingEdited: undefined,
};
export const createPageSlice: SliceCreator = (set, get) => {
@@ -196,22 +194,12 @@ export const createPageSlice: SliceCreator = (set, get) => {
},
};
- const moduleStateActions: Pick = {
- setModuleUUIDBeingEdited(moduleStateUUID: string | undefined) {
- set((store) => {
- store.moduleUUIDBeingEdited = moduleStateUUID;
- });
- },
- };
-
return {
...initialStates,
...pageActions,
...activePageActions,
...pageComponentActions,
...activeEntityFileActions,
- ...moduleStateActions,
- detachAllModuleInstances: createDetachAllModuleInstances(get),
};
};
diff --git a/packages/studio/src/store/slices/createPreviousSaveSlice.ts b/packages/studio-ui/src/store/slices/createPreviousSaveSlice.ts
similarity index 84%
rename from packages/studio/src/store/slices/createPreviousSaveSlice.ts
rename to packages/studio-ui/src/store/slices/createPreviousSaveSlice.ts
index 0e0c9af15..aeed09ee0 100644
--- a/packages/studio/src/store/slices/createPreviousSaveSlice.ts
+++ b/packages/studio-ui/src/store/slices/createPreviousSaveSlice.ts
@@ -8,9 +8,6 @@ const createPreviousSaveSlice: SliceCreator = (set) => ({
siteSettings: {
values: initialStudioData.siteSettings?.values,
},
- fileMetadatas: {
- UUIDToFileMetadata: initialStudioData.UUIDToFileMetadata,
- },
setPreviousSave(saveState: PreviousSaveSliceState) {
set(saveState);
},
diff --git a/packages/studio/src/store/slices/createSiteSettingsSlice.ts b/packages/studio-ui/src/store/slices/createSiteSettingsSlice.ts
similarity index 100%
rename from packages/studio/src/store/slices/createSiteSettingsSlice.ts
rename to packages/studio-ui/src/store/slices/createSiteSettingsSlice.ts
diff --git a/packages/studio/src/store/slices/createStudioConfigSlice.ts b/packages/studio-ui/src/store/slices/createStudioConfigSlice.ts
similarity index 100%
rename from packages/studio/src/store/slices/createStudioConfigSlice.ts
rename to packages/studio-ui/src/store/slices/createStudioConfigSlice.ts
diff --git a/packages/studio/src/store/slices/createStudioEnvDataSlice.ts b/packages/studio-ui/src/store/slices/createStudioEnvDataSlice.ts
similarity index 100%
rename from packages/studio/src/store/slices/createStudioEnvDataSlice.ts
rename to packages/studio-ui/src/store/slices/createStudioEnvDataSlice.ts
diff --git a/packages/studio/src/store/useStudioStore.ts b/packages/studio-ui/src/store/useStudioStore.ts
similarity index 93%
rename from packages/studio/src/store/useStudioStore.ts
rename to packages/studio-ui/src/store/useStudioStore.ts
index 664e0fd24..1f565e666 100644
--- a/packages/studio/src/store/useStudioStore.ts
+++ b/packages/studio-ui/src/store/useStudioStore.ts
@@ -5,16 +5,16 @@ 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 getCreateModuleAction from "./createModuleAction";
import StudioActions from "./StudioActions";
import createStudioConfigSlice from "./slices/createStudioConfigSlice";
import createPreviousSaveSlice from "./slices/createPreviousSaveSlice";
import { addZundoMiddleware } from "./zundoMiddleware";
import createStudioEnvDataSlice from "./slices/createStudioEnvDataSlice";
import createAccountContentSlice from "./slices/accountContent/createAccountContentSlice";
+import createLayoutSlice from "./slices/createLayoutSlice";
enableMapSet();
@@ -36,9 +36,9 @@ const useStudioStore = create()(
return {
fileMetadatas: lens(createFileMetadataSlice),
pages: lens(createPageSlice),
+ layouts: lens(createLayoutSlice),
siteSettings: lens(createSiteSettingSlice),
pagePreview: lens(createPagePreviewSlice),
- createModule: getCreateModuleAction(get),
previousSave: lens(createPreviousSaveSlice),
actions: new StudioActions(
() => get().pages,
diff --git a/packages/studio/src/store/useTemporalStore.ts b/packages/studio-ui/src/store/useTemporalStore.ts
similarity index 100%
rename from packages/studio/src/store/useTemporalStore.ts
rename to packages/studio-ui/src/store/useTemporalStore.ts
diff --git a/packages/studio/src/store/zundoMiddleware.ts b/packages/studio-ui/src/store/zundoMiddleware.ts
similarity index 74%
rename from packages/studio/src/store/zundoMiddleware.ts
rename to packages/studio-ui/src/store/zundoMiddleware.ts
index 5b1f56fc1..90775fd10 100644
--- a/packages/studio/src/store/zundoMiddleware.ts
+++ b/packages/studio-ui/src/store/zundoMiddleware.ts
@@ -1,19 +1,14 @@
-import FileMetadataSlice from "./models/slices/FileMetadataSlice";
import PageSlice from "./models/slices/PageSlice";
import SiteSettingSlice from "./models/slices/SiteSettingsSlice";
import { StudioStore } from "./models/StudioStore";
-import { isEqual } from "lodash";
+import isEqual from "lodash/isEqual";
import { ZundoOptions, temporal } from "zundo";
import { TemporalStudioStore } from "./useTemporalStore";
import { StateCreator } from "zustand";
type UserUpdatableStore = {
siteSettings: Pick;
- fileMetadatas: Pick;
- pages: Pick<
- PageSlice,
- "pages" | "activePageName" | "activeComponentUUID" | "moduleUUIDBeingEdited"
- >;
+ pages: Pick;
};
/**
@@ -24,21 +19,15 @@ type UserUpdatableStore = {
*/
function getUserUpdatableStore(store: StudioStore): UserUpdatableStore {
const { values } = store.siteSettings;
- const { UUIDToFileMetadata } = store.fileMetadatas;
- const { pages, activePageName, activeComponentUUID, moduleUUIDBeingEdited } =
- store.pages;
+ const { pages, activePageName, activeComponentUUID } = store.pages;
return {
siteSettings: {
values,
},
- fileMetadatas: {
- UUIDToFileMetadata,
- },
pages: {
pages,
activePageName,
activeComponentUUID,
- moduleUUIDBeingEdited,
},
};
}
diff --git a/packages/studio/src/utils/PageDataValidator.ts b/packages/studio-ui/src/utils/PageDataValidator.ts
similarity index 100%
rename from packages/studio/src/utils/PageDataValidator.ts
rename to packages/studio-ui/src/utils/PageDataValidator.ts
diff --git a/packages/studio/src/utils/PropValueHelpers.ts b/packages/studio-ui/src/utils/PropValueHelpers.ts
similarity index 100%
rename from packages/studio/src/utils/PropValueHelpers.ts
rename to packages/studio-ui/src/utils/PropValueHelpers.ts
diff --git a/packages/studio/src/utils/StreamScopeParser.ts b/packages/studio-ui/src/utils/StreamScopeParser.ts
similarity index 100%
rename from packages/studio/src/utils/StreamScopeParser.ts
rename to packages/studio-ui/src/utils/StreamScopeParser.ts
diff --git a/packages/studio/src/utils/TemplateExpressionFormatter.ts b/packages/studio-ui/src/utils/TemplateExpressionFormatter.ts
similarity index 100%
rename from packages/studio/src/utils/TemplateExpressionFormatter.ts
rename to packages/studio-ui/src/utils/TemplateExpressionFormatter.ts
diff --git a/packages/studio/src/utils/createIsSupportedPropMetadata.ts b/packages/studio-ui/src/utils/createIsSupportedPropMetadata.ts
similarity index 100%
rename from packages/studio/src/utils/createIsSupportedPropMetadata.ts
rename to packages/studio-ui/src/utils/createIsSupportedPropMetadata.ts
diff --git a/packages/studio/src/utils/dynamicImportFromBrowser.ts b/packages/studio-ui/src/utils/dynamicImportFromBrowser.ts
similarity index 71%
rename from packages/studio/src/utils/dynamicImportFromBrowser.ts
rename to packages/studio-ui/src/utils/dynamicImportFromBrowser.ts
index 3494f3bdc..fbcad436f 100644
--- a/packages/studio/src/utils/dynamicImportFromBrowser.ts
+++ b/packages/studio-ui/src/utils/dynamicImportFromBrowser.ts
@@ -1,3 +1,5 @@
+import path from "path-browserify";
+
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* In order to support dynamic imports from the browser on Windows,
@@ -8,5 +10,6 @@
export default function dynamicImportFromBrowser(
absFilepath: string
): Promise {
- return import(/* @vite-ignore */ "/@fs/" + absFilepath);
+ const importPath = path.join("/@fs", absFilepath);
+ return import(/* @vite-ignore */ importPath);
}
diff --git a/packages/studio/src/utils/filterEntityData.ts b/packages/studio-ui/src/utils/filterEntityData.ts
similarity index 100%
rename from packages/studio/src/utils/filterEntityData.ts
rename to packages/studio-ui/src/utils/filterEntityData.ts
diff --git a/packages/studio/src/utils/getFunctionComponent.ts b/packages/studio-ui/src/utils/getFunctionComponent.ts
similarity index 100%
rename from packages/studio/src/utils/getFunctionComponent.ts
rename to packages/studio-ui/src/utils/getFunctionComponent.ts
diff --git a/packages/studio/src/utils/getPropsForPreview.ts b/packages/studio-ui/src/utils/getPropsForPreview.ts
similarity index 95%
rename from packages/studio/src/utils/getPropsForPreview.ts
rename to packages/studio-ui/src/utils/getPropsForPreview.ts
index a8eabbc67..cb17535ca 100644
--- a/packages/studio/src/utils/getPropsForPreview.ts
+++ b/packages/studio-ui/src/utils/getPropsForPreview.ts
@@ -8,7 +8,7 @@ import {
PropVal,
PropType,
} from "@yext/studio-plugin";
-import { get } from "lodash";
+import get from "lodash/get";
import TemplateExpressionFormatter from "./TemplateExpressionFormatter";
/**
@@ -197,12 +197,6 @@ function getExpressionValue(
if (TypeGuards.isStreamsDataExpression(expression)) {
return getValueFromPath(expression, "document");
}
- if (expression.startsWith("props.")) {
- return getValueFromPath(expression, "props");
- }
- if (expression.startsWith("item")) {
- return getValueFromPath(expression, "item");
- }
console.warn(
`Invalid expression: ${expression}.` +
" Expressions must reference an expression source from",
@@ -230,5 +224,5 @@ function logInvalidExpressionWarning(
}
export type ExpressionSources = {
- [key in "document" | "siteSettings" | "props"]?: Record;
-} & { item?: unknown };
+ [key in "document" | "siteSettings"]?: Record;
+};
diff --git a/packages/studio/src/utils/getSelectCssClasses.ts b/packages/studio-ui/src/utils/getSelectCssClasses.ts
similarity index 100%
rename from packages/studio/src/utils/getSelectCssClasses.ts
rename to packages/studio-ui/src/utils/getSelectCssClasses.ts
diff --git a/packages/studio/src/utils/rectToJson.ts b/packages/studio-ui/src/utils/rectToJson.ts
similarity index 100%
rename from packages/studio/src/utils/rectToJson.ts
rename to packages/studio-ui/src/utils/rectToJson.ts
diff --git a/packages/studio/src/utils/removeTopLevelFragments.ts b/packages/studio-ui/src/utils/removeTopLevelFragments.ts
similarity index 88%
rename from packages/studio/src/utils/removeTopLevelFragments.ts
rename to packages/studio-ui/src/utils/removeTopLevelFragments.ts
index c1e674ace..975797700 100644
--- a/packages/studio/src/utils/removeTopLevelFragments.ts
+++ b/packages/studio-ui/src/utils/removeTopLevelFragments.ts
@@ -1,8 +1,8 @@
import {
ComponentStateKind,
ComponentState,
- FileMetadata,
PageState,
+ LayoutState,
} from "@yext/studio-plugin";
/**
@@ -10,14 +10,10 @@ import {
* and removes all top level fragments.
*/
export default function removeTopLevelFragments<
- T extends PageState | FileMetadata
+ T extends PageState | LayoutState
>(record: Record): Record {
const entries = Object.entries(record).map(
([key, componentTreeContainer]) => {
- if (!("componentTree" in componentTreeContainer)) {
- return [key, componentTreeContainer];
- }
-
const updatedContainer = {
...componentTreeContainer,
componentTree: removeTopLevelFragmentsFromTree(
diff --git a/packages/studio/tests/__fixtures__/componentStates.ts b/packages/studio-ui/tests/__fixtures__/componentStates.ts
similarity index 100%
rename from packages/studio/tests/__fixtures__/componentStates.ts
rename to packages/studio-ui/tests/__fixtures__/componentStates.ts
diff --git a/packages/studio/tests/__fixtures__/mockStoreNestedComponents.ts b/packages/studio-ui/tests/__fixtures__/mockStoreNestedComponents.ts
similarity index 65%
rename from packages/studio/tests/__fixtures__/mockStoreNestedComponents.ts
rename to packages/studio-ui/tests/__fixtures__/mockStoreNestedComponents.ts
index 073dde9f1..1193a4916 100644
--- a/packages/studio/tests/__fixtures__/mockStoreNestedComponents.ts
+++ b/packages/studio-ui/tests/__fixtures__/mockStoreNestedComponents.ts
@@ -5,7 +5,6 @@ import {
FileMetadataKind,
FileMetadata,
ComponentMetadata,
- ModuleMetadata,
ComponentState,
StandardComponentState,
} from "@yext/studio-plugin";
@@ -56,70 +55,9 @@ const containerMetadata: ComponentMetadata = {
filepath: path.resolve(__dirname, "../__mocks__/Container.tsx"),
};
-const moduleMetadata: ModuleMetadata = {
- kind: FileMetadataKind.Module,
- metadataUUID: "panel-metadata-uuid",
- propShape: {
- text: { type: PropValueType.string, required: false },
- },
- filepath: path.resolve(__dirname, "../__mocks__/Panel.tsx"),
- componentTree: [
- {
- kind: ComponentStateKind.Fragment,
- uuid: "fragment-uuid",
- },
- {
- ...componentState,
- uuid: "internal-banner-uuid-0",
- props: {
- title: {
- kind: PropValueKind.Expression,
- valueType: PropValueType.string,
- value: "props.text",
- },
- },
- parentUUID: "fragment-uuid",
- },
- {
- ...componentState,
- uuid: "internal-banner-uuid-1",
- parentUUID: "fragment-uuid",
- },
- ],
-};
-
-const moduleWithObjPropsMetadata: ModuleMetadata = {
- kind: FileMetadataKind.Module,
- metadataUUID: "module-obj-props-metadata-uuid",
- propShape: {
- obj: {
- type: PropValueType.Object,
- required: false,
- shape: {
- text: { type: PropValueType.string, required: false },
- },
- },
- },
- filepath: "unused",
- componentTree: [
- {
- ...componentState,
- props: {
- title: {
- kind: PropValueKind.Expression,
- valueType: PropValueType.string,
- value: "`hello ${props.obj?.text}`",
- },
- },
- },
- ],
-};
-
export const mockUUIDToFileMetadata: Record = {
"banner-metadata-uuid": componentMetadata,
"container-metadata-uuid": containerMetadata,
- "panel-metadata-uuid": moduleMetadata,
- "module-obj-props-metadata-uuid": moduleWithObjPropsMetadata,
};
export const nestedComponentTree: ComponentState[] = [
diff --git a/packages/studio/tests/__mocks__/Banner.tsx b/packages/studio-ui/tests/__mocks__/Banner.tsx
similarity index 100%
rename from packages/studio/tests/__mocks__/Banner.tsx
rename to packages/studio-ui/tests/__mocks__/Banner.tsx
diff --git a/packages/studio/tests/__mocks__/Container.tsx b/packages/studio-ui/tests/__mocks__/Container.tsx
similarity index 100%
rename from packages/studio/tests/__mocks__/Container.tsx
rename to packages/studio-ui/tests/__mocks__/Container.tsx
diff --git a/packages/studio/tests/__mocks__/Panel.tsx b/packages/studio-ui/tests/__mocks__/Panel.tsx
similarity index 100%
rename from packages/studio/tests/__mocks__/Panel.tsx
rename to packages/studio-ui/tests/__mocks__/Panel.tsx
diff --git a/packages/studio/tests/__mocks__/entityFile.json b/packages/studio-ui/tests/__mocks__/entityFile.json
similarity index 100%
rename from packages/studio/tests/__mocks__/entityFile.json
rename to packages/studio-ui/tests/__mocks__/entityFile.json
diff --git a/packages/studio/tests/__mocks__/mockLocalData.json b/packages/studio-ui/tests/__mocks__/mockLocalData.json
similarity index 100%
rename from packages/studio/tests/__mocks__/mockLocalData.json
rename to packages/studio-ui/tests/__mocks__/mockLocalData.json
diff --git a/packages/studio/tests/__mocks__/siteSettings.ts b/packages/studio-ui/tests/__mocks__/siteSettings.ts
similarity index 100%
rename from packages/studio/tests/__mocks__/siteSettings.ts
rename to packages/studio-ui/tests/__mocks__/siteSettings.ts
diff --git a/packages/studio/tests/__setup__/setup-env.ts b/packages/studio-ui/tests/__setup__/setup-env.ts
similarity index 100%
rename from packages/studio/tests/__setup__/setup-env.ts
rename to packages/studio-ui/tests/__setup__/setup-env.ts
diff --git a/packages/studio/tests/__setup__/svgTransformer.cjs b/packages/studio-ui/tests/__setup__/svgTransformer.cjs
similarity index 100%
rename from packages/studio/tests/__setup__/svgTransformer.cjs
rename to packages/studio-ui/tests/__setup__/svgTransformer.cjs
diff --git a/packages/studio/tests/__utils__/helpers.ts b/packages/studio-ui/tests/__utils__/helpers.ts
similarity index 100%
rename from packages/studio/tests/__utils__/helpers.ts
rename to packages/studio-ui/tests/__utils__/helpers.ts
diff --git a/packages/studio/tests/__utils__/mockActiveComponentState.ts b/packages/studio-ui/tests/__utils__/mockActiveComponentState.ts
similarity index 100%
rename from packages/studio/tests/__utils__/mockActiveComponentState.ts
rename to packages/studio-ui/tests/__utils__/mockActiveComponentState.ts
diff --git a/packages/studio/tests/__utils__/mockActivePage.ts b/packages/studio-ui/tests/__utils__/mockActivePage.ts
similarity index 100%
rename from packages/studio/tests/__utils__/mockActivePage.ts
rename to packages/studio-ui/tests/__utils__/mockActivePage.ts
diff --git a/packages/studio/tests/__utils__/mockActivePageTree.ts b/packages/studio-ui/tests/__utils__/mockActivePageTree.ts
similarity index 68%
rename from packages/studio/tests/__utils__/mockActivePageTree.ts
rename to packages/studio-ui/tests/__utils__/mockActivePageTree.ts
index 59f67c11c..c00f2507c 100644
--- a/packages/studio/tests/__utils__/mockActivePageTree.ts
+++ b/packages/studio-ui/tests/__utils__/mockActivePageTree.ts
@@ -1,13 +1,9 @@
import { ComponentState } from "@yext/studio-plugin";
import mockStore from "./mockStore";
-export function mockActivePageTree(
- componentTree: ComponentState[],
- moduleUUIDBeingEdited?: string
-) {
+export function mockActivePageTree(componentTree: ComponentState[]) {
mockStore({
pages: {
- moduleUUIDBeingEdited,
activePageName: "pagename",
pages: {
pagename: {
diff --git a/packages/studio/tests/__utils__/mockPageSliceState.ts b/packages/studio-ui/tests/__utils__/mockPageSliceState.ts
similarity index 100%
rename from packages/studio/tests/__utils__/mockPageSliceState.ts
rename to packages/studio-ui/tests/__utils__/mockPageSliceState.ts
diff --git a/packages/studio/tests/__utils__/mockStore.ts b/packages/studio-ui/tests/__utils__/mockStore.ts
similarity index 100%
rename from packages/studio/tests/__utils__/mockStore.ts
rename to packages/studio-ui/tests/__utils__/mockStore.ts
diff --git a/packages/studio/tests/components/ActiveComponentPropEditors.test.tsx b/packages/studio-ui/tests/components/ActiveComponentPropEditors.test.tsx
similarity index 93%
rename from packages/studio/tests/components/ActiveComponentPropEditors.test.tsx
rename to packages/studio-ui/tests/components/ActiveComponentPropEditors.test.tsx
index f4b726bf3..2d568659f 100644
--- a/packages/studio/tests/components/ActiveComponentPropEditors.test.tsx
+++ b/packages/studio-ui/tests/components/ActiveComponentPropEditors.test.tsx
@@ -11,9 +11,6 @@ import {
PropValues,
PropValueType,
StandardComponentState,
- StandardOrModuleComponentState,
- TypeGuards,
- ValidFileMetadata,
} from "@yext/studio-plugin";
import userEvent from "@testing-library/user-event";
import useStudioStore from "../../src/store/useStudioStore";
@@ -74,46 +71,16 @@ const getComponentProps = () =>
(
useStudioStore
.getState()
- .actions.getActiveComponentState() as StandardOrModuleComponentState
+ .actions.getActiveComponentState() as StandardComponentState
).props;
describe("ComponentStateKind.Component", () => {
- testStandardOrModuleComponentState(
- activeComponentState,
- activeComponentMetadata
- );
-});
-
-describe("ComponentStateKind.Module", () => {
- const activeModuleState: ComponentState = {
- kind: ComponentStateKind.Module,
- componentName: "ModuleBanner",
- props: {},
- uuid: "modulebanner-uuid",
- metadataUUID: "modulebanner-metadata-uuid",
- };
-
- const activeModuleMetadata: FileMetadata = {
- kind: FileMetadataKind.Module,
- metadataUUID: "modulebanner-metadata-uuid",
- filepath: "mock-filepath",
- componentTree: [],
- };
- testStandardOrModuleComponentState(activeModuleState, activeModuleMetadata);
-});
-
-function testStandardOrModuleComponentState(
- state: StandardOrModuleComponentState,
- metadata: ValidFileMetadata
-) {
- const componentKindLabel =
- state.kind === ComponentStateKind.Standard ? "component" : "module";
-
+ const componentKindLabel = "component";
beforeEach(() => {
mockStoreActiveComponent({
- activeComponent: state,
+ activeComponent: activeComponentState,
activeComponentMetadata: {
- ...metadata,
+ ...activeComponentMetadata,
propShape,
},
});
@@ -122,14 +89,14 @@ function testStandardOrModuleComponentState(
it(`renders message when there are no editable props`, () => {
render(
false}
/>
);
screen.getByText(
- `${state.componentName} has no Editable Properties in this Panel.`
+ `${activeComponentState.componentName} has no Editable Properties in this Panel.`
);
expect(screen.queryByText("title")).toBeNull();
expect(screen.queryByText("num")).toBeNull();
@@ -138,8 +105,8 @@ function testStandardOrModuleComponentState(
});
it(`renders prop editors for each of the active ${componentKindLabel}'s non string props`, () => {
- const activeState: StandardOrModuleComponentState = {
- ...state,
+ const activeState: StandardComponentState = {
+ ...activeComponentState,
props: {
bgColor: {
kind: PropValueKind.Literal,
@@ -163,7 +130,7 @@ function testStandardOrModuleComponentState(
it(`renders tooltip for each of the active ${componentKindLabel}'s props with docs`, async () => {
render(
);
@@ -181,7 +148,7 @@ function testStandardOrModuleComponentState(
activeComponentMetadata?.kind === FileMetadataKind.Error ||
!activeComponentMetadata?.propShape ||
!activeComponentState ||
- !TypeGuards.isStandardOrModuleComponentState(activeComponentState)
+ activeComponentState?.kind !== ComponentStateKind.Standard
) {
return null;
}
@@ -192,8 +159,8 @@ function testStandardOrModuleComponentState(
/>
);
}
- const activeComponent: StandardOrModuleComponentState = {
- ...state,
+ const activeComponent: StandardComponentState = {
+ ...activeComponentState,
props: {
title: {
kind: PropValueKind.Literal,
@@ -222,7 +189,7 @@ function testStandardOrModuleComponentState(
mockStoreActiveComponent({
activeComponent: activeComponent,
activeComponentMetadata: {
- ...metadata,
+ ...activeComponentMetadata,
propShape,
},
});
@@ -285,7 +252,7 @@ function testStandardOrModuleComponentState(
jest.useRealTimers();
});
});
-}
+});
it("converts string literals to string expressions when propKind = Expression", async () => {
const props: PropValues = {
@@ -781,7 +748,7 @@ function ActiveComponentPropEditorsWrapper(props: { propShape: PropShape }) {
const state = useStudioStore().pages.pages["index"].componentTree[0];
return (
);
diff --git a/packages/studio/tests/components/ActivePagePanel.test.tsx b/packages/studio-ui/tests/components/ActivePagePanel.test.tsx
similarity index 100%
rename from packages/studio/tests/components/ActivePagePanel.test.tsx
rename to packages/studio-ui/tests/components/ActivePagePanel.test.tsx
diff --git a/packages/studio/tests/components/AddElementButton.test.tsx b/packages/studio-ui/tests/components/AddElementButton.test.tsx
similarity index 54%
rename from packages/studio/tests/components/AddElementButton.test.tsx
rename to packages/studio-ui/tests/components/AddElementButton.test.tsx
index aee84a0f9..f79cfd743 100644
--- a/packages/studio/tests/components/AddElementButton.test.tsx
+++ b/packages/studio-ui/tests/components/AddElementButton.test.tsx
@@ -2,8 +2,6 @@ import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import AddElementButton from "../../src/components/AddElementButton";
import mockActivePage from "../__utils__/mockActivePage";
-import mockStore from "../__utils__/mockStore";
-import { FileMetadataKind } from "@yext/studio-plugin";
it("renders the button when there is an active page state (but no menu)", () => {
mockActivePage({
@@ -16,7 +14,6 @@ it("renders the button when there is an active page state (but no menu)", () =>
expect(screen.getByRole("button")).toBeDefined();
expect(screen.queryByText("Components")).toBeNull();
expect(screen.queryByText("Layouts")).toBeNull();
- expect(screen.queryByText("Modules")).toBeNull();
});
it("does not render when there is no active page state", () => {
@@ -35,31 +32,4 @@ it("clicking the button opens the menu", async () => {
await userEvent.click(screen.getByRole("button"));
expect(screen.getByText("Components")).toBeDefined();
expect(screen.getByText("Layouts")).toBeDefined();
- expect(screen.queryByText("Modules")).toBeNull();
-});
-
-it("the menu will render modules only if there are available modules", async () => {
- mockActivePage({
- componentTree: [],
- filepath: "",
- cssImports: [],
- });
- mockStore({
- fileMetadatas: {
- UUIDToFileMetadata: {
- "module-metadata-uuid": {
- kind: FileMetadataKind.Module,
- componentTree: [],
- metadataUUID: "module-metadata-uuid",
- filepath: "-filepath-",
- },
- },
- },
- });
-
- render( );
- await userEvent.click(screen.getByRole("button"));
- expect(screen.getByText("Components")).toBeDefined();
- expect(screen.getByText("Layouts")).toBeDefined();
- expect(screen.getByText("Modules")).toBeDefined();
});
diff --git a/packages/studio/tests/components/AddElementMenu.test.tsx b/packages/studio-ui/tests/components/AddElementMenu.test.tsx
similarity index 56%
rename from packages/studio/tests/components/AddElementMenu.test.tsx
rename to packages/studio-ui/tests/components/AddElementMenu.test.tsx
index fafd1f3ee..a562912d2 100644
--- a/packages/studio/tests/components/AddElementMenu.test.tsx
+++ b/packages/studio-ui/tests/components/AddElementMenu.test.tsx
@@ -20,19 +20,30 @@ beforeEach(() => {
"uuid-component": {
kind: FileMetadataKind.Component,
metadataUUID: "comp",
- filepath: "blah/Mock-Component.tsx",
+ filepath: "blah/MockComponent.tsx",
},
"uuid-container": {
kind: FileMetadataKind.Component,
metadataUUID: "cont",
acceptsChildren: true,
- filepath: "blah/Mock-Container.tsx",
+ filepath: "blah/MockContainer.tsx",
},
- "uuid-module": {
- kind: FileMetadataKind.Module,
- metadataUUID: "modu",
- componentTree: [],
- filepath: "blah/Mock-Module.tsx",
+ },
+ },
+ layouts: {
+ layoutNameToLayoutState: {
+ MyMockLayout: {
+ componentTree: [
+ {
+ kind: ComponentStateKind.Standard,
+ metadataUUID: "comp",
+ componentName: "MockComponent",
+ props: {},
+ uuid: "component-inside-layout-uuid",
+ },
+ ],
+ cssImports: [],
+ filepath: "/filepath/to/MyMockLayout.tsx",
},
},
},
@@ -41,18 +52,17 @@ beforeEach(() => {
it("renders Components on load", () => {
render( );
- expect(screen.getByText("Mock-Component")).toBeDefined();
- expect(screen.queryByText("Mock-Container")).toBeNull();
- expect(screen.queryByText("Mock-Module")).toBeNull();
+ expect(screen.getByText("MockComponent")).toBeDefined();
+ expect(screen.queryByText("MockContainer")).toBeNull();
expect(closeMenu).not.toBeCalled();
});
it("can add a component to the tree", async () => {
render( );
- await userEvent.click(screen.getByText("Mock-Component"));
+ await userEvent.click(screen.getByText("MockComponent"));
expect(useStudioStore.getState().actions.getComponentTree()).toEqual([
{
- componentName: "Mock-Component",
+ componentName: "MockComponent",
kind: ComponentStateKind.Standard,
metadataUUID: "comp",
props: {},
@@ -62,22 +72,13 @@ it("can add a component to the tree", async () => {
expect(closeMenu).toBeCalledTimes(1);
});
-it("can switch to Layouts", async () => {
- render( );
- await userEvent.click(screen.getByText("Layouts"));
- expect(screen.queryByText("Mock-Component")).toBeNull();
- expect(screen.getByText("Mock-Container")).toBeDefined();
- expect(screen.queryByText("Mock-Module")).toBeNull();
- expect(closeMenu).not.toBeCalled();
-});
-
it("can add a container to the tree", async () => {
render( );
- await userEvent.click(screen.getByText("Layouts"));
- await userEvent.click(screen.getByText("Mock-Container"));
+ await userEvent.click(screen.getByText("Containers"));
+ await userEvent.click(screen.getByText("MockContainer"));
expect(useStudioStore.getState().actions.getComponentTree()).toEqual([
{
- componentName: "Mock-Container",
+ componentName: "MockContainer",
kind: ComponentStateKind.Standard,
metadataUUID: "cont",
props: {},
@@ -87,24 +88,15 @@ it("can add a container to the tree", async () => {
expect(closeMenu).toBeCalledTimes(1);
});
-it("can switch to Modules", async () => {
+it("can add layouts to the tree", async () => {
render( );
- await userEvent.click(screen.getByText("Modules"));
- expect(screen.queryByText("Mock-Component")).toBeNull();
- expect(screen.queryByText("Mock-Container")).toBeNull();
- expect(screen.getByText("Mock-Module")).toBeDefined();
- expect(closeMenu).not.toBeCalled();
-});
-
-it("can add a module to the tree", async () => {
- render( );
- await userEvent.click(screen.getByText("Modules"));
- await userEvent.click(screen.getByText("Mock-Module"));
+ await userEvent.click(screen.getByText("Layouts"));
+ await userEvent.click(screen.getByText("MyMockLayout"));
expect(useStudioStore.getState().actions.getComponentTree()).toEqual([
{
- componentName: "Mock-Module",
- kind: ComponentStateKind.Module,
- metadataUUID: "modu",
+ componentName: "MockComponent",
+ kind: ComponentStateKind.Standard,
+ metadataUUID: "comp",
props: {},
uuid: expect.any(String),
},
diff --git a/packages/studio/tests/components/AddPageFlow/AddPageButton.test.tsx b/packages/studio-ui/tests/components/AddPageFlow/AddPageButton.test.tsx
similarity index 100%
rename from packages/studio/tests/components/AddPageFlow/AddPageButton.test.tsx
rename to packages/studio-ui/tests/components/AddPageFlow/AddPageButton.test.tsx
diff --git a/packages/studio/tests/components/ArrayPropEditor.test.tsx b/packages/studio-ui/tests/components/ArrayPropEditor.test.tsx
similarity index 100%
rename from packages/studio/tests/components/ArrayPropEditor.test.tsx
rename to packages/studio-ui/tests/components/ArrayPropEditor.test.tsx
diff --git a/packages/studio/tests/components/ComponentTree.test.tsx b/packages/studio-ui/tests/components/ComponentTree.test.tsx
similarity index 100%
rename from packages/studio/tests/components/ComponentTree.test.tsx
rename to packages/studio-ui/tests/components/ComponentTree.test.tsx
diff --git a/packages/studio/tests/components/EditStreamScopeButton.test.tsx b/packages/studio-ui/tests/components/EditStreamScopeButton.test.tsx
similarity index 100%
rename from packages/studio/tests/components/EditStreamScopeButton.test.tsx
rename to packages/studio-ui/tests/components/EditStreamScopeButton.test.tsx
diff --git a/packages/studio/tests/components/EntityPicker.test.tsx b/packages/studio-ui/tests/components/EntityPicker.test.tsx
similarity index 100%
rename from packages/studio/tests/components/EntityPicker.test.tsx
rename to packages/studio-ui/tests/components/EntityPicker.test.tsx
diff --git a/packages/studio/tests/components/ErrorBoundary.test.tsx b/packages/studio-ui/tests/components/ErrorBoundary.test.tsx
similarity index 100%
rename from packages/studio/tests/components/ErrorBoundary.test.tsx
rename to packages/studio-ui/tests/components/ErrorBoundary.test.tsx
diff --git a/packages/studio/tests/components/FieldPicker/FieldPicker.test.tsx b/packages/studio-ui/tests/components/FieldPicker/FieldPicker.test.tsx
similarity index 100%
rename from packages/studio/tests/components/FieldPicker/FieldPicker.test.tsx
rename to packages/studio-ui/tests/components/FieldPicker/FieldPicker.test.tsx
diff --git a/packages/studio/tests/components/Highlighter.test.tsx b/packages/studio-ui/tests/components/Highlighter.test.tsx
similarity index 100%
rename from packages/studio/tests/components/Highlighter.test.tsx
rename to packages/studio-ui/tests/components/Highlighter.test.tsx
diff --git a/packages/studio/tests/components/InfoButton.test.tsx b/packages/studio-ui/tests/components/InfoButton.test.tsx
similarity index 100%
rename from packages/studio/tests/components/InfoButton.test.tsx
rename to packages/studio-ui/tests/components/InfoButton.test.tsx
diff --git a/packages/studio/tests/components/OpenLivePreviewButton.test.tsx b/packages/studio-ui/tests/components/OpenLivePreviewButton.test.tsx
similarity index 100%
rename from packages/studio/tests/components/OpenLivePreviewButton.test.tsx
rename to packages/studio-ui/tests/components/OpenLivePreviewButton.test.tsx
diff --git a/packages/studio/tests/components/PageSettingsButton.test.tsx b/packages/studio-ui/tests/components/PageSettingsButton.test.tsx
similarity index 100%
rename from packages/studio/tests/components/PageSettingsButton.test.tsx
rename to packages/studio-ui/tests/components/PageSettingsButton.test.tsx
diff --git a/packages/studio/tests/components/PreviewPanel.test.tsx b/packages/studio-ui/tests/components/PreviewPanel.test.tsx
similarity index 59%
rename from packages/studio/tests/components/PreviewPanel.test.tsx
rename to packages/studio-ui/tests/components/PreviewPanel.test.tsx
index ffdd86d5f..780da0ba6 100644
--- a/packages/studio/tests/components/PreviewPanel.test.tsx
+++ b/packages/studio-ui/tests/components/PreviewPanel.test.tsx
@@ -10,35 +10,11 @@ import {
import mockStore from "../__utils__/mockStore";
import {
ComponentState,
- ComponentStateKind,
- ModuleState,
PropValueKind,
PropValueType,
- RepeaterState,
} from "@yext/studio-plugin";
import useImportedComponents from "../../src/hooks/useImportedComponents";
-const moduleState: ModuleState = {
- kind: ComponentStateKind.Module,
- componentName: "Panel",
- props: {
- text: {
- kind: PropValueKind.Literal,
- value: "This is Panel module",
- valueType: PropValueType.string,
- },
- },
- uuid: "panel-uuid",
- metadataUUID: "panel-metadata-uuid",
-};
-
-const repeaterState: RepeaterState = {
- kind: ComponentStateKind.Repeater,
- uuid: "panel-uuid",
- listExpression: "document.favs",
- repeatedComponent: moduleState,
-};
-
const mockSetState = jest.fn();
beforeEach(() => {
@@ -59,36 +35,6 @@ describe("renders preview", () => {
expect(banner2).toBeDefined();
});
- it("renders component tree with Module component type", async () => {
- const tree = [moduleState];
- await mockPreviewState(tree);
- render( );
- const panel = await screen.findByText(/This is Panel module/);
- const banner = await screen.findByText(/This is Banner/);
- expect(panel).toBeDefined();
- expect(banner).toBeDefined();
- });
-
- it("renders component tree with Repeater component over a module", async () => {
- const tree = [repeaterState];
- await mockPreviewState(tree);
- render( );
- const panels = await screen.findAllByText(/This is Panel module/);
- const banners = await screen.findAllByText(/This is Banner/);
- expect(panels).toHaveLength(3);
- expect(banners).toHaveLength(3);
- });
-
- it("does not render Repeater if list expression is not found", async () => {
- const tree = [{ ...repeaterState, listExpression: "document.services" }];
- await mockPreviewState(tree);
- render( );
- const panels = screen.queryByText(/This is Panel module/);
- const banners = screen.queryByText(/This is Banner/);
- expect(panels).toBeNull();
- expect(banners).toBeNull();
- });
-
it("renders component with transformed props", async () => {
const tree: ComponentState[] = [
{
@@ -106,16 +52,6 @@ describe("renders preview", () => {
},
},
},
- {
- ...moduleState,
- props: {
- text: {
- kind: PropValueKind.Expression,
- value: "siteSettings.someText",
- valueType: PropValueType.string,
- },
- },
- },
];
await mockPreviewState(tree);
render( );
@@ -123,8 +59,6 @@ describe("renders preview", () => {
expect(siteSettingsExpressionProp).toBeDefined();
const documentExpressionProp = await screen.findByText(/123/);
expect(documentExpressionProp).toBeDefined();
- const moduleExpressionProp = await screen.findByText(/mock-text/);
- expect(moduleExpressionProp).toBeDefined();
});
it("can render component using nested siteSettings expression", async () => {
@@ -170,32 +104,6 @@ describe("renders preview", () => {
const nestedPropUsage = await screen.findByText(/eggyweggy/);
expect(nestedPropUsage).toBeDefined();
});
-
- it("can render repeated module using item expression", async () => {
- const tree: ComponentState[] = [
- {
- ...repeaterState,
- repeatedComponent: {
- ...moduleState,
- props: {
- text: {
- kind: PropValueKind.Expression,
- value: "item",
- valueType: PropValueType.string,
- },
- },
- },
- },
- ];
- await mockPreviewState(tree);
- render( );
- const catItemProp = await screen.findByText(/cat/);
- expect(catItemProp).toBeDefined();
- const dogItemProp = await screen.findByText(/dog/);
- expect(dogItemProp).toBeDefined();
- const sleepItemProp = await screen.findByText(/sleep/);
- expect(sleepItemProp).toBeDefined();
- });
});
it("clicking a component in the preview updates the activeComponentUUID", async () => {
@@ -216,33 +124,6 @@ it("clicking a component in the preview updates the activeComponentUUID", async
);
});
-it("can preview a module with object props", async () => {
- const componentTree: ComponentState[] = [
- {
- kind: ComponentStateKind.Module,
- componentName: "ModuleWithObjProps",
- uuid: "module-obj-props-uuid",
- metadataUUID: "module-obj-props-metadata-uuid",
- props: {
- obj: {
- kind: PropValueKind.Literal,
- valueType: PropValueType.Object,
- value: {
- text: {
- kind: PropValueKind.Expression,
- valueType: PropValueType.string,
- value: "document.name",
- },
- },
- },
- },
- },
- ];
- await mockPreviewState(componentTree);
- render( );
- expect(screen.getByText("hello bob")).toBeDefined();
-});
-
async function mockPreviewState(componentTree: ComponentState[]) {
mockStore({
pages: {
diff --git a/packages/studio/tests/components/PropEditor.test.tsx b/packages/studio-ui/tests/components/PropEditor.test.tsx
similarity index 100%
rename from packages/studio/tests/components/PropEditor.test.tsx
rename to packages/studio-ui/tests/components/PropEditor.test.tsx
diff --git a/packages/studio/tests/components/PropInput.test.tsx b/packages/studio-ui/tests/components/PropInput.test.tsx
similarity index 100%
rename from packages/studio/tests/components/PropInput.test.tsx
rename to packages/studio-ui/tests/components/PropInput.test.tsx
diff --git a/packages/studio-ui/tests/components/PropsPanel.test.tsx b/packages/studio-ui/tests/components/PropsPanel.test.tsx
new file mode 100644
index 000000000..8a3fa3068
--- /dev/null
+++ b/packages/studio-ui/tests/components/PropsPanel.test.tsx
@@ -0,0 +1,21 @@
+import { ComponentStateKind } from "@yext/studio-plugin";
+import mockStoreActiveComponent from "../__utils__/mockActiveComponentState";
+import PropsPanel from "../../src/components/PropsPanel";
+import { render } from "@testing-library/react";
+
+it("does not render prop editor(s) for fragment component", () => {
+ mockStoreActiveComponent({
+ activeComponent: {
+ kind: ComponentStateKind.Fragment,
+ uuid: "fragment-uuid",
+ },
+ });
+ const { container } = render( );
+ expect(container).toBeEmptyDOMElement();
+});
+
+it("does not render prop editor(s) when there's no selected active component", () => {
+ mockStoreActiveComponent({});
+ const { container } = render( );
+ expect(container).toBeEmptyDOMElement();
+});
diff --git a/packages/studio/tests/components/RemoveElementButton.test.tsx b/packages/studio-ui/tests/components/RemoveElementButton.test.tsx
similarity index 100%
rename from packages/studio/tests/components/RemoveElementButton.test.tsx
rename to packages/studio-ui/tests/components/RemoveElementButton.test.tsx
diff --git a/packages/studio/tests/components/RemovePageButton.test.tsx b/packages/studio-ui/tests/components/RemovePageButton.test.tsx
similarity index 100%
rename from packages/studio/tests/components/RemovePageButton.test.tsx
rename to packages/studio-ui/tests/components/RemovePageButton.test.tsx
diff --git a/packages/studio/tests/components/SaveButton.test.tsx b/packages/studio-ui/tests/components/SaveButton.test.tsx
similarity index 96%
rename from packages/studio/tests/components/SaveButton.test.tsx
rename to packages/studio-ui/tests/components/SaveButton.test.tsx
index a68a50f92..dbbcd73b4 100644
--- a/packages/studio/tests/components/SaveButton.test.tsx
+++ b/packages/studio-ui/tests/components/SaveButton.test.tsx
@@ -36,9 +36,6 @@ it("enables the button when there are pending SiteSettingsValues changes", () =>
siteSettings: {
values: undefined,
},
- fileMetadatas: {
- UUIDToFileMetadata: {},
- },
},
siteSettings: {
values: {
@@ -72,9 +69,6 @@ it("disables the button when there are no pending changes", () => {
},
},
},
- fileMetadatas: {
- UUIDToFileMetadata: {},
- },
},
siteSettings: {
values: {
diff --git a/packages/studio/tests/components/SiteSettingsPanel.test.tsx b/packages/studio-ui/tests/components/SiteSettingsPanel.test.tsx
similarity index 100%
rename from packages/studio/tests/components/SiteSettingsPanel.test.tsx
rename to packages/studio-ui/tests/components/SiteSettingsPanel.test.tsx
diff --git a/packages/studio/tests/components/TailwindPropInput.test.tsx b/packages/studio-ui/tests/components/TailwindPropInput.test.tsx
similarity index 100%
rename from packages/studio/tests/components/TailwindPropInput.test.tsx
rename to packages/studio-ui/tests/components/TailwindPropInput.test.tsx
diff --git a/packages/studio/tests/components/UndefinedMenuButton.test.tsx b/packages/studio-ui/tests/components/UndefinedMenuButton.test.tsx
similarity index 100%
rename from packages/studio/tests/components/UndefinedMenuButton.test.tsx
rename to packages/studio-ui/tests/components/UndefinedMenuButton.test.tsx
diff --git a/packages/studio/tests/components/UndoRedo.test.tsx b/packages/studio-ui/tests/components/UndoRedo.test.tsx
similarity index 100%
rename from packages/studio/tests/components/UndoRedo.test.tsx
rename to packages/studio-ui/tests/components/UndoRedo.test.tsx
diff --git a/packages/studio/tests/components/ViewportButton.test.tsx b/packages/studio-ui/tests/components/ViewportButton.test.tsx
similarity index 100%
rename from packages/studio/tests/components/ViewportButton.test.tsx
rename to packages/studio-ui/tests/components/ViewportButton.test.tsx
diff --git a/packages/studio/tests/hooks/useImportedComponents.test.tsx b/packages/studio-ui/tests/hooks/useImportedComponents.test.tsx
similarity index 100%
rename from packages/studio/tests/hooks/useImportedComponents.test.tsx
rename to packages/studio-ui/tests/hooks/useImportedComponents.test.tsx
diff --git a/packages/studio/tests/store/StudioActions/AddComponentAction.test.ts b/packages/studio-ui/tests/store/StudioActions/AddComponentAction.test.ts
similarity index 71%
rename from packages/studio/tests/store/StudioActions/AddComponentAction.test.ts
rename to packages/studio-ui/tests/store/StudioActions/AddComponentAction.test.ts
index 4cdd97bf9..ea54be75c 100644
--- a/packages/studio/tests/store/StudioActions/AddComponentAction.test.ts
+++ b/packages/studio-ui/tests/store/StudioActions/AddComponentAction.test.ts
@@ -26,14 +26,6 @@ const initialTree: ComponentState[] = [
uuid: "mock-component-uuid",
parentUUID: "mock-container-uuid",
},
- {
- componentName: "Mock-Module",
- kind: ComponentStateKind.Module,
- metadataUUID: "uuid-module",
- props: {},
- uuid: "mock-module-uuid",
- parentUUID: "mock-container-uuid",
- },
{
kind: ComponentStateKind.Fragment,
uuid: "mock-fragment-uuid",
@@ -62,47 +54,12 @@ beforeEach(() => {
acceptsChildren: true,
filepath: "blah/Mock-Container.tsx",
},
- "uuid-module": {
- kind: FileMetadataKind.Module,
- metadataUUID: "uuid-module",
- componentTree: [],
- filepath: "blah/Mock-Module.tsx",
- },
- StarModuleMetadataUUID: {
- kind: FileMetadataKind.Module,
- componentTree: initialTree,
- metadataUUID: "StarModuleMetadataUUID",
- filepath: "unused",
- },
},
},
});
});
-describe("adds components to ModuleMetadata when a module is being edited", () => {
- beforeEach(() => {
- mockActivePageTree(
- [
- {
- kind: ComponentStateKind.Module,
- uuid: "ModuleState.uuid",
- metadataUUID: "StarModuleMetadataUUID",
- componentName: "StarModule",
- props: {},
- },
- ],
- "ModuleState.uuid"
- );
- });
-
- insertionOrderTestSuite(() => {
- return useStudioStore.getState().fileMetadatas.UUIDToFileMetadata[
- "StarModuleMetadataUUID"
- ];
- });
-});
-
-describe("adds components to the active PageState when no module is being edited", () => {
+describe("adds components to the active PageState", () => {
beforeEach(() => {
mockActivePageTree(initialTree);
});
@@ -169,20 +126,6 @@ function insertionOrderTestSuite(
);
});
- it("puts new component directly after module if it is active component", () => {
- useStudioStore.getState().pages.setActiveComponentUUID("mock-module-uuid");
- useStudioStore.getState().actions.addComponent(componentMetadata);
- expect(getExpectedObject()).toEqual(
- expect.objectContaining({
- componentTree: [
- ...initialTree.slice(0, 3),
- { ...newComponentState, parentUUID: "mock-container-uuid" },
- ...initialTree.slice(3),
- ],
- })
- );
- });
-
it("puts new component at start if fragment is active component", () => {
useStudioStore
.getState()
diff --git a/packages/studio/tests/store/StudioActions/CreateComponentStateAction.test.ts b/packages/studio-ui/tests/store/StudioActions/CreateComponentStateAction.test.ts
similarity index 89%
rename from packages/studio/tests/store/StudioActions/CreateComponentStateAction.test.ts
rename to packages/studio-ui/tests/store/StudioActions/CreateComponentStateAction.test.ts
index 05151ffea..9c57787e5 100644
--- a/packages/studio/tests/store/StudioActions/CreateComponentStateAction.test.ts
+++ b/packages/studio-ui/tests/store/StudioActions/CreateComponentStateAction.test.ts
@@ -2,7 +2,6 @@ import {
ComponentMetadata,
ComponentStateKind,
FileMetadataKind,
- ModuleMetadata,
PropValueKind,
PropValueType,
} from "@yext/studio-plugin";
@@ -30,10 +29,9 @@ it("creates the expected component state", () => {
});
it("adds default values for required props", () => {
- const moduleMetadata: ModuleMetadata = {
- kind: FileMetadataKind.Module,
- filepath: "./ModuleLol.tsx",
- componentTree: [],
+ const componentMetadata: ComponentMetadata = {
+ kind: FileMetadataKind.Component,
+ filepath: "./Component.tsx",
metadataUUID: "unused",
propShape: {
document: {
@@ -56,7 +54,7 @@ it("adds default values for required props", () => {
const actualState = useStudioStore
.getState()
- .actions.createComponentState(moduleMetadata);
+ .actions.createComponentState(componentMetadata);
expect(actualState).toEqual(
expect.objectContaining({
diff --git a/packages/studio/tests/store/StudioActions/GenerateTestDataAction.test.ts b/packages/studio-ui/tests/store/StudioActions/GenerateTestDataAction.test.ts
similarity index 100%
rename from packages/studio/tests/store/StudioActions/GenerateTestDataAction.test.ts
rename to packages/studio-ui/tests/store/StudioActions/GenerateTestDataAction.test.ts
diff --git a/packages/studio/tests/store/StudioActions/UpdateActivePageAction.test.ts b/packages/studio-ui/tests/store/StudioActions/UpdateActivePageAction.test.ts
similarity index 100%
rename from packages/studio/tests/store/StudioActions/UpdateActivePageAction.test.ts
rename to packages/studio-ui/tests/store/StudioActions/UpdateActivePageAction.test.ts
diff --git a/packages/studio-ui/tests/store/StudioActions/activeComponentActions.test.ts b/packages/studio-ui/tests/store/StudioActions/activeComponentActions.test.ts
new file mode 100644
index 000000000..0367b40d7
--- /dev/null
+++ b/packages/studio-ui/tests/store/StudioActions/activeComponentActions.test.ts
@@ -0,0 +1,109 @@
+import {
+ ComponentState,
+ ComponentStateKind,
+ FileMetadataKind,
+ PropValueKind,
+ PropValueType,
+ PropValues,
+} from "@yext/studio-plugin";
+import useStudioStore from "../../../src/store/useStudioStore";
+import mockStore from "../../__utils__/mockStore";
+
+describe("getActiveComponentState", () => {
+ it("can get the current active component within a page", () => {
+ mockInitialStore();
+ const componentState = useStudioStore
+ .getState()
+ .actions.getActiveComponentState();
+ expect(componentState).toEqual(
+ expect.objectContaining({
+ componentName: "MyBanner",
+ })
+ );
+ });
+});
+
+describe("getComponentTree", () => {
+ it("can get the component tree when a page is being edited", () => {
+ mockInitialStore();
+ const tree = useStudioStore.getState().actions.getComponentTree();
+ expect(tree).toEqual([
+ expect.objectContaining({
+ componentName: "MyBanner",
+ }),
+ ]);
+ });
+});
+
+describe("updateComponentTree", () => {
+ it("can rearrange the component tree when a page is being edited", () => {
+ mockInitialStore();
+ const updatedTree: ComponentState[] = [
+ {
+ kind: ComponentStateKind.BuiltIn,
+ props: {},
+ componentName: "div",
+ uuid: "div-0",
+ },
+ ];
+ useStudioStore.getState().actions.updateComponentTree(updatedTree);
+ const tree = useStudioStore.getState().actions.getComponentTree();
+ expect(tree).toEqual(updatedTree);
+ });
+});
+
+describe("updateActiveComponentProps", () => {
+ it("updates the active component props in the current active page", () => {
+ mockInitialStore();
+ const updatedProps: PropValues = {
+ hi: {
+ kind: PropValueKind.Literal,
+ valueType: PropValueType.string,
+ value: "bye bye bocchi",
+ },
+ };
+ useStudioStore.getState().actions.updateActiveComponentProps(updatedProps);
+ const componentStateAfterUpdate = useStudioStore
+ .getState()
+ .actions.getActiveComponentState();
+ expect(componentStateAfterUpdate).toEqual(
+ expect.objectContaining({
+ componentName: "MyBanner",
+ props: updatedProps,
+ })
+ );
+ });
+});
+
+function mockInitialStore() {
+ mockStore({
+ pages: {
+ activePageName: "testpage",
+ activeComponentUUID: "banner-0",
+ pages: {
+ testpage: {
+ componentTree: [
+ {
+ kind: ComponentStateKind.Standard,
+ componentName: "MyBanner",
+ props: {},
+ uuid: "banner-0",
+ metadataUUID: "bannerMeta",
+ },
+ ],
+ cssImports: [],
+ filepath: "page-filepath",
+ },
+ },
+ },
+ fileMetadatas: {
+ UUIDToFileMetadata: {
+ bannerMeta: {
+ metadataUUID: "bannerMeta",
+ kind: FileMetadataKind.Component,
+ filepath: "component-filepath",
+ },
+ },
+ },
+ });
+}
diff --git a/packages/studio/tests/store/StudioActions/createPage.test.ts b/packages/studio-ui/tests/store/StudioActions/createPage.test.ts
similarity index 100%
rename from packages/studio/tests/store/StudioActions/createPage.test.ts
rename to packages/studio-ui/tests/store/StudioActions/createPage.test.ts
diff --git a/packages/studio/tests/store/StudioActions/refreshActivePageEntities.test.ts b/packages/studio-ui/tests/store/StudioActions/refreshActivePageEntities.test.ts
similarity index 100%
rename from packages/studio/tests/store/StudioActions/refreshActivePageEntities.test.ts
rename to packages/studio-ui/tests/store/StudioActions/refreshActivePageEntities.test.ts
diff --git a/packages/studio-ui/tests/store/StudioActions/removeComponent.test.ts b/packages/studio-ui/tests/store/StudioActions/removeComponent.test.ts
new file mode 100644
index 000000000..ae4c3a877
--- /dev/null
+++ b/packages/studio-ui/tests/store/StudioActions/removeComponent.test.ts
@@ -0,0 +1,46 @@
+import { ComponentState, ComponentStateKind } from "@yext/studio-plugin";
+import useStudioStore from "../../../src/store/useStudioStore";
+import { searchBarComponent } from "../../__fixtures__/componentStates";
+import mockStore from "../../__utils__/mockStore";
+
+const initialTree: ComponentState[] = [
+ {
+ kind: ComponentStateKind.Fragment,
+ uuid: "mock-uuid-0",
+ },
+ {
+ ...searchBarComponent,
+ uuid: "mock-uuid-1",
+ parentUUID: "mock-uuid-0",
+ },
+ {
+ ...searchBarComponent,
+ uuid: "mock-uuid-2",
+ parentUUID: "mock-uuid-1",
+ },
+ {
+ ...searchBarComponent,
+ uuid: "mock-uuid-3",
+ parentUUID: "mock-uuid-0",
+ },
+];
+
+it("removes component and its children from the active PageState", () => {
+ mockStore({
+ pages: {
+ activePageName: "pagename",
+ pages: {
+ pagename: {
+ componentTree: initialTree,
+ cssImports: [],
+ filepath: "unused",
+ },
+ },
+ },
+ });
+
+ useStudioStore.getState().actions.removeComponent("mock-uuid-1");
+ const updatedTree =
+ useStudioStore.getState().pages.pages["pagename"].componentTree;
+ expect(updatedTree).toEqual([initialTree[0], initialTree[3]]);
+});
diff --git a/packages/studio/tests/store/StudioActions/saveChanges.test.ts b/packages/studio-ui/tests/store/StudioActions/saveChanges.test.ts
similarity index 76%
rename from packages/studio/tests/store/StudioActions/saveChanges.test.ts
rename to packages/studio-ui/tests/store/StudioActions/saveChanges.test.ts
index ffb57b4dc..4a2579775 100644
--- a/packages/studio/tests/store/StudioActions/saveChanges.test.ts
+++ b/packages/studio-ui/tests/store/StudioActions/saveChanges.test.ts
@@ -1,7 +1,7 @@
import useStudioStore from "../../../src/store/useStudioStore";
import * as sendMessageModule from "../../../src/messaging/sendMessage";
import mockStore from "../../__utils__/mockStore";
-import { FileMetadata, FileMetadataKind, MessageID } from "@yext/studio-plugin";
+import { MessageID } from "@yext/studio-plugin";
import { PagesRecord } from "../../../src/store/models/slices/PageSlice";
const mockPages: PagesRecord = {
@@ -12,15 +12,6 @@ const mockPages: PagesRecord = {
},
};
-const mockUUIDToFileMetadata: Record = {
- "module-uuid": {
- kind: FileMetadataKind.Module,
- componentTree: [],
- metadataUUID: "module-uuid",
- filepath: "mock-filepath",
- },
-};
-
beforeEach(() => {
mockStore({
pages: {
@@ -30,9 +21,6 @@ beforeEach(() => {
pagesToUpdate: new Set(["UpdateMe"]),
},
},
- fileMetadatas: {
- UUIDToFileMetadata: mockUUIDToFileMetadata,
- },
});
});
@@ -42,7 +30,6 @@ it("sends pending changes to server to update files", async () => {
expect(sendMessageSpy).toBeCalledTimes(1);
expect(sendMessageSpy).toBeCalledWith(MessageID.SaveChanges, {
pageNameToPageState: mockPages,
- UUIDToFileMetadata: mockUUIDToFileMetadata,
pendingChanges: {
pagesToRemove: ["RemoveMe"],
pagesToUpdate: ["UpdateMe"],
diff --git a/packages/studio/tests/store/StudioActions/updateActiveComponentProps.test.ts b/packages/studio-ui/tests/store/StudioActions/updateActiveComponentProps.test.ts
similarity index 100%
rename from packages/studio/tests/store/StudioActions/updateActiveComponentProps.test.ts
rename to packages/studio-ui/tests/store/StudioActions/updateActiveComponentProps.test.ts
diff --git a/packages/studio/tests/store/createAccountContentSlice/createAccountContentSlice.test.tsx b/packages/studio-ui/tests/store/createAccountContentSlice/createAccountContentSlice.test.tsx
similarity index 100%
rename from packages/studio/tests/store/createAccountContentSlice/createAccountContentSlice.test.tsx
rename to packages/studio-ui/tests/store/createAccountContentSlice/createAccountContentSlice.test.tsx
diff --git a/packages/studio/tests/store/createAccountContentSlice/utils.test.tsx b/packages/studio-ui/tests/store/createAccountContentSlice/utils.test.tsx
similarity index 100%
rename from packages/studio/tests/store/createAccountContentSlice/utils.test.tsx
rename to packages/studio-ui/tests/store/createAccountContentSlice/utils.test.tsx
diff --git a/packages/studio-ui/tests/store/createFileMetadataSlice/createFileMetadataSlice.test.tsx b/packages/studio-ui/tests/store/createFileMetadataSlice/createFileMetadataSlice.test.tsx
new file mode 100644
index 000000000..c0c9b3b84
--- /dev/null
+++ b/packages/studio-ui/tests/store/createFileMetadataSlice/createFileMetadataSlice.test.tsx
@@ -0,0 +1,54 @@
+import {
+ ComponentMetadata,
+ FileMetadataKind,
+ PropValueType,
+} from "@yext/studio-plugin";
+import useStudioStore from "../../../src/store/useStudioStore";
+import { FileMetadataSliceStates } from "../../../src/store/models/slices/FileMetadataSlice";
+import mockStore from "../../__utils__/mockStore";
+
+const componentMetadata: ComponentMetadata = {
+ kind: FileMetadataKind.Component,
+ metadataUUID: "mock-metadata-uuid",
+ filepath: "mock-filepath",
+ propShape: {
+ myText: {
+ type: PropValueType.string,
+ tooltip: "a random string",
+ required: false,
+ },
+ },
+};
+
+it("returns a FileMetadata using getFileMetadata", () => {
+ setInitialState({
+ UUIDToFileMetadata: {
+ "uuid-1": componentMetadata,
+ },
+ });
+ const fileMetadata = useStudioStore
+ .getState()
+ .fileMetadatas.getFileMetadata("uuid-1");
+ expect(fileMetadata).toEqual(componentMetadata);
+});
+
+it("updates UUIDToImportedComponent using setImportedComponent", () => {
+ const importedComponent = () => hello world
;
+ const newImportedComponents = {
+ Banner: importedComponent,
+ };
+ useStudioStore
+ .getState()
+ .fileMetadatas.setImportedComponent("Banner", importedComponent);
+ const UUIDToImportedComponent =
+ useStudioStore.getState().fileMetadatas.UUIDToImportedComponent;
+ expect(UUIDToImportedComponent).toEqual(newImportedComponents);
+});
+
+function setInitialState(initialState: Partial): void {
+ const baseState: FileMetadataSliceStates = {
+ UUIDToFileMetadata: {},
+ UUIDToImportedComponent: {},
+ };
+ mockStore({ fileMetadatas: { ...baseState, ...initialState } });
+}
diff --git a/packages/studio/tests/store/createPageSlice/activeEntityActions.test.ts b/packages/studio-ui/tests/store/createPageSlice/activeEntityActions.test.ts
similarity index 100%
rename from packages/studio/tests/store/createPageSlice/activeEntityActions.test.ts
rename to packages/studio-ui/tests/store/createPageSlice/activeEntityActions.test.ts
diff --git a/packages/studio/tests/store/createPageSlice/activePageActions.test.ts b/packages/studio-ui/tests/store/createPageSlice/activePageActions.test.ts
similarity index 100%
rename from packages/studio/tests/store/createPageSlice/activePageActions.test.ts
rename to packages/studio-ui/tests/store/createPageSlice/activePageActions.test.ts
diff --git a/packages/studio/tests/store/createPageSlice/pageActions.test.ts b/packages/studio-ui/tests/store/createPageSlice/pageActions.test.ts
similarity index 100%
rename from packages/studio/tests/store/createPageSlice/pageActions.test.ts
rename to packages/studio-ui/tests/store/createPageSlice/pageActions.test.ts
diff --git a/packages/studio/tests/store/createPageSlice/pageComponentActions.test.ts b/packages/studio-ui/tests/store/createPageSlice/pageComponentActions.test.ts
similarity index 100%
rename from packages/studio/tests/store/createPageSlice/pageComponentActions.test.ts
rename to packages/studio-ui/tests/store/createPageSlice/pageComponentActions.test.ts
diff --git a/packages/studio/tests/store/createSiteSettings.test.ts b/packages/studio-ui/tests/store/createSiteSettings.test.ts
similarity index 100%
rename from packages/studio/tests/store/createSiteSettings.test.ts
rename to packages/studio-ui/tests/store/createSiteSettings.test.ts
diff --git a/packages/studio/tests/tsconfig.json b/packages/studio-ui/tests/tsconfig.json
similarity index 85%
rename from packages/studio/tests/tsconfig.json
rename to packages/studio-ui/tests/tsconfig.json
index 8e3f1c089..79aeb2055 100644
--- a/packages/studio/tests/tsconfig.json
+++ b/packages/studio-ui/tests/tsconfig.json
@@ -1,6 +1,6 @@
{
"extends": "../tsconfig.json",
- "include": ["**/*", "../src/index.d.ts"],
+ "include": ["**/*", "../src/global.d.ts"],
"compilerOptions": {
"noEmit": true,
"types": [
diff --git a/packages/studio/tests/utils/PageDataValidator.test.ts b/packages/studio-ui/tests/utils/PageDataValidator.test.ts
similarity index 100%
rename from packages/studio/tests/utils/PageDataValidator.test.ts
rename to packages/studio-ui/tests/utils/PageDataValidator.test.ts
diff --git a/packages/studio/tests/utils/PropValueHelpers.test.ts b/packages/studio-ui/tests/utils/PropValueHelpers.test.ts
similarity index 100%
rename from packages/studio/tests/utils/PropValueHelpers.test.ts
rename to packages/studio-ui/tests/utils/PropValueHelpers.test.ts
diff --git a/packages/studio/tests/utils/TemplateExpressionFormatter.test.ts b/packages/studio-ui/tests/utils/TemplateExpressionFormatter.test.ts
similarity index 100%
rename from packages/studio/tests/utils/TemplateExpressionFormatter.test.ts
rename to packages/studio-ui/tests/utils/TemplateExpressionFormatter.test.ts
diff --git a/packages/studio/tests/utils/filterEntityData.test.tsx b/packages/studio-ui/tests/utils/filterEntityData.test.tsx
similarity index 100%
rename from packages/studio/tests/utils/filterEntityData.test.tsx
rename to packages/studio-ui/tests/utils/filterEntityData.test.tsx
diff --git a/packages/studio/tests/utils/getPropsForPreview.test.ts b/packages/studio-ui/tests/utils/getPropsForPreview.test.ts
similarity index 85%
rename from packages/studio/tests/utils/getPropsForPreview.test.ts
rename to packages/studio-ui/tests/utils/getPropsForPreview.test.ts
index 535409de2..61e26fd2a 100644
--- a/packages/studio/tests/utils/getPropsForPreview.test.ts
+++ b/packages/studio-ui/tests/utils/getPropsForPreview.test.ts
@@ -70,18 +70,6 @@ const arrayPropMetadata: PropMetadata = {
},
required: false,
};
-
-const parentPropShape: PropShape = {
- title: {
- type: PropValueType.string,
- required: false,
- },
- parentExpression: {
- type: PropValueType.string,
- required: false,
- },
-};
-
it("returns value as is for primitive prop of type Literal", () => {
const transformedProps = getPropsForPreview(
{
@@ -176,32 +164,6 @@ describe("expression value handling", () => {
});
});
- it("can handle expressions that reference literal props", () => {
- const transformedProps = transformFooProp("props.title", {
- title: {
- kind: PropValueKind.Literal,
- valueType: PropValueType.string,
- value: "the title prop",
- },
- });
- expect(transformedProps).toEqual({
- foo: "the title prop",
- });
- });
-
- it("can handle expressions that reference expression props", () => {
- const transformedProps = transformFooProp("props.title", {
- title: {
- kind: PropValueKind.Expression,
- valueType: PropValueType.string,
- value: "document.name",
- },
- });
- expect(transformedProps).toEqual({
- foo: "office space",
- });
- });
-
it("can handle expression that references an array of objects", () => {
const transformedProps = getPropsForPreview(
{
@@ -274,22 +236,6 @@ describe("template string literal value handling", () => {
foo: "1 ${unknownSource.city} 2",
});
});
-
- it("can handle an expression prop that references a parent expression prop", () => {
- const transformedProps = transformFooProp(
- "`childProp - ${props.parentExpression}`",
- {
- parentExpression: {
- kind: PropValueKind.Expression,
- valueType: PropValueType.string,
- value: "`parentProp - ${document.name}`",
- },
- }
- );
- expect(transformedProps).toEqual({
- foo: "childProp - parentProp - office space",
- });
- });
});
it("converts expressions using streams data into bracket syntax", () => {
@@ -313,7 +259,7 @@ it("applies expression sources for streams data", () => {
});
});
-function transformFooProp(value: string, parentProps: PropValues = {}) {
+function transformFooProp(value: string) {
return getPropsForPreview(
{
foo: {
@@ -325,11 +271,6 @@ function transformFooProp(value: string, parentProps: PropValues = {}) {
propShape,
{
...expressionSources,
- props: getPropsForPreview(
- parentProps,
- parentPropShape,
- expressionSources
- ),
}
);
}
diff --git a/packages/studio/tests/utils/removeTopLevelFragments.test.ts b/packages/studio-ui/tests/utils/removeTopLevelFragments.test.ts
similarity index 52%
rename from packages/studio/tests/utils/removeTopLevelFragments.test.ts
rename to packages/studio-ui/tests/utils/removeTopLevelFragments.test.ts
index 1eb93dee8..fa964ca30 100644
--- a/packages/studio/tests/utils/removeTopLevelFragments.test.ts
+++ b/packages/studio-ui/tests/utils/removeTopLevelFragments.test.ts
@@ -1,8 +1,6 @@
import {
ComponentState,
ComponentStateKind,
- FileMetadata,
- FileMetadataKind,
PageState,
StandardComponentState,
} from "@yext/studio-plugin";
@@ -50,41 +48,3 @@ it("removes top level fragments from a PageState record", () => {
},
]);
});
-
-it("removes top level fragments from a FileMetadata record", () => {
- const componentTree: ComponentState[] = [
- {
- kind: ComponentStateKind.Fragment,
- uuid: "fragment-uuid",
- },
- childComponent,
- ];
- const fileMetadataRecord: Record = {
- "module-metadata-uuid": {
- kind: FileMetadataKind.Module,
- componentTree,
- metadataUUID: "module-metadata-uuid",
- filepath: "/unused",
- },
- "component-metadata-uuid": {
- kind: FileMetadataKind.Component,
- filepath: "/unused",
- metadataUUID: "component-metadata-uuid",
- },
- };
-
- const updatedRecord = removeTopLevelFragments(fileMetadataRecord);
-
- expect(updatedRecord["component-metadata-uuid"]).toEqual(
- fileMetadataRecord["component-metadata-uuid"]
- );
- expect(updatedRecord["module-metadata-uuid"]).toEqual({
- ...fileMetadataRecord["module-metadata-uuid"],
- componentTree: [
- {
- ...childComponent,
- parentUUID: undefined,
- },
- ],
- });
-});
diff --git a/packages/studio-ui/tsconfig.json b/packages/studio-ui/tsconfig.json
new file mode 100644
index 000000000..70d77dd47
--- /dev/null
+++ b/packages/studio-ui/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "lib",
+ "types": ["@yext/studio-plugin/virtual-module", "vite-plugin-svgr/client"]
+ },
+ "include": ["src"]
+}
diff --git a/packages/studio-ui/vite.config.ts b/packages/studio-ui/vite.config.ts
new file mode 100644
index 000000000..6a068f8e5
--- /dev/null
+++ b/packages/studio-ui/vite.config.ts
@@ -0,0 +1,35 @@
+// vite.config.js
+import { resolve } from "path";
+import { defineConfig, PluginOption } from "vite";
+import svgr from "vite-plugin-svgr";
+import { visualizer } from "rollup-plugin-visualizer";
+import dts from "vite-plugin-dts";
+import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
+
+export default defineConfig({
+ plugins: [
+ svgr(),
+ dts(),
+ cssInjectedByJsPlugin(),
+ visualizer() as PluginOption,
+ ],
+ build: {
+ outDir: "lib",
+ sourcemap: true,
+ lib: {
+ entry: resolve(__dirname, "src/index.ts"),
+ formats: ["es"],
+ fileName: "src/index",
+ },
+ rollupOptions: {
+ external: [
+ "virtual_yext-studio-git-data",
+ "virtual_yext-studio",
+ "@pathToUserProjectRoot/tailwind.config",
+ "react",
+ "react-dom",
+ "react/jsx-runtime",
+ ],
+ },
+ },
+});
diff --git a/packages/studio/.prettierignore b/packages/studio/.prettierignore
deleted file mode 100644
index 0b9be3b37..000000000
--- a/packages/studio/.prettierignore
+++ /dev/null
@@ -1,4 +0,0 @@
-dist/
-lib/
-coverage/
-src/tailwind-full.css
\ No newline at end of file
diff --git a/packages/studio/package.json b/packages/studio/package.json
index f51349439..71522c355 100644
--- a/packages/studio/package.json
+++ b/packages/studio/package.json
@@ -1,6 +1,6 @@
{
"name": "@yext/studio",
- "version": "0.20.0",
+ "version": "0.22.0",
"types": "./lib/types.d.ts",
"type": "module",
"bin": {
@@ -10,55 +10,23 @@
"dev": "tsc --watch --preserveWatchOutput -p tsconfig.node.json & tsc --watch --preserveWatchOutput -p tsconfig.json",
"build": "run-script-os",
"build:default": "npm run build:windows && chmod 755 lib/bin/studio.js",
- "build:windows": "rimraf lib && tsc -p tsconfig.node.json && tsc -p tsconfig.json",
- "preview": "vite preview",
- "test": "jest",
- "typecheck-jest": "npx tsc -p tests/tsconfig.json"
+ "build:windows": "rimraf lib && tsc -p tsconfig.node.json && tsc -p tsconfig.json"
},
"dependencies": {
- "@dhmk/zustand-lens": "^2.0.5",
- "@minoru/react-dnd-treeview": "^3.4.1",
- "@restart/ui": "^1.5.2",
"@vitejs/plugin-react": "^4.0.4",
- "@yext/studio-plugin": "0.20.0",
+ "@yext/studio-plugin": "0.22.0",
+ "@yext/studio-ui": "0.22.0",
"autoprefixer": "^10.4.14",
"cac": "^6.7.14",
- "classnames": "^2.3.2",
"cross-env": "^7.0.3",
- "immer": "^9.0.21",
- "lodash": "^4.17.21",
- "path-browserify": "^1.0.1",
"postcss": "^8.4.27",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-modal": "3.16.1",
- "react-select": "^5.7.4",
- "react-toastify": "^9.1.1",
- "react-tooltip": "^5.18.0",
- "tailwind-merge": "^1.8.1",
- "tailwindcss": "^3.3.3",
"vite": "^4.4.7",
- "vite-plugin-svgr": "^2.4.0",
- "zundo": "2.0.0-beta.12",
- "zustand": "^4.3.2"
+ "vite-plugin-svgr": "^2.4.0"
},
"devDependencies": {
- "@babel/core": "^7.20.5",
- "@babel/plugin-syntax-flow": "^7.18.6",
- "@babel/plugin-transform-react-jsx": "^7.19.0",
- "@rollup/plugin-typescript": "^10.0.1",
- "@testing-library/jest-dom": "^5.16.5",
- "@testing-library/react": "^13.4.0",
- "@testing-library/user-event": "^14.4.3",
- "@types/jest": "^29.2.4",
- "@types/lodash": "^4.14.191",
"@types/node": "^18.11.15",
- "@types/path-browserify": "^1.0.0",
- "@types/react": "^18.0.26",
- "@types/react-dom": "^18.0.10",
- "@types/react-modal": "3.13.1",
- "jest": "^29.5.0",
- "jest-environment-jsdom": "^29.3.1",
- "resize-observer-polyfill": "^1.5.1"
+ "@types/react": "^18.0.26"
}
}
diff --git a/packages/studio/src/components/AddElementMenu/ElementSelector.tsx b/packages/studio/src/components/AddElementMenu/ElementSelector.tsx
deleted file mode 100644
index a9ced1fbf..000000000
--- a/packages/studio/src/components/AddElementMenu/ElementSelector.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-import { FileMetadataKind, ValidFileMetadata } from "@yext/studio-plugin";
-import { useCallback } from "react";
-import useStudioStore from "../../store/useStudioStore";
-import path from "path-browserify";
-import { ElementType } from "./AddElementMenu";
-import renderIconForType from "../common/renderIconForType";
-
-interface ElementSelectorProps {
- activeType: ElementType;
- afterSelect?: () => void;
-}
-
-/**
- * The list of available, addable elements for the current activeType.
- */
-export default function ElementSelector({
- activeType,
- afterSelect,
-}: ElementSelectorProps) {
- const UUIDToFileMetadata = useStudioStore((store) => {
- return store.fileMetadatas.UUIDToFileMetadata;
- });
-
- const addableElements = Object.values(UUIDToFileMetadata).filter(
- (metadata): metadata is ValidFileMetadata => {
- if (activeType === ElementType.Components) {
- return (
- metadata.kind === FileMetadataKind.Component &&
- !metadata.acceptsChildren
- );
- } else if (activeType === ElementType.Containers) {
- return !!(
- metadata.kind === FileMetadataKind.Component &&
- metadata.acceptsChildren
- );
- } else {
- return metadata.kind === FileMetadataKind.Module;
- }
- }
- );
-
- if (addableElements.length === 0) {
- return (
-
- Nothing to see here!
-
- );
- }
-
- return (
-
- {addableElements.map((metadata) => {
- return (
-
- );
- })}
-
- );
-}
-
-function Option({
- metadata,
- activeType,
- afterSelect,
-}: {
- metadata: ValidFileMetadata;
-} & ElementSelectorProps) {
- const componentName = path.basename(metadata.filepath, ".tsx");
- const moduleMetadataBeingEdited = useStudioStore((store) =>
- store.actions.getModuleMetadataBeingEdited()
- );
-
- const addComponent = useStudioStore((store) => {
- return store.actions.addComponent;
- });
-
- const handleSelect = useCallback(() => {
- addComponent(metadata);
- afterSelect?.();
- }, [afterSelect, addComponent, metadata]);
-
- // Prevent users from adding infinite looping modules.
- const isSameAsActiveModule =
- moduleMetadataBeingEdited?.metadataUUID === metadata.metadataUUID;
-
- return (
-
- {renderIconForType(activeType)}
- {componentName}
-
- );
-}
diff --git a/packages/studio/src/components/ModuleActions.tsx b/packages/studio/src/components/ModuleActions.tsx
deleted file mode 100644
index e210cbe0c..000000000
--- a/packages/studio/src/components/ModuleActions.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import {
- FileMetadata,
- FileMetadataKind,
- TypeGuards,
- ComponentState,
-} from "@yext/studio-plugin";
-import ModuleEditActions from "./ModuleActions/ModuleEditActions";
-import CreateModuleButton from "./ModuleActions/CreateModuleButton";
-
-/**
- * Renders either a {@link CreateModuleButton} or the {@link ModuleEditActions}, depending
- * on if the active Component is already a Module or not.
- *
- * @param metadata - The {@link FileMetadata} of the active Component.
- * @param state - The {@link ComponentState} of the active Component.
- */
-export default function ModuleActions(props: {
- metadata: FileMetadata;
- state: ComponentState;
-}) {
- const { metadata, state } = props;
- const isModule =
- metadata.kind === FileMetadataKind.Module &&
- (TypeGuards.isModuleState(state) || TypeGuards.isRepeaterState(state));
-
- return (
-
-
Module Actions
-
- {isModule ? (
-
- ) : (
-
- )}
-
-
- );
-}
diff --git a/packages/studio/src/components/ModuleActions/ActionIconWrapper.tsx b/packages/studio/src/components/ModuleActions/ActionIconWrapper.tsx
deleted file mode 100644
index 5b762fc7e..000000000
--- a/packages/studio/src/components/ModuleActions/ActionIconWrapper.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import classNames from "classnames";
-import { PropsWithChildren, useMemo } from "react";
-import { Tooltip } from "react-tooltip";
-
-import { v4 } from "uuid";
-
-export default function ActionIconWrapper(
- props: PropsWithChildren<{
- tooltip: string;
- disabled?: boolean;
- }>
-) {
- const anchorId = useMemo(() => v4(), []);
- const className = classNames("rounded p-1", {
- "text-gray-400": props.disabled,
- "text-violet-600 hover:bg-slate-200": !props.disabled,
- });
- return (
-
- {props.children}
-
-
- );
-}
diff --git a/packages/studio/src/components/ModuleActions/CreateModuleButton.tsx b/packages/studio/src/components/ModuleActions/CreateModuleButton.tsx
deleted file mode 100644
index ba66b1b40..000000000
--- a/packages/studio/src/components/ModuleActions/CreateModuleButton.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import FormModal, { FormData } from "../common/FormModal";
-import ButtonWithModal, {
- renderModalFunction,
-} from "../common/ButtonWithModal";
-import useStudioStore from "../../store/useStudioStore";
-import { useCallback, useState } from "react";
-import { ComponentStateKind } from "@yext/studio-plugin";
-
-type CreateModuleForm = { modulePath: string };
-
-const formData: FormData = {
- modulePath: { description: "Give the module a name:" },
-};
-
-/**
- * Renders a button for creating a module.
- */
-export default function CreateModuleButton(): JSX.Element | null {
- const [getActiveComponentState, createModule] = useStudioStore((store) => [
- store.actions.getActiveComponentState,
- store.createModule,
- ]);
- const [errorMessage, setErrorMessage] = useState("");
-
- const handleModalSave = useCallback(
- (form: CreateModuleForm) => {
- try {
- createModule(form.modulePath);
- return true;
- } catch (err: unknown) {
- if (err instanceof Error) {
- setErrorMessage(err.message);
- return false;
- } else {
- throw err;
- }
- }
- },
- [setErrorMessage, createModule]
- );
-
- const renderModal: renderModalFunction = useCallback(
- (isOpen, handleClose) => {
- return (
-
- );
- },
- [errorMessage, handleModalSave]
- );
-
- const activeComponentState = getActiveComponentState();
- if (
- !activeComponentState ||
- activeComponentState.kind === ComponentStateKind.Module
- ) {
- return null;
- }
-
- return (
-
- );
-}
diff --git a/packages/studio/src/components/ModuleActions/DeleteModuleButton.tsx b/packages/studio/src/components/ModuleActions/DeleteModuleButton.tsx
deleted file mode 100644
index 1d3b87bc3..000000000
--- a/packages/studio/src/components/ModuleActions/DeleteModuleButton.tsx
+++ /dev/null
@@ -1,154 +0,0 @@
-import { ReactComponent as DeleteModuleIcon } from "../../icons/deletemodule.svg";
-import ButtonWithModal, {
- renderModalFunction,
-} from "../common/ButtonWithModal";
-import { useCallback, useMemo } from "react";
-import {
- ComponentStateHelpers,
- ComponentStateKind,
- ModuleMetadata,
- TypeGuards,
-} from "@yext/studio-plugin";
-import DialogModal from "../common/DialogModal";
-import path from "path-browserify";
-import useStudioStore from "../../store/useStudioStore";
-import ActionIconWrapper from "./ActionIconWrapper";
-
-/**
- * When clicked, stages a module for deletion.
- * When the changes are committed, the module file will itself be deleted
- */
-export default function DeleteModuleButton({
- metadata,
-}: {
- metadata: ModuleMetadata;
-}) {
- const moduleName = path.basename(metadata.filepath, ".tsx");
- const detachAllModuleInstances = useStudioStore(
- (store) => store.pages.detachAllModuleInstances
- );
- const setActiveComponentUUID = useStudioStore(
- (store) => store.pages.setActiveComponentUUID
- );
- const removeFileMetadata = useStudioStore(
- (store) => store.fileMetadatas.removeFileMetadata
- );
- const pagesRecord = useStudioStore((store) => store.pages.pages);
-
- const isUsedInRepeater = Object.values(pagesRecord).some((pageState) =>
- pageState.componentTree.some(
- (c) =>
- TypeGuards.isRepeaterState(c) &&
- c.repeatedComponent.metadataUUID === metadata.metadataUUID
- )
- );
-
- const moduleUsages = Object.keys(pagesRecord).reduce(
- (usageList, pageName) => {
- const usageCount = pagesRecord[pageName].componentTree
- .filter(TypeGuards.isEditableComponentState)
- .map(ComponentStateHelpers.extractRepeatedState)
- .filter(
- (c) =>
- c.kind === ComponentStateKind.Module &&
- c.metadataUUID === metadata.metadataUUID
- ).length;
- usageList.push({
- pageName,
- usageCount,
- });
- return usageList;
- },
- [] as { pageName: string; usageCount: number }[]
- );
-
- const handleDelete = useCallback(() => {
- setActiveComponentUUID(undefined);
- detachAllModuleInstances(metadata);
- removeFileMetadata(metadata.metadataUUID);
- }, [
- setActiveComponentUUID,
- detachAllModuleInstances,
- metadata,
- removeFileMetadata,
- ]);
-
- const modalBody = useMemo(() => {
- return (
-
-
- Deleting this module will remove its status as a module, removing it
- from the Insert panel, and detach all other instances of it across
- your site. This will not delete the page elements themselves.
- {moduleUsages.length > 0 && " This module is found on:"}
-
- {moduleUsages.length > 0 && renderModuleUsages(moduleUsages)}
-
Press 'Delete' to confirm this."
-
- );
- }, [moduleUsages]);
-
- const renderModal: renderModalFunction = useCallback(
- (isOpen, handleClose) => {
- return (
-
- );
- },
- [handleDelete, modalBody]
- );
-
- const tooltipText = isUsedInRepeater
- ? "Unable to delete module while it is used in a list anywhere in the site"
- : "Delete";
-
- return (
- <>
-
-
-
- }
- />
- >
- );
-}
-
-function renderModuleUsages(
- moduleUsages: { pageName: string; usageCount: number }[]
-) {
- const usages = moduleUsages.filter(({ usageCount }) => usageCount > 0);
- if (usages.length === 0) {
- return null;
- }
-
- const usageCountText = (usageCount: number) => {
- if (usageCount > 1) {
- return `${usageCount} instances`;
- }
- return `1 instance`;
- };
-
- return (
-
- {usages.map(({ pageName, usageCount }) => {
- return (
-
- {pageName}: {usageCountText(usageCount)}
-
- );
- })}
-
- );
-}
diff --git a/packages/studio/src/components/ModuleActions/DetachModuleButton.tsx b/packages/studio/src/components/ModuleActions/DetachModuleButton.tsx
deleted file mode 100644
index c312b1480..000000000
--- a/packages/studio/src/components/ModuleActions/DetachModuleButton.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import ActionIconWrapper from "./ActionIconWrapper";
-import { ReactComponent as DetachModuleIcon } from "../../icons/detachmodule.svg";
-import { useCallback } from "react";
-import {
- ComponentStateHelpers,
- ModuleMetadata,
- ModuleState,
- RepeaterState,
- TypeGuards,
-} from "@yext/studio-plugin";
-import useStudioStore from "../../store/useStudioStore";
-
-export default function DetachModuleButton(props: {
- metadata: ModuleMetadata;
- state: ModuleState | RepeaterState;
-}) {
- const { metadata, state } = props;
- const detachModuleInstance = useStudioStore(
- (store) => store.actions.detachModuleInstance
- );
-
- const isRepeater = TypeGuards.isRepeaterState(state);
-
- const handleClick = useCallback(() => {
- !isRepeater && detachModuleInstance(metadata, state);
- }, [detachModuleInstance, metadata, state, isRepeater]);
-
- const moduleName =
- ComponentStateHelpers.extractRepeatedState(state).componentName;
- const tooltipText = isRepeater
- ? "Unable to detach module instance since it is in a list"
- : "Detach";
-
- return (
-
-
-
-
-
- );
-}
diff --git a/packages/studio/src/components/ModuleActions/EditModuleButton.tsx b/packages/studio/src/components/ModuleActions/EditModuleButton.tsx
deleted file mode 100644
index 50010c9cc..000000000
--- a/packages/studio/src/components/ModuleActions/EditModuleButton.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import {
- ComponentStateHelpers,
- ModuleState,
- RepeaterState,
-} from "@yext/studio-plugin";
-import { useCallback } from "react";
-import { ReactComponent as EditModuleIcon } from "../../icons/editmodule.svg";
-import useStudioStore from "../../store/useStudioStore";
-import ActionIconWrapper from "./ActionIconWrapper";
-
-export default function EditModuleButton({
- state,
-}: {
- state: ModuleState | RepeaterState;
-}) {
- const [setModuleUUIDBeingEdited, setActiveComponentUUID] = useStudioStore(
- (store) => [
- store.pages.setModuleUUIDBeingEdited,
- store.pages.setActiveComponentUUID,
- ]
- );
-
- const handleClick = useCallback(() => {
- setActiveComponentUUID(undefined);
- setModuleUUIDBeingEdited(state.uuid);
- }, [state.uuid, setModuleUUIDBeingEdited, setActiveComponentUUID]);
-
- const moduleName =
- ComponentStateHelpers.extractRepeatedState(state).componentName;
-
- return (
-
-
-
-
-
- );
-}
diff --git a/packages/studio/src/components/ModuleActions/ModuleEditActions.tsx b/packages/studio/src/components/ModuleActions/ModuleEditActions.tsx
deleted file mode 100644
index 5011ed645..000000000
--- a/packages/studio/src/components/ModuleActions/ModuleEditActions.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import {
- ModuleState,
- ModuleMetadata,
- RepeaterState,
-} from "@yext/studio-plugin";
-import DeleteModuleButton from "./DeleteModuleButton";
-import DetachModuleButton from "./DetachModuleButton";
-import EditModuleButton from "./EditModuleButton";
-
-/**
- * Displays a list of available actions for manipulating a Module.
- */
-export default function ModuleEditActions({
- metadata,
- state,
-}: {
- metadata: ModuleMetadata;
- state: ModuleState | RepeaterState;
-}) {
- return (
- <>
-
-
-
- >
- );
-}
diff --git a/packages/studio/src/components/ModulePreview.tsx b/packages/studio/src/components/ModulePreview.tsx
deleted file mode 100644
index 068a24ece..000000000
--- a/packages/studio/src/components/ModulePreview.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Dispatch, SetStateAction, useMemo } from "react";
-import ComponentTreePreview from "./ComponentTreePreview";
-import { ModuleState } from "@yext/studio-plugin";
-import useStudioStore from "../store/useStudioStore";
-import { ExpressionSources } from "../utils/getPropsForPreview";
-import { ITooltip } from "react-tooltip";
-
-export default function ModulePreview(props: {
- expressionSources: ExpressionSources;
- previewProps: Record;
- moduleState: ModuleState;
- setTooltipProps: Dispatch>;
-}) {
- const { expressionSources, previewProps, moduleState, setTooltipProps } =
- props;
-
- const getModuleMetadata = useStudioStore(
- (store) => store.fileMetadatas.getModuleMetadata
- );
- const componentTree = getModuleMetadata(
- moduleState.metadataUUID
- ).componentTree;
-
- const moduleExpressionSources = useMemo(
- () => ({
- ...expressionSources,
- props: previewProps,
- }),
- [expressionSources, previewProps]
- );
-
- return (
-
- );
-}
diff --git a/packages/studio/src/components/PreviewPanel.tsx b/packages/studio/src/components/PreviewPanel.tsx
deleted file mode 100644
index 94b288546..000000000
--- a/packages/studio/src/components/PreviewPanel.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import { Dispatch, SetStateAction, useMemo } from "react";
-import useStudioStore from "../store/useStudioStore";
-import usePreviewProps from "../hooks/usePreviewProps";
-import ComponentTreePreview from "./ComponentTreePreview";
-import useRawSiteSettings from "../hooks/useRawSiteSettings";
-import { ComponentStateHelpers, TypeGuards } from "@yext/studio-plugin";
-import { get } from "lodash";
-import { ITooltip } from "react-tooltip";
-
-export default function PreviewPanel(props: {
- setTooltipProps: Dispatch>;
-}) {
- const { setTooltipProps } = props;
- const [componentTree, moduleUUIDBeingEdited, getComponentState] =
- useStudioStore((store) => [
- store.actions.getComponentTree(),
- store.pages.moduleUUIDBeingEdited,
- store.actions.getComponentState,
- ]);
-
- const pageExpressionSources = usePageExpressionSources();
-
- const state = moduleUUIDBeingEdited
- ? getComponentState(componentTree, moduleUUIDBeingEdited)
- : undefined;
- const list =
- state && TypeGuards.isRepeaterState(state)
- ? get(pageExpressionSources, state.listExpression)
- : undefined;
- const item = Array.isArray(list) ? list[0] : undefined;
-
- const extractedState =
- state && TypeGuards.isEditableComponentState(state)
- ? ComponentStateHelpers.extractRepeatedState(state)
- : undefined;
- const parentPreviewProps = usePreviewProps(
- extractedState,
- pageExpressionSources,
- item
- );
-
- const expressionSources = useMemo(
- () => ({
- ...pageExpressionSources,
- ...(moduleUUIDBeingEdited && { props: parentPreviewProps }),
- }),
- [pageExpressionSources, moduleUUIDBeingEdited, parentPreviewProps]
- );
-
- if (!componentTree) {
- return null;
- }
-
- return (
-
- );
-}
-
-function usePageExpressionSources() {
- const activeEntityData = useStudioStore((store) =>
- store.pages.getActiveEntityData()
- );
- const rawSiteSettings = useRawSiteSettings();
- const pageExpressionSources = useMemo(
- () => ({
- document: activeEntityData,
- siteSettings: rawSiteSettings,
- }),
- [activeEntityData, rawSiteSettings]
- );
-
- return pageExpressionSources;
-}
diff --git a/packages/studio/src/components/PropsPanel.tsx b/packages/studio/src/components/PropsPanel.tsx
deleted file mode 100644
index 56b56fdba..000000000
--- a/packages/studio/src/components/PropsPanel.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import useActiveComponentWithProps from "../hooks/useActiveComponentWithProps";
-import ActiveComponentPropEditors from "./ActiveComponentPropEditors";
-import { ComponentStateKind } from "@yext/studio-plugin";
-import ModuleActions from "./ModuleActions";
-import RepeaterPanel from "./RepeaterPanel";
-import Divider from "./common/Divider";
-
-/**
- * Renders prop editors for the active component selected by the user.
- */
-export default function PropsPanel(): JSX.Element | null {
- const activeComponentWithProps = useActiveComponentWithProps();
- if (!activeComponentWithProps) {
- return null;
- }
- const {
- activeComponentMetadata,
- activeComponentState,
- extractedComponentState,
- propShape,
- } = activeComponentWithProps;
-
- const isModule = extractedComponentState.kind === ComponentStateKind.Module;
-
- return (
- <>
- {isModule && (
- <>
-
-
- >
- )}
-
-
- >
- );
-}
diff --git a/packages/studio/src/components/RepeaterPanel.tsx b/packages/studio/src/components/RepeaterPanel.tsx
deleted file mode 100644
index c1833b46a..000000000
--- a/packages/studio/src/components/RepeaterPanel.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { TypeGuards } from "@yext/studio-plugin";
-import Divider from "./common/Divider";
-import useActiveComponent from "../hooks/useActiveComponent";
-import MessageBubble from "./common/MessageBubble";
-
-export default function RepeaterPanel() {
- const { activeComponentState } = useActiveComponent();
- if (
- !activeComponentState ||
- !TypeGuards.isRepeaterState(activeComponentState)
- ) {
- return null;
- }
-
- return (
-
- );
-}
diff --git a/packages/studio/src/components/RepeaterPreview.tsx b/packages/studio/src/components/RepeaterPreview.tsx
deleted file mode 100644
index 526f04d24..000000000
--- a/packages/studio/src/components/RepeaterPreview.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { RepeaterState } from "@yext/studio-plugin";
-import { get } from "lodash";
-import { Dispatch, SetStateAction, useCallback, useMemo } from "react";
-import { ExpressionSources } from "../utils/getPropsForPreview";
-import ComponentPreview from "./ComponentPreview";
-import { ITooltip } from "react-tooltip";
-
-interface RepeaterPreviewProps {
- repeaterState: RepeaterState;
- expressionSources: ExpressionSources;
- setTooltipProps: Dispatch>;
-}
-
-/**
- * Renders the preview for a Repeater component.
- */
-export default function RepeaterPreview({
- repeaterState,
- expressionSources,
- setTooltipProps,
-}: RepeaterPreviewProps): JSX.Element | null {
- const { repeatedComponent, listExpression } = repeaterState;
- const repeatedElementState = useMemo(
- () => ({
- ...repeatedComponent,
- uuid: repeaterState.uuid,
- parentUUID: repeaterState.parentUUID,
- }),
- [repeatedComponent, repeaterState]
- );
-
- const renderRepeatedElement = useCallback(
- (item: unknown, key: number | string) => (
-
- ),
- [repeatedElementState, expressionSources, setTooltipProps]
- );
-
- const list = get(expressionSources, listExpression) as unknown;
- if (!Array.isArray(list)) {
- console.warn(
- `Unable to render list repeater. Expected "${listExpression}" to reference an array in `,
- expressionSources
- );
- return null;
- }
- return <>{list.map(renderRepeatedElement)}>;
-}
diff --git a/packages/studio/src/icons/hexagon.svg b/packages/studio/src/icons/hexagon.svg
deleted file mode 100644
index 2eec703a5..000000000
--- a/packages/studio/src/icons/hexagon.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/packages/studio/src/main.tsx b/packages/studio/src/main.tsx
index 9c5588d5f..14484cffb 100644
--- a/packages/studio/src/main.tsx
+++ b/packages/studio/src/main.tsx
@@ -1,10 +1,8 @@
import React from "react";
import ReactDOM from "react-dom/client";
-import App from "./App";
+import { App, hotReloadStore, StudioHMRUpdateID } from "@yext/studio-ui";
+import type { StudioHMRPayload } from "@yext/studio-plugin";
import "./tailwind-directives.css";
-import "react-tooltip/dist/react-tooltip.css";
-import { StudioHMRPayload, StudioHMRUpdateID } from "@yext/studio-plugin";
-import hotReloadStore from "./store/hotReloadStore";
if (import.meta.hot) {
import.meta.hot.on(StudioHMRUpdateID, (hmrPayload: StudioHMRPayload) => {
diff --git a/packages/studio/src/store/createModuleAction.ts b/packages/studio/src/store/createModuleAction.ts
deleted file mode 100644
index b50f6b790..000000000
--- a/packages/studio/src/store/createModuleAction.ts
+++ /dev/null
@@ -1,121 +0,0 @@
-import { StudioStore } from "./models/StudioStore";
-import path from "path-browserify";
-import {
- ComponentState,
- ComponentTreeHelpers,
- FileMetadataKind,
- ModuleMetadata,
- PropValueType,
-} from "@yext/studio-plugin";
-import { differenceWith, isEqual } from "lodash";
-import { v4 } from "uuid";
-
-export default function getCreateModuleAction(
- get: () => StudioStore
-): StudioStore["createModule"] {
- function throwIfInvalidFilepath(filepath: string) {
- const modulesFolder = get().studioConfig.paths.modules;
- const moduleName = path.basename(filepath, ".tsx");
- if (!filepath.startsWith(modulesFolder)) {
- throw new Error(
- `Error creating module: modulePath is invalid: "${path.relative(
- modulesFolder,
- filepath
- )}".`
- );
- } else if (moduleName.charAt(0) !== moduleName.charAt(0).toUpperCase()) {
- throw new Error(
- "Error creating module: Module names must start with an uppercase letter."
- );
- } else if (
- Object.values(get().fileMetadatas.UUIDToFileMetadata).some(
- (fileMetadata) =>
- path.basename(fileMetadata.filepath, ".tsx") === moduleName
- )
- ) {
- throw new Error(
- `Error creating module: module name "${moduleName}" is already used.`
- );
- }
- }
-
- function createModuleMetadata(
- filepath: string,
- descendants: ComponentState[],
- activeComponentState: ComponentState
- ): ModuleMetadata {
- const moduleMetadata: ModuleMetadata = {
- kind: FileMetadataKind.Module,
- componentTree: [
- { ...activeComponentState, parentUUID: undefined },
- ...descendants,
- ],
- metadataUUID: v4(),
- filepath,
- propShape: {},
- };
- if (get().studioConfig.isPagesJSRepo) {
- moduleMetadata.propShape = {
- document: {
- type: PropValueType.Record,
- recordKey: "string",
- recordValue: "any",
- required: true,
- },
- };
- }
- return moduleMetadata;
- }
-
- function createModule(
- filepath: string,
- componentTree: ComponentState[],
- activeComponentState: ComponentState
- ) {
- const descendants = ComponentTreeHelpers.getDescendants(
- activeComponentState,
- componentTree
- );
- const moduleMetadata: ModuleMetadata = createModuleMetadata(
- filepath,
- descendants,
- activeComponentState
- );
- get().fileMetadatas.setFileMetadata(
- moduleMetadata.metadataUUID,
- moduleMetadata
- );
- const moduleState = get().actions.createComponentState(moduleMetadata);
- const updatedPageComponentTree: ComponentState[] = differenceWith(
- componentTree,
- descendants,
- isEqual
- ).map((c) => {
- if (c.uuid === activeComponentState.uuid) {
- return {
- ...moduleState,
- parentUUID: c.parentUUID,
- };
- }
- return c;
- });
- get().actions.updateComponentTree(updatedPageComponentTree);
- get().pages.setActiveComponentUUID(moduleState.uuid);
- }
-
- return (modulePath: string) => {
- if (!modulePath) {
- throw new Error("Error creating module: a modulePath is required.");
- }
- const modulesFolder = get().studioConfig.paths.modules;
- const filepath = path.join(modulesFolder, modulePath + ".tsx");
- throwIfInvalidFilepath(filepath);
-
- const componentTree = get().actions.getComponentTree() ?? [];
- const activeComponentState = get().actions.getActiveComponentState();
- if (!activeComponentState) {
- throw new Error("Tried to create module without active component.");
- }
- createModule(filepath, componentTree, activeComponentState);
- };
-}
diff --git a/packages/studio/src/store/slices/createFileMetadataSlice.ts b/packages/studio/src/store/slices/createFileMetadataSlice.ts
deleted file mode 100644
index 540ac7b2f..000000000
--- a/packages/studio/src/store/slices/createFileMetadataSlice.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-import {
- ComponentMetadata,
- ComponentState,
- FileMetadata,
- FileMetadataKind,
- ModuleMetadata,
-} from "@yext/studio-plugin";
-import initialStudioData from "virtual_yext-studio";
-import FileMetadataSlice from "../models/slices/FileMetadataSlice";
-import { SliceCreator } from "../models/utils";
-import removeTopLevelFragments from "../../utils/removeTopLevelFragments";
-
-const createFileMetadataSlice: SliceCreator = (
- set,
- get
-) => ({
- UUIDToFileMetadata: removeTopLevelFragments(
- initialStudioData.UUIDToFileMetadata
- ),
- UUIDToImportedComponent: {},
- setFileMetadata: (metadataUUID: string, metadata: FileMetadata) =>
- set((store) => {
- store.UUIDToFileMetadata[metadataUUID] = metadata;
- }),
- getFileMetadata: (metadataUUID: string) =>
- get().UUIDToFileMetadata[metadataUUID],
- removeFileMetadata: (metadataUUID: string) =>
- set((store) => {
- const metadata = store.UUIDToFileMetadata[metadataUUID];
- if (metadata.kind === FileMetadataKind.Module) {
- delete store.UUIDToFileMetadata[metadataUUID];
- } else {
- console.error(
- "removeFileMetadata is only allowed for modules, not:",
- metadata.kind
- );
- }
- }),
- getComponentMetadata: (metadataUUID) => {
- const fileMetadata = get().getFileMetadata(metadataUUID);
- assertIsComponentMetadata(fileMetadata);
- return fileMetadata;
- },
- getModuleMetadata: (metadataUUID) => {
- const fileMetadata = get().getFileMetadata(metadataUUID);
- assertIsModuleMetadata(fileMetadata);
- return fileMetadata;
- },
- setImportedComponent(uuid, importedComponent) {
- set((store) => {
- store.UUIDToImportedComponent[uuid] = importedComponent;
- });
- },
- setComponentTreeInModule(
- metadataUUID: string,
- componentTree: ComponentState[]
- ) {
- set((store) => {
- const fileMetadata = store.UUIDToFileMetadata[metadataUUID];
- assertIsModuleMetadata(fileMetadata);
- fileMetadata.componentTree = componentTree;
- });
- },
-});
-
-function assertIsModuleMetadata(
- fileMetadata?: FileMetadata
-): asserts fileMetadata is ModuleMetadata {
- if (fileMetadata?.kind !== FileMetadataKind.Module) {
- throw new Error(
- `Expected a ModuleMetadata, instead received ${JSON.stringify(
- fileMetadata,
- null,
- 2
- )}.`
- );
- }
-}
-
-function assertIsComponentMetadata(
- fileMetadata?: FileMetadata
-): asserts fileMetadata is ComponentMetadata {
- if (fileMetadata?.kind !== FileMetadataKind.Component) {
- throw new Error(
- `Expected a ComponentMetadata, instead received ${JSON.stringify(
- fileMetadata,
- null,
- 2
- )}.`
- );
- }
-}
-
-export default createFileMetadataSlice;
diff --git a/packages/studio/src/store/slices/pages/detachAllModuleInstances.ts b/packages/studio/src/store/slices/pages/detachAllModuleInstances.ts
deleted file mode 100644
index 7339be374..000000000
--- a/packages/studio/src/store/slices/pages/detachAllModuleInstances.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import PageSlice from "../../models/slices/PageSlice";
-import {
- ComponentState,
- ComponentStateKind,
- ComponentTreeHelpers,
- ModuleMetadata,
-} from "@yext/studio-plugin";
-import { v4 } from "uuid";
-
-/**
- * Creates an action that loops through all pages, and detaches all instances of the given module.
- */
-export default function createDetachAllModuleInstances(get: () => PageSlice) {
- return (metadata: ModuleMetadata) => {
- const pagesSlice = get();
- pagesSlice.setActiveComponentUUID(undefined);
- Object.keys(pagesSlice.pages).forEach((pageName) => {
- const originalPage = pagesSlice.pages[pageName];
- const updatedComponentTree = detachModuleInstances(
- metadata,
- originalPage.componentTree
- );
- get().setComponentTreeInPage(pageName, updatedComponentTree);
- });
- };
-}
-
-/**
- * Loops through the given componentTree, and detaches all modules that are instances
- * of the passed in ModuleMetadata.
- */
-function detachModuleInstances(
- metadata: ModuleMetadata,
- componentTree: ComponentState[]
-) {
- return componentTree.flatMap((componentStateToDetach) => {
- const isModule = componentStateToDetach.kind === ComponentStateKind.Module;
- if (
- !isModule ||
- componentStateToDetach.metadataUUID !== metadata.metadataUUID
- ) {
- return componentStateToDetach;
- }
- const detachedTree =
- ComponentTreeHelpers.mapComponentTreeParentsFirst(
- metadata.componentTree,
- (child, parentValue) => {
- return {
- ...child,
- uuid: v4(),
- parentUUID: parentValue?.uuid ?? componentStateToDetach.parentUUID,
- };
- }
- );
- return detachedTree;
- });
-}
diff --git a/packages/studio/types.ts b/packages/studio/src/types.ts
similarity index 100%
rename from packages/studio/types.ts
rename to packages/studio/src/types.ts
diff --git a/packages/studio/tailwind.config.ts b/packages/studio/tailwind.config.ts
index 510862865..509ef7c38 100644
--- a/packages/studio/tailwind.config.ts
+++ b/packages/studio/tailwind.config.ts
@@ -7,7 +7,7 @@ import {
} from "@yext/studio-plugin";
import path from "path";
import fs from "fs";
-import generateTailwindSafelist from "./src/utils/generateTailwindSafelist";
+import { generateTailwindSafelist } from "@yext/studio-plugin";
const getRootDir = (): string => {
const cliArgs: CliArgs = JSON.parse(
@@ -50,6 +50,7 @@ export default {
content: [
path.resolve(__dirname, "src/**/*.{ts,tsx}"),
path.resolve(__dirname, "index.html"),
+ path.join(path.dirname(require.resolve("@yext/studio-ui")), "**/*.js"),
...transformedUserContent,
],
safelist: generateTailwindSafelist(userTailwindTheme),
diff --git a/packages/studio/tests/__utils__/mockRepeaterActiveComponent.ts b/packages/studio/tests/__utils__/mockRepeaterActiveComponent.ts
deleted file mode 100644
index beaed0bf5..000000000
--- a/packages/studio/tests/__utils__/mockRepeaterActiveComponent.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import {
- RepeaterState,
- ComponentStateKind,
- PropValueKind,
- PropValueType,
- ComponentMetadata,
- FileMetadataKind,
- ModuleMetadata,
-} from "@yext/studio-plugin";
-import mockStoreActiveComponent from "./mockActiveComponentState";
-
-const repeaterComponentState: RepeaterState = {
- kind: ComponentStateKind.Repeater,
- uuid: "1234",
- listExpression: "someList",
- repeatedComponent: {
- kind: ComponentStateKind.Standard,
- componentName: "Standard",
- props: {
- text: {
- kind: PropValueKind.Literal,
- valueType: PropValueType.string,
- value: "test",
- },
- num: {
- kind: PropValueKind.Literal,
- valueType: PropValueType.number,
- value: 5,
- },
- },
- metadataUUID: "5678",
- },
-};
-const componentMetadata: ComponentMetadata = {
- kind: FileMetadataKind.Component,
- filepath: "/some/file",
- metadataUUID: "5678",
- propShape: {
- text: {
- type: PropValueType.string,
- required: false,
- },
- num: {
- type: PropValueType.number,
- required: false,
- },
- },
-};
-
-const repeaterModuleState: RepeaterState = {
- kind: ComponentStateKind.Repeater,
- uuid: "1234",
- listExpression: "someList",
- repeatedComponent: {
- kind: ComponentStateKind.Module,
- componentName: "Mod",
- props: {},
- metadataUUID: "5678",
- },
-};
-const moduleMetadata: ModuleMetadata = {
- kind: FileMetadataKind.Module,
- filepath: "/some/file",
- metadataUUID: "5678",
- propShape: {},
- componentTree: [],
-};
-
-export function mockRepeaterActiveComponent(isRepeatedModule = false) {
- if (isRepeatedModule) {
- return mockStoreActiveComponent({
- activeComponent: repeaterModuleState,
- activeComponentMetadata: moduleMetadata,
- });
- }
- return mockStoreActiveComponent({
- activeComponent: repeaterComponentState,
- activeComponentMetadata: componentMetadata,
- });
-}
diff --git a/packages/studio/tests/components/CreateModuleButton.test.tsx b/packages/studio/tests/components/CreateModuleButton.test.tsx
deleted file mode 100644
index 9bc5187ec..000000000
--- a/packages/studio/tests/components/CreateModuleButton.test.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-import { render, screen } from "@testing-library/react";
-import CreateModuleButton from "../../src/components/ModuleActions/CreateModuleButton";
-import { ComponentStateKind, FileMetadataKind } from "@yext/studio-plugin";
-import useStudioStore from "../../src/store/useStudioStore";
-import userEvent from "@testing-library/user-event";
-import { searchBarComponent } from "../__fixtures__/componentStates";
-import mockStore from "../__utils__/mockStore";
-
-beforeEach(() => {
- mockStore({
- pages: {
- pages: {
- universal: {
- componentTree: [
- {
- kind: ComponentStateKind.Fragment,
- uuid: "mock-uuid-0",
- },
- {
- ...searchBarComponent,
- uuid: "mock-uuid-1",
- parentUUID: "mock-uuid-0",
- },
- {
- kind: ComponentStateKind.Module,
- uuid: "mock-uuid-2",
- parentUUID: "mock-uuid-1",
- componentName: "module",
- props: {},
- metadataUUID: "mock-metadata",
- },
- {
- ...searchBarComponent,
- uuid: "mock-uuid-3",
- parentUUID: "mock-uuid-0",
- },
- ],
- cssImports: [],
- filepath: "mock-filepath",
- },
- },
- activePageName: "universal",
- activeComponentUUID: "mock-uuid-1",
- },
- fileMetadatas: {
- UUIDToFileMetadata: {
- "Testy-metadata-uuid": {
- kind: FileMetadataKind.Module,
- componentTree: [],
- metadataUUID: "Testy-metadata-uuid",
- filepath: "src/modules/Testy.tsx",
- },
- },
- },
- });
-});
-
-it("does not render when there is no active page state", async () => {
- await useStudioStore.getState().actions.updateActivePage(undefined);
- render( );
- expect(screen.queryByRole("button")).toBeNull();
-});
-
-it("does not render when there is no active component state", () => {
- useStudioStore.getState().pages.setActiveComponentUUID(undefined);
- render( );
- expect(screen.queryByRole("button")).toBeNull();
-});
-
-it("does not render when the active component is a module", () => {
- useStudioStore.getState().pages.setActiveComponentUUID("mock-uuid-2");
- render( );
- expect(screen.queryByRole("button")).toBeNull();
-});
-
-it("gives an error if the module name is already used", async () => {
- render( );
- await userEvent.click(screen.getByRole("button"));
- const textbox = screen.getByRole("textbox");
- await userEvent.type(textbox, "Testy");
- const saveButton = screen.getByRole("button", { name: "Save" });
- await userEvent.click(saveButton);
- expect(
- screen.getByText(
- 'Error creating module: module name "Testy" is already used.'
- )
- ).toBeDefined();
-});
-
-it("gives an error if the module path is invalid", async () => {
- render( );
- await userEvent.click(screen.getByRole("button"));
- const textbox = screen.getByRole("textbox");
- await userEvent.type(textbox, "../Test");
- const saveButton = screen.getByRole("button", { name: "Save" });
- await userEvent.click(saveButton);
- expect(
- screen.getByText(
- 'Error creating module: modulePath is invalid: "../Test.tsx".'
- )
- ).toBeDefined();
-});
-
-it("closes the modal when a module is successfully created", async () => {
- const createModuleSpy = jest.spyOn(useStudioStore.getState(), "createModule");
- render( );
- await userEvent.click(screen.getByRole("button"));
- const textbox = screen.getByRole("textbox");
- await userEvent.type(textbox, "Module");
- const saveButton = screen.getByRole("button", { name: "Save" });
- await userEvent.click(saveButton);
- expect(createModuleSpy).toBeCalledWith("Module");
- expect(screen.queryByText("Save")).toBeNull();
-});
diff --git a/packages/studio/tests/components/ModuleActions.test.tsx/DeleteModuleButton.test.tsx b/packages/studio/tests/components/ModuleActions.test.tsx/DeleteModuleButton.test.tsx
deleted file mode 100644
index f6ab5df45..000000000
--- a/packages/studio/tests/components/ModuleActions.test.tsx/DeleteModuleButton.test.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import { render, screen } from "@testing-library/react";
-import userEvent from "@testing-library/user-event";
-import {
- ComponentStateKind,
- FileMetadataKind,
- ModuleMetadata,
- PageState,
-} from "@yext/studio-plugin";
-import DeleteModuleButton from "../../../src/components/ModuleActions/DeleteModuleButton";
-import useStudioStore from "../../../src/store/useStudioStore";
-import mockStore from "../../__utils__/mockStore";
-
-it("can open modal and delete modules", async () => {
- const basicPageState: PageState = {
- componentTree: [
- {
- kind: ComponentStateKind.Module,
- componentName: "Star",
- props: {},
- uuid: "first-comp-state",
- metadataUUID: "star-metadata-uuid",
- },
- ],
- cssImports: [],
- filepath: "unused",
- };
-
- const testModuleMetadata: ModuleMetadata = {
- kind: FileMetadataKind.Module,
- metadataUUID: "star-metadata-uuid",
- componentTree: [
- {
- kind: ComponentStateKind.Standard,
- componentName: "Banner",
- props: {},
- uuid: "will-be-replaced",
- metadataUUID: "banner-metadata-uuid",
- },
- ],
- filepath: "unused/Star.tsx",
- };
-
- mockStore({
- pages: {
- activeComponentUUID: "will be unset after",
- pages: {
- firstPage: basicPageState,
- },
- },
- fileMetadatas: {
- UUIDToFileMetadata: {
- "star-metadata-uuid": testModuleMetadata,
- },
- },
- });
-
- render( );
- const openModalButton = await screen.findByRole("button", {
- name: "Delete Module Star",
- });
- await userEvent.click(openModalButton);
- const deleteModuleConfirmation = await screen.findByRole("button", {
- name: "Delete",
- });
- await userEvent.click(deleteModuleConfirmation);
- expect(useStudioStore.getState().pages.activeComponentUUID).toBeUndefined();
- expect(useStudioStore.getState().pages.pages).toEqual({
- firstPage: expect.objectContaining({
- componentTree: [
- expect.objectContaining({ kind: ComponentStateKind.Standard }),
- ],
- }),
- });
- expect(useStudioStore.getState().fileMetadatas.UUIDToFileMetadata).toEqual(
- {}
- );
-});
diff --git a/packages/studio/tests/components/ModuleActions.test.tsx/EditModuleButton.test.tsx b/packages/studio/tests/components/ModuleActions.test.tsx/EditModuleButton.test.tsx
deleted file mode 100644
index 1b80106bf..000000000
--- a/packages/studio/tests/components/ModuleActions.test.tsx/EditModuleButton.test.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { render, screen } from "@testing-library/react";
-import userEvent from "@testing-library/user-event";
-import {
- ComponentStateKind,
- FileMetadataKind,
- ModuleMetadata,
- ModuleState,
-} from "@yext/studio-plugin";
-import EditModuleButton from "../../../src/components/ModuleActions/EditModuleButton";
-import useStudioStore from "../../../src/store/useStudioStore";
-import mockStore from "../../__utils__/mockStore";
-
-it("sets module UUID being edited and resets active component when clicked", async () => {
- const moduleState: ModuleState = {
- kind: ComponentStateKind.Module,
- componentName: "Star",
- props: {},
- uuid: "first-comp-state",
- metadataUUID: "star-metadata-uuid",
- };
- const testModuleMetadata: ModuleMetadata = {
- kind: FileMetadataKind.Module,
- metadataUUID: "star-metadata-uuid",
- componentTree: [
- {
- kind: ComponentStateKind.Standard,
- componentName: "Banner",
- props: {},
- uuid: "will-be-replaced",
- metadataUUID: "banner-metadata-uuid",
- },
- ],
- filepath: "unused/Star.tsx",
- };
-
- mockStore({
- pages: {
- activeComponentUUID: "first-comp-state",
- pages: {
- firstPage: {
- componentTree: [moduleState],
- cssImports: [],
- filepath: "unused",
- },
- },
- },
- fileMetadatas: {
- UUIDToFileMetadata: {
- "star-metadata-uuid": testModuleMetadata,
- },
- },
- });
-
- render( );
- const editButton = await screen.findByRole("button", {
- name: "Edit Module Star",
- });
- await userEvent.click(editButton);
- expect(useStudioStore.getState().pages.moduleUUIDBeingEdited).toEqual(
- "first-comp-state"
- );
- expect(useStudioStore.getState().pages.activeComponentUUID).toBeUndefined();
-});
diff --git a/packages/studio/tests/components/PropsPanel.test.tsx b/packages/studio/tests/components/PropsPanel.test.tsx
deleted file mode 100644
index 24e62c1f8..000000000
--- a/packages/studio/tests/components/PropsPanel.test.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-import {
- ComponentMetadata,
- ComponentStateKind,
- FileMetadataKind,
- ModuleMetadata,
- ModuleState,
- StandardComponentState,
-} from "@yext/studio-plugin";
-import mockStoreActiveComponent from "../__utils__/mockActiveComponentState";
-import PropsPanel from "../../src/components/PropsPanel";
-import { render, screen } from "@testing-library/react";
-import { mockRepeaterActiveComponent } from "../__utils__/mockRepeaterActiveComponent";
-
-it("does not render prop editor(s) for fragment component", () => {
- mockStoreActiveComponent({
- activeComponent: {
- kind: ComponentStateKind.Fragment,
- uuid: "fragment-uuid",
- },
- });
- const { container } = render( );
- expect(container).toBeEmptyDOMElement();
-});
-
-it("does not render prop editor(s) when there's no selected active component", () => {
- mockStoreActiveComponent({});
- const { container } = render( );
- expect(container).toBeEmptyDOMElement();
-});
-
-it("does not render 'Create Module' button for Standard Component", () => {
- const state: StandardComponentState = {
- kind: ComponentStateKind.Standard,
- componentName: "Standard",
- props: {},
- uuid: "1234",
- metadataUUID: "5678",
- };
- const metadata: ComponentMetadata = {
- kind: FileMetadataKind.Component,
- filepath: "/some/file",
- metadataUUID: "5678",
- propShape: {},
- };
-
- mockStoreActiveComponent({
- activeComponent: state,
- activeComponentMetadata: metadata,
- });
-
- render( );
- expect(screen.queryByText("Create Module")).toBeNull();
-});
-
-it("renders Module Actions for Active Module", () => {
- const state: ModuleState = {
- kind: ComponentStateKind.Module,
- componentName: "Test",
- props: {},
- uuid: "1234",
- metadataUUID: "5678",
- };
- const metadata: ModuleMetadata = {
- kind: FileMetadataKind.Module,
- filepath: "/some/file",
- metadataUUID: "5678",
- propShape: {},
- componentTree: [],
- };
-
- mockStoreActiveComponent({
- activeComponent: state,
- activeComponentMetadata: metadata,
- });
-
- render( );
- expect(screen.getAllByRole("button")).toHaveLength(3);
- screen.getByRole("button", { name: "Edit Module Test" });
- screen.getByRole("button", { name: "Detach Module Test" });
- screen.getByRole("button", { name: "Delete Module file" });
-});
-
-describe("Repeaters", () => {
- it("renders repeated component's props", () => {
- mockRepeaterActiveComponent();
- render( );
- screen.getByText("text");
- expect(screen.getAllByRole("textbox")[0]).toHaveValue("test");
- screen.getByText("num");
- expect(screen.getByRole("textbox", { name: "num" })).toHaveValue("5");
- });
-
- it("does not render Create Module button for a repeated component", () => {
- mockRepeaterActiveComponent();
- render( );
- expect(screen.queryByText("Create Module")).toBeNull();
- });
-
- it("renders Module Actions for a repeated module", () => {
- mockRepeaterActiveComponent(true);
- render( );
- expect(screen.getAllByRole("button")).toHaveLength(3);
- const editButton = screen.getByRole("button", { name: "Edit Module Mod" });
- expect(editButton).toBeEnabled();
- const detachButton = screen.getByRole("button", {
- name: "Detach Module Mod",
- });
- expect(detachButton).toBeDisabled();
- const deleteButton = screen.getByRole("button", {
- name: "Delete Module file",
- });
- expect(deleteButton).toBeDisabled();
- });
-});
diff --git a/packages/studio/tests/components/RepeaterPanel.test.tsx b/packages/studio/tests/components/RepeaterPanel.test.tsx
deleted file mode 100644
index 3ba995070..000000000
--- a/packages/studio/tests/components/RepeaterPanel.test.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { ComponentStateKind, FileMetadataKind } from "@yext/studio-plugin";
-import mockStoreActiveComponent from "../__utils__/mockActiveComponentState";
-import { render, screen } from "@testing-library/react";
-import { mockRepeaterActiveComponent } from "../__utils__/mockRepeaterActiveComponent";
-import RepeaterPanel from "../../src/components/RepeaterPanel";
-
-it("does not render panel for non-repeater component", () => {
- mockStoreActiveComponent({
- activeComponent: {
- kind: ComponentStateKind.Standard,
- uuid: "banner-uuid",
- metadataUUID: "metadata-uuid",
- componentName: "Banner",
- props: {},
- },
- activeComponentMetadata: {
- kind: FileMetadataKind.Component,
- filepath: "/some/file",
- metadataUUID: "metadata-uuid",
- propShape: {},
- },
- });
- const { container } = render( );
- expect(container).toBeEmptyDOMElement();
-});
-
-it("only renders message for repeated component", () => {
- mockRepeaterActiveComponent();
- render( );
- expect(
- screen.getByText(
- "This component is repeated in a list. Please contact a developer to edit the list settings."
- )
- ).toBeDefined();
- expect(screen.queryByText("List")).toBeFalsy();
- expect(screen.queryByRole("checkbox")).toBeFalsy();
- expect(screen.queryByText("List Field")).toBeFalsy();
- expect(screen.queryByRole("textbox")).toBeFalsy();
-});
diff --git a/packages/studio/tests/store/StudioActions/activeComponentActions.test.ts b/packages/studio/tests/store/StudioActions/activeComponentActions.test.ts
deleted file mode 100644
index ca6b02782..000000000
--- a/packages/studio/tests/store/StudioActions/activeComponentActions.test.ts
+++ /dev/null
@@ -1,176 +0,0 @@
-import {
- ComponentState,
- ComponentStateKind,
- FileMetadataKind,
- PropValueKind,
- PropValues,
- PropValueType,
-} from "@yext/studio-plugin";
-import useStudioStore from "../../../src/store/useStudioStore";
-import mockStore from "../../__utils__/mockStore";
-
-describe("getActiveComponentState", () => {
- it("can get the current active component within a module", () => {
- mockInitialStore(true);
- const componentState = useStudioStore
- .getState()
- .actions.getActiveComponentState();
- expect(componentState).toEqual(
- expect.objectContaining({
- componentName: "Banner",
- })
- );
- });
-
- it("can get the current active component within a page", () => {
- mockInitialStore(false);
- const componentState = useStudioStore
- .getState()
- .actions.getActiveComponentState();
- expect(componentState).toEqual(
- expect.objectContaining({
- componentName: "MyModule",
- })
- );
- });
-});
-
-describe("updateActiveComponentProps", () => {
- it("when a module is being edited, updates the props inside the module's tree", () => {
- mockInitialStore(true);
- const updatedProps: PropValues = {
- hi: {
- kind: PropValueKind.Literal,
- valueType: PropValueType.string,
- value: "bye bye bocchi",
- },
- };
- useStudioStore.getState().actions.updateActiveComponentProps(updatedProps);
- const componentStateAfterUpdate = useStudioStore
- .getState()
- .actions.getActiveComponentState();
- expect(componentStateAfterUpdate).toEqual(
- expect.objectContaining({
- componentName: "Banner",
- props: updatedProps,
- })
- );
- });
-
- it("when a module is not being edited, updates the props in the current active page", () => {
- mockInitialStore(false);
- const updatedProps: PropValues = {
- hi: {
- kind: PropValueKind.Literal,
- valueType: PropValueType.string,
- value: "bye bye bocchi",
- },
- };
- useStudioStore.getState().actions.updateActiveComponentProps(updatedProps);
- const componentStateAfterUpdate = useStudioStore
- .getState()
- .actions.getActiveComponentState();
- expect(componentStateAfterUpdate).toEqual(
- expect.objectContaining({
- componentName: "MyModule",
- props: updatedProps,
- })
- );
- });
-});
-
-describe("getComponentTree", () => {
- it("can get the component tree when a module is being edited", () => {
- mockInitialStore(true);
- const tree = useStudioStore.getState().actions.getComponentTree();
- expect(tree).toEqual([
- expect.objectContaining({
- componentName: "Banner",
- }),
- ]);
- });
- it("can get the component tree when a page is being edited", () => {
- mockInitialStore(false);
- const tree = useStudioStore.getState().actions.getComponentTree();
- expect(tree).toEqual([
- expect.objectContaining({
- componentName: "MyModule",
- }),
- ]);
- });
-});
-
-describe("updateComponentTree", () => {
- it("can rearrange the component tree when a module is being edited", () => {
- mockInitialStore(true);
- const updatedTree: ComponentState[] = [
- {
- kind: ComponentStateKind.BuiltIn,
- props: {},
- componentName: "div",
- uuid: "div-0",
- },
- ];
- useStudioStore.getState().actions.updateComponentTree(updatedTree);
- const tree = useStudioStore.getState().actions.getComponentTree();
- expect(tree).toEqual(updatedTree);
- });
-
- it("can rearrange the component tree when a page is being edited", () => {
- mockInitialStore(false);
- const updatedTree: ComponentState[] = [
- {
- kind: ComponentStateKind.BuiltIn,
- props: {},
- componentName: "div",
- uuid: "div-0",
- },
- ];
- useStudioStore.getState().actions.updateComponentTree(updatedTree);
- const tree = useStudioStore.getState().actions.getComponentTree();
- expect(tree).toEqual(updatedTree);
- });
-});
-
-function mockInitialStore(isEditingModule: boolean) {
- mockStore({
- pages: {
- activePageName: "testpage",
- moduleUUIDBeingEdited: isEditingModule ? "module-0" : undefined,
- activeComponentUUID: isEditingModule ? "banner-0" : "module-0",
- pages: {
- testpage: {
- componentTree: [
- {
- kind: ComponentStateKind.Module,
- componentName: "MyModule",
- props: {},
- uuid: "module-0",
- metadataUUID: "moduleMeta",
- },
- ],
- cssImports: [],
- filepath: "page-filepath",
- },
- },
- },
- fileMetadatas: {
- UUIDToFileMetadata: {
- moduleMeta: {
- metadataUUID: "moduleMeta",
- kind: FileMetadataKind.Module,
- filepath: "module-filepath",
- componentTree: [
- {
- kind: ComponentStateKind.Standard,
- props: {},
- componentName: "Banner",
- uuid: "banner-0",
- metadataUUID: "bannerMeta",
- },
- ],
- },
- },
- },
- });
-}
diff --git a/packages/studio/tests/store/StudioActions/detachModuleInstance.test.ts b/packages/studio/tests/store/StudioActions/detachModuleInstance.test.ts
deleted file mode 100644
index 89b4d53c1..000000000
--- a/packages/studio/tests/store/StudioActions/detachModuleInstance.test.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import {
- ComponentState,
- ComponentStateKind,
- FileMetadataKind,
- ModuleMetadata,
- ModuleState,
-} from "@yext/studio-plugin";
-import useStudioStore from "../../../src/store/useStudioStore";
-import mockStore from "../../__utils__/mockStore";
-
-it("can detach module instances", () => {
- const moduleMetadata: ModuleMetadata = {
- kind: FileMetadataKind.Module,
- metadataUUID: "my-module",
- filepath: "unuesd",
- componentTree: [
- {
- kind: ComponentStateKind.Standard,
- componentName: "Container",
- uuid: "button-0",
- props: {},
- metadataUUID: "container-metadata",
- },
- {
- kind: ComponentStateKind.Standard,
- componentName: "Banner",
- uuid: "banner-0",
- props: {},
- metadataUUID: "banner-metadata",
- parentUUID: "button-0",
- },
- ],
- };
- const tree: ComponentState[] = [
- {
- kind: ComponentStateKind.BuiltIn,
- componentName: "div",
- uuid: "div-0",
- props: {},
- },
- {
- kind: ComponentStateKind.Module,
- componentName: "MyModule",
- props: {},
- uuid: "module-0",
- metadataUUID: "my-module",
- parentUUID: "div-0",
- },
- {
- kind: ComponentStateKind.BuiltIn,
- componentName: "div",
- uuid: "div-1",
- props: {},
- parentUUID: "div-0",
- },
- ];
- mockStore({
- pages: {
- activePageName: "testpage",
- pages: {
- testpage: {
- componentTree: tree,
- cssImports: [],
- filepath: "unused",
- },
- },
- },
- fileMetadatas: {
- UUIDToFileMetadata: {
- "my-module": moduleMetadata,
- },
- },
- });
- useStudioStore
- .getState()
- .actions.detachModuleInstance(moduleMetadata, tree[1] as ModuleState);
- const actualTree = useStudioStore.getState().actions.getComponentTree();
- expect(actualTree).toEqual([
- tree[0],
- {
- ...moduleMetadata.componentTree[0],
- uuid: expect.any(String),
- parentUUID: "div-0",
- },
- {
- ...moduleMetadata.componentTree[1],
- uuid: expect.any(String),
- parentUUID: actualTree?.[1]?.uuid,
- },
- tree[2],
- ]);
-});
diff --git a/packages/studio/tests/store/StudioActions/removeComponent.test.ts b/packages/studio/tests/store/StudioActions/removeComponent.test.ts
deleted file mode 100644
index 0967886bc..000000000
--- a/packages/studio/tests/store/StudioActions/removeComponent.test.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-import {
- ComponentState,
- ComponentStateKind,
- FileMetadataKind,
-} from "@yext/studio-plugin";
-import useStudioStore from "../../../src/store/useStudioStore";
-import { searchBarComponent } from "../../__fixtures__/componentStates";
-import mockStore from "../../__utils__/mockStore";
-
-const initialTree: ComponentState[] = [
- {
- kind: ComponentStateKind.Fragment,
- uuid: "mock-uuid-0",
- },
- {
- ...searchBarComponent,
- uuid: "mock-uuid-1",
- parentUUID: "mock-uuid-0",
- },
- {
- ...searchBarComponent,
- uuid: "mock-uuid-2",
- parentUUID: "mock-uuid-1",
- },
- {
- ...searchBarComponent,
- uuid: "mock-uuid-3",
- parentUUID: "mock-uuid-0",
- },
-];
-
-it("removes component and its children from ModuleMetadata when a module is being edited", () => {
- mockStore({
- fileMetadatas: {
- UUIDToFileMetadata: {
- StarModuleMetadataUUID: {
- kind: FileMetadataKind.Module,
- componentTree: initialTree,
- metadataUUID: "StarModuleMetadataUUID",
- filepath: "unused",
- },
- },
- },
- pages: {
- moduleUUIDBeingEdited: "ModuleState.uuid",
- activePageName: "pagename",
- pages: {
- pagename: {
- componentTree: [
- {
- kind: ComponentStateKind.Module,
- uuid: "ModuleState.uuid",
- metadataUUID: "StarModuleMetadataUUID",
- componentName: "StarModule",
- props: {},
- },
- ],
- cssImports: [],
- filepath: "unused",
- },
- },
- },
- });
- useStudioStore.getState().actions.removeComponent("mock-uuid-1");
- expect(
- useStudioStore.getState().fileMetadatas.UUIDToFileMetadata[
- "StarModuleMetadataUUID"
- ]
- ).toEqual(
- expect.objectContaining({
- componentTree: [initialTree[0], initialTree[3]],
- })
- );
-});
-
-it("removes component and its children from the active PageState when no module is being edited", () => {
- mockStore({
- pages: {
- activePageName: "pagename",
- pages: {
- pagename: {
- componentTree: initialTree,
- cssImports: [],
- filepath: "unused",
- },
- },
- },
- });
-
- useStudioStore.getState().actions.removeComponent("mock-uuid-1");
- const updatedTree =
- useStudioStore.getState().pages.pages["pagename"].componentTree;
- expect(updatedTree).toEqual([initialTree[0], initialTree[3]]);
-});
diff --git a/packages/studio/tests/store/createFileMetadataSlice/createFileMetadataSlice.test.tsx b/packages/studio/tests/store/createFileMetadataSlice/createFileMetadataSlice.test.tsx
deleted file mode 100644
index 72295a56f..000000000
--- a/packages/studio/tests/store/createFileMetadataSlice/createFileMetadataSlice.test.tsx
+++ /dev/null
@@ -1,108 +0,0 @@
-import {
- ComponentMetadata,
- FileMetadataKind,
- ModuleMetadata,
- PropValueType,
-} from "@yext/studio-plugin";
-import useStudioStore from "../../../src/store/useStudioStore";
-import { FileMetadataSliceStates } from "../../../src/store/models/slices/FileMetadataSlice";
-import mockStore from "../../__utils__/mockStore";
-
-const componentMetadata: ComponentMetadata = {
- kind: FileMetadataKind.Component,
- metadataUUID: "mock-metadata-uuid",
- filepath: "mock-filepath",
- propShape: {
- myText: {
- type: PropValueType.string,
- tooltip: "a random string",
- required: false,
- },
- },
-};
-const moduleMetadata: ModuleMetadata = {
- kind: FileMetadataKind.Module,
- metadataUUID: "mock-metadataUUID",
- filepath: "mock-filepath",
- componentTree: [],
-};
-
-it("updates UUIDToFileMetadata using setFileMetadata", () => {
- useStudioStore
- .getState()
- .fileMetadatas.setFileMetadata("some-uuid", componentMetadata);
- const UUIDToFileMetadata =
- useStudioStore.getState().fileMetadatas.UUIDToFileMetadata;
- expect(UUIDToFileMetadata).toEqual({
- "some-uuid": componentMetadata,
- });
-});
-
-it("returns a FileMetadata using getFileMetadata", () => {
- setInitialState({
- UUIDToFileMetadata: {
- "uuid-1": componentMetadata,
- },
- });
- const fileMetadata = useStudioStore
- .getState()
- .fileMetadatas.getFileMetadata("uuid-1");
- expect(fileMetadata).toEqual(componentMetadata);
-});
-
-it("errors when removeFileMetadata is called on a non-module", () => {
- const initialState = {
- UUIDToFileMetadata: {
- "uuid-1": componentMetadata,
- },
- };
- setInitialState(initialState);
- const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation();
- useStudioStore.getState().fileMetadatas.removeFileMetadata("uuid-1");
- expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
- expect(consoleErrorSpy).toHaveBeenCalledWith(
- "removeFileMetadata is only allowed for modules, not:",
- FileMetadataKind.Component
- );
-
- const UUIDToFileMetadata =
- useStudioStore.getState().fileMetadatas.UUIDToFileMetadata;
- expect(UUIDToFileMetadata).toEqual(initialState.UUIDToFileMetadata);
-});
-
-it("removeFileMetadata can remove a module metadata", () => {
- const initialState = {
- UUIDToFileMetadata: {
- "uuid-1": moduleMetadata,
- },
- };
- setInitialState(initialState);
- useStudioStore.getState().fileMetadatas.removeFileMetadata("uuid-1");
- const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation();
- expect(consoleErrorSpy).toHaveBeenCalledTimes(0);
-
- const UUIDToFileMetadata =
- useStudioStore.getState().fileMetadatas.UUIDToFileMetadata;
- expect(UUIDToFileMetadata).toEqual({});
-});
-
-it("updates UUIDToImportedComponent using setImportedComponent", () => {
- const importedComponent = () => hello world
;
- const newImportedComponents = {
- Banner: importedComponent,
- };
- useStudioStore
- .getState()
- .fileMetadatas.setImportedComponent("Banner", importedComponent);
- const UUIDToImportedComponent =
- useStudioStore.getState().fileMetadatas.UUIDToImportedComponent;
- expect(UUIDToImportedComponent).toEqual(newImportedComponents);
-});
-
-function setInitialState(initialState: Partial): void {
- const baseState: FileMetadataSliceStates = {
- UUIDToFileMetadata: {},
- UUIDToImportedComponent: {},
- };
- mockStore({ fileMetadatas: { ...baseState, ...initialState } });
-}
diff --git a/packages/studio/tests/store/createModule.test.ts b/packages/studio/tests/store/createModule.test.ts
deleted file mode 100644
index b5d7b759f..000000000
--- a/packages/studio/tests/store/createModule.test.ts
+++ /dev/null
@@ -1,180 +0,0 @@
-import {
- ComponentStateKind,
- FileMetadata,
- FileMetadataKind,
- PropValueType,
-} from "@yext/studio-plugin";
-import useStudioStore from "../../src/store/useStudioStore";
-import { searchBarComponent } from "../__fixtures__/componentStates";
-import mockStore from "../__utils__/mockStore";
-import path from "path-browserify";
-
-const UUIDToFileMetadata: Record = {
- [path.resolve(__dirname, "../__mocks__", "./test.tsx")]: {
- kind: FileMetadataKind.Module,
- componentTree: [],
- metadataUUID: "test",
- filepath: path.resolve(__dirname, "../__mocks__", "./test.tsx"),
- },
-};
-
-beforeEach(() => {
- mockStore({
- pages: {
- pages: {
- universal: {
- componentTree: [
- {
- kind: ComponentStateKind.Fragment,
- uuid: "mock-uuid-0",
- },
- {
- ...searchBarComponent,
- uuid: "mock-uuid-1",
- parentUUID: "mock-uuid-0",
- },
- {
- kind: ComponentStateKind.Module,
- uuid: "mock-uuid-2",
- parentUUID: "mock-uuid-1",
- componentName: "module",
- props: {},
- metadataUUID: "mock-metadata",
- },
- {
- ...searchBarComponent,
- uuid: "mock-uuid-3",
- parentUUID: "mock-uuid-0",
- },
- ],
- cssImports: [],
- filepath: "mock-filepath",
- },
- },
- activePageName: "universal",
- activeComponentUUID: "mock-uuid-1",
- },
- fileMetadatas: {
- UUIDToFileMetadata,
- },
- });
-});
-
-it("adds module metadata to UUIDToFileMetadata", () => {
- const moduleName = "Module";
- useStudioStore.getState().createModule(moduleName);
- const UUIDToFileMetadata =
- useStudioStore.getState().fileMetadatas.UUIDToFileMetadata;
- expect(Object.values(UUIDToFileMetadata).at(-1)).toEqual({
- kind: FileMetadataKind.Module,
- componentTree: [
- {
- ...searchBarComponent,
- uuid: "mock-uuid-1",
- },
- {
- kind: ComponentStateKind.Module,
- uuid: "mock-uuid-2",
- parentUUID: "mock-uuid-1",
- componentName: "module",
- props: {},
- metadataUUID: "mock-metadata",
- },
- ],
- filepath: expect.stringContaining(moduleName + ".tsx"),
- metadataUUID: expect.any(String),
- propShape: {},
- });
-});
-
-it("adds document to prop interface when isPagesJSRepo = true", () => {
- useStudioStore.setState((state) => {
- state.studioConfig.isPagesJSRepo = true;
- });
- const moduleName = "Module";
- useStudioStore.getState().createModule(moduleName);
- const UUIDToFileMetadata =
- useStudioStore.getState().fileMetadatas.UUIDToFileMetadata;
- expect(Object.values(UUIDToFileMetadata).at(-1)).toEqual(
- expect.objectContaining({
- propShape: {
- document: {
- type: PropValueType.Record,
- recordKey: "string",
- recordValue: "any",
- required: true,
- },
- },
- })
- );
-});
-
-it("updates active page state to include new module component", () => {
- useStudioStore.getState().createModule("Module");
- const activeComponentState = useStudioStore
- .getState()
- .pages.getActivePageState();
- expect(activeComponentState).toEqual({
- componentTree: [
- {
- kind: ComponentStateKind.Fragment,
- uuid: "mock-uuid-0",
- },
- {
- kind: ComponentStateKind.Module,
- uuid: expect.anything(),
- parentUUID: "mock-uuid-0",
- componentName: "Module",
- metadataUUID: expect.any(String),
- props: {},
- },
- {
- ...searchBarComponent,
- uuid: "mock-uuid-3",
- parentUUID: "mock-uuid-0",
- },
- ],
- cssImports: [],
- filepath: "mock-filepath",
- });
-});
-
-it("sets active component to the new module component", () => {
- useStudioStore.getState().createModule("Module");
- const activeComponentState = useStudioStore
- .getState()
- .actions.getActiveComponentState();
- expect(activeComponentState).toEqual({
- kind: ComponentStateKind.Module,
- uuid: expect.any(String),
- parentUUID: "mock-uuid-0",
- componentName: "Module",
- metadataUUID: expect.any(String),
- props: {},
- });
-});
-
-describe("errors", () => {
- it("throws an error when there is no active component state", () => {
- useStudioStore.getState().pages.setActiveComponentUUID(undefined);
- const action = () => useStudioStore.getState().createModule("Any");
- expect(action).toThrow("Tried to create module without active component.");
- });
-
- it("throws an error for an empty moduleName", () => {
- const action = () => useStudioStore.getState().createModule("");
- expect(action).toThrow("Error creating module: a modulePath is required.");
- });
-
- it("throws an error for a modulePath outside the allowed path for modules", () => {
- const action = () => useStudioStore.getState().createModule("../Module");
- expect(action).toThrow(
- `Error creating module: modulePath is invalid: "../Module.tsx".`
- );
- });
-
- it("throws an error when module name starts with a lowercase letter", () => {
- const action = () => useStudioStore.getState().createModule("bob/test");
- expect(action).toThrow("Module names must start with an uppercase letter.");
- });
-});
diff --git a/packages/studio/tests/store/createPageSlice/detachAllModuleInstances.test.ts b/packages/studio/tests/store/createPageSlice/detachAllModuleInstances.test.ts
deleted file mode 100644
index c5055576d..000000000
--- a/packages/studio/tests/store/createPageSlice/detachAllModuleInstances.test.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-import {
- ComponentStateKind,
- FileMetadataKind,
- PageState,
-} from "@yext/studio-plugin";
-import useStudioStore from "../../../src/store/useStudioStore";
-import mockStore from "../../__utils__/mockStore";
-
-it("can delete a module and detach all of its instances across multiple pages", () => {
- const basicPageState: PageState = {
- componentTree: [
- {
- kind: ComponentStateKind.Module,
- componentName: "Star",
- props: {},
- uuid: "first-comp-state",
- metadataUUID: "star-metadata-uuid",
- },
- ],
- cssImports: [],
- filepath: "unused",
- };
- mockStore({
- pages: {
- pages: {
- firstPage: basicPageState,
- secondPage: basicPageState,
- },
- },
- });
-
- useStudioStore.getState().pages.detachAllModuleInstances({
- kind: FileMetadataKind.Module,
- metadataUUID: "star-metadata-uuid",
- componentTree: [
- {
- kind: ComponentStateKind.Standard,
- componentName: "Banner",
- props: {},
- uuid: "will-be-replaced",
- metadataUUID: "banner-metadata-uuid",
- },
- ],
- filepath: "unused/Star.tsx",
- });
-
- const expectedUpdatedTree = [
- {
- kind: ComponentStateKind.Standard,
- componentName: "Banner",
- props: {},
- uuid: expect.any(String),
- metadataUUID: "banner-metadata-uuid",
- },
- ];
-
- expect(useStudioStore.getState().pages.pages).toEqual({
- firstPage: {
- ...basicPageState,
- componentTree: expectedUpdatedTree,
- },
- secondPage: {
- ...basicPageState,
- componentTree: expectedUpdatedTree,
- },
- });
-});
diff --git a/packages/studio/tsconfig.json b/packages/studio/tsconfig.json
index b43cff8f0..6635747b7 100644
--- a/packages/studio/tsconfig.json
+++ b/packages/studio/tsconfig.json
@@ -1,18 +1,6 @@
{
+ "extends": "../../tsconfig.json",
"compilerOptions": {
- "module": "ESNext",
- "strict": true,
- "esModuleInterop": true,
- "skipLibCheck": true,
- "noImplicitAny": false,
- "moduleResolution": "node",
- "resolveJsonModule": true,
- "forceConsistentCasingInFileNames": true,
- "declaration": true,
- "declarationMap": true,
- "sourceMap": true,
- "jsx": "react-jsx",
- "target": "ESNext",
"outDir": "lib",
"types": [
"vite/client",
@@ -20,5 +8,5 @@
"vite-plugin-svgr/client"
]
},
- "include": ["src", "types.ts"]
+ "include": ["src"]
}
diff --git a/tsconfig.json b/tsconfig.json
index 8f75020fc..1bc767abe 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -18,6 +18,7 @@
"**/*.ts",
"**/*.tsx",
"**/*.js",
+ "**/*.mjs",
"packages/studio/postcss.config.cjs"
]
}
diff --git a/turbo.json b/turbo.json
index 42e89e0e2..69a14bc3e 100644
--- a/turbo.json
+++ b/turbo.json
@@ -3,7 +3,7 @@
"pipeline": {
"build": {
"dependsOn": ["^build"],
- "outputs": ["dist/**", "lib/**"]
+ "outputs": ["lib/**"]
},
"dev": {
"cache": false
diff --git a/update-minor-versions.mjs b/update-minor-versions.mjs
new file mode 100644
index 000000000..4f838e1b8
--- /dev/null
+++ b/update-minor-versions.mjs
@@ -0,0 +1,49 @@
+import { execSync } from "child_process";
+import fs from "fs";
+
+function bumpStudioPlugin() {
+ console.log("... bumping studio-plugin");
+ execSync("npm version minor -w=packages/studio-plugin");
+ return readVersion("./packages/studio-plugin/package.json");
+}
+
+function bumpStudio(pluginVersion, uiVersion) {
+ console.log("... bumping studio");
+ const packageJsonPath = "./packages/studio/package.json";
+ const packageJson = readJson(packageJsonPath);
+ // `npm i @yext/studio-plugin@${newVersion} --save-exact` does not update the package json,
+ // likely because the new version does not exist yet.
+ packageJson.dependencies["@yext/studio-plugin"] = pluginVersion;
+ packageJson.dependencies["@yext/studio-ui"] = uiVersion;
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
+ execSync("npm version minor -w=packages/studio");
+}
+
+function bumpStudioUI() {
+ console.log("... bumping studio-ui");
+ execSync("npm version minor -w=packages/studio-ui");
+ return readVersion("./packages/studio-ui/package.json");
+}
+
+function readJson(filepath) {
+ return JSON.parse(fs.readFileSync(filepath, "utf-8"));
+}
+
+function readVersion(packageJsonPath) {
+ const packageJson = readJson(packageJsonPath);
+ const newVersion = packageJson?.version;
+ if (!newVersion) {
+ throw new Error(
+ `Could not parse version from package.json at ${packageJsonPath}`
+ );
+ }
+ return newVersion;
+}
+
+function main() {
+ const pluginVersion = bumpStudioPlugin();
+ const uiVersion = bumpStudioUI();
+ bumpStudio(pluginVersion, uiVersion);
+}
+
+main();