slice-machine-ui 1.22.3-beta.1 → 1.22.3-beta.3

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 (154) hide show
  1. package/components/DeleteVariationModal/index.tsx +12 -11
  2. package/components/Forms/CreateSliceModal/CreateSliceModal.tsx +2 -5
  3. package/components/Forms/RenameCustomTypeModal/RenameCustomTypeModal.tsx +9 -5
  4. package/components/Forms/RenameVariationModal/RenameVariationModal.tsx +12 -11
  5. package/components/ItemHeader/index.tsx +1 -0
  6. package/components/ListItem/index.tsx +5 -2
  7. package/components/ScreenshotChangesModal/VariationDropZone.tsx +31 -9
  8. package/components/ScreenshotChangesModal/index.tsx +3 -0
  9. package/components/ScreenshotChangesModal/useCustomScreenshot.tsx +5 -5
  10. package/components/Simulator/index.tsx +9 -3
  11. package/lib/builders/CustomTypeBuilder/SliceZone/SlicesTemplatesModal.tsx +2 -2
  12. package/lib/builders/CustomTypeBuilder/SliceZone/UpdateSliceZoneModal.tsx +2 -2
  13. package/lib/builders/CustomTypeBuilder/SliceZone/index.tsx +12 -20
  14. package/lib/builders/CustomTypeBuilder/TabModal/update.tsx +3 -1
  15. package/lib/builders/CustomTypeBuilder/TabZone/index.tsx +119 -52
  16. package/lib/builders/CustomTypeBuilder/index.tsx +39 -42
  17. package/lib/builders/SliceBuilder/FieldZones/index.tsx +81 -56
  18. package/lib/builders/SliceBuilder/Sidebar/AddVariationModal.tsx +24 -16
  19. package/lib/builders/SliceBuilder/Sidebar/index.tsx +44 -46
  20. package/lib/builders/SliceBuilder/SimulatorButton/index.tsx +4 -28
  21. package/lib/builders/SliceBuilder/index.tsx +10 -96
  22. package/lib/builders/common/Zone/Card/components/Hints/CodeBlock.tsx +5 -1
  23. package/lib/builders/common/Zone/Card/components/NewField.tsx +16 -15
  24. package/lib/builders/common/Zone/Card/index.jsx +2 -0
  25. package/lib/builders/common/Zone/index.jsx +3 -1
  26. package/lib/models/common/ComponentUI.ts +0 -14
  27. package/lib/models/common/CustomType/sliceZone.ts +0 -29
  28. package/lib/models/common/CustomType/tab.ts +10 -142
  29. package/lib/models/common/widgets/Group/ListItem/index.jsx +61 -23
  30. package/out/404.html +1 -1
  31. package/out/_next/static/chunks/065a3ddb-9a38ca0d60f0bf2f.js +1 -0
  32. package/out/_next/static/chunks/183-4feb6bb3eddbc768.js +1 -0
  33. package/out/_next/static/chunks/18539003-7b2711e6c51d6fac.js +97 -0
  34. package/out/_next/static/chunks/1cc2734a-2c5b6c0367fcde86.js +1 -0
  35. package/out/_next/static/chunks/25-8fedd33ad9b6de5b.js +1 -0
  36. package/out/_next/static/chunks/318-541db69860d31138.js +1 -0
  37. package/out/_next/static/chunks/{505-3f1cf09a086dcba8.js → 376-0ed5822f8afcaa7c.js} +3 -3
  38. package/out/_next/static/chunks/4c744e84-e3161d754618373f.js +1 -0
  39. package/out/_next/static/chunks/50-cfc2514205b86001.js +1 -0
  40. package/out/_next/static/chunks/543-f8766685f7f451e2.js +1 -0
  41. package/out/_next/static/chunks/59b4e022-7a3b5554b806349f.js +1 -0
  42. package/out/_next/static/chunks/696-b12edad30a3617e5.js +31 -0
  43. package/out/_next/static/chunks/727-15cf3f2fea9a37e5.js +1 -0
  44. package/out/_next/static/chunks/731-501990ebe6a71b32.js +1 -0
  45. package/out/_next/static/chunks/787-727e3aef5d2a10a8.js +1 -0
  46. package/out/_next/static/chunks/904-ccd8ca3368ce29fc.js +1 -0
  47. package/out/_next/static/chunks/{918-38d08f1e885b222d.js → 918-5bb55fb464e34768.js} +1 -1
  48. package/out/_next/static/chunks/f36c6662-03bb5e9d9aa0b0aa.js +1 -0
  49. package/out/_next/static/chunks/pages/_app-20876abeebe0fda7.js +430 -0
  50. package/out/_next/static/chunks/pages/changelog-68d115a14f1423d7.js +1 -0
  51. package/out/_next/static/chunks/pages/changes-6f62ae7b6243a1c7.js +1 -0
  52. package/out/_next/static/chunks/pages/custom-types/{[customTypeId]-3a37eb3373bb8823.js → [customTypeId]-3f862ad6111a1fc0.js} +1 -1
  53. package/out/_next/static/chunks/pages/{custom-types-2f5664d9492b13fb.js → custom-types-94b8f8dbf7727412.js} +1 -1
  54. package/out/_next/static/chunks/pages/{index-d8e3a2c0d712904f.js → index-64cfb8b0948ba8ef.js} +1 -1
  55. package/out/_next/static/chunks/pages/page-types/{[pageTypeId]-733f3890c0466a05.js → [pageTypeId]-d1ffca11c42f2603.js} +1 -1
  56. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/simulator-897ca93c114a1435.js +1 -0
  57. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-e89e2e6f772a3454.js +1 -0
  58. package/out/_next/static/chunks/pages/slices-9628f8aa54968d6a.js +1 -0
  59. package/out/_next/static/css/{1bbf650dfd37ce69.css → 1aa04669758c24f4.css} +1 -1
  60. package/out/_next/static/j_voPTrYzsuLCERqtl3Hn/_buildManifest.js +1 -0
  61. package/out/changelog.html +1 -1
  62. package/out/changes.html +1 -1
  63. package/out/custom-types/[customTypeId].html +1 -1
  64. package/out/custom-types.html +1 -1
  65. package/out/index.html +1 -1
  66. package/out/labs.html +1 -1
  67. package/out/page-types/[pageTypeId].html +1 -1
  68. package/out/slices/[lib]/[sliceName]/[variation]/simulator.html +1 -1
  69. package/out/slices/[lib]/[sliceName]/[variation].html +1 -1
  70. package/out/slices.html +1 -1
  71. package/package.json +6 -6
  72. package/pages/slices/[lib]/[sliceName]/[variation]/index.tsx +25 -11
  73. package/pages/slices/[lib]/[sliceName]/[variation]/simulator.tsx +12 -9
  74. package/src/apiClient.ts +7 -6
  75. package/src/components/CodeBlock/CodeBlock.tsx +1 -0
  76. package/src/components/Window/Window.tsx +7 -1
  77. package/src/domain/__tests__/customType.test.ts +785 -0
  78. package/src/domain/customType.ts +519 -6
  79. package/src/domain/slice.ts +166 -1
  80. package/src/features/autoSave/AutoSaveStatusIndicator.tsx +43 -0
  81. package/src/features/autoSave/useAutoSave.tsx +177 -0
  82. package/src/features/customTypes/EditDropdown.tsx +3 -0
  83. package/src/features/customTypes/actions/convertCustomToPageType.ts +2 -4
  84. package/src/features/customTypes/customTypesBuilder/CustomTypeProvider.tsx +90 -0
  85. package/src/features/customTypes/customTypesBuilder/CustomTypesBuilderPage.tsx +46 -79
  86. package/src/features/customTypes/customTypesMessages.ts +4 -0
  87. package/src/features/slices/actions/addSlicesToSliceZone.ts +2 -7
  88. package/src/features/slices/actions/uploadSliceScreenshot.ts +45 -0
  89. package/src/features/slices/convertLegacySlice/ConvertLegacySliceAsNewVariationDialog.tsx +5 -5
  90. package/src/features/slices/convertLegacySlice/ConvertLegacySliceButton.tsx +4 -10
  91. package/src/features/slices/sliceBuilder/SliceBuilderProvider.tsx +110 -0
  92. package/src/features/slices/sliceBuilder/actions/deleteVariation.ts +8 -24
  93. package/src/features/slices/sliceBuilder/actions/renameVariation.ts +8 -24
  94. package/src/features/slices/sliceCards/SharedSliceCard.tsx +1 -0
  95. package/src/hooks/useCurrentSlice.tsx +4 -16
  96. package/src/hooks/useScreenshotChangesModal.ts +1 -0
  97. package/src/hooks/useServerState.ts +1 -58
  98. package/src/modules/availableCustomTypes/index.ts +8 -3
  99. package/src/modules/simulator/index.ts +0 -2
  100. package/src/modules/slices/index.ts +32 -25
  101. package/src/modules/slices/selector.ts +30 -0
  102. package/src/modules/useSliceMachineActions.ts +20 -270
  103. package/src/redux/reducer.ts +0 -4
  104. package/src/redux/saga.ts +0 -6
  105. package/src/redux/type.ts +0 -4
  106. package/test/src/modules/availableCustomTypes/index.test.ts +8 -10
  107. package/test/src/modules/simulator.test.ts +0 -2
  108. package/lib/models/common/CustomType/group.ts +0 -80
  109. package/lib/models/common/Variation.ts +0 -100
  110. package/out/_next/static/ZrWICkLApT4ZATA_akn-2/_buildManifest.js +0 -1
  111. package/out/_next/static/chunks/18539003-4460ccbfe3109578.js +0 -97
  112. package/out/_next/static/chunks/482-34cee2a2071c235f.js +0 -1
  113. package/out/_next/static/chunks/550-c80fd46fb64d1eef.js +0 -1
  114. package/out/_next/static/chunks/80-7b8a97d0122ff74a.js +0 -1
  115. package/out/_next/static/chunks/840-163f8a1040e6820d.js +0 -1
  116. package/out/_next/static/chunks/904-519dbf0f7e8c6243.js +0 -1
  117. package/out/_next/static/chunks/958-8aca174b80cb0c35.js +0 -1
  118. package/out/_next/static/chunks/pages/_app-132564608e9a9502.js +0 -458
  119. package/out/_next/static/chunks/pages/changelog-4d0f2f1222e403fe.js +0 -1
  120. package/out/_next/static/chunks/pages/changes-52c9d917808d1461.js +0 -1
  121. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/simulator-7b17cb59d0db03b5.js +0 -1
  122. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-ef11594ce026b650.js +0 -1
  123. package/out/_next/static/chunks/pages/slices-884a936a9a062a65.js +0 -1
  124. package/src/domain/__tests__/CustomTypeModel.test.ts +0 -247
  125. package/src/layouts/WithSlice.tsx +0 -26
  126. package/src/modules/screenshots/actions.ts +0 -21
  127. package/src/modules/screenshots/sagas.ts +0 -70
  128. package/src/modules/selectedCustomType/actions.ts +0 -147
  129. package/src/modules/selectedCustomType/index.ts +0 -11
  130. package/src/modules/selectedCustomType/reducer.ts +0 -287
  131. package/src/modules/selectedCustomType/sagas.ts +0 -72
  132. package/src/modules/selectedCustomType/selectors.ts +0 -33
  133. package/src/modules/selectedCustomType/stateHelpers.ts +0 -42
  134. package/src/modules/selectedCustomType/types.ts +0 -9
  135. package/src/modules/selectedSlice/actions.ts +0 -106
  136. package/src/modules/selectedSlice/reducer.ts +0 -175
  137. package/src/modules/selectedSlice/sagas.ts +0 -72
  138. package/src/modules/selectedSlice/selectors.ts +0 -55
  139. package/src/modules/selectedSlice/types.ts +0 -3
  140. package/src/modules/toaster/utils.tsx +0 -23
  141. package/test/lib/models/common/CustomType/sliceZone.test.ts +0 -49
  142. package/test/pages/custom-types.test.tsx +0 -625
  143. package/test/src/modules/selectedCustomType/__fixtures__/model.json +0 -119
  144. package/test/src/modules/selectedCustomType/reducer.test.ts +0 -322
  145. package/test/src/modules/selectedCustomType/sagas.test.ts +0 -89
  146. package/test/src/modules/selectedCustomType/selectors.test.ts +0 -54
  147. package/test/src/modules/selectedSlice/__fixtures__/mocks.json +0 -171
  148. package/test/src/modules/selectedSlice/__fixtures__/model.json +0 -35
  149. package/test/src/modules/selectedSlice/__testutils__/getRefreshStateCreatorPayloadData.ts +0 -72
  150. package/test/src/modules/selectedSlice/__testutils__/getSelectedSliceDummyData.ts +0 -30
  151. package/test/src/modules/selectedSlice/reducer.test.ts +0 -170
  152. package/test/src/modules/selectedSlice/sagas.test.ts +0 -82
  153. package/test/src/modules/toaster/utils.test.ts +0 -50
  154. /package/out/_next/static/{ZrWICkLApT4ZATA_akn-2 → j_voPTrYzsuLCERqtl3Hn}/_ssgManifest.js +0 -0
@@ -1,4 +1,4 @@
1
- import { Button, Box } from "@prismicio/editor-ui";
1
+ import { Box } from "@prismicio/editor-ui";
2
2
  import Head from "next/head";
3
3
  import { useRouter } from "next/router";
4
4
  import { type FC, useEffect } from "react";
@@ -13,23 +13,19 @@ import {
13
13
  AppLayoutHeader,
14
14
  } from "@components/AppLayout";
15
15
  import { CustomTypeBuilder } from "@lib/builders/CustomTypeBuilder";
16
- import { type CustomTypeSM, CustomTypes } from "@lib/models/common/CustomType";
17
- import { hasLocal, hasRemote } from "@lib/models/common/ModelData";
16
+ import { CustomTypeSM, CustomTypes } from "@lib/models/common/CustomType";
17
+ import { hasLocal } from "@lib/models/common/ModelData";
18
18
  import { readBuilderPageDynamicSegment } from "@src/features/customTypes/customTypesConfig";
19
19
  import { selectCustomTypeById } from "@src/modules/availableCustomTypes";
20
- import { LoadingKeysEnum } from "@src/modules/loading/types";
21
- import { isLoading } from "@src/modules/loading";
22
- import {
23
- isSelectedCustomTypeTouched,
24
- selectCurrentCustomType,
25
- } from "@src/modules/selectedCustomType";
26
- import useSliceMachineActions from "@src/modules/useSliceMachineActions";
27
20
  import type { SliceMachineStoreType } from "@src/redux/type";
21
+ import { getFormat } from "@src/domain/customType";
22
+ import { AutoSaveStatusIndicator } from "@src/features/autoSave/AutoSaveStatusIndicator";
28
23
 
29
24
  import { CUSTOM_TYPES_CONFIG } from "../customTypesConfig";
30
25
  import { CUSTOM_TYPES_MESSAGES } from "../customTypesMessages";
31
26
  import { EditDropdown } from "../EditDropdown";
32
27
  import { PageSnippetDialog } from "./PageSnippetDialog";
28
+ import { CustomTypeProvider } from "./CustomTypeProvider";
33
29
 
34
30
  export const CustomTypesBuilderPage: FC = () => {
35
31
  const router = useRouter();
@@ -42,8 +38,6 @@ export const CustomTypesBuilderPage: FC = () => {
42
38
  }),
43
39
  );
44
40
 
45
- const { cleanupCustomTypeStore } = useSliceMachineActions();
46
-
47
41
  useEffect(() => {
48
42
  // TODO(DT-1801): When creating a custom type, don't redirect to the builder page until the custom type is created
49
43
  if (!selectedCustomType || !hasLocal(selectedCustomType)) {
@@ -51,14 +45,6 @@ export const CustomTypesBuilderPage: FC = () => {
51
45
  }
52
46
  }, [selectedCustomType, router]);
53
47
 
54
- useEffect(() => {
55
- return () => {
56
- cleanupCustomTypeStore();
57
- };
58
- // we don't want to re-run this effect when the cleanupCustomTypeStore is redefined
59
- // eslint-disable-next-line react-hooks/exhaustive-deps
60
- }, []);
61
-
62
48
  if (!selectedCustomType || !hasLocal(selectedCustomType)) {
63
49
  return <AppLayout />;
64
50
  }
@@ -70,9 +56,6 @@ export const CustomTypesBuilderPage: FC = () => {
70
56
  </Head>
71
57
  <CustomTypesBuilderPageWithProvider
72
58
  customType={selectedCustomType.local}
73
- remoteCustomType={
74
- hasRemote(selectedCustomType) ? selectedCustomType.remote : undefined
75
- }
76
59
  />
77
60
  </>
78
61
  );
@@ -80,70 +63,54 @@ export const CustomTypesBuilderPage: FC = () => {
80
63
 
81
64
  type CustomTypesBuilderPageWithProviderProps = {
82
65
  customType: CustomTypeSM;
83
- remoteCustomType: CustomTypeSM | undefined;
84
66
  };
85
67
 
86
68
  const CustomTypesBuilderPageWithProvider: React.FC<
87
69
  CustomTypesBuilderPageWithProviderProps
88
- > = ({ customType, remoteCustomType }) => {
89
- const { initCustomTypeStore, saveCustomType } = useSliceMachineActions();
90
-
91
- useEffect(
92
- () => {
93
- initCustomTypeStore(customType, remoteCustomType);
94
- },
95
- // eslint-disable-next-line react-hooks/exhaustive-deps
96
- [
97
- /* leave this empty to prevent local updates to disappear */
98
- ],
99
- );
100
-
101
- const { currentCustomType, hasPendingModifications, isSavingCustomType } =
102
- useSelector((store: SliceMachineStoreType) => ({
103
- currentCustomType: selectCurrentCustomType(store),
104
- hasPendingModifications: isSelectedCustomTypeTouched(store),
105
- isSavingCustomType: isLoading(store, LoadingKeysEnum.SAVE_CUSTOM_TYPE),
106
- }));
107
-
108
- if (currentCustomType === null) {
70
+ > = ({ customType: customTypeFromStore }) => {
71
+ if (customTypeFromStore === null) {
109
72
  return <AppLayout />;
110
73
  }
111
-
112
- const config = CUSTOM_TYPES_CONFIG[currentCustomType.format];
113
- const messages = CUSTOM_TYPES_MESSAGES[currentCustomType.format];
114
-
115
74
  return (
116
75
  <AppLayout>
117
- <AppLayoutHeader>
118
- <AppLayoutBackButton url={config.tablePagePathname} />
119
- <AppLayoutBreadcrumb
120
- folder={messages.name({ start: true, plural: true })}
121
- page={currentCustomType.label ?? currentCustomType.id}
122
- />
123
- <AppLayoutActions>
124
- {currentCustomType.format === "page" ? (
125
- <PageSnippetDialog model={CustomTypes.fromSM(currentCustomType)} />
126
- ) : undefined}
127
- <EditDropdown
128
- isChangesLocal
129
- format={currentCustomType.format}
130
- customType={CustomTypes.fromSM(currentCustomType)}
131
- />
132
- <Button
133
- onClick={saveCustomType}
134
- loading={isSavingCustomType}
135
- data-testid="builder-save-button"
136
- disabled={!hasPendingModifications || isSavingCustomType}
137
- >
138
- Save
139
- </Button>
140
- </AppLayoutActions>
141
- </AppLayoutHeader>
142
- <AppLayoutContent>
143
- <Box flexDirection="column" minWidth={0}>
144
- <CustomTypeBuilder customType={currentCustomType} />
145
- </Box>
146
- </AppLayoutContent>
76
+ <CustomTypeProvider
77
+ initialCustomType={CustomTypes.fromSM(customTypeFromStore)}
78
+ >
79
+ {({ autoSaveStatus, customType, setCustomType }) => {
80
+ const format = getFormat(customType);
81
+ const config = CUSTOM_TYPES_CONFIG[customTypeFromStore.format];
82
+ const messages = CUSTOM_TYPES_MESSAGES[customTypeFromStore.format];
83
+
84
+ return (
85
+ <>
86
+ <AppLayoutHeader>
87
+ <AppLayoutBackButton url={config.tablePagePathname} />
88
+ <AppLayoutBreadcrumb
89
+ folder={messages.name({ start: true, plural: true })}
90
+ page={customType.label ?? customType.id}
91
+ />
92
+ <AppLayoutActions>
93
+ <AutoSaveStatusIndicator status={autoSaveStatus} />
94
+ {customType.format === "page" ? (
95
+ <PageSnippetDialog model={customType} />
96
+ ) : undefined}
97
+ <EditDropdown
98
+ isChangesLocal
99
+ format={format}
100
+ customType={customType}
101
+ setLocalCustomType={setCustomType}
102
+ />
103
+ </AppLayoutActions>
104
+ </AppLayoutHeader>
105
+ <AppLayoutContent>
106
+ <Box flexDirection="column" minWidth={0}>
107
+ <CustomTypeBuilder />
108
+ </Box>
109
+ </AppLayoutContent>
110
+ </>
111
+ );
112
+ }}
113
+ </CustomTypeProvider>
147
114
  </AppLayout>
148
115
  );
149
116
  };
@@ -12,6 +12,8 @@ export const CUSTOM_TYPES_MESSAGES = {
12
12
  inputPlaceholder: `ID to query the page type in the API (e.g. 'BlogPost')`,
13
13
  blankSlateDescription:
14
14
  "Page types are models that your editors will use to create website pages in the Page Builder.",
15
+ autoSaveFailed:
16
+ "Failed to save page type. Check your browser's console for more information.",
15
17
  },
16
18
  custom: {
17
19
  name: ({ start, plural }: CustomTypesMessagesNameArgs) =>
@@ -21,5 +23,7 @@ export const CUSTOM_TYPES_MESSAGES = {
21
23
  inputPlaceholder: `ID to query the custom type in the API (e.g. 'Author')`,
22
24
  blankSlateDescription:
23
25
  "Custom types are models that your editors can use to create menus or objects in the Page Builder.",
26
+ autoSaveFailed:
27
+ "Failed to save custom type. Check your browser's console for more information.",
24
28
  },
25
29
  };
@@ -1,8 +1,7 @@
1
1
  import { SharedSlice } from "@prismicio/types-internal/lib/customtypes";
2
2
 
3
3
  import { telemetry } from "@src/apiClient";
4
- import { managerClient } from "@src/managerClient";
5
- import { CustomTypeSM, CustomTypes } from "@lib/models/common/CustomType";
4
+ import { CustomTypeSM } from "@lib/models/common/CustomType";
6
5
 
7
6
  export type AddSlicesToSliceZoneArgs = {
8
7
  customType: CustomTypeSM;
@@ -10,7 +9,7 @@ export type AddSlicesToSliceZoneArgs = {
10
9
  slices: SharedSlice[];
11
10
  };
12
11
 
13
- export async function addSlicesToSliceZone({
12
+ export function addSlicesToSliceZone({
14
13
  customType,
15
14
  tabId,
16
15
  slices,
@@ -40,10 +39,6 @@ export async function addSlicesToSliceZone({
40
39
  };
41
40
  });
42
41
 
43
- await managerClient.customTypes.updateCustomType({
44
- model: CustomTypes.fromSM(newCustomType),
45
- });
46
-
47
42
  void telemetry.track({
48
43
  event: "custom-type:slice-zone-updated",
49
44
  customTypeId: customType.id,
@@ -0,0 +1,45 @@
1
+ import { toast } from "react-toastify";
2
+
3
+ import { ComponentUI } from "@lib/models/common/ComponentUI";
4
+ import { generateSliceCustomScreenshot, telemetry } from "@src/apiClient";
5
+
6
+ type UploadSliceScreenshotArgs = {
7
+ file: File;
8
+ method: "upload" | "dragAndDrop";
9
+ slice: ComponentUI;
10
+ variationId: string;
11
+ };
12
+
13
+ export async function uploadSliceScreenshot(args: UploadSliceScreenshotArgs) {
14
+ const { variationId, slice, file, method } = args;
15
+
16
+ try {
17
+ const form = new FormData();
18
+ form.append("file", file);
19
+ form.append("libraryName", slice.from);
20
+ form.append("sliceName", slice.model.name);
21
+ form.append("variationId", variationId);
22
+ const { errors, url } = await generateSliceCustomScreenshot({
23
+ libraryName: slice.from,
24
+ sliceId: slice.model.id,
25
+ variationId,
26
+ file,
27
+ });
28
+
29
+ if (errors.length > 0) {
30
+ throw errors;
31
+ }
32
+
33
+ void telemetry.track({ event: "screenshot-taken", type: "custom", method });
34
+
35
+ return {
36
+ ...slice,
37
+ screenshots: { ...slice.screenshots, [variationId]: { url } },
38
+ };
39
+ } catch (error) {
40
+ const message = `Screenshot not saved`;
41
+ console.error(message, error);
42
+
43
+ toast.error(message);
44
+ }
45
+ }
@@ -1,5 +1,6 @@
1
1
  import { useState, type FC } from "react";
2
2
  import { Formik } from "formik";
3
+ import { camelCase } from "lodash";
3
4
  import {
4
5
  Box,
5
6
  Dialog,
@@ -13,7 +14,6 @@ import {
13
14
  SelectItem,
14
15
  } from "@prismicio/editor-ui";
15
16
 
16
- import { Variation } from "@models/common/Variation";
17
17
  import { LibraryUI } from "@models/common/LibraryUI";
18
18
 
19
19
  import * as styles from "./ConvertLegacySliceButton.css";
@@ -51,7 +51,7 @@ export const ConvertLegacySliceAsNewVariationDialog: FC<DialogProps> = ({
51
51
  initialValues={{
52
52
  libraryID: localSharedSlices[0]?.from,
53
53
  sliceID: localSharedSlices[0]?.model.id,
54
- variationID: Variation.generateId(slice.key),
54
+ variationID: camelCase(slice.key),
55
55
  variationName: sliceName,
56
56
  }}
57
57
  validate={(values) => {
@@ -136,7 +136,7 @@ export const ConvertLegacySliceAsNewVariationDialog: FC<DialogProps> = ({
136
136
  };
137
137
 
138
138
  if (inferIDFromName) {
139
- values.variationID = Variation.generateId(
139
+ values.variationID = camelCase(
140
140
  values.variationName,
141
141
  );
142
142
  }
@@ -156,14 +156,14 @@ export const ConvertLegacySliceAsNewVariationDialog: FC<DialogProps> = ({
156
156
  ) : null}
157
157
  </label>
158
158
  <FormInput
159
- placeholder={Variation.generateId(slice.key)}
159
+ placeholder={camelCase(slice.key)}
160
160
  error={typeof formik.errors.variationID === "string"}
161
161
  value={formik.values.variationID}
162
162
  onValueChange={(value) => {
163
163
  setInferIDFromName(false);
164
164
  void formik.setFieldValue(
165
165
  "variationID",
166
- Variation.generateId(value.slice(0, 30)),
166
+ camelCase(value.slice(0, 30)),
167
167
  );
168
168
  }}
169
169
  data-cy="variation-id-input"
@@ -11,7 +11,6 @@ import {
11
11
 
12
12
  import { NonSharedSliceInSliceZone } from "@models/common/CustomType/sliceZone";
13
13
  import { ComponentUI } from "@models/common/ComponentUI";
14
- import { CustomTypes } from "@models/common/CustomType";
15
14
  import { getLibraries } from "@src/modules/slices";
16
15
  import { SliceMachineStoreType } from "@src/redux/type";
17
16
  import { managerClient } from "@src/managerClient";
@@ -19,6 +18,7 @@ import { getState, telemetry } from "@src/apiClient";
19
18
  import useSliceMachineActions from "@src/modules/useSliceMachineActions";
20
19
  import { ToasterType } from "@src/modules/toaster";
21
20
  import { getFieldMappingFingerprint } from "@src/domain/slice";
21
+ import { useCustomTypeState } from "@src/features/customTypes/customTypesBuilder/CustomTypeProvider";
22
22
 
23
23
  import { NonSharedSliceCardProps } from "../sliceCards/NonSharedSliceCard";
24
24
  import { ConvertLegacySliceAsNewSliceDialog } from "./ConvertLegacySliceAsNewSliceDialog";
@@ -36,15 +36,11 @@ export const ConvertLegacySliceButton: FC<ConvertLegacySliceButtonProps> = ({
36
36
  slice,
37
37
  path,
38
38
  }) => {
39
- const {
40
- refreshState,
41
- openToaster,
42
- initCustomTypeStore,
43
- saveCustomTypeSuccess,
44
- } = useSliceMachineActions();
39
+ const { refreshState, openToaster } = useSliceMachineActions();
45
40
 
46
41
  const [isLoading, setIsLoading] = useState(false);
47
42
  const [dialog, setDialog] = useState<LegacySliceConversionType | undefined>();
43
+ const { setCustomType } = useCustomTypeState();
48
44
 
49
45
  const { libraries: allLibraries } = useSelector(
50
46
  (store: SliceMachineStoreType) => ({
@@ -159,9 +155,7 @@ export const ConvertLegacySliceButton: FC<ConvertLegacySliceButtonProps> = ({
159
155
  break;
160
156
  }
161
157
 
162
- const customTypeSM = CustomTypes.toSM(customType);
163
- initCustomTypeStore(customTypeSM, customTypeSM);
164
- saveCustomTypeSuccess(customType);
158
+ setCustomType(customType);
165
159
  };
166
160
 
167
161
  const formProps = {
@@ -0,0 +1,110 @@
1
+ import {
2
+ ReactNode,
3
+ createContext,
4
+ useCallback,
5
+ useContext,
6
+ useMemo,
7
+ useState,
8
+ } from "react";
9
+ import { useStableCallback } from "@prismicio/editor-support/React";
10
+ import { useRouter } from "next/router";
11
+
12
+ import {
13
+ AutoSaveStatus,
14
+ useAutoSave,
15
+ } from "@src/features/autoSave/useAutoSave";
16
+ import { ComponentUI } from "@lib/models/common/ComponentUI";
17
+ import { readSliceMocks, updateSlice } from "@src/apiClient";
18
+ import useSliceMachineActions from "@src/modules/useSliceMachineActions";
19
+ import { VariationSM } from "@lib/models/common/Slice";
20
+
21
+ type SliceContext = {
22
+ slice: ComponentUI;
23
+ autoSaveStatus: AutoSaveStatus;
24
+ setSlice: (slice: ComponentUI) => void;
25
+ variation: VariationSM;
26
+ };
27
+
28
+ type SliceBuilderProviderProps = {
29
+ children: ReactNode | ((value: SliceContext) => ReactNode);
30
+ initialSlice: ComponentUI;
31
+ };
32
+
33
+ const SliceContextValue = createContext<SliceContext | undefined>(undefined);
34
+
35
+ export function SliceBuilderProvider(props: SliceBuilderProviderProps) {
36
+ const { children, initialSlice } = props;
37
+
38
+ const router = useRouter();
39
+ const [slice, setSliceState] = useState(initialSlice);
40
+ const { autoSaveStatus, setNextSave } = useAutoSave({
41
+ errorMessage:
42
+ "Failed to save slice. Check your browser's console for more information.",
43
+ });
44
+ const { saveSliceSuccess } = useSliceMachineActions();
45
+ const stableSaveSliceSuccess = useStableCallback(saveSliceSuccess);
46
+
47
+ const variation = useMemo(() => {
48
+ const variationName = router.query.variation;
49
+ const v = slice.model.variations.find(
50
+ (variation) => variation.id === variationName,
51
+ );
52
+
53
+ if (v) {
54
+ return v;
55
+ }
56
+
57
+ throw new Error("Variation not found");
58
+ }, [slice, router]);
59
+
60
+ const setSlice = useCallback(
61
+ (slice: ComponentUI) => {
62
+ setSliceState(slice);
63
+ setNextSave(async () => {
64
+ const { errors: updateSliceErrors } = await updateSlice(slice);
65
+
66
+ if (updateSliceErrors.length > 0) {
67
+ throw updateSliceErrors;
68
+ }
69
+
70
+ const { errors: readSliceMockErrors, mocks } = await readSliceMocks({
71
+ libraryID: slice.from,
72
+ sliceID: slice.model.id,
73
+ });
74
+
75
+ if (readSliceMockErrors.length > 0) {
76
+ throw readSliceMockErrors;
77
+ }
78
+
79
+ stableSaveSliceSuccess({ ...slice, mocks });
80
+ });
81
+ },
82
+ [setNextSave, stableSaveSliceSuccess],
83
+ );
84
+
85
+ const contextValue: SliceContext = useMemo(
86
+ () => ({
87
+ autoSaveStatus,
88
+ slice,
89
+ setSlice,
90
+ variation,
91
+ }),
92
+ [autoSaveStatus, slice, setSlice, variation],
93
+ );
94
+
95
+ return (
96
+ <SliceContextValue.Provider value={contextValue}>
97
+ {typeof children === "function" ? children(contextValue) : children}
98
+ </SliceContextValue.Provider>
99
+ );
100
+ }
101
+
102
+ export function useSliceState() {
103
+ const sliceState = useContext(SliceContextValue);
104
+
105
+ if (!sliceState) {
106
+ throw new Error("SliceBuilderProvider not found");
107
+ }
108
+
109
+ return sliceState;
110
+ }
@@ -1,8 +1,6 @@
1
1
  import type { NextRouter } from "next/router";
2
- import type { Dispatch, SetStateAction } from "react";
2
+ import { toast } from "react-toastify";
3
3
 
4
- import type { SliceBuilderState } from "@builders/SliceBuilder";
5
- import { SliceToastMessage } from "@components/ToasterContainer";
6
4
  import type { ComponentUI } from "@lib/models/common/ComponentUI";
7
5
  import type { VariationSM } from "@lib/models/common/Slice";
8
6
  import {
@@ -16,16 +14,13 @@ import { SLICES_CONFIG } from "@src/features/slices/slicesConfig";
16
14
  type DeleteVariationArgs = {
17
15
  component: ComponentUI;
18
16
  router: NextRouter;
19
- setSliceBuilderState: Dispatch<SetStateAction<SliceBuilderState>>;
20
- updateAndSaveSlice: (component: ComponentUI) => void;
17
+ saveSliceSuccess: (component: ComponentUI) => void;
21
18
  variation: VariationSM;
22
19
  };
23
20
 
24
21
  export async function deleteVariation(
25
22
  args: DeleteVariationArgs,
26
- ): Promise<void> {
27
- args.setSliceBuilderState({ loading: true, done: false });
28
-
23
+ ): Promise<ComponentUI> {
29
24
  try {
30
25
  // The slice may have been edited so we need to update the file system.
31
26
  const { errors: updateSliceErrors } = await updateSlice(args.component);
@@ -62,27 +57,16 @@ export async function deleteVariation(
62
57
  variationId: slice.variations[0].id,
63
58
  });
64
59
  await args.router.replace(url);
65
- args.updateAndSaveSlice({ ...args.component, model: slice, mocks });
66
60
 
67
- // Finally, display a success toast.
68
- const path = `${args.component.from}/${args.component.model.name}/model.json`;
69
- args.setSliceBuilderState({
70
- loading: false,
71
- done: true,
72
- error: false,
73
- message: SliceToastMessage({ path }),
74
- });
61
+ const newComponent = { ...args.component, model: slice, mocks };
62
+ args.saveSliceSuccess(newComponent);
63
+
64
+ return newComponent;
75
65
  } catch (error) {
76
66
  const message = `Could not delete variation \`${args.variation.name}\``;
77
67
  console.error(message, error);
78
68
 
79
- // Display a failure toast.
80
- args.setSliceBuilderState({
81
- loading: false,
82
- done: true,
83
- error: true,
84
- message,
85
- });
69
+ toast.error(message);
86
70
 
87
71
  throw error;
88
72
  }
@@ -1,7 +1,5 @@
1
- import type { Dispatch, SetStateAction } from "react";
1
+ import { toast } from "react-toastify";
2
2
 
3
- import type { SliceBuilderState } from "@builders/SliceBuilder";
4
- import { SliceToastMessage } from "@components/ToasterContainer";
5
3
  import type { ComponentUI } from "@lib/models/common/ComponentUI";
6
4
  import type { VariationSM } from "@lib/models/common/Slice";
7
5
  import {
@@ -13,17 +11,14 @@ import {
13
11
 
14
12
  type RenameVariationArgs = {
15
13
  component: ComponentUI;
16
- setSliceBuilderState: Dispatch<SetStateAction<SliceBuilderState>>;
17
- updateAndSaveSlice: (component: ComponentUI) => void;
14
+ saveSliceSuccess: (component: ComponentUI) => void;
18
15
  variation: VariationSM;
19
16
  variationName: string;
20
17
  };
21
18
 
22
19
  export async function renameVariation(
23
20
  args: RenameVariationArgs,
24
- ): Promise<void> {
25
- args.setSliceBuilderState({ loading: true, done: false });
26
-
21
+ ): Promise<ComponentUI> {
27
22
  try {
28
23
  // The slice may have been edited so we need to update the file system.
29
24
  const { errors: updateSliceErrors } = await updateSlice(args.component);
@@ -53,27 +48,16 @@ export async function renameVariation(
53
48
  libraryID: args.component.from,
54
49
  sliceID: args.component.model.id,
55
50
  });
56
- args.updateAndSaveSlice({ ...args.component, model: slice, mocks });
57
51
 
58
- // Finally, display a success toast.
59
- const path = `${args.component.from}/${args.component.model.name}/model.json`;
60
- args.setSliceBuilderState({
61
- loading: false,
62
- done: true,
63
- error: false,
64
- message: SliceToastMessage({ path }),
65
- });
52
+ const newComponent = { ...args.component, model: slice, mocks };
53
+ args.saveSliceSuccess(newComponent);
54
+
55
+ return newComponent;
66
56
  } catch (error) {
67
57
  const message = `Could not rename variation \`${args.variation.name}\``;
68
58
  console.error(message, error);
69
59
 
70
- // Display a failure toast.
71
- args.setSliceBuilderState({
72
- loading: false,
73
- done: true,
74
- error: true,
75
- message,
76
- });
60
+ toast.error(message);
77
61
 
78
62
  throw error;
79
63
  }
@@ -206,6 +206,7 @@ export const SharedSliceCard: FC<SharedSliceCardProps> = (props) => {
206
206
  <IconButton
207
207
  icon="close"
208
208
  onClick={() => !disabled && action.onRemove()}
209
+ hiddenLabel="Remove slice"
209
210
  />
210
211
  ) : action.type === "status" ? (
211
212
  <StatusBadge
@@ -1,18 +1,15 @@
1
+ import { useRouter } from "next/router";
2
+ import { useSelector } from "react-redux";
3
+
1
4
  import { ComponentUI } from "@lib/models/common/ComponentUI";
2
5
  import { VariationSM } from "@lib/models/common/Slice";
3
- import { selectCurrentSlice } from "@src/modules/selectedSlice/selectors";
4
- import useSliceMachineActions from "@src/modules/useSliceMachineActions";
6
+ import { selectCurrentSlice } from "@src/modules/slices/selector";
5
7
  import { SliceMachineStoreType } from "@src/redux/type";
6
8
 
7
- import { useRouter } from "next/router";
8
- import { useEffect } from "react";
9
- import { useSelector } from "react-redux";
10
-
11
9
  type UseCurrentSliceRet = { slice?: ComponentUI; variation?: VariationSM };
12
10
 
13
11
  const useCurrentSlice = (): UseCurrentSliceRet => {
14
12
  const router = useRouter();
15
- const { initSliceStore } = useSliceMachineActions();
16
13
 
17
14
  const { slice } = useSelector((store: SliceMachineStoreType) => ({
18
15
  slice: selectCurrentSlice(
@@ -22,14 +19,6 @@ const useCurrentSlice = (): UseCurrentSliceRet => {
22
19
  ),
23
20
  }));
24
21
 
25
- useEffect(() => {
26
- if (slice) {
27
- initSliceStore(slice);
28
- } else {
29
- void router.replace("/");
30
- }
31
- }, [initSliceStore, slice, router]);
32
-
33
22
  if (!slice) {
34
23
  return {};
35
24
  }
@@ -39,7 +28,6 @@ const useCurrentSlice = (): UseCurrentSliceRet => {
39
28
  );
40
29
 
41
30
  if (!variation) {
42
- void router.replace("/");
43
31
  return {};
44
32
  }
45
33
 
@@ -6,6 +6,7 @@ import { SliceVariationSelector } from "@components/ScreenshotChangesModal";
6
6
  type ModalPayload = {
7
7
  sliceFilterFn: (s: ComponentUI[]) => ComponentUI[];
8
8
  defaultVariationSelector?: SliceVariationSelector;
9
+ onUploadSuccess?: (newSlice: ComponentUI) => void;
9
10
  };
10
11
 
11
12
  type Payload = {