diff --git a/e2e-tests/__screenshots__/darwin/add-element.spec.ts/can-add-a-Footer-component-1.png b/e2e-tests/__screenshots__/darwin/add-element.spec.ts/can-add-a-Footer-component-1.png index 4e0f423ef..2c03cee67 100644 Binary files a/e2e-tests/__screenshots__/darwin/add-element.spec.ts/can-add-a-Footer-component-1.png and b/e2e-tests/__screenshots__/darwin/add-element.spec.ts/can-add-a-Footer-component-1.png differ diff --git a/e2e-tests/__screenshots__/darwin/add-element.spec.ts/can-add-a-Footer-component-2.png b/e2e-tests/__screenshots__/darwin/add-element.spec.ts/can-add-a-Footer-component-2.png index adcac9863..10c4d8640 100644 Binary files a/e2e-tests/__screenshots__/darwin/add-element.spec.ts/can-add-a-Footer-component-2.png and b/e2e-tests/__screenshots__/darwin/add-element.spec.ts/can-add-a-Footer-component-2.png differ diff --git a/e2e-tests/__screenshots__/darwin/add-element.spec.ts/can-add-a-Footer-component-3.png b/e2e-tests/__screenshots__/darwin/add-element.spec.ts/can-add-a-Footer-component-3.png index 7450669d4..b6b83a9fd 100644 Binary files a/e2e-tests/__screenshots__/darwin/add-element.spec.ts/can-add-a-Footer-component-3.png and b/e2e-tests/__screenshots__/darwin/add-element.spec.ts/can-add-a-Footer-component-3.png differ diff --git a/e2e-tests/__screenshots__/darwin/add-entity-page.spec.ts/can-add-an-entity-page-2.png b/e2e-tests/__screenshots__/darwin/add-entity-page.spec.ts/can-add-an-entity-page-2.png index 2ebf24643..a96ab7707 100644 Binary files a/e2e-tests/__screenshots__/darwin/add-entity-page.spec.ts/can-add-an-entity-page-2.png and b/e2e-tests/__screenshots__/darwin/add-entity-page.spec.ts/can-add-an-entity-page-2.png differ diff --git a/e2e-tests/__screenshots__/darwin/add-entity-page.spec.ts/can-add-an-entity-page-3.png b/e2e-tests/__screenshots__/darwin/add-entity-page.spec.ts/can-add-an-entity-page-3.png index 44521756c..a96ab7707 100644 Binary files a/e2e-tests/__screenshots__/darwin/add-entity-page.spec.ts/can-add-an-entity-page-3.png and b/e2e-tests/__screenshots__/darwin/add-entity-page.spec.ts/can-add-an-entity-page-3.png differ diff --git a/e2e-tests/__screenshots__/darwin/add-entity-page.spec.ts/can-add-an-entity-page-4.png b/e2e-tests/__screenshots__/darwin/add-entity-page.spec.ts/can-add-an-entity-page-4.png index 7354033a0..1886c01b1 100644 Binary files a/e2e-tests/__screenshots__/darwin/add-entity-page.spec.ts/can-add-an-entity-page-4.png and b/e2e-tests/__screenshots__/darwin/add-entity-page.spec.ts/can-add-an-entity-page-4.png differ diff --git a/e2e-tests/__screenshots__/darwin/add-entity-page.spec.ts/can-add-an-entity-page-5.png b/e2e-tests/__screenshots__/darwin/add-entity-page.spec.ts/can-add-an-entity-page-5.png index 4689de19b..ce111edb2 100644 Binary files a/e2e-tests/__screenshots__/darwin/add-entity-page.spec.ts/can-add-an-entity-page-5.png and b/e2e-tests/__screenshots__/darwin/add-entity-page.spec.ts/can-add-an-entity-page-5.png differ diff --git a/e2e-tests/__screenshots__/darwin/add-static-page.spec.ts/can-add-a-static-page-2.png b/e2e-tests/__screenshots__/darwin/add-static-page.spec.ts/can-add-a-static-page-2.png index 505ff8189..a71a1207c 100644 Binary files a/e2e-tests/__screenshots__/darwin/add-static-page.spec.ts/can-add-a-static-page-2.png and b/e2e-tests/__screenshots__/darwin/add-static-page.spec.ts/can-add-a-static-page-2.png differ diff --git a/e2e-tests/__screenshots__/darwin/add-static-page.spec.ts/can-add-a-static-page-3.png b/e2e-tests/__screenshots__/darwin/add-static-page.spec.ts/can-add-a-static-page-3.png index 4110d1060..05c050caf 100644 Binary files a/e2e-tests/__screenshots__/darwin/add-static-page.spec.ts/can-add-a-static-page-3.png and b/e2e-tests/__screenshots__/darwin/add-static-page.spec.ts/can-add-a-static-page-3.png differ diff --git a/e2e-tests/__screenshots__/darwin/custom-tailwind.spec.ts/TailwindClass-prop-editor-1.png b/e2e-tests/__screenshots__/darwin/custom-tailwind.spec.ts/TailwindClass-prop-editor-1.png index a78416765..a78bd6dca 100644 Binary files a/e2e-tests/__screenshots__/darwin/custom-tailwind.spec.ts/TailwindClass-prop-editor-1.png and b/e2e-tests/__screenshots__/darwin/custom-tailwind.spec.ts/TailwindClass-prop-editor-1.png differ diff --git a/e2e-tests/__screenshots__/darwin/custom-tailwind.spec.ts/TailwindClass-prop-editor-2.png b/e2e-tests/__screenshots__/darwin/custom-tailwind.spec.ts/TailwindClass-prop-editor-2.png index 675fe0995..ffefeb942 100644 Binary files a/e2e-tests/__screenshots__/darwin/custom-tailwind.spec.ts/TailwindClass-prop-editor-2.png and b/e2e-tests/__screenshots__/darwin/custom-tailwind.spec.ts/TailwindClass-prop-editor-2.png differ diff --git a/e2e-tests/__screenshots__/darwin/custom-tailwind.spec.ts/TailwindClass-prop-editor-3.png b/e2e-tests/__screenshots__/darwin/custom-tailwind.spec.ts/TailwindClass-prop-editor-3.png index f83821e8b..c70f3ad78 100644 Binary files a/e2e-tests/__screenshots__/darwin/custom-tailwind.spec.ts/TailwindClass-prop-editor-3.png and b/e2e-tests/__screenshots__/darwin/custom-tailwind.spec.ts/TailwindClass-prop-editor-3.png differ diff --git a/e2e-tests/__screenshots__/darwin/field-picker-dropdown.spec.ts/renders-field-picker-dropdown-2.png b/e2e-tests/__screenshots__/darwin/field-picker-dropdown.spec.ts/renders-field-picker-dropdown-2.png index e05c3f91f..f49361ec2 100644 Binary files a/e2e-tests/__screenshots__/darwin/field-picker-dropdown.spec.ts/renders-field-picker-dropdown-2.png and b/e2e-tests/__screenshots__/darwin/field-picker-dropdown.spec.ts/renders-field-picker-dropdown-2.png differ diff --git a/e2e-tests/__screenshots__/darwin/field-picker-dropdown.spec.ts/renders-field-picker-dropdown-3.png b/e2e-tests/__screenshots__/darwin/field-picker-dropdown.spec.ts/renders-field-picker-dropdown-3.png index febcd5363..ff3a0963f 100644 Binary files a/e2e-tests/__screenshots__/darwin/field-picker-dropdown.spec.ts/renders-field-picker-dropdown-3.png and b/e2e-tests/__screenshots__/darwin/field-picker-dropdown.spec.ts/renders-field-picker-dropdown-3.png differ diff --git a/e2e-tests/__screenshots__/darwin/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-3.png b/e2e-tests/__screenshots__/darwin/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-3.png index be1ed91e2..abc58ecd7 100644 Binary files a/e2e-tests/__screenshots__/darwin/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-3.png and b/e2e-tests/__screenshots__/darwin/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-3.png differ diff --git a/e2e-tests/__screenshots__/darwin/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-4.png b/e2e-tests/__screenshots__/darwin/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-4.png index 887e1d556..4af887918 100644 Binary files a/e2e-tests/__screenshots__/darwin/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-4.png and b/e2e-tests/__screenshots__/darwin/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-4.png differ diff --git a/e2e-tests/__screenshots__/darwin/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-5.png b/e2e-tests/__screenshots__/darwin/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-5.png index 0acdba16e..b876ce8eb 100644 Binary files a/e2e-tests/__screenshots__/darwin/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-5.png and b/e2e-tests/__screenshots__/darwin/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-5.png differ diff --git a/e2e-tests/__screenshots__/darwin/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-6.png b/e2e-tests/__screenshots__/darwin/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-6.png index cfe0edebc..a1b9e255a 100644 Binary files a/e2e-tests/__screenshots__/darwin/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-6.png and b/e2e-tests/__screenshots__/darwin/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-6.png differ diff --git a/e2e-tests/__screenshots__/darwin/nested-props.spec.ts/renders-nested-props-1.png b/e2e-tests/__screenshots__/darwin/nested-props.spec.ts/renders-nested-props-1.png index 71393d600..6b75b0168 100644 Binary files a/e2e-tests/__screenshots__/darwin/nested-props.spec.ts/renders-nested-props-1.png and b/e2e-tests/__screenshots__/darwin/nested-props.spec.ts/renders-nested-props-1.png differ diff --git a/e2e-tests/__screenshots__/darwin/rearrange-elements.spec.ts/can-rearrange-elements-in-tree-1.png b/e2e-tests/__screenshots__/darwin/rearrange-elements.spec.ts/can-rearrange-elements-in-tree-1.png index d880120f6..840b34c77 100644 Binary files a/e2e-tests/__screenshots__/darwin/rearrange-elements.spec.ts/can-rearrange-elements-in-tree-1.png and b/e2e-tests/__screenshots__/darwin/rearrange-elements.spec.ts/can-rearrange-elements-in-tree-1.png differ diff --git a/e2e-tests/__screenshots__/darwin/undefined-menu-button.spec.ts/renders-undefined-menu-button-1.png b/e2e-tests/__screenshots__/darwin/undefined-menu-button.spec.ts/renders-undefined-menu-button-1.png index 3a65d72c5..dcb9d9fd5 100644 Binary files a/e2e-tests/__screenshots__/darwin/undefined-menu-button.spec.ts/renders-undefined-menu-button-1.png and b/e2e-tests/__screenshots__/darwin/undefined-menu-button.spec.ts/renders-undefined-menu-button-1.png differ diff --git a/e2e-tests/__screenshots__/win32/add-element.spec.ts/can-add-a-Footer-component-1.png b/e2e-tests/__screenshots__/win32/add-element.spec.ts/can-add-a-Footer-component-1.png index ef5568ff6..7f4991c0b 100644 Binary files a/e2e-tests/__screenshots__/win32/add-element.spec.ts/can-add-a-Footer-component-1.png and b/e2e-tests/__screenshots__/win32/add-element.spec.ts/can-add-a-Footer-component-1.png differ diff --git a/e2e-tests/__screenshots__/win32/add-element.spec.ts/can-add-a-Footer-component-2.png b/e2e-tests/__screenshots__/win32/add-element.spec.ts/can-add-a-Footer-component-2.png index 899e540fd..31622d52d 100644 Binary files a/e2e-tests/__screenshots__/win32/add-element.spec.ts/can-add-a-Footer-component-2.png and b/e2e-tests/__screenshots__/win32/add-element.spec.ts/can-add-a-Footer-component-2.png differ diff --git a/e2e-tests/__screenshots__/win32/add-element.spec.ts/can-add-a-Footer-component-3.png b/e2e-tests/__screenshots__/win32/add-element.spec.ts/can-add-a-Footer-component-3.png index 0c374cbf8..67a0ee43b 100644 Binary files a/e2e-tests/__screenshots__/win32/add-element.spec.ts/can-add-a-Footer-component-3.png and b/e2e-tests/__screenshots__/win32/add-element.spec.ts/can-add-a-Footer-component-3.png differ diff --git a/e2e-tests/__screenshots__/win32/add-entity-page.spec.ts/can-add-an-entity-page-2.png b/e2e-tests/__screenshots__/win32/add-entity-page.spec.ts/can-add-an-entity-page-2.png index 482b56262..0e0106ca4 100644 Binary files a/e2e-tests/__screenshots__/win32/add-entity-page.spec.ts/can-add-an-entity-page-2.png and b/e2e-tests/__screenshots__/win32/add-entity-page.spec.ts/can-add-an-entity-page-2.png differ diff --git a/e2e-tests/__screenshots__/win32/add-entity-page.spec.ts/can-add-an-entity-page-3.png b/e2e-tests/__screenshots__/win32/add-entity-page.spec.ts/can-add-an-entity-page-3.png index 3779a475e..0e0106ca4 100644 Binary files a/e2e-tests/__screenshots__/win32/add-entity-page.spec.ts/can-add-an-entity-page-3.png and b/e2e-tests/__screenshots__/win32/add-entity-page.spec.ts/can-add-an-entity-page-3.png differ diff --git a/e2e-tests/__screenshots__/win32/add-entity-page.spec.ts/can-add-an-entity-page-4.png b/e2e-tests/__screenshots__/win32/add-entity-page.spec.ts/can-add-an-entity-page-4.png index 5d55f63d5..507af62ae 100644 Binary files a/e2e-tests/__screenshots__/win32/add-entity-page.spec.ts/can-add-an-entity-page-4.png and b/e2e-tests/__screenshots__/win32/add-entity-page.spec.ts/can-add-an-entity-page-4.png differ diff --git a/e2e-tests/__screenshots__/win32/add-entity-page.spec.ts/can-add-an-entity-page-5.png b/e2e-tests/__screenshots__/win32/add-entity-page.spec.ts/can-add-an-entity-page-5.png index 409e37498..7eaf6a78d 100644 Binary files a/e2e-tests/__screenshots__/win32/add-entity-page.spec.ts/can-add-an-entity-page-5.png and b/e2e-tests/__screenshots__/win32/add-entity-page.spec.ts/can-add-an-entity-page-5.png differ diff --git a/e2e-tests/__screenshots__/win32/add-static-page.spec.ts/can-add-a-static-page-2.png b/e2e-tests/__screenshots__/win32/add-static-page.spec.ts/can-add-a-static-page-2.png index b052c54ff..a625ba1da 100644 Binary files a/e2e-tests/__screenshots__/win32/add-static-page.spec.ts/can-add-a-static-page-2.png and b/e2e-tests/__screenshots__/win32/add-static-page.spec.ts/can-add-a-static-page-2.png differ diff --git a/e2e-tests/__screenshots__/win32/add-static-page.spec.ts/can-add-a-static-page-3.png b/e2e-tests/__screenshots__/win32/add-static-page.spec.ts/can-add-a-static-page-3.png index 0ecf97fa3..06569a36c 100644 Binary files a/e2e-tests/__screenshots__/win32/add-static-page.spec.ts/can-add-a-static-page-3.png and b/e2e-tests/__screenshots__/win32/add-static-page.spec.ts/can-add-a-static-page-3.png differ diff --git a/e2e-tests/__screenshots__/win32/custom-tailwind.spec.ts/TailwindClass-prop-editor-1.png b/e2e-tests/__screenshots__/win32/custom-tailwind.spec.ts/TailwindClass-prop-editor-1.png index 61fba9033..026f06dfd 100644 Binary files a/e2e-tests/__screenshots__/win32/custom-tailwind.spec.ts/TailwindClass-prop-editor-1.png and b/e2e-tests/__screenshots__/win32/custom-tailwind.spec.ts/TailwindClass-prop-editor-1.png differ diff --git a/e2e-tests/__screenshots__/win32/custom-tailwind.spec.ts/TailwindClass-prop-editor-2.png b/e2e-tests/__screenshots__/win32/custom-tailwind.spec.ts/TailwindClass-prop-editor-2.png index 4bfea426f..ec14a93e5 100644 Binary files a/e2e-tests/__screenshots__/win32/custom-tailwind.spec.ts/TailwindClass-prop-editor-2.png and b/e2e-tests/__screenshots__/win32/custom-tailwind.spec.ts/TailwindClass-prop-editor-2.png differ diff --git a/e2e-tests/__screenshots__/win32/custom-tailwind.spec.ts/TailwindClass-prop-editor-3.png b/e2e-tests/__screenshots__/win32/custom-tailwind.spec.ts/TailwindClass-prop-editor-3.png index 509b42009..718722766 100644 Binary files a/e2e-tests/__screenshots__/win32/custom-tailwind.spec.ts/TailwindClass-prop-editor-3.png and b/e2e-tests/__screenshots__/win32/custom-tailwind.spec.ts/TailwindClass-prop-editor-3.png differ diff --git a/e2e-tests/__screenshots__/win32/field-picker-dropdown.spec.ts/renders-field-picker-dropdown-2.png b/e2e-tests/__screenshots__/win32/field-picker-dropdown.spec.ts/renders-field-picker-dropdown-2.png index 73c36f63e..cc10bc50e 100644 Binary files a/e2e-tests/__screenshots__/win32/field-picker-dropdown.spec.ts/renders-field-picker-dropdown-2.png and b/e2e-tests/__screenshots__/win32/field-picker-dropdown.spec.ts/renders-field-picker-dropdown-2.png differ diff --git a/e2e-tests/__screenshots__/win32/field-picker-dropdown.spec.ts/renders-field-picker-dropdown-3.png b/e2e-tests/__screenshots__/win32/field-picker-dropdown.spec.ts/renders-field-picker-dropdown-3.png index 007555fb3..971190100 100644 Binary files a/e2e-tests/__screenshots__/win32/field-picker-dropdown.spec.ts/renders-field-picker-dropdown-3.png and b/e2e-tests/__screenshots__/win32/field-picker-dropdown.spec.ts/renders-field-picker-dropdown-3.png differ diff --git a/e2e-tests/__screenshots__/win32/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-3.png b/e2e-tests/__screenshots__/win32/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-3.png index 3ce7df8c0..25ad1bf47 100644 Binary files a/e2e-tests/__screenshots__/win32/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-3.png and b/e2e-tests/__screenshots__/win32/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-3.png differ diff --git a/e2e-tests/__screenshots__/win32/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-4.png b/e2e-tests/__screenshots__/win32/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-4.png index d6b1128d2..6a99b8296 100644 Binary files a/e2e-tests/__screenshots__/win32/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-4.png and b/e2e-tests/__screenshots__/win32/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-4.png differ diff --git a/e2e-tests/__screenshots__/win32/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-5.png b/e2e-tests/__screenshots__/win32/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-5.png index 4b5d63e4c..2dca89b4a 100644 Binary files a/e2e-tests/__screenshots__/win32/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-5.png and b/e2e-tests/__screenshots__/win32/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-5.png differ diff --git a/e2e-tests/__screenshots__/win32/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-6.png b/e2e-tests/__screenshots__/win32/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-6.png index 3f3f21326..f18971e13 100644 Binary files a/e2e-tests/__screenshots__/win32/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-6.png and b/e2e-tests/__screenshots__/win32/hmr/update-component-initial-props.spec.ts/can-update-initial-props-of-a-component-and-see-UI-is-updated-via-HMR-6.png differ diff --git a/e2e-tests/__screenshots__/win32/nested-props.spec.ts/renders-nested-props-1.png b/e2e-tests/__screenshots__/win32/nested-props.spec.ts/renders-nested-props-1.png index 3baa83d9d..fff3bdcec 100644 Binary files a/e2e-tests/__screenshots__/win32/nested-props.spec.ts/renders-nested-props-1.png and b/e2e-tests/__screenshots__/win32/nested-props.spec.ts/renders-nested-props-1.png differ diff --git a/e2e-tests/__screenshots__/win32/rearrange-elements.spec.ts/can-rearrange-elements-in-tree-1.png b/e2e-tests/__screenshots__/win32/rearrange-elements.spec.ts/can-rearrange-elements-in-tree-1.png index 2362d37c1..b42c0e6b4 100644 Binary files a/e2e-tests/__screenshots__/win32/rearrange-elements.spec.ts/can-rearrange-elements-in-tree-1.png and b/e2e-tests/__screenshots__/win32/rearrange-elements.spec.ts/can-rearrange-elements-in-tree-1.png differ diff --git a/e2e-tests/__screenshots__/win32/undefined-menu-button.spec.ts/renders-undefined-menu-button-1.png b/e2e-tests/__screenshots__/win32/undefined-menu-button.spec.ts/renders-undefined-menu-button-1.png index 0fb1e225d..b3326f784 100644 Binary files a/e2e-tests/__screenshots__/win32/undefined-menu-button.spec.ts/renders-undefined-menu-button-1.png and b/e2e-tests/__screenshots__/win32/undefined-menu-button.spec.ts/renders-undefined-menu-button-1.png differ diff --git a/e2e-tests/src/components/CustomTailwindButton.tsx b/e2e-tests/src/components/CustomTailwindButton.tsx index 6559a3b58..17e35c971 100644 --- a/e2e-tests/src/components/CustomTailwindButton.tsx +++ b/e2e-tests/src/components/CustomTailwindButton.tsx @@ -4,6 +4,8 @@ interface Props { className?: TailwindClass; } +export const initialProps: Props = { className: "" }; + export default function CustomTailwindButton(props: Props) { return ; } diff --git a/e2e-tests/tests/__fixtures__/add-entity-page-expected-page.tsx b/e2e-tests/tests/__fixtures__/add-entity-page-expected-page.tsx index 7a14f0281..ed251bcd9 100644 --- a/e2e-tests/tests/__fixtures__/add-entity-page-expected-page.tsx +++ b/e2e-tests/tests/__fixtures__/add-entity-page-expected-page.tsx @@ -4,10 +4,7 @@ export const config: TemplateConfig = { stream: { $id: "studio-stream-id-EntityPage", localization: { locales: ["en"] }, - filter: { - entityTypes: ["entity1"], - savedFilterIds: ["entity2", "entity3"], - }, + filter: {}, fields: [], }, }; diff --git a/e2e-tests/tests/add-entity-page.spec.ts b/e2e-tests/tests/add-entity-page.spec.ts index 34a2d8ac1..09043e9a0 100644 --- a/e2e-tests/tests/add-entity-page.spec.ts +++ b/e2e-tests/tests/add-entity-page.spec.ts @@ -1,6 +1,5 @@ import { expect } from "@playwright/test"; import { studioTest } from "./infra/studioTest.js"; -import { StreamScopeForm } from "./infra/StudioPlaywrightPage.js"; import fs from "fs"; const expectedPage = fs.readFileSync( @@ -12,11 +11,9 @@ studioTest("can add an entity page", async ({ page, studioPage }) => { const pageInTree = page.getByText("EntityPage"); await expect(pageInTree).toHaveCount(0); - const streamScopeForm: StreamScopeForm = { - entityTypes: "entity1", - savedFilterIds: "entity2,entity3", - }; - await studioPage.addEntityPage("EntityPage", streamScopeForm, "entity-page"); + // TODO: Specify a stream scope once we can supply an API key for populating + // the store with account content + await studioPage.addEntityPage("EntityPage", {}, "entity-page"); await expect(pageInTree).toHaveCount(1); await studioPage.takePageScreenshotAfterImgRender(); await studioPage.saveButton.click(); diff --git a/e2e-tests/tests/custom-tailwind.spec.ts b/e2e-tests/tests/custom-tailwind.spec.ts index f42602e38..7256dfac6 100644 --- a/e2e-tests/tests/custom-tailwind.spec.ts +++ b/e2e-tests/tests/custom-tailwind.spec.ts @@ -33,7 +33,7 @@ studioTest("TailwindClass prop editor", async ({ studioPage, page }) => { const editorSidebar = page.getByTestId("EditorSidebar"); await expect(editorSidebar).toHaveScreenshot(); const classPicker = editorSidebar.getByRole("button", { - name: "Toggle tailwind class picker", + name: "Toggle pill picker", }); await classPicker.click(); await expect(editorSidebar).toHaveScreenshot(); diff --git a/package-lock.json b/package-lock.json index e2cdf4ad4..a9bd9ac27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3052,6 +3052,59 @@ "zustand": "^4.0.0" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/babel-plugin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache/node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, "node_modules/@emotion/is-prop-valid": { "version": "0.8.8", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", @@ -3067,6 +3120,74 @@ "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", "optional": true }, + "node_modules/@emotion/react": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz", + "integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.2", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", + "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", + "dependencies": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/serialize/node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + }, "node_modules/@esbuild/android-arm": { "version": "0.15.18", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", @@ -5958,6 +6079,14 @@ "@types/react": "*" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", + "integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -11963,6 +12092,11 @@ "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -12668,7 +12802,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "peer": true, "dependencies": { "react-is": "^16.7.0" } @@ -15644,6 +15777,11 @@ "node": ">= 4.0.0" } }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -20068,6 +20206,26 @@ "node": ">=10" } }, + "node_modules/react-select": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.7.4.tgz", + "integrity": "sha512-NhuE56X+p9QDFh4BgeygHFIvJJszO1i1KSkg/JPcIJrbovyRtI+GuOEa4XzFCEpZRAEoEI8u/cAHK+jG/PgUzQ==", + "dependencies": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.1.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-toastify": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.1.tgz", @@ -21532,6 +21690,11 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, "node_modules/sucrase": { "version": "3.34.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", @@ -26512,6 +26675,7 @@ "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", diff --git a/packages/studio-plugin/src/createStudioPlugin.ts b/packages/studio-plugin/src/createStudioPlugin.ts index ed7bd1416..8d35632ab 100644 --- a/packages/studio-plugin/src/createStudioPlugin.ts +++ b/packages/studio-plugin/src/createStudioPlugin.ts @@ -16,6 +16,7 @@ import { STUDIO_PROCESS_ARGS_OBJ } from "./constants"; import LocalDataMappingManager from "./LocalDataMappingManager"; import getStudioViteOptions from "./viteconfig/getStudioViteOptions"; import { createDevServer } from "@yext/pages"; +import ManagementApiService from "./http/ManagementApiService"; /** * Handles server-client communication. @@ -65,6 +66,12 @@ export default async function createStudioPlugin( ); await pagesDevPortPromise; + + const managementAPIService = + studioConfig.isPagesJSRepo && process.env.YEXT_STUDIO_API_KEY + ? new ManagementApiService(process.env.YEXT_STUDIO_API_KEY) + : undefined; + return { name: "yext-studio-vite-plugin", config(config) { @@ -94,7 +101,8 @@ export default async function createStudioPlugin( orchestrator, localDataMappingManager, pathToUserProjectRoot, - studioConfig.paths + studioConfig.paths, + managementAPIService ), }; } diff --git a/packages/studio-plugin/src/http/types/index.ts b/packages/studio-plugin/src/http/types/index.ts new file mode 100644 index 000000000..093725b64 --- /dev/null +++ b/packages/studio-plugin/src/http/types/index.ts @@ -0,0 +1,2 @@ +export { EntitiesResponse } from "./Entities"; +export { SavedFilterData } from "./SavedFilters"; diff --git a/packages/studio-plugin/src/index.ts b/packages/studio-plugin/src/index.ts index b13d3af8d..c669a0175 100644 --- a/packages/studio-plugin/src/index.ts +++ b/packages/studio-plugin/src/index.ts @@ -5,3 +5,4 @@ export { STREAM_LOCALIZATION, STUDIO_PROCESS_ARGS_OBJ, } from "./constants"; +export * from "./http/types"; diff --git a/packages/studio-ui/.size-limit.cjs b/packages/studio-ui/.size-limit.cjs index 2e0d4b7ba..329cf0193 100644 --- a/packages/studio-ui/.size-limit.cjs +++ b/packages/studio-ui/.size-limit.cjs @@ -1,7 +1,7 @@ module.exports = [ { path: "lib/src/index.js", - limit: "700 kB", + limit: "850 kB", gzip: false, }, ]; diff --git a/packages/studio-ui/package.json b/packages/studio-ui/package.json index 4a3943a08..6bbc8e6f4 100644 --- a/packages/studio-ui/package.json +++ b/packages/studio-ui/package.json @@ -38,6 +38,7 @@ "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", diff --git a/packages/studio-ui/src/components/ActivePagePanel.tsx b/packages/studio-ui/src/components/ActivePagePanel.tsx index 865988bc8..1898e68ac 100644 --- a/packages/studio-ui/src/components/ActivePagePanel.tsx +++ b/packages/studio-ui/src/components/ActivePagePanel.tsx @@ -5,6 +5,7 @@ import { PropsWithChildren, useMemo } from "react"; import RemovePageButton from "./RemovePageButton"; import { Tooltip } from "react-tooltip"; import PageSettingsButton from "./PageSettingsButton/PageSettingsButton"; +import EditStreamScopeButton from "./EditStreamScopeButton"; /** * ActivePagePanel displays the available pages and allows the user to switch @@ -55,6 +56,10 @@ function PageItem({ pageName }: { pageName: string }) { void updateActivePage(pageName); } + const isEntityPage = useStudioStore( + (store) => !!store.pages.pages[pageName].pagesJS?.streamScope + ); + return (
@@ -68,6 +73,7 @@ function PageItem({ pageName }: { pageName: string }) {
+ {isEntityPage && } {isPagesJSRepo && }
diff --git a/packages/studio-ui/src/components/AddPageButton/AddPageContext.ts b/packages/studio-ui/src/components/AddPageButton/AddPageContext.ts index 7f6995a63..307248097 100644 --- a/packages/studio-ui/src/components/AddPageButton/AddPageContext.ts +++ b/packages/studio-ui/src/components/AddPageButton/AddPageContext.ts @@ -1,5 +1,5 @@ import { StreamScope } from "@yext/studio-plugin"; -import { createContext } from "react"; +import { createContext, useContext } from "react"; export interface AddPageData { isStatic: boolean; @@ -17,6 +17,13 @@ export interface AddPageContextValue { actions: AddPageActions; } +export function useStreamScope() { + const { state, actions } = useContext(AddPageContext); + const { streamScope } = state; + const { setStreamScope } = actions; + return [streamScope, setStreamScope] as const; +} + const AddPageContext = createContext( {} as AddPageContextValue ); diff --git a/packages/studio-ui/src/components/AddPageButton/EntityIdField.tsx b/packages/studio-ui/src/components/AddPageButton/EntityIdField.tsx new file mode 100644 index 000000000..cc3aa982c --- /dev/null +++ b/packages/studio-ui/src/components/AddPageButton/EntityIdField.tsx @@ -0,0 +1,140 @@ +import Select, { MultiValue, StylesConfig } from "react-select"; +import useStudioStore from "../../store/useStudioStore"; +import StreamScopeFieldLabel from "../common/StreamScopeFieldLabel"; +import { ChangeEvent, useCallback, useMemo, useState } from "react"; +import { pillContainerClass } from "../PillPicker/PillPickerInput"; +import classNames from "classnames"; +import { updateScopeField } from "../StreamScopePicker"; + +interface Props { + disabled: boolean; + updateSelection: updateScopeField; + selectedIds?: string[]; +} + +export default function EntityIdField({ + disabled, + updateSelection, + selectedIds, +}: Props) { + const [entitiesRecord, fetchEntities] = useStudioStore((store) => [ + store.accountContent.entitiesRecord, + store.accountContent.fetchEntities, + ]); + const availableEntityTypes = Object.keys(entitiesRecord); + const [isLoading, setIsLoading] = useState(false); + const [entityType, setEntityType] = useState( + availableEntityTypes[0] + ); + const entityOptions = useEntityOptions(entityType); + const selectedOptions = useMemo( + () => selectedIds?.map((id) => ({ value: id, label: id })), + [selectedIds] + ); + + const onEntityTypeChange = useCallback( + (e: ChangeEvent) => { + setEntityType(e.target.value); + }, + [] + ); + + const onEntityIdChange = useCallback( + (selectedItems: MultiValue<{ value: string }>) => { + updateSelection(selectedItems.map((item) => item.value)); + }, + [updateSelection] + ); + + const onMenuScrollToBottom = useCallback(async () => { + if (!entityType) { + return; + } + const currentNumEntities = entitiesRecord[entityType].entities.length; + if (currentNumEntities >= entitiesRecord[entityType].totalCount) { + return; + } + setIsLoading(true); + await fetchEntities(entityType, Math.floor(currentNumEntities / 50)); + setIsLoading(false); + }, [entitiesRecord, entityType, fetchEntities]); + + const containerClass = "flex flex-col mb-4"; + + if (availableEntityTypes.length === 0) { + return ( +
+
+ +
+
+ No entities found in the account. +
+
+ ); + } + + return ( +
+
+ +
+
+ + diff --git a/packages/studio-ui/src/components/FieldPicker/FieldPickerInput.tsx b/packages/studio-ui/src/components/FieldPicker/FieldPickerInput.tsx index 830bd6a7b..252cee632 100644 --- a/packages/studio-ui/src/components/FieldPicker/FieldPickerInput.tsx +++ b/packages/studio-ui/src/components/FieldPicker/FieldPickerInput.tsx @@ -31,7 +31,7 @@ export default function FieldPickerInput({ const filteredData = filterEntityData(fieldFilter, entityData); const hasFilteredData = Object.keys(filteredData).length > 0; const inputBoxCssClasses = classNames( - "border border-gray-300 focus:border-indigo-500 rounded-lg py-2 pl-2 w-full", + "border border-gray-400 focus:border-indigo-500 rounded-lg py-2 pl-2 w-full", hasFilteredData ? "pr-8" : "pr-2" ); diff --git a/packages/studio-ui/src/components/PageSettingsButton/EntityPageModal.tsx b/packages/studio-ui/src/components/PageSettingsButton/EntityPageModal.tsx deleted file mode 100644 index 4b143df31..000000000 --- a/packages/studio-ui/src/components/PageSettingsButton/EntityPageModal.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import useStudioStore from "../../store/useStudioStore"; -import { useCallback, useMemo, useState } from "react"; -import FormModal, { FormData } from "../common/FormModal"; -import { GetPathVal, PropValueKind, ResponseType } from "@yext/studio-plugin"; -import TemplateExpressionFormatter from "../../utils/TemplateExpressionFormatter"; -import PropValueHelpers from "../../utils/PropValueHelpers"; -import StreamScopeParser, { - StreamScopeForm, -} from "../../utils/StreamScopeParser"; -import { PageSettingsModalProps } from "./PageSettingsButton"; -import { StaticPageSettings } from "./StaticPageModal"; -import { streamScopeFormData } from "../AddPageButton/StreamScopeCollector"; -import PageDataValidator from "../../utils/PageDataValidator"; -import { toast } from "react-toastify"; -import isEqual from "lodash/isEqual"; - -type EntityPageSettings = StaticPageSettings & StreamScopeForm; - -/** - * EntityPageModal is a form modal that displays the page settings - * for an entity page and allows editing of these settings - * (URL slug and stream scope). - */ -export default function EntityPageModal({ - pageName, - isOpen, - handleClose, -}: PageSettingsModalProps): JSX.Element { - const [ - currGetPathValue, - updateGetPathValue, - streamScope, - updateStreamScope, - generateTestData, - ] = useStudioStore((store) => [ - store.pages.pages[pageName].pagesJS?.getPathValue, - store.pages.updateGetPathValue, - store.pages.pages[pageName].pagesJS?.streamScope, - store.pages.updateStreamScope, - store.actions.generateTestData, - ]); - const [errorMessage, setErrorMessage] = useState(""); - const pageDataValidator = useMemo(() => new PageDataValidator(true), []); - const isURLEditable = useMemo( - () => pageDataValidator.checkIsURLEditable(currGetPathValue?.value), - [currGetPathValue?.value, pageDataValidator] - ); - - const initialFormValue: EntityPageSettings = useMemo( - () => ({ - url: isURLEditable ? getUrlDisplayValue(currGetPathValue) : "", - ...StreamScopeParser.convertStreamScopeToForm(streamScope), - }), - [currGetPathValue, streamScope, isURLEditable] - ); - - const entityFormData: FormData = useMemo( - () => ({ - url: { - description: "URL Slug", - optional: !isURLEditable, - placeholder: isURLEditable - ? "" - : "", - disabled: !isURLEditable, - }, - ...streamScopeFormData, - }), - [isURLEditable] - ); - - const handleModalSave = useCallback( - async (form: EntityPageSettings) => { - const getPathValue: GetPathVal = { - kind: PropValueKind.Expression, - value: TemplateExpressionFormatter.getRawValue(form.url), - }; - const validationResult = pageDataValidator.validate({ - url: getPathValue.value, - }); - if (!validationResult.valid) { - setErrorMessage(validationResult.errorMessages.join("\r\n")); - return false; - } - if (form.url || currGetPathValue) { - updateGetPathValue(pageName, getPathValue); - } - const parsedForm = StreamScopeParser.parseStreamScope(form); - updateStreamScope(pageName, parsedForm); - const regenerateTestData = async () => { - const response = await generateTestData(); - if (response.type === ResponseType.Error) { - toast.error( - "Error generating test data, but entity page settings were still updated." - ); - } - }; - if (!isEqual(parsedForm, streamScope)) { - await regenerateTestData(); - } - return true; - }, - [ - updateGetPathValue, - updateStreamScope, - currGetPathValue, - pageName, - pageDataValidator, - generateTestData, - streamScope, - ] - ); - - return ( - - ); -} - -function getUrlDisplayValue(getPathValue: GetPathVal | undefined): string { - const getPathExpression = PropValueHelpers.getTemplateExpression( - getPathValue ?? { kind: PropValueKind.Literal, value: "" } - ); - return TemplateExpressionFormatter.getTemplateStringDisplayValue( - getPathExpression - ); -} diff --git a/packages/studio-ui/src/components/PageSettingsButton/PageSettingsButton.tsx b/packages/studio-ui/src/components/PageSettingsButton/PageSettingsButton.tsx index 77dda0f03..b2e492688 100644 --- a/packages/studio-ui/src/components/PageSettingsButton/PageSettingsButton.tsx +++ b/packages/studio-ui/src/components/PageSettingsButton/PageSettingsButton.tsx @@ -1,11 +1,9 @@ -import useStudioStore from "../../store/useStudioStore"; import { ReactComponent as Gear } from "../../icons/gear.svg"; import { useCallback } from "react"; import ButtonWithModal, { renderModalFunction, } from "../common/ButtonWithModal"; -import StaticPageModal from "./StaticPageModal"; -import EntityPageModal from "./EntityPageModal"; +import PageSettingsModal from "./PageSettingsModal"; export interface PageSettingsModalProps { pageName: string; @@ -25,15 +23,8 @@ interface PageSettingsButtonProps { export default function PageSettingsButton({ pageName, }: PageSettingsButtonProps): JSX.Element { - const isEntityPage = useStudioStore( - (store) => !!store.pages.pages[pageName].pagesJS?.streamScope - ); - const renderModal: renderModalFunction = useCallback( (isOpen, handleClose) => { - const PageSettingsModal = isEntityPage - ? EntityPageModal - : StaticPageModal; return ( ); }, - [isEntityPage, pageName] + [pageName] ); return ( diff --git a/packages/studio-ui/src/components/PageSettingsButton/PageSettingsModal.tsx b/packages/studio-ui/src/components/PageSettingsButton/PageSettingsModal.tsx new file mode 100644 index 000000000..f079995ac --- /dev/null +++ b/packages/studio-ui/src/components/PageSettingsButton/PageSettingsModal.tsx @@ -0,0 +1,119 @@ +import useStudioStore from "../../store/useStudioStore"; +import { useCallback, useMemo, useState } from "react"; +import FormModal, { FormData } from "../common/FormModal"; +import { GetPathVal, PropValueKind } from "@yext/studio-plugin"; +import { PageSettingsModalProps } from "./PageSettingsButton"; +import PageDataValidator from "../../utils/PageDataValidator"; +import PropValueHelpers from "../../utils/PropValueHelpers"; +import TemplateExpressionFormatter from "../../utils/TemplateExpressionFormatter"; + +export type PageSettings = { + url: string; +}; + +/** + * PageSettingsModal is a form modal that displays the page settings + * for a page and allows editing of these settings (URL slug). + */ +export default function PageSettingsModal({ + pageName, + isOpen, + handleClose, +}: PageSettingsModalProps): JSX.Element { + const isEntityPage = useStudioStore( + (store) => !!store.pages.pages[pageName].pagesJS?.streamScope + ); + const [currGetPathValue, updateGetPathValue] = useStudioStore((store) => [ + store.pages.pages[pageName].pagesJS?.getPathValue, + store.pages.updateGetPathValue, + ]); + const [errorMessage, setErrorMessage] = useState(""); + const pageDataValidator = useMemo( + () => new PageDataValidator(isEntityPage), + [isEntityPage] + ); + const isURLEditable = useMemo( + () => pageDataValidator.checkIsURLEditable(currGetPathValue?.value), + [currGetPathValue?.value, pageDataValidator] + ); + + const initialFormValue: PageSettings = useMemo(() => { + if (isEntityPage) { + return { + url: isURLEditable ? getUrlDisplayValue(currGetPathValue) : "", + }; + } + return { url: currGetPathValue?.value ?? "" }; + }, [currGetPathValue, isEntityPage, isURLEditable]); + + const formData: FormData = useMemo( + () => ({ + url: { + description: "URL Slug", + optional: !isURLEditable, + placeholder: isURLEditable + ? "" + : "", + disabled: !isURLEditable, + }, + }), + [isURLEditable] + ); + + const handleModalSave = useCallback( + (form: PageSettings) => { + const getPathValue: GetPathVal = isEntityPage + ? { + kind: PropValueKind.Expression, + value: TemplateExpressionFormatter.getRawValue(form.url), + } + : { + kind: PropValueKind.Literal, + value: form.url, + }; + const validationResult = pageDataValidator.validate({ + url: getPathValue.value, + }); + if (!validationResult.valid) { + setErrorMessage(validationResult.errorMessages.join("\r\n")); + return false; + } + if (!isEntityPage || form.url || currGetPathValue) { + updateGetPathValue(pageName, getPathValue); + } + return true; + }, + [ + isEntityPage, + pageDataValidator, + currGetPathValue, + updateGetPathValue, + pageName, + ] + ); + + return ( + + ); +} + +function getUrlDisplayValue(getPathValue: GetPathVal | undefined): string { + const getPathExpression = PropValueHelpers.getTemplateExpression( + getPathValue ?? { kind: PropValueKind.Literal, value: "" } + ); + return TemplateExpressionFormatter.getTemplateStringDisplayValue( + getPathExpression + ); +} diff --git a/packages/studio-ui/src/components/PageSettingsButton/StaticPageModal.tsx b/packages/studio-ui/src/components/PageSettingsButton/StaticPageModal.tsx deleted file mode 100644 index 71078d6cd..000000000 --- a/packages/studio-ui/src/components/PageSettingsButton/StaticPageModal.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import useStudioStore from "../../store/useStudioStore"; -import { useCallback, useMemo, useState } from "react"; -import FormModal, { FormData } from "../common/FormModal"; -import { GetPathVal, PropValueKind } from "@yext/studio-plugin"; -import { PageSettingsModalProps } from "./PageSettingsButton"; -import PageDataValidator from "../../utils/PageDataValidator"; - -export type StaticPageSettings = { - url: string; -}; - -/** - * StaticPageModal is a form modal that displays the page settings - * for a static page and allows editing of these settings (URL slug). - */ -export default function StaticPageModal({ - pageName, - isOpen, - handleClose, -}: PageSettingsModalProps): JSX.Element { - const [currGetPathValue, updateGetPathValue] = useStudioStore((store) => [ - store.pages.pages[pageName].pagesJS?.getPathValue, - store.pages.updateGetPathValue, - ]); - const [errorMessage, setErrorMessage] = useState(""); - const pageDataValidator = useMemo(() => new PageDataValidator(), []); - const isURLEditable = useMemo( - () => pageDataValidator.checkIsURLEditable(currGetPathValue?.value), - [currGetPathValue?.value, pageDataValidator] - ); - - const initialFormValue: StaticPageSettings = useMemo( - () => ({ url: currGetPathValue?.value ?? "" }), - [currGetPathValue] - ); - - const staticFormData: FormData = useMemo( - () => ({ - url: { - description: "URL Slug", - optional: !isURLEditable, - placeholder: isURLEditable - ? "" - : "", - disabled: !isURLEditable, - }, - }), - [isURLEditable] - ); - - const handleModalSave = useCallback( - (form: StaticPageSettings) => { - const getPathValue: GetPathVal = { - kind: PropValueKind.Literal, - value: form.url, - }; - const validationResult = pageDataValidator.validate({ - url: getPathValue.value, - }); - if (!validationResult.valid) { - setErrorMessage(validationResult.errorMessages.join("\r\n")); - return false; - } - updateGetPathValue(pageName, getPathValue); - return true; - }, - [updateGetPathValue, pageName, pageDataValidator] - ); - - return ( - - ); -} diff --git a/packages/studio-ui/src/components/PillPicker/PillPicker.tsx b/packages/studio-ui/src/components/PillPicker/PillPicker.tsx new file mode 100644 index 000000000..4af1da992 --- /dev/null +++ b/packages/studio-ui/src/components/PillPicker/PillPicker.tsx @@ -0,0 +1,96 @@ +import { useCallback, useRef, useState } from "react"; +import { ReactComponent as EmbedIcon } from "../../icons/embed.svg"; +import useRootClose from "@restart/ui/useRootClose"; +import classNames from "classnames"; + +interface PillPickerProps { + availableItems: string[]; + selectItem: (item: string) => void; + disabled?: boolean; + getDisplayName?: (item: string) => string; +} + +/** + * An icon that displays a dropdown of available items when clicked. + */ +export default function PillPicker({ + availableItems, + selectItem, + disabled, + getDisplayName, +}: PillPickerProps) { + const containerRef = useRef(null); + const [isOpen, setIsOpen] = useState(false); + + useRootClose(containerRef, () => { + setIsOpen(false); + }); + + const toggleOpen = useCallback(() => { + if (disabled) { + return; + } + setIsOpen((isOpen) => !isOpen); + }, [disabled]); + + const embedIconClasses = classNames("opacity-50", { + "cursor-pointer hover:opacity-100": !disabled, + "cursor-default": disabled, + }); + + return ( +
+ + {isOpen && renderDropdown(availableItems, selectItem, getDisplayName)} +
+ ); +} + +function renderDropdown( + items: string[], + selectItem: (val: string) => void, + getDisplayName?: (item: string) => string +) { + return ( +
+
    + {items.map((item) => { + return ( + + ); + })} +
+
+ ); +} + +function DropdownItem(props: { + item: string; + selectItem: (val: string) => void; + displayName?: string; +}) { + const { item, selectItem, displayName } = props; + + const handleClick = useCallback(() => { + selectItem(item); + }, [item, selectItem]); + + return ( +
  • + {displayName ?? item} +
  • + ); +} diff --git a/packages/studio-ui/src/components/PillPicker/PillPickerInput.tsx b/packages/studio-ui/src/components/PillPicker/PillPickerInput.tsx new file mode 100644 index 000000000..4c0dde3c4 --- /dev/null +++ b/packages/studio-ui/src/components/PillPicker/PillPickerInput.tsx @@ -0,0 +1,107 @@ +import { useCallback } from "react"; +import { ReactComponent as X } from "../../icons/x.svg"; +import classNames from "classnames"; +import PillPicker from "./PillPicker"; + +export const pillContainerClass = + "flex flex-wrap min-h-[38px] items-center border border-gray-400 rounded-lg pt-2 pb-1 px-2 w-full text-sm"; + +interface PillPickerInputProps { + selectedItems: string[] | undefined; + availableItems: string[] | undefined; + updateSelectedItems: (selectedItems: string[]) => void; + emptyText: string; + getDisplayName?: (item: string) => string; + disabled?: boolean; +} + +/** + * An input displaying pills for each selected item and a PillPicker icon. + */ +export default function PillPickerInput({ + selectedItems, + availableItems, + updateSelectedItems, + emptyText, + getDisplayName, + disabled, +}: PillPickerInputProps) { + const hasAvailableItems = !!availableItems?.length; + const isEmptyAndNoAvailbleItems = + !hasAvailableItems && !selectedItems?.length; + + const addItem = useCallback( + (item: string) => { + const updatedSelectedItems = new Set(selectedItems); + updatedSelectedItems.add(item); + updateSelectedItems([...updatedSelectedItems]); + }, + [updateSelectedItems, selectedItems] + ); + + const removeItem = useCallback( + (item: string) => { + const updatedSelectedItems = new Set(selectedItems); + updatedSelectedItems.delete(item); + updateSelectedItems([...updatedSelectedItems]); + }, + [updateSelectedItems, selectedItems] + ); + + const containerClasses = classNames(pillContainerClass, { + "bg-gray-50": disabled || isEmptyAndNoAvailbleItems, + "pb-2 text-gray-500": isEmptyAndNoAvailbleItems, + }); + + if (isEmptyAndNoAvailbleItems) { + return
    {emptyText}
    ; + } + + return ( +
    + {selectedItems?.map((item) => { + return ( + + ); + })} + {hasAvailableItems && ( + + )} +
    + ); +} + +function Pill(props: { + item: string; + removeItem: (val: string) => void; + displayName?: string; + disabled?: boolean; +}) { + const { item, removeItem, displayName, disabled } = props; + + const handleClick = useCallback(() => { + removeItem(item); + }, [item, removeItem]); + + return ( + + ); +} diff --git a/packages/studio-ui/src/components/SaveButton.tsx b/packages/studio-ui/src/components/SaveButton.tsx index d2ac1514f..4c0e7341f 100644 --- a/packages/studio-ui/src/components/SaveButton.tsx +++ b/packages/studio-ui/src/components/SaveButton.tsx @@ -19,7 +19,7 @@ export default function SaveButton() { return (