slice-machine-ui 2.2.2-beta.1 → 2.2.2-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 (81) hide show
  1. package/next.config.mjs +11 -1
  2. package/out/404.html +1 -1
  3. package/out/_next/static/KD7BhDNRF_N5IlJuDq1eR/_buildManifest.js +1 -0
  4. package/out/_next/static/chunks/18539003-36b102fa41de015e.js +22 -0
  5. package/out/_next/static/chunks/{66-d37d30551288858b.js → 210-565d01ee9777d1a1.js} +2 -2
  6. package/out/_next/static/chunks/212-58a99ab312f8a03b.js +1 -0
  7. package/out/_next/static/chunks/448-7a6843206737d199.js +1 -0
  8. package/out/_next/static/chunks/465-695ac5fedef986eb.js +1 -0
  9. package/out/_next/static/chunks/574-dfeb0e6aa3079da1.js +1 -0
  10. package/out/_next/static/chunks/756-98ae0a6e755f46f2.js +10 -0
  11. package/out/_next/static/chunks/816-96fc9a79308019c7.js +1 -0
  12. package/out/_next/static/chunks/pages/_app-6c8c02648847371c.js +536 -0
  13. package/out/_next/static/chunks/pages/changelog-f0cd26a9ae095e9e.js +1 -0
  14. package/out/_next/static/chunks/pages/changes-1a57f90433a0b5e2.js +1 -0
  15. package/out/_next/static/chunks/pages/custom-types/{[customTypeId]-74be2c0ac9c854c7.js → [customTypeId]-a6ff1618238b3510.js} +1 -1
  16. package/out/_next/static/chunks/pages/index-a0530bd55c7e1872.js +1 -0
  17. package/out/_next/static/chunks/pages/labs-7b0abeef69494aec.js +1 -0
  18. package/out/_next/static/chunks/pages/page-types/{[pageTypeId]-d35c87b9aaec445a.js → [pageTypeId]-c300f963db847451.js} +1 -1
  19. package/out/_next/static/chunks/pages/settings-f82286dde10f624c.js +1 -0
  20. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/simulator-453f946e8879c829.js +1 -0
  21. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-b530a5e0ac9d0391.js +1 -0
  22. package/out/_next/static/chunks/pages/slices-7411822d74f1f6c1.js +1 -0
  23. package/out/_next/static/css/ee6cd1a80767d31e.css +1 -0
  24. package/out/changelog.html +1 -1
  25. package/out/changes.html +1 -1
  26. package/out/custom-types/[customTypeId].html +1 -1
  27. package/out/custom-types.html +1 -1
  28. package/out/index.html +1 -1
  29. package/out/labs.html +1 -1
  30. package/out/page-types/[pageTypeId].html +1 -1
  31. package/out/settings.html +1 -1
  32. package/out/slices/[lib]/[sliceName]/[variation]/simulator.html +1 -1
  33. package/out/slices/[lib]/[sliceName]/[variation].html +1 -1
  34. package/out/slices.html +1 -1
  35. package/package.json +9 -9
  36. package/src/hooks/useNestedGroupExperiment.ts +8 -0
  37. package/src/legacy/components/ListItem/index.tsx +1 -1
  38. package/src/legacy/lib/builders/CustomTypeBuilder/TabZone/index.tsx +21 -3
  39. package/src/legacy/lib/builders/SliceBuilder/FieldZones/index.tsx +23 -6
  40. package/src/legacy/lib/builders/SliceBuilder/VariationsList/index.tsx +4 -2
  41. package/src/legacy/lib/builders/SliceBuilder/index.tsx +1 -1
  42. package/src/legacy/lib/builders/common/EditModal/index.jsx +1 -1
  43. package/src/legacy/lib/builders/common/Zone/Card/components/Hints/index.tsx +8 -1
  44. package/src/legacy/lib/builders/common/Zone/Card/components/NewField.tsx +1 -1
  45. package/src/legacy/lib/builders/common/Zone/Card/index.jsx +3 -1
  46. package/src/legacy/lib/builders/utils/index.ts +8 -0
  47. package/src/legacy/lib/models/common/Group.ts +51 -28
  48. package/src/legacy/lib/models/common/widgets/Group/ListItem/index.jsx +22 -8
  49. package/src/legacy/lib/models/common/widgets/Group/createGroupWidget.ts +83 -0
  50. package/src/legacy/lib/models/common/widgets/Group/index.tsx +72 -0
  51. package/src/legacy/lib/models/common/widgets/NestedGroup/index.tsx +40 -0
  52. package/src/legacy/lib/models/common/widgets/Widget.ts +10 -1
  53. package/src/legacy/lib/models/common/widgets/groupWidgets.ts +7 -0
  54. package/src/legacy/lib/models/common/widgets/index.ts +7 -31
  55. package/src/legacy/lib/models/common/widgets/nonGroupWidgets.ts +33 -0
  56. package/test/lib/builders/buildersUtils.test.ts +1 -1
  57. package/test/lib/models/common/widgets/index.test.ts +1 -1
  58. package/out/_next/static/JGphUg6eFq0-W11Pa7cMU/_buildManifest.js +0 -1
  59. package/out/_next/static/chunks/18539003-eda642ad962ef598.js +0 -22
  60. package/out/_next/static/chunks/212-cbaa35389c0aacf1.js +0 -1
  61. package/out/_next/static/chunks/264-4bb6f3fb14863ef3.js +0 -1
  62. package/out/_next/static/chunks/465-47a6d2a5f66f521e.js +0 -1
  63. package/out/_next/static/chunks/574-6039c48fbc93b4a8.js +0 -1
  64. package/out/_next/static/chunks/756-1e44fe84bedc262b.js +0 -10
  65. package/out/_next/static/chunks/816-68d4db26c0adab2e.js +0 -1
  66. package/out/_next/static/chunks/pages/_app-05ac13d573829b68.js +0 -532
  67. package/out/_next/static/chunks/pages/changelog-d309ea186a0e56bf.js +0 -1
  68. package/out/_next/static/chunks/pages/changes-1bb1a7b784a22151.js +0 -1
  69. package/out/_next/static/chunks/pages/index-785a0ab6e2a4fc27.js +0 -1
  70. package/out/_next/static/chunks/pages/labs-2e4f3a5f68e65b93.js +0 -1
  71. package/out/_next/static/chunks/pages/settings-25eb02893ed871b7.js +0 -1
  72. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/simulator-b47a72d5e94bd946.js +0 -1
  73. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-996e20b9b295a912.js +0 -1
  74. package/out/_next/static/chunks/pages/slices-cc44eeccd58d23a9.js +0 -1
  75. package/out/_next/static/css/b3bb731ce567d63f.css +0 -1
  76. package/src/legacy/lib/models/common/widgets/Group/index.ts +0 -44
  77. package/src/legacy/lib/models/common/widgets/ctBuilderArray.ts +0 -20
  78. package/src/legacy/lib/models/common/widgets/groupBuilderArray.ts +0 -18
  79. package/src/legacy/lib/models/common/widgets/sliceBuilderArray.ts +0 -19
  80. package/src/legacy/lib/models/common/widgets/withGroup.ts +0 -2
  81. /package/out/_next/static/{JGphUg6eFq0-W11Pa7cMU → KD7BhDNRF_N5IlJuDq1eR}/_ssgManifest.js +0 -0
@@ -30,7 +30,7 @@ import {
30
30
  TabFieldsModel,
31
31
  TabSM,
32
32
  } from "@/legacy/lib/models/common/CustomType";
33
- import ctBuilderArray from "@/legacy/lib/models/common/widgets/ctBuilderArray";
33
+ import { Widgets } from "@/legacy/lib/models/common/widgets";
34
34
  import type { AnyWidget } from "@/legacy/lib/models/common/widgets/Widget";
35
35
  import {
36
36
  ensureDnDDestination,
@@ -39,11 +39,29 @@ import {
39
39
  import { transformKeyAccessor } from "@/legacy/lib/utils/str";
40
40
  import { getContentTypeForTracking } from "@/utils/getContentTypeForTracking";
41
41
 
42
- import * as Widgets from "../../../../lib/models/common/widgets/withGroup";
43
42
  import EditModal from "../../common/EditModal";
44
43
  import Zone from "../../common/Zone";
45
44
  import SliceZone from "../SliceZone";
46
45
 
46
+ const widgetsArray = [
47
+ Widgets.UID,
48
+ Widgets.Group,
49
+ Widgets.StructuredText,
50
+ Widgets.Image,
51
+ Widgets.Link,
52
+ Widgets.LinkToMedia,
53
+ Widgets.ContentRelationship,
54
+ Widgets.Select,
55
+ Widgets.Boolean,
56
+ Widgets.Date,
57
+ Widgets.Timestamp,
58
+ Widgets.Embed,
59
+ Widgets.Number,
60
+ Widgets.GeoPoint,
61
+ Widgets.Color,
62
+ Widgets.Text,
63
+ ];
64
+
47
65
  interface TabZoneProps {
48
66
  tabId: string;
49
67
  }
@@ -227,7 +245,7 @@ const TabZone: FC<TabZoneProps> = ({ tabId }) => {
227
245
  poolOfFieldsToCheck={poolOfFields}
228
246
  showHints={true}
229
247
  EditModal={EditModal}
230
- widgetsArray={ctBuilderArray}
248
+ widgetsArray={widgetsArray}
231
249
  onDeleteItem={onDeleteItem}
232
250
  onSave={onSave}
233
251
  onSaveNewField={onSaveNewField}
@@ -27,9 +27,7 @@ import {
27
27
  SlicePrimaryFieldSM,
28
28
  WidgetsArea,
29
29
  } from "@/legacy/lib/models/common/Slice";
30
- import * as Widgets from "@/legacy/lib/models/common/widgets";
31
- import groupBuilderWidgetsArray from "@/legacy/lib/models/common/widgets/groupBuilderArray";
32
- import sliceBuilderWidgetsArray from "@/legacy/lib/models/common/widgets/sliceBuilderArray";
30
+ import { Widgets } from "@/legacy/lib/models/common/widgets";
33
31
  import { ensureDnDDestination } from "@/legacy/lib/utils";
34
32
  import { transformKeyAccessor } from "@/legacy/lib/utils/str";
35
33
  import { getContentTypeForTracking } from "@/utils/getContentTypeForTracking";
@@ -42,6 +40,25 @@ const dataTipText2 = `The repeatable zone is for a group<br/>
42
40
  of fields that you want to be able to repeat an<br/>
43
41
  indeterminate number of times, like FAQs`;
44
42
 
43
+ const itemsWidgetsArray = [
44
+ Widgets.StructuredText,
45
+ Widgets.Image,
46
+ Widgets.Link,
47
+ Widgets.LinkToMedia,
48
+ Widgets.ContentRelationship,
49
+ Widgets.Select,
50
+ Widgets.Boolean,
51
+ Widgets.Date,
52
+ Widgets.Timestamp,
53
+ Widgets.Embed,
54
+ Widgets.Number,
55
+ Widgets.GeoPoint,
56
+ Widgets.Color,
57
+ Widgets.Text,
58
+ ];
59
+
60
+ const primaryWidgetsArray = [Widgets.Group, ...itemsWidgetsArray];
61
+
45
62
  const FieldZones: FC = () => {
46
63
  const { slice, setSlice, variation } = useSliceState();
47
64
  const [
@@ -110,7 +127,7 @@ const FieldZones: FC = () => {
110
127
  label: string;
111
128
  widgetTypeName: keyof typeof Widgets;
112
129
  }) => {
113
- const widget = sliceBuilderWidgetsArray.find(
130
+ const widget = primaryWidgetsArray.find(
114
131
  (sliceBuilderWidget) =>
115
132
  sliceBuilderWidget.CUSTOM_NAME === widgetTypeName ||
116
133
  sliceBuilderWidget.TYPE_NAME === widgetTypeName,
@@ -188,7 +205,7 @@ const FieldZones: FC = () => {
188
205
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
189
206
  fields={variation.primary}
190
207
  EditModal={EditModal}
191
- widgetsArray={sliceBuilderWidgetsArray}
208
+ widgetsArray={primaryWidgetsArray}
192
209
  onDeleteItem={_onDeleteItem(WidgetsArea.Primary)}
193
210
  onSave={_onSave(WidgetsArea.Primary)}
194
211
  onSaveNewField={_onSaveNewField(WidgetsArea.Primary)}
@@ -216,7 +233,7 @@ const FieldZones: FC = () => {
216
233
  isRepeatable
217
234
  title="Repeatable Zone"
218
235
  dataTip={dataTipText2}
219
- widgetsArray={groupBuilderWidgetsArray}
236
+ widgetsArray={itemsWidgetsArray}
220
237
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
221
238
  fields={variation.items}
222
239
  EditModal={EditModal}
@@ -60,7 +60,9 @@ export const VariationsList: FC<VariationsListProps> = (props) => {
60
60
  }}
61
61
  startIcon="add"
62
62
  color="grey"
63
- />
63
+ >
64
+ Add a variation
65
+ </Button>
64
66
  </Box>
65
67
  <div
66
68
  className={styles.hideScrollbar}
@@ -124,7 +126,7 @@ export const VariationsList: FC<VariationsListProps> = (props) => {
124
126
  // `Gradient`.
125
127
  sx={{ position: "relative" }}
126
128
  >
127
- Add a new variation
129
+ Add a variation
128
130
  </Button>
129
131
  </Box>
130
132
  </div>
@@ -20,7 +20,7 @@ import { VariationsList } from "./VariationsList";
20
20
 
21
21
  export const SliceBuilder: FC = () => {
22
22
  const { slice, actionQueueStatus } = useSliceState();
23
- const horizontalScroll = useMediaQuery({ max: "medium" });
23
+ const horizontalScroll = useMediaQuery({ max: "large" });
24
24
 
25
25
  const contentDisplayProps = horizontalScroll
26
26
  ? { gridTemplateRows: "304px 1fr" }
@@ -11,7 +11,7 @@ import {
11
11
  createInitialValues,
12
12
  } from "@/legacy/lib/forms";
13
13
  import { DeprecatedMockConfigMessage } from "@/legacy/lib/models/common/DeprecatedMockConfigMessage";
14
- import * as Widgets from "@/legacy/lib/models/common/widgets/withGroup";
14
+ import { Widgets } from "@/legacy/lib/models/common/widgets";
15
15
  import { removeProp } from "@/legacy/lib/utils";
16
16
 
17
17
  import { findWidgetByConfigOrType } from "../../utils";
@@ -9,9 +9,15 @@ interface HintProps {
9
9
  show: boolean;
10
10
  item: Item;
11
11
  renderHintBase: RenderHintBaseFN;
12
+ hintItemName?: string;
12
13
  }
13
14
 
14
- const Hint: React.FC<HintProps> = ({ show, renderHintBase, item }) => {
15
+ const Hint: React.FC<HintProps> = ({
16
+ show,
17
+ renderHintBase,
18
+ item,
19
+ hintItemName,
20
+ }) => {
15
21
  const fieldPathString = renderHintBase({ item });
16
22
 
17
23
  // TODO: Call `swr`'s global `mutate` function when something changes to clear the cache.
@@ -20,6 +26,7 @@ const Hint: React.FC<HintProps> = ({ show, renderHintBase, item }) => {
20
26
  return await managerClient.snippets.readSnippets({
21
27
  fieldPath: fieldPathString.split("."),
22
28
  model: item.value,
29
+ itemName: hintItemName,
23
30
  });
24
31
  });
25
32
 
@@ -15,8 +15,8 @@ import {
15
15
  } from "@/legacy/lib/forms";
16
16
  import { validateId } from "@/legacy/lib/forms/defaults";
17
17
  import { InputType } from "@/legacy/lib/forms/fields";
18
+ import { Widgets } from "@/legacy/lib/models/common/widgets";
18
19
  import { AnyWidget } from "@/legacy/lib/models/common/widgets/Widget";
19
- import * as Widgets from "@/legacy/lib/models/common/widgets/withGroup";
20
20
  import { slugify } from "@/legacy/lib/utils/str";
21
21
 
22
22
  import { ErrorTooltip } from "./ErrorTooltip";
@@ -3,7 +3,7 @@ import { Box, Text } from "theme-ui";
3
3
 
4
4
  import Li from "@/legacy/components/Li";
5
5
  import ListItem from "@/legacy/components/ListItem";
6
- import * as Widgets from "@/legacy/lib/models/common/widgets/withGroup";
6
+ import { Widgets } from "@/legacy/lib/models/common/widgets";
7
7
 
8
8
  import { findWidgetByConfigOrType } from "../../../utils";
9
9
  import Hint from "./components/Hints";
@@ -103,6 +103,8 @@ const FieldZone = ({
103
103
  isRepeatable={isRepeatable}
104
104
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
105
105
  renderHintBase={renderHintBase}
106
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
107
+ hintItemName={widget.hintItemName}
106
108
  Widgets={Widgets}
107
109
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/strict-boolean-expressions
108
110
  typeName={widget.CUSTOM_NAME || widget.TYPE_NAME}
@@ -1,3 +1,5 @@
1
+ import { GroupFieldType } from "@prismicio/types-internal/lib/customtypes/widgets";
2
+
1
3
  import { TabField } from "@/legacy/lib/models/common/CustomType";
2
4
 
3
5
  interface Widgets {
@@ -21,6 +23,12 @@ export const findWidgetByConfigOrType = (
21
23
  return widgets.Link;
22
24
  }
23
25
  }
26
+ // The Group & NestedGroup widgets share the "Group" type. They are never
27
+ // in the same widget group, so this will use whichever is provided. For example,
28
+ // in the custom list item for Group, we always pass NestedGroup as the Group widget.
29
+ if (type === GroupFieldType) {
30
+ return widgets.Group ?? widgets.NestedGroup;
31
+ }
24
32
 
25
33
  return widgets[type];
26
34
  };
@@ -1,32 +1,49 @@
1
- import { Group } from "@prismicio/types-internal/lib/customtypes/widgets";
1
+ import { NestableWidget } from "@prismicio/types-internal/lib/customtypes";
2
+ import {
3
+ Group,
4
+ GroupFieldType,
5
+ } from "@prismicio/types-internal/lib/customtypes/widgets";
2
6
  import { StringOrNull } from "@prismicio/types-internal/lib/validators";
3
7
  import { getOrElseW } from "fp-ts/lib/Either";
4
8
  import * as t from "io-ts";
5
9
 
6
10
  import { FieldsSM } from "./Fields";
7
11
 
8
- export const GroupConfig = t.exact(
9
- t.partial({
10
- label: StringOrNull,
11
- repeat: t.boolean,
12
- fields: FieldsSM,
13
- }),
14
- );
15
- export type GroupConfig = t.TypeOf<typeof GroupConfig>;
16
-
17
- export const GroupSM = t.exact(
18
- t.intersection([
19
- t.type({
20
- type: t.literal("Group"),
21
- }),
12
+ const createGroupConfig = <TFields extends t.Mixed>(fields: TFields) =>
13
+ t.exact(
22
14
  t.partial({
23
- fieldset: StringOrNull,
24
- icon: t.string,
25
- description: t.string,
26
- config: GroupConfig,
15
+ label: StringOrNull,
16
+ repeat: t.boolean,
17
+ fields,
27
18
  }),
28
- ]),
19
+ );
20
+
21
+ const createGroupSM = <TFields extends t.Mixed>(fields: TFields) =>
22
+ t.exact(
23
+ t.intersection([
24
+ t.type({
25
+ type: t.literal("Group"),
26
+ }),
27
+ t.partial({
28
+ fieldset: StringOrNull,
29
+ icon: t.string,
30
+ description: t.string,
31
+ config: createGroupConfig(fields),
32
+ }),
33
+ ]),
34
+ );
35
+
36
+ export const NestedGroupSM = createGroupSM(FieldsSM);
37
+ export type NestedGroupSM = t.TypeOf<typeof NestedGroupSM>;
38
+
39
+ const GroupFieldsSM = t.array(
40
+ t.type({
41
+ key: t.string,
42
+ value: t.union([NestableWidget, NestedGroupSM]),
43
+ }),
29
44
  );
45
+
46
+ export const GroupSM = createGroupSM(GroupFieldsSM);
30
47
  export type GroupSM = t.TypeOf<typeof GroupSM>;
31
48
 
32
49
  export const Groups = {
@@ -34,10 +51,13 @@ export const Groups = {
34
51
  const fields = (() => {
35
52
  if (!group.config?.fields) return;
36
53
 
37
- return group.config.fields.reduce(
38
- (acc, { key, value }) => ({ ...acc, [key]: value }),
39
- {},
40
- );
54
+ return group.config.fields.reduce((acc, { key, value }) => {
55
+ if (value.type === GroupFieldType) {
56
+ return { ...acc, [key]: Groups.fromSM(value) };
57
+ } else {
58
+ return { ...acc, [key]: value };
59
+ }
60
+ }, {});
41
61
  })();
42
62
 
43
63
  return getOrElseW(() => {
@@ -58,10 +78,13 @@ export const Groups = {
58
78
  if (!group.config?.fields) return;
59
79
 
60
80
  // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
61
- return Object.entries(group.config?.fields || {}).map(([key, value]) => ({
62
- key,
63
- value,
64
- }));
81
+ return Object.entries(group.config?.fields || {}).map(([key, value]) => {
82
+ if (value.type === GroupFieldType) {
83
+ return { key, value: Groups.toSM(value) };
84
+ } else {
85
+ return { key, value };
86
+ }
87
+ });
65
88
  })();
66
89
 
67
90
  return getOrElseW(() => {
@@ -1,3 +1,4 @@
1
+ import { GroupFieldType } from "@prismicio/types-internal/lib/customtypes/widgets";
1
2
  import { Fragment, useState } from "react";
2
3
  import { DragDropContext, Droppable } from "react-beautiful-dnd";
3
4
  import { flushSync } from "react-dom";
@@ -17,16 +18,17 @@ import Hint from "@/legacy/lib/builders/common/Zone/Card/components/Hints";
17
18
  import NewField from "@/legacy/lib/builders/common/Zone/Card/components/NewField";
18
19
  import { findWidgetByConfigOrType } from "@/legacy/lib/builders/utils";
19
20
  import { Groups } from "@/legacy/lib/models/common/Group";
20
- import * as Widgets from "@/legacy/lib/models/common/widgets";
21
- import groupBuilderWidgetsArray from "@/legacy/lib/models/common/widgets/groupBuilderArray";
22
21
  import { ensureDnDDestination } from "@/legacy/lib/utils";
23
22
  import { transformKeyAccessor } from "@/legacy/lib/utils/str";
24
23
  import { getContentTypeForTracking } from "@/utils/getContentTypeForTracking";
25
24
 
26
25
  /* eslint-disable */
27
- const CustomListItem = ({
26
+ export const CustomListItem = ({
28
27
  tabId,
29
28
  widget,
29
+ Widgets,
30
+ widgetsArray,
31
+ hintBase,
30
32
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
31
33
  parentSnapshot,
32
34
  showHints,
@@ -86,7 +88,7 @@ const CustomListItem = ({
86
88
  group: Groups.fromSM(groupItem.value),
87
89
  previousFieldId: previousKey,
88
90
  newFieldId: newKey,
89
- field: value,
91
+ field: value.type === GroupFieldType ? Groups.fromSM(value) : value,
90
92
  });
91
93
 
92
94
  saveItem({
@@ -215,6 +217,8 @@ const CustomListItem = ({
215
217
  key: item.key,
216
218
  enterEditMode,
217
219
  deleteItem: onDeleteItem,
220
+ saveItem: onSaveField,
221
+ showHints,
218
222
  renderFieldAccessor: (key) =>
219
223
  `item${transformKeyAccessor(item.key)}`,
220
224
  // eslint-disable-next-line @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/restrict-template-expressions
@@ -232,13 +236,25 @@ const CustomListItem = ({
232
236
  isRepeatable={isRepeatable}
233
237
  renderHintBase={({ item }) =>
234
238
  // eslint-disable-next-line @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-unsafe-member-access
235
- `item${transformKeyAccessor(item.key)}`
239
+ `${hintBase}${transformKeyAccessor(item.key)}`
236
240
  }
241
+ hintItemName={widget.hintItemName}
237
242
  Widgets={Widgets}
238
243
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
239
244
  typeName={widget.CUSTOM_NAME || widget.TYPE_NAME}
240
245
  />
241
246
  );
247
+
248
+ if (widget.CustomListItem) {
249
+ const { CustomListItem } = widget;
250
+ return (
251
+ <CustomListItem
252
+ {...props}
253
+ HintElement={HintElement}
254
+ />
255
+ );
256
+ }
257
+
242
258
  return (
243
259
  <ListItem {...props} HintElement={HintElement} />
244
260
  );
@@ -270,7 +286,7 @@ const CustomListItem = ({
270
286
  data={{ isOpen: selectMode }}
271
287
  close={() => setSelectMode(false)}
272
288
  onSelect={onSelectFieldType}
273
- widgetsArray={groupBuilderWidgetsArray}
289
+ widgetsArray={widgetsArray}
274
290
  />
275
291
  <EditModal
276
292
  data={editModalData}
@@ -282,6 +298,4 @@ const CustomListItem = ({
282
298
  </Fragment>
283
299
  );
284
300
  };
285
-
286
- export default CustomListItem;
287
301
  /* eslint-enable */
@@ -0,0 +1,83 @@
1
+ import { MdPlaylistAdd } from "react-icons/md";
2
+ import * as yup from "yup";
3
+
4
+ import { TabField } from "@/legacy/lib/models/common/CustomType";
5
+ import {
6
+ type GroupSM,
7
+ type NestedGroupSM,
8
+ } from "@/legacy/lib/models/common/Group";
9
+ import { type Widget } from "@/legacy/lib/models/common/widgets/Widget";
10
+
11
+ import { type GroupListItemProps } from ".";
12
+ import Form, { FormFields } from "./Form";
13
+
14
+ const Meta = {
15
+ icon: MdPlaylistAdd,
16
+ title: "Group",
17
+ description: "A repeatable set of fields",
18
+ };
19
+
20
+ const createSchema = (typeRegex: RegExp) =>
21
+ yup.object().shape({
22
+ type: yup
23
+ .string()
24
+ .matches(typeRegex, { excludeEmptyString: true })
25
+ .required(),
26
+ config: yup.object().shape({
27
+ fields: yup.array(),
28
+ label: yup.string(),
29
+ placeholder: yup.string(),
30
+ repeat: yup.boolean().optional(),
31
+ }),
32
+ });
33
+ export type SchemaType = ReturnType<typeof createSchema>;
34
+
35
+ type CreateGroupWidgetArgsBase<T extends TabField> = {
36
+ schemaTypeRegex: RegExp;
37
+ customListItem: (props: GroupListItemProps<T>) => JSX.Element;
38
+ hintItemName: string;
39
+ customName?: string;
40
+ };
41
+ type CreateTopGroupWidgetArgs = CreateGroupWidgetArgsBase<GroupSM>;
42
+ type CreateNestedGroupWidgetArgs = CreateGroupWidgetArgsBase<NestedGroupSM> & {
43
+ customName: "NestedGroup";
44
+ };
45
+ type CreateGroupWidgetArgs =
46
+ | CreateTopGroupWidgetArgs
47
+ | CreateNestedGroupWidgetArgs;
48
+
49
+ export function createGroupWidget(
50
+ args: CreateTopGroupWidgetArgs,
51
+ ): Widget<GroupSM, SchemaType>;
52
+
53
+ export function createGroupWidget(
54
+ args: CreateNestedGroupWidgetArgs,
55
+ ): Widget<NestedGroupSM, SchemaType>;
56
+
57
+ export function createGroupWidget({
58
+ schemaTypeRegex,
59
+ customListItem,
60
+ customName,
61
+ hintItemName,
62
+ }: CreateGroupWidgetArgs):
63
+ | Widget<GroupSM, SchemaType>
64
+ | Widget<NestedGroupSM, SchemaType> {
65
+ return {
66
+ Meta,
67
+ Form,
68
+ FormFields,
69
+ schema: createSchema(schemaTypeRegex),
70
+ create: (label: string) => ({
71
+ type: "Group",
72
+ config: {
73
+ label,
74
+ repeat: true,
75
+ fields: [],
76
+ },
77
+ }),
78
+ CustomListItem: customListItem,
79
+ TYPE_NAME: "Group",
80
+ CUSTOM_NAME: customName,
81
+ hintItemName,
82
+ };
83
+ }
@@ -0,0 +1,72 @@
1
+ import { type DroppableStateSnapshot } from "react-beautiful-dnd";
2
+
3
+ import { useNestedGroupExperiment } from "@/hooks/useNestedGroupExperiment";
4
+ import { type Item } from "@/legacy/components/ListItem";
5
+ import { type TabField } from "@/legacy/lib/models/common/CustomType";
6
+ import { type GroupSM } from "@/legacy/lib/models/common/Group";
7
+ import { Widgets } from "@/legacy/lib/models/common/widgets/groupWidgets";
8
+ import { type Widget } from "@/legacy/lib/models/common/widgets/Widget";
9
+
10
+ import { createGroupWidget, type SchemaType } from "./createGroupWidget";
11
+ import { CustomListItem } from "./ListItem";
12
+
13
+ const widgetsArray = [
14
+ Widgets.NestedGroup,
15
+ Widgets.StructuredText,
16
+ Widgets.Image,
17
+ Widgets.Link,
18
+ Widgets.LinkToMedia,
19
+ Widgets.ContentRelationship,
20
+ Widgets.Select,
21
+ Widgets.Boolean,
22
+ Widgets.Date,
23
+ Widgets.Timestamp,
24
+ Widgets.Embed,
25
+ Widgets.Number,
26
+ Widgets.GeoPoint,
27
+ Widgets.Color,
28
+ Widgets.Text,
29
+ ];
30
+
31
+ export interface GroupListItemProps<F extends TabField> {
32
+ tabId: string;
33
+ widget: Widget<F, SchemaType>;
34
+ parentSnapshot: DroppableStateSnapshot;
35
+ showHints: boolean;
36
+ isRepeatable: boolean;
37
+ item: Item<F>;
38
+ draggableId: string;
39
+ saveItem: ({
40
+ apiId,
41
+ newKey,
42
+ value,
43
+ }: {
44
+ apiId: string;
45
+ newKey: string;
46
+ value: F;
47
+ }) => void;
48
+ HintElement: JSX.Element;
49
+ }
50
+
51
+ const hintItemName = "item";
52
+
53
+ const GroupListItem = (props: GroupListItemProps<GroupSM>): JSX.Element => {
54
+ const nestedGroupExperiment = useNestedGroupExperiment();
55
+ const maybeFilteredWidgetsArray = nestedGroupExperiment.eligible
56
+ ? widgetsArray
57
+ : widgetsArray.filter((widget) => widget.CUSTOM_NAME !== "NestedGroup");
58
+ return (
59
+ <CustomListItem
60
+ Widgets={Widgets}
61
+ widgetsArray={maybeFilteredWidgetsArray}
62
+ hintBase={hintItemName}
63
+ {...props}
64
+ />
65
+ );
66
+ };
67
+
68
+ export const GroupWidget = createGroupWidget({
69
+ schemaTypeRegex: /^Group$/,
70
+ customListItem: GroupListItem,
71
+ hintItemName,
72
+ });
@@ -0,0 +1,40 @@
1
+ import { type NestedGroupSM } from "@/legacy/lib/models/common/Group";
2
+ import { type GroupListItemProps } from "@/legacy/lib/models/common/widgets/Group";
3
+ import { createGroupWidget } from "@/legacy/lib/models/common/widgets/Group/createGroupWidget";
4
+ import { CustomListItem } from "@/legacy/lib/models/common/widgets/Group/ListItem";
5
+ import { NonGroupWidgets as Widgets } from "@/legacy/lib/models/common/widgets/nonGroupWidgets";
6
+
7
+ const widgetsArray = [
8
+ Widgets.StructuredText,
9
+ Widgets.Image,
10
+ Widgets.Link,
11
+ Widgets.LinkToMedia,
12
+ Widgets.ContentRelationship,
13
+ Widgets.Select,
14
+ Widgets.Boolean,
15
+ Widgets.Date,
16
+ Widgets.Timestamp,
17
+ Widgets.Embed,
18
+ Widgets.Number,
19
+ Widgets.GeoPoint,
20
+ Widgets.Color,
21
+ Widgets.Text,
22
+ ];
23
+
24
+ const hintItemName = "subItem";
25
+
26
+ const NestedGroupListItem = (props: GroupListItemProps<NestedGroupSM>) => (
27
+ <CustomListItem
28
+ Widgets={Widgets}
29
+ widgetsArray={widgetsArray}
30
+ hintBase={hintItemName}
31
+ {...props}
32
+ />
33
+ );
34
+
35
+ export const NestedGroupWidget = createGroupWidget({
36
+ schemaTypeRegex: /^NestedGroup$/,
37
+ customListItem: NestedGroupListItem,
38
+ customName: "NestedGroup",
39
+ hintItemName,
40
+ });
@@ -3,8 +3,9 @@ import { IconType } from "react-icons";
3
3
  import { AnyObjectSchema } from "yup";
4
4
 
5
5
  import { TabField } from "@/legacy/lib/models/common/CustomType";
6
+ import { GroupSM, NestedGroupSM } from "@/legacy/lib/models/common/Group";
6
7
 
7
- export interface Widget<F extends TabField, S extends AnyObjectSchema> {
8
+ interface WidgetBase<F extends TabField, S extends AnyObjectSchema> {
8
9
  TYPE_NAME: FieldType;
9
10
  create: (label: string) => F;
10
11
  Meta: {
@@ -23,5 +24,13 @@ export interface Widget<F extends TabField, S extends AnyObjectSchema> {
23
24
  prepareInitialValues?: (props: F["config"]) => F["config"];
24
25
  }
25
26
 
27
+ export type Widget<F extends TabField, S extends AnyObjectSchema> = F extends
28
+ | GroupSM
29
+ | NestedGroupSM
30
+ ? WidgetBase<F, S> & {
31
+ hintItemName: string;
32
+ }
33
+ : WidgetBase<F, S>;
34
+
26
35
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-explicit-any, @typescript-eslint/no-explicit-any
27
36
  export type AnyWidget = Widget<any, any>;
@@ -0,0 +1,7 @@
1
+ import { NestedGroupWidget as NestedGroup } from "./NestedGroup";
2
+ import { NonGroupWidgets } from "./nonGroupWidgets";
3
+
4
+ export const Widgets = {
5
+ ...NonGroupWidgets,
6
+ NestedGroup,
7
+ };