slice-machine-ui 1.23.2-beta.3 → 1.23.2-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/components/Forms/CreateCustomTypeModal/CreateCustomTypeModal.tsx +61 -38
  2. package/components/Forms/RenameCustomTypeModal/RenameCustomTypeModal.tsx +2 -2
  3. package/components/Navigation/ChangesListItem.tsx +14 -32
  4. package/out/404.html +1 -1
  5. package/out/_next/static/{RRBm5bm2F__Ws0vsvXhN6 → GqPZYfMQrQUPwvgI_rvdq}/_buildManifest.js +1 -1
  6. package/out/_next/static/chunks/{183-aab7066c73ebde44.js → 183-4feb6bb3eddbc768.js} +1 -1
  7. package/out/_next/static/chunks/{318-2112803ca34b2f49.js → 318-34a2dea7cfd4661d.js} +1 -1
  8. package/out/_next/static/chunks/727-c5028806448c4387.js +1 -0
  9. package/out/_next/static/chunks/731-341869976a48a09a.js +1 -0
  10. package/out/_next/static/chunks/{904-9a4c1264727ae89a.js → 904-833eed675f19ab60.js} +1 -1
  11. package/out/_next/static/chunks/pages/{_app-9f3513533bb2dfb8.js → _app-4a63c0e108026c4d.js} +2 -2
  12. package/out/_next/static/chunks/pages/{changes-2a976f9efeb719aa.js → changes-92c84f6cac0285fd.js} +1 -1
  13. package/out/_next/static/chunks/pages/index-ca194b18d0e3930d.js +1 -0
  14. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-2d3d84de925b289e.js +1 -0
  15. package/out/changelog.html +1 -1
  16. package/out/changes.html +1 -1
  17. package/out/custom-types/[customTypeId].html +1 -1
  18. package/out/custom-types.html +1 -1
  19. package/out/index.html +1 -1
  20. package/out/labs.html +1 -1
  21. package/out/page-types/[pageTypeId].html +1 -1
  22. package/out/slices/[lib]/[sliceName]/[variation]/simulator.html +1 -1
  23. package/out/slices/[lib]/[sliceName]/[variation].html +1 -1
  24. package/out/slices.html +1 -1
  25. package/package.json +3 -3
  26. package/src/domain/__tests__/customType.test.ts +179 -0
  27. package/src/domain/customType.ts +110 -2
  28. package/src/features/customTypes/PageTypeOnboarding.tsx +21 -26
  29. package/src/features/customTypes/actions/convertCustomToPageType.ts +14 -10
  30. package/src/features/customTypes/actions/createCustomType.ts +69 -0
  31. package/src/features/customTypes/actions/deleteCustomType.ts +13 -3
  32. package/src/features/customTypes/actions/renameCustomType.ts +10 -4
  33. package/src/features/customTypes/customTypesBuilder/CustomTypesBuilderPage.tsx +3 -1
  34. package/src/features/customTypes/customTypesTable/CustomTypesTable.tsx +8 -8
  35. package/src/features/customTypes/customTypesTable/CustomTypesTablePage.tsx +18 -17
  36. package/src/modules/availableCustomTypes/index.ts +26 -132
  37. package/src/modules/loading/types.ts +0 -3
  38. package/src/modules/modal/types.ts +0 -1
  39. package/src/modules/useSliceMachineActions.ts +72 -78
  40. package/src/modules/userContext/index.ts +3 -3
  41. package/src/redux/saga.ts +0 -2
  42. package/test/src/modules/availableCustomTypes/index.test.ts +4 -156
  43. package/out/_next/static/chunks/727-3a3bc32518778317.js +0 -1
  44. package/out/_next/static/chunks/731-501990ebe6a71b32.js +0 -1
  45. package/out/_next/static/chunks/pages/index-64cfb8b0948ba8ef.js +0 -1
  46. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-b2a9d773ce5d7739.js +0 -1
  47. package/src/features/customTypes/__tests__/createCustomType.test.ts +0 -159
  48. package/src/features/customTypes/customTypesTable/createCustomType.ts +0 -107
  49. package/test/components/CreateCustomTypeModal.test.tsx +0 -73
  50. /package/out/_next/static/{RRBm5bm2F__Ws0vsvXhN6 → GqPZYfMQrQUPwvgI_rvdq}/_ssgManifest.js +0 -0
@@ -104,8 +104,15 @@ type GetGroupFieldArgs = {
104
104
  groupFieldId: string;
105
105
  };
106
106
 
107
- export function getFormat(custom: CustomType): CustomTypeFormat {
108
- return custom.format ?? "custom";
107
+ type CreateArgs = {
108
+ format: CustomTypeFormat;
109
+ id: string;
110
+ label: string;
111
+ repeatable: boolean;
112
+ };
113
+
114
+ export function getFormat(customType: CustomType): CustomTypeFormat {
115
+ return customType.format ?? "custom";
109
116
  }
110
117
 
111
118
  export function getSectionEntries(
@@ -633,3 +640,104 @@ export function updateGroupFields(
633
640
  },
634
641
  };
635
642
  }
643
+
644
+ export function create(args: CreateArgs) {
645
+ const { id, label, repeatable, format } = args;
646
+ const mainTab = makeMainTab(repeatable, format);
647
+
648
+ return {
649
+ format,
650
+ id,
651
+ json: mainTab,
652
+ label,
653
+ repeatable,
654
+ status: true,
655
+ };
656
+ }
657
+
658
+ function makeMainTab(
659
+ repeatable: boolean,
660
+ format: CustomTypeFormat,
661
+ ): CustomType["json"] {
662
+ if (repeatable === false && format === "page") {
663
+ return { ...DEFAULT_MAIN_WITH_SLICE_ZONE, ...DEFAULT_SEO_TAB };
664
+ }
665
+
666
+ if (repeatable === false) {
667
+ return DEFAULT_MAIN;
668
+ }
669
+
670
+ if (format === "page") {
671
+ return {
672
+ ...DEFAULT_MAIN_WITH_UID_AND_SLICE_ZONE,
673
+ ...DEFAULT_SEO_TAB,
674
+ };
675
+ }
676
+
677
+ return DEFAULT_MAIN_WITH_UID;
678
+ }
679
+
680
+ const DEFAULT_MAIN: CustomType["json"] = {
681
+ Main: {},
682
+ };
683
+
684
+ const DEFAULT_MAIN_WITH_SLICE_ZONE: CustomType["json"] = {
685
+ Main: {
686
+ slices: {
687
+ config: {
688
+ choices: {},
689
+ },
690
+ fieldset: "Slice Zone",
691
+ type: "Slices",
692
+ },
693
+ },
694
+ };
695
+
696
+ const DEFAULT_MAIN_WITH_UID: CustomType["json"] = {
697
+ Main: {
698
+ uid: {
699
+ config: {
700
+ label: "UID",
701
+ },
702
+ type: "UID",
703
+ },
704
+ },
705
+ };
706
+
707
+ const DEFAULT_MAIN_WITH_UID_AND_SLICE_ZONE: CustomType["json"] = {
708
+ Main: {
709
+ ...DEFAULT_MAIN_WITH_UID.Main,
710
+ ...DEFAULT_MAIN_WITH_SLICE_ZONE.Main,
711
+ },
712
+ };
713
+
714
+ const DEFAULT_SEO_TAB: CustomType["json"] = {
715
+ "SEO & Metadata": {
716
+ meta_description: {
717
+ config: {
718
+ label: "Meta Description",
719
+ placeholder: "A brief summary of the page",
720
+ },
721
+ type: "Text",
722
+ },
723
+ meta_image: {
724
+ config: {
725
+ constraint: {
726
+ height: 1260,
727
+ width: 2400,
728
+ },
729
+ label: "Meta Image",
730
+ thumbnails: [],
731
+ },
732
+ type: "Image",
733
+ },
734
+ meta_title: {
735
+ config: {
736
+ label: "Meta Title",
737
+ placeholder:
738
+ "A title of the page used for social media and search engines",
739
+ },
740
+ type: "Text",
741
+ },
742
+ },
743
+ };
@@ -1,35 +1,15 @@
1
- import { useEffect, useState } from "react";
2
- import { useSelector } from "react-redux";
1
+ import { useState } from "react";
3
2
  import { Box, Button, Text } from "@prismicio/editor-ui";
4
3
  import { cache } from "@prismicio/editor-support/Suspense";
5
4
 
6
5
  import { CreateCustomTypeModal } from "@components/Forms/CreateCustomTypeModal";
7
- import { SliceMachineStoreType } from "@src/redux/type";
8
- import useSliceMachineActions from "@src/modules/useSliceMachineActions";
9
- import { LoadingKeysEnum } from "@src/modules/loading/types";
10
- import { isLoading } from "@src/modules/loading";
11
6
  import { BlankPageIcon } from "@src/icons/BlankPageIcon";
12
7
  import { getIsEmptyProject } from "@src/hooks/useIsEmptyProject";
13
8
 
14
9
  export function PageTypeOnboarding() {
15
- const { openCreateCustomTypeModal } = useSliceMachineActions();
16
- const { isCreatingCustomType } = useSelector(
17
- (store: SliceMachineStoreType) => ({
18
- isCreatingCustomType: isLoading(
19
- store,
20
- LoadingKeysEnum.CREATE_CUSTOM_TYPE,
21
- ),
22
- }),
23
- );
24
- // State to ensure button loader is still visible while redirecting
25
- const [isButtonLoading, setIsButtonLoading] = useState(false);
26
-
27
- useEffect(() => {
28
- if (isCreatingCustomType) {
29
- setIsButtonLoading(true);
30
- cache.clear(getIsEmptyProject, []);
31
- }
32
- }, [isCreatingCustomType]);
10
+ const [isCreatingCustomType, setIsCreatingCustomType] = useState(false);
11
+ const [isCreateCustomTypeModalOpen, setIsCreateCustomTypeModalOpen] =
12
+ useState(false);
33
13
 
34
14
  return (
35
15
  <Box
@@ -53,10 +33,25 @@ export function PageTypeOnboarding() {
53
33
  Welcome to Slice Machine. Your journey starts with your first page
54
34
  type — the model that you will use to create pages for your website.
55
35
  </Text>
56
- <Button onClick={openCreateCustomTypeModal} loading={isButtonLoading}>
36
+ <Button
37
+ loading={isCreatingCustomType}
38
+ onClick={() => {
39
+ setIsCreateCustomTypeModalOpen(true);
40
+ }}
41
+ >
57
42
  Create a page type
58
43
  </Button>
59
- <CreateCustomTypeModal format="page" origin="onboarding" />
44
+ <CreateCustomTypeModal
45
+ format="page"
46
+ isCreating={isCreatingCustomType}
47
+ isOpen={isCreateCustomTypeModalOpen}
48
+ origin="onboarding"
49
+ onCreateChange={(isCreating) => {
50
+ setIsCreatingCustomType(isCreating);
51
+ cache.clear(getIsEmptyProject, []);
52
+ }}
53
+ onOpenChange={setIsCreateCustomTypeModalOpen}
54
+ />
60
55
  </Box>
61
56
  </Box>
62
57
  );
@@ -9,17 +9,20 @@ import { CUSTOM_TYPES_MESSAGES } from "../customTypesMessages";
9
9
 
10
10
  export async function convertCustomToPageType(
11
11
  customType: CustomType,
12
- saveCustomType: (customType: CustomType) => void,
12
+ onSuccess: (newCustomType: CustomType) => void,
13
13
  ) {
14
14
  const customTypesMessages =
15
15
  CUSTOM_TYPES_MESSAGES[customType.format as CustomTypeFormat];
16
16
 
17
17
  try {
18
18
  const newCustomType = convertToPageType(customType);
19
- await updateCustomType(newCustomType);
19
+ const { errors } = await updateCustomType(newCustomType);
20
20
 
21
- // Update the custom type in the redux store
22
- saveCustomType(newCustomType);
21
+ if (errors.length > 0) {
22
+ throw errors;
23
+ }
24
+
25
+ onSuccess(newCustomType);
23
26
 
24
27
  toast.success(
25
28
  `${customTypesMessages.name({
@@ -28,11 +31,12 @@ export async function convertCustomToPageType(
28
31
  })} converted to page type`,
29
32
  );
30
33
  } catch (e) {
31
- toast.error(
32
- `Internal Error: ${customTypesMessages.name({
33
- start: true,
34
- plural: false,
35
- })} not converted to page type`,
36
- );
34
+ const errorMessage = `Internal Error: ${customTypesMessages.name({
35
+ start: true,
36
+ plural: false,
37
+ })} not converted to page type`;
38
+
39
+ console.error(errorMessage, e);
40
+ toast.error(errorMessage);
37
41
  }
38
42
  }
@@ -0,0 +1,69 @@
1
+ import { toast } from "react-toastify";
2
+ import { CustomType } from "@prismicio/types-internal/lib/customtypes";
3
+
4
+ import { ToastMessageWithPath } from "@components/ToasterContainer";
5
+ import { CustomTypeFormat } from "@slicemachine/manager";
6
+ import { telemetry, updateCustomType } from "@src/apiClient";
7
+ import { create } from "@src/domain/customType";
8
+
9
+ import { CUSTOM_TYPES_MESSAGES } from "../customTypesMessages";
10
+
11
+ type DeleteCustomTypeArgs = {
12
+ format: CustomTypeFormat;
13
+ id: string;
14
+ label: string;
15
+ origin: CustomTypeOrigin;
16
+ repeatable: boolean;
17
+ onSuccess: (newCustomType: CustomType) => Promise<void>;
18
+ };
19
+
20
+ export type CustomTypeOrigin = "onboarding" | "table";
21
+
22
+ export async function createCustomType(args: DeleteCustomTypeArgs) {
23
+ const { id, label, repeatable, format, onSuccess } = args;
24
+ const customTypesMessages = CUSTOM_TYPES_MESSAGES[format];
25
+
26
+ const newCustomType: CustomType = create({
27
+ id,
28
+ label,
29
+ repeatable,
30
+ format,
31
+ });
32
+
33
+ try {
34
+ const { errors } = await updateCustomType(newCustomType);
35
+
36
+ if (errors.length > 0) {
37
+ throw errors;
38
+ }
39
+
40
+ void telemetry.track({
41
+ event: "custom-type:created",
42
+ id: newCustomType.id,
43
+ name: label,
44
+ format: format,
45
+ type: repeatable ? "repeatable" : "single",
46
+ origin: "table",
47
+ });
48
+
49
+ await onSuccess(newCustomType);
50
+
51
+ toast.success(
52
+ ToastMessageWithPath({
53
+ message: `${customTypesMessages.name({
54
+ start: true,
55
+ plural: false,
56
+ })} saved successfully at `,
57
+ path: `./customtypes/${newCustomType.id}/index.json`,
58
+ }),
59
+ );
60
+ } catch (e) {
61
+ const errorMessage = `Internal Error: ${customTypesMessages.name({
62
+ start: true,
63
+ plural: false,
64
+ })} not saved`;
65
+
66
+ console.error(errorMessage, e);
67
+ toast.error(errorMessage);
68
+ }
69
+ }
@@ -1,8 +1,10 @@
1
+ import { toast } from "react-toastify";
1
2
  import { CustomType } from "@prismicio/types-internal/lib/customtypes";
3
+
2
4
  import { managerClient } from "@src/managerClient";
5
+ import { CustomTypeFormat } from "@slicemachine/manager";
6
+
3
7
  import { CUSTOM_TYPES_MESSAGES } from "../customTypesMessages";
4
- import { CustomTypeFormat } from "@slicemachine/manager/*";
5
- import { toast } from "react-toastify";
6
8
 
7
9
  type DeleteCustomTypeArgs = {
8
10
  customType: CustomType;
@@ -17,7 +19,13 @@ export async function deleteCustomType({
17
19
  CUSTOM_TYPES_MESSAGES[customType.format as CustomTypeFormat];
18
20
 
19
21
  try {
20
- await managerClient.customTypes.deleteCustomType({ id: customType.id });
22
+ const { errors } = await managerClient.customTypes.deleteCustomType({
23
+ id: customType.id,
24
+ });
25
+
26
+ if (errors.length > 0) {
27
+ throw errors;
28
+ }
21
29
 
22
30
  onSuccess();
23
31
 
@@ -32,6 +40,8 @@ export async function deleteCustomType({
32
40
  start: true,
33
41
  plural: false,
34
42
  })} could not be deleted`;
43
+
35
44
  console.error(errorMessage, e);
45
+ toast.error(errorMessage);
36
46
  }
37
47
  }
@@ -1,8 +1,10 @@
1
+ import { toast } from "react-toastify";
1
2
  import { CustomType } from "@prismicio/types-internal/lib/customtypes";
3
+
2
4
  import { managerClient } from "@src/managerClient";
3
- import { CUSTOM_TYPES_MESSAGES } from "../customTypesMessages";
4
5
  import { CustomTypeFormat } from "@slicemachine/manager";
5
- import { toast } from "react-toastify";
6
+
7
+ import { CUSTOM_TYPES_MESSAGES } from "../customTypesMessages";
6
8
 
7
9
  type RenameCustomTypeArgs = {
8
10
  model: CustomType;
@@ -23,10 +25,14 @@ export async function renameCustomType({
23
25
  label: newLabel,
24
26
  };
25
27
 
26
- await managerClient.customTypes.renameCustomType({
28
+ const { errors } = await managerClient.customTypes.renameCustomType({
27
29
  model: renamedCustomType,
28
30
  });
29
31
 
32
+ if (errors.length > 0) {
33
+ throw errors;
34
+ }
35
+
30
36
  onSuccess(renamedCustomType);
31
37
 
32
38
  toast.success(
@@ -40,8 +46,8 @@ export async function renameCustomType({
40
46
  start: true,
41
47
  plural: false,
42
48
  })} could not be renamed`;
43
- console.error(errorMessage, e);
44
49
 
50
+ console.error(errorMessage, e);
45
51
  toast.error(errorMessage);
46
52
  }
47
53
  }
@@ -39,7 +39,9 @@ export const CustomTypesBuilderPage: FC = () => {
39
39
  );
40
40
 
41
41
  useEffect(() => {
42
- // TODO(DT-1801): When creating a custom type, don't redirect to the builder page until the custom type is created
42
+ // This is a fallback in case the user try to directly access a custom type
43
+ // that don't exist
44
+ // TODO(DT-1801): Implement a 404 page
43
45
  if (!selectedCustomType || !hasLocal(selectedCustomType)) {
44
46
  void router.replace("/");
45
47
  }
@@ -11,16 +11,9 @@ import {
11
11
  } from "@src/components/Table";
12
12
  import { ReusableIcon } from "@src/icons/ReusableIcon";
13
13
  import { UniqueIcon } from "@src/icons/UniqueIcon";
14
- import useSliceMachineActions from "@src/modules/useSliceMachineActions";
15
14
  import { type CustomType } from "@prismicio/types-internal/lib/customtypes";
16
15
  import { type CustomTypeFormat } from "@slicemachine/manager";
17
16
  import { CUSTOM_TYPES_MESSAGES } from "@src/features/customTypes/customTypesMessages";
18
- import { CUSTOM_TYPES_CONFIG } from "../customTypesConfig";
19
- import {
20
- useCustomTypes,
21
- useCustomTypesAutoRevalidation,
22
- } from "./useCustomTypes";
23
-
24
17
  import {
25
18
  BlankSlate,
26
19
  BlankSlateImage,
@@ -29,18 +22,25 @@ import {
29
22
  BlankSlateActions,
30
23
  BlankSlateContent,
31
24
  } from "@src/components/BlankSlate";
25
+
26
+ import { CUSTOM_TYPES_CONFIG } from "../customTypesConfig";
32
27
  import { EditDropdown } from "../EditDropdown";
28
+ import {
29
+ useCustomTypes,
30
+ useCustomTypesAutoRevalidation,
31
+ } from "./useCustomTypes";
33
32
 
34
33
  type CustomTypesTableProps = {
35
34
  format: CustomTypeFormat;
36
35
  isCreatingCustomType: boolean;
36
+ openCreateCustomTypeModal: () => void;
37
37
  };
38
38
 
39
39
  export const CustomTypesTable: FC<CustomTypesTableProps> = ({
40
40
  format,
41
41
  isCreatingCustomType,
42
+ openCreateCustomTypeModal,
42
43
  }) => {
43
- const { openCreateCustomTypeModal } = useSliceMachineActions();
44
44
  const router = useRouter();
45
45
  const { customTypes, updateCustomTypes } = useCustomTypes(format);
46
46
  const sortedCustomTypes = customTypes.sort(
@@ -1,4 +1,4 @@
1
- import { type FC, Suspense } from "react";
1
+ import { type FC, Suspense, useState } from "react";
2
2
  import {
3
3
  Button,
4
4
  ErrorBoundary,
@@ -7,7 +7,6 @@ import {
7
7
  DefaultErrorMessage,
8
8
  } from "@prismicio/editor-ui";
9
9
  import Head from "next/head";
10
- import { useSelector } from "react-redux";
11
10
 
12
11
  import {
13
12
  AppLayout,
@@ -16,13 +15,10 @@ import {
16
15
  AppLayoutContent,
17
16
  AppLayoutHeader,
18
17
  } from "@components/AppLayout";
19
- import useSliceMachineActions from "@src/modules/useSliceMachineActions";
20
- import { isLoading } from "@src/modules/loading";
21
- import { type SliceMachineStoreType } from "@src/redux/type";
22
- import { LoadingKeysEnum } from "@src/modules/loading/types";
23
18
  import { CreateCustomTypeModal } from "@components/Forms/CreateCustomTypeModal";
24
19
  import { CUSTOM_TYPES_MESSAGES } from "@src/features/customTypes/customTypesMessages";
25
20
  import type { CustomTypeFormat } from "@slicemachine/manager";
21
+
26
22
  import { CustomTypesTable } from "./CustomTypesTable";
27
23
 
28
24
  type CustomTypesTablePageProps = {
@@ -33,15 +29,9 @@ export const CustomTypesTablePage: FC<CustomTypesTablePageProps> = ({
33
29
  format,
34
30
  }) => {
35
31
  const customTypesMessages = CUSTOM_TYPES_MESSAGES[format];
36
- const { openCreateCustomTypeModal } = useSliceMachineActions();
37
- const { isCreatingCustomType } = useSelector(
38
- (store: SliceMachineStoreType) => ({
39
- isCreatingCustomType: isLoading(
40
- store,
41
- LoadingKeysEnum.CREATE_CUSTOM_TYPE,
42
- ),
43
- }),
44
- );
32
+ const [isCreatingCustomType, setIsCreatingCustomType] = useState(false);
33
+ const [isCreateCustomTypeModalOpen, setIsCreateCustomTypeModalOpen] =
34
+ useState(false);
45
35
 
46
36
  return (
47
37
  <>
@@ -101,7 +91,9 @@ export const CustomTypesTablePage: FC<CustomTypesTablePageProps> = ({
101
91
  <Button
102
92
  data-cy="create-ct"
103
93
  loading={isCreatingCustomType}
104
- onClick={openCreateCustomTypeModal}
94
+ onClick={() => {
95
+ setIsCreateCustomTypeModalOpen(true);
96
+ }}
105
97
  startIcon="add"
106
98
  >
107
99
  Create
@@ -113,8 +105,17 @@ export const CustomTypesTablePage: FC<CustomTypesTablePageProps> = ({
113
105
  <CustomTypesTable
114
106
  format={format}
115
107
  isCreatingCustomType={isCreatingCustomType}
108
+ openCreateCustomTypeModal={() => {
109
+ setIsCreateCustomTypeModalOpen(true);
110
+ }}
111
+ />
112
+ <CreateCustomTypeModal
113
+ format={format}
114
+ isCreating={isCreatingCustomType}
115
+ isOpen={isCreateCustomTypeModalOpen}
116
+ onCreateChange={setIsCreatingCustomType}
117
+ onOpenChange={setIsCreateCustomTypeModalOpen}
116
118
  />
117
- <CreateCustomTypeModal format={format} />
118
119
  </Box>
119
120
  </AppLayoutContent>
120
121
  </AppLayout>