slice-machine-ui 2.16.2-alpha.xru-allow-only-one-ct-in-cr.2 → 2.16.2-alpha.xru-tracking-cr.1

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 (25) hide show
  1. package/out/404.html +1 -1
  2. package/out/_next/static/chunks/630-b30898be5b2e7426.js +1 -0
  3. package/out/_next/static/chunks/{882-53d88aa0b7e3ffeb.js → 882-151468121d542ed6.js} +1 -1
  4. package/out/_next/static/chunks/pages/{_app-a203a07847d5ad74.js → _app-34613004ab3b5a37.js} +1 -1
  5. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/{[variation]-94c9cad9da2c3089.js → [variation]-bd5e45632c419567.js} +1 -1
  6. package/out/_next/static/{bgu0uRm1HsJC8lMB5rDdp → u8olyg-rloZ5jxoy3aeI5}/_buildManifest.js +1 -1
  7. package/out/changelog.html +1 -1
  8. package/out/changes.html +1 -1
  9. package/out/custom-types/[customTypeId].html +1 -1
  10. package/out/custom-types.html +1 -1
  11. package/out/index.html +1 -1
  12. package/out/labs.html +1 -1
  13. package/out/page-types/[pageTypeId].html +1 -1
  14. package/out/settings.html +1 -1
  15. package/out/slices/[lib]/[sliceName]/[variation]/simulator.html +1 -1
  16. package/out/slices/[lib]/[sliceName]/[variation].html +1 -1
  17. package/out/slices.html +1 -1
  18. package/package.json +3 -3
  19. package/src/features/builder/fields/ContentRelationshipFieldPicker.tsx +87 -43
  20. package/src/features/builder/fields/__tests__/ContentRelationshipFieldPicker.test.ts +265 -0
  21. package/src/utils/tracking/getLinkTrackingProperties.ts +26 -0
  22. package/src/utils/tracking/trackFieldAdded.ts +2 -5
  23. package/src/utils/tracking/trackFieldUpdated.ts +2 -5
  24. package/out/_next/static/chunks/630-0072f05ace60ea05.js +0 -1
  25. /package/out/_next/static/{bgu0uRm1HsJC8lMB5rDdp → u8olyg-rloZ5jxoy3aeI5}/_ssgManifest.js +0 -0
@@ -328,31 +328,32 @@ function ContentRelationshipFieldPickerContent(
328
328
  subtitle={
329
329
  <>
330
330
  <Text color="inherit" variant="bold">
331
- Legacy mode. Keep one custom type to enable the
332
- improved Content Relationship feature.
331
+ Legacy mode. Keep only one type to enable the improved
332
+ Content Relationship feature.
333
333
  </Text>
334
334
  <br />
335
- <Box alignItems="center" gap={4}>
336
- <a
337
- href="https://prismic.io/docs/fields/content-relationship"
338
- target="_blank"
339
- rel="noopener noreferrer"
340
- style={{
341
- color: "inherit",
342
- textDecoration: "none",
343
- fontWeight: "bold",
344
- }}
345
- >
346
- <Text color="inherit" variant="bold">
347
- See documentation
348
- </Text>
349
- </a>
335
+ <a
336
+ href="https://prismic.io/docs/fields/content-relationship"
337
+ target="_blank"
338
+ rel="noopener noreferrer"
339
+ style={{
340
+ color: "inherit",
341
+ textDecoration: "none",
342
+ fontWeight: "bold",
343
+ display: "flex",
344
+ alignItems: "center",
345
+ gap: 4,
346
+ }}
347
+ >
348
+ <Text color="inherit" variant="bold">
349
+ See documentation
350
+ </Text>
350
351
  <Icon
351
352
  name="arrowForward"
352
353
  size="small"
353
354
  color="inherit"
354
355
  />
355
- </Box>
356
+ </a>
356
357
  </>
357
358
  }
358
359
  />
@@ -437,7 +438,7 @@ function EmptyView(props: EmptyViewProps) {
437
438
  No type selected
438
439
  </Text>
439
440
  <Text color="grey11" component="p" align="center">
440
- Select a type editors can link to.
441
+ Select the type editors can link to.
441
442
  <br />
442
443
  Then, choose which fields to return in the API.
443
444
  </Text>
@@ -532,7 +533,7 @@ function TreeViewCustomType(props: TreeViewCustomTypeProps) {
532
533
  ({ fieldId, field }) => {
533
534
  // Group field
534
535
 
535
- if (isGroupField(field)) {
536
+ if (field.type === "Group") {
536
537
  const onGroupFieldChange = (
537
538
  newGroupFields: PickerFirstLevelGroupFieldValue,
538
539
  ) => {
@@ -619,8 +620,8 @@ function TreeViewCustomType(props: TreeViewCustomTypeProps) {
619
620
  key={customType.id}
620
621
  title={customType.id}
621
622
  subtitle={
622
- exposedFieldsCount > 0
623
- ? getExposedFieldsLabel(exposedFieldsCount)
623
+ exposedFieldsCount.pickedFields > 0
624
+ ? getExposedFieldsLabel(exposedFieldsCount.pickedFields)
624
625
  : "(No fields returned in the API)"
625
626
  }
626
627
  badge={getTypeFormatLabel(customType.format)}
@@ -666,7 +667,9 @@ function TreeViewContentRelationshipField(
666
667
  return (
667
668
  <TreeViewSection
668
669
  title={fieldId}
669
- subtitle={getExposedFieldsLabel(countPickedFields(crFieldsCheckMap))}
670
+ subtitle={getExposedFieldsLabel(
671
+ countPickedFields(crFieldsCheckMap).pickedFields,
672
+ )}
670
673
  >
671
674
  {resolvedCustomTypes.map((customType) => {
672
675
  if (typeof customType === "string") return null;
@@ -686,7 +689,7 @@ function TreeViewContentRelationshipField(
686
689
  ({ fieldId, field }) => {
687
690
  // Group field
688
691
 
689
- if (isGroupField(field)) {
692
+ if (field.type === "Group") {
690
693
  const onGroupFieldsChange = (
691
694
  newGroupFields: PickerLeafGroupFieldValue,
692
695
  ) => {
@@ -740,7 +743,7 @@ function TreeViewContentRelationshipField(
740
743
  key={customType.id}
741
744
  title={customType.id}
742
745
  subtitle={getExposedFieldsLabel(
743
- countPickedFields(nestedCtFieldsCheckMap),
746
+ countPickedFields(nestedCtFieldsCheckMap).pickedFields,
744
747
  )}
745
748
  badge={getTypeFormatLabel(customType.format)}
746
749
  >
@@ -793,7 +796,9 @@ function TreeViewLeafGroupField(props: TreeViewLeafGroupFieldProps) {
793
796
  <TreeViewSection
794
797
  key={groupId}
795
798
  title={groupId}
796
- subtitle={getExposedFieldsLabel(countPickedFields(groupFieldsCheckMap))}
799
+ subtitle={getExposedFieldsLabel(
800
+ countPickedFields(groupFieldsCheckMap).pickedFields,
801
+ )}
797
802
  badge="Group"
798
803
  >
799
804
  {renderedFields}
@@ -826,7 +831,9 @@ function TreeViewFirstLevelGroupField(
826
831
  <TreeViewSection
827
832
  key={groupId}
828
833
  title={groupId}
829
- subtitle={getExposedFieldsLabel(countPickedFields(groupFieldsCheckMap))}
834
+ subtitle={getExposedFieldsLabel(
835
+ countPickedFields(groupFieldsCheckMap).pickedFields,
836
+ )}
830
837
  badge="Group"
831
838
  >
832
839
  {getGroupFields(group).map(({ fieldId, field }) => {
@@ -940,7 +947,7 @@ function resolveContentRelationshipCustomTypes(
940
947
  * Converts a Link config `customtypes` ({@link LinkCustomtypes}) structure into
941
948
  * picker fields check map ({@link PickerCustomTypes}).
942
949
  */
943
- function convertLinkCustomtypesToFieldCheckMap(
950
+ export function convertLinkCustomtypesToFieldCheckMap(
944
951
  customTypes: LinkCustomtypes,
945
952
  ): PickerCustomTypes {
946
953
  return customTypes.reduce<PickerCustomTypes>((customTypes, customType) => {
@@ -1166,26 +1173,63 @@ function createContentRelationshipLinkCustomtypes(
1166
1173
  * Generic recursive function that goes down the fields check map and counts all
1167
1174
  * the properties that are set to true, which correspond to selected fields.
1168
1175
  *
1176
+ * Distinguishes between all picked fields and nested picked fields within a
1177
+ * content relationship field.
1178
+ *
1169
1179
  * It's not type safe, but checks the type of the values at runtime so that
1170
1180
  * it only recurses into valid objects, and only counts checkbox fields.
1171
1181
  */
1172
- function countPickedFields(
1182
+ export function countPickedFields(
1173
1183
  fields: Record<string, unknown> | undefined,
1174
- ): number {
1175
- if (!fields) return 0;
1176
- return Object.values(fields).reduce<number>((count, value) => {
1177
- if (!isValidObject(value)) return count;
1178
- if (isCheckboxValue(value)) return count + (value.value ? 1 : 0);
1179
- return count + countPickedFields(value);
1180
- }, 0);
1181
- }
1182
- function isCheckboxValue(value: unknown): value is PickerCheckboxField {
1183
- if (!isValidObject(value)) return false;
1184
- return "type" in value && value.type === "checkbox";
1185
- }
1184
+ isNested = false,
1185
+ ): {
1186
+ pickedFields: number;
1187
+ nestedPickedFields: number;
1188
+ } {
1189
+ if (!fields) return { pickedFields: 0, nestedPickedFields: 0 };
1190
+
1191
+ return Object.values(fields).reduce<{
1192
+ pickedFields: number;
1193
+ nestedPickedFields: number;
1194
+ }>(
1195
+ (accumulator, value) => {
1196
+ if (!isValidObject(value)) return accumulator;
1197
+
1198
+ if ("type" in value && value.type === "checkbox") {
1199
+ const isChecked = Boolean(value.value);
1200
+ if (!isChecked) return accumulator;
1201
+
1202
+ return {
1203
+ pickedFields: accumulator.pickedFields + 1,
1204
+ nestedPickedFields:
1205
+ accumulator.nestedPickedFields + (isNested ? 1 : 0),
1206
+ };
1207
+ }
1186
1208
 
1187
- function isGroupField(field: NestableWidget | Group): field is Group {
1188
- return field.type === "Group";
1209
+ if ("type" in value && value.type === "contentRelationship") {
1210
+ const { pickedFields } = countPickedFields(value, isNested);
1211
+ const { nestedPickedFields } = countPickedFields(value, true);
1212
+
1213
+ return {
1214
+ pickedFields: accumulator.pickedFields + pickedFields,
1215
+ nestedPickedFields:
1216
+ accumulator.nestedPickedFields + nestedPickedFields,
1217
+ };
1218
+ }
1219
+
1220
+ const { pickedFields } = countPickedFields(value, isNested);
1221
+ const { nestedPickedFields } = countPickedFields(value, isNested);
1222
+
1223
+ return {
1224
+ pickedFields: accumulator.pickedFields + pickedFields,
1225
+ nestedPickedFields: accumulator.nestedPickedFields + nestedPickedFields,
1226
+ };
1227
+ },
1228
+ {
1229
+ pickedFields: 0,
1230
+ nestedPickedFields: 0,
1231
+ },
1232
+ );
1189
1233
  }
1190
1234
 
1191
1235
  function isContentRelationshipField(
@@ -0,0 +1,265 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import {
4
+ convertLinkCustomtypesToFieldCheckMap,
5
+ countPickedFields,
6
+ } from "../ContentRelationshipFieldPicker";
7
+
8
+ describe("ContentRelationshipFieldPicker", () => {
9
+ it("should count picked fields with a custom type as string", () => {
10
+ const customtypes = ["ct1"];
11
+
12
+ const result = countPickedFields(
13
+ convertLinkCustomtypesToFieldCheckMap(customtypes),
14
+ );
15
+
16
+ expect(result).toEqual({
17
+ pickedFields: 0,
18
+ nestedPickedFields: 0,
19
+ });
20
+ });
21
+
22
+ it("should count picked fields with multiple custom types as string", () => {
23
+ const customtypes = ["ct1", "ct2"];
24
+
25
+ const result = countPickedFields(
26
+ convertLinkCustomtypesToFieldCheckMap(customtypes),
27
+ );
28
+
29
+ expect(result).toEqual({
30
+ pickedFields: 0,
31
+ nestedPickedFields: 0,
32
+ });
33
+ });
34
+
35
+ it("should count picked fields with custom type as object and one field", () => {
36
+ const customtypes = [
37
+ {
38
+ id: "ct1",
39
+ fields: ["f1"],
40
+ },
41
+ ];
42
+
43
+ const result = countPickedFields(
44
+ convertLinkCustomtypesToFieldCheckMap(customtypes),
45
+ );
46
+
47
+ expect(result).toEqual({
48
+ pickedFields: 1,
49
+ nestedPickedFields: 0,
50
+ });
51
+ });
52
+
53
+ it("should count picked fields with custom type as object and one nested field", () => {
54
+ const customtypes = [
55
+ {
56
+ id: "ct1",
57
+ fields: [
58
+ {
59
+ id: "f1",
60
+ customtypes: [
61
+ {
62
+ id: "ct2",
63
+ fields: ["f1"],
64
+ },
65
+ ],
66
+ },
67
+ ],
68
+ },
69
+ ];
70
+
71
+ const result = countPickedFields(
72
+ convertLinkCustomtypesToFieldCheckMap(customtypes),
73
+ );
74
+
75
+ expect(result).toEqual({
76
+ pickedFields: 1,
77
+ nestedPickedFields: 1,
78
+ });
79
+ });
80
+
81
+ it("should count picked fields with custom type as object with group field", () => {
82
+ const customtypes = [
83
+ {
84
+ id: "ct1",
85
+ fields: [
86
+ {
87
+ id: "g1",
88
+ fields: ["f1", "f2"],
89
+ },
90
+ ],
91
+ },
92
+ ];
93
+
94
+ const result = countPickedFields(
95
+ convertLinkCustomtypesToFieldCheckMap(customtypes),
96
+ );
97
+
98
+ expect(result).toEqual({
99
+ pickedFields: 2,
100
+ nestedPickedFields: 0,
101
+ });
102
+ });
103
+
104
+ it("should count picked fields with custom type as object with group field and nested custom type", () => {
105
+ const customtypes = [
106
+ {
107
+ id: "ct1",
108
+ fields: [
109
+ {
110
+ id: "g1",
111
+ fields: [
112
+ "f1",
113
+ {
114
+ id: "f2",
115
+ customtypes: [
116
+ {
117
+ id: "ct2",
118
+ fields: ["f1"],
119
+ },
120
+ ],
121
+ },
122
+ ],
123
+ },
124
+ ],
125
+ },
126
+ ];
127
+
128
+ const result = countPickedFields(
129
+ convertLinkCustomtypesToFieldCheckMap(customtypes),
130
+ );
131
+
132
+ expect(result).toEqual({
133
+ pickedFields: 2,
134
+ nestedPickedFields: 1,
135
+ });
136
+ });
137
+
138
+ it("should count picked fields with custom type as object with group field, nested custom type and nested group field", () => {
139
+ const customtypes = [
140
+ {
141
+ id: "ct1",
142
+ fields: [
143
+ {
144
+ id: "g1",
145
+ fields: [
146
+ "f1",
147
+ {
148
+ id: "f2",
149
+ customtypes: [
150
+ {
151
+ id: "ct2",
152
+ fields: [
153
+ "f1",
154
+ {
155
+ id: "g2",
156
+ fields: ["f1", "f2"],
157
+ },
158
+ ],
159
+ },
160
+ ],
161
+ },
162
+ ],
163
+ },
164
+ ],
165
+ },
166
+ ];
167
+
168
+ const result = countPickedFields(
169
+ convertLinkCustomtypesToFieldCheckMap(customtypes),
170
+ );
171
+
172
+ expect(result).toEqual({
173
+ pickedFields: 4,
174
+ nestedPickedFields: 3,
175
+ });
176
+ });
177
+
178
+ it("should count picked fields with custom type as object with nested custom type and nested group field", () => {
179
+ const customtypes = [
180
+ {
181
+ id: "ct1",
182
+ fields: [
183
+ "f1",
184
+ {
185
+ id: "f2",
186
+ customtypes: [
187
+ {
188
+ id: "ct2",
189
+ fields: [
190
+ "f1",
191
+ {
192
+ id: "g2",
193
+ fields: ["f1", "f2"],
194
+ },
195
+ ],
196
+ },
197
+ ],
198
+ },
199
+ ],
200
+ },
201
+ ];
202
+
203
+ const result = countPickedFields(
204
+ convertLinkCustomtypesToFieldCheckMap(customtypes),
205
+ );
206
+
207
+ expect(result).toEqual({
208
+ pickedFields: 4,
209
+ nestedPickedFields: 3,
210
+ });
211
+ });
212
+
213
+ it("should count picked fields with custom type as object with nested custom type without fields", () => {
214
+ const customtypes = [
215
+ {
216
+ id: "ct1",
217
+ fields: [
218
+ "f1",
219
+ {
220
+ id: "f2",
221
+ customtypes: [
222
+ {
223
+ id: "ct2",
224
+ fields: [],
225
+ },
226
+ ],
227
+ },
228
+ ],
229
+ },
230
+ ];
231
+
232
+ const result = countPickedFields(
233
+ convertLinkCustomtypesToFieldCheckMap(customtypes),
234
+ );
235
+
236
+ expect(result).toEqual({
237
+ pickedFields: 1,
238
+ nestedPickedFields: 0,
239
+ });
240
+ });
241
+
242
+ it("should count picked fields with custom type as object with group field without fields", () => {
243
+ const customtypes = [
244
+ {
245
+ id: "ct1",
246
+ fields: [
247
+ "f1",
248
+ {
249
+ id: "g1",
250
+ fields: [],
251
+ },
252
+ ],
253
+ },
254
+ ];
255
+
256
+ const result = countPickedFields(
257
+ convertLinkCustomtypesToFieldCheckMap(customtypes),
258
+ );
259
+
260
+ expect(result).toEqual({
261
+ pickedFields: 1,
262
+ nestedPickedFields: 0,
263
+ });
264
+ });
265
+ });
@@ -0,0 +1,26 @@
1
+ import { Link } from "@prismicio/types-internal/lib/customtypes/widgets";
2
+
3
+ import {
4
+ convertLinkCustomtypesToFieldCheckMap,
5
+ countPickedFields,
6
+ } from "@/features/builder/fields/ContentRelationshipFieldPicker";
7
+
8
+ export function getLinkTrackingProperties(field: Link) {
9
+ return {
10
+ allowText: field.config?.allowText,
11
+ repeat: field.config?.repeat,
12
+ variants: field.config?.variants,
13
+ linkSelect: field.config?.select,
14
+ ...(field.config?.select === "document" &&
15
+ field.config?.customtypes &&
16
+ (() => {
17
+ const { pickedFields, nestedPickedFields } = countPickedFields(
18
+ convertLinkCustomtypesToFieldCheckMap(field.config?.customtypes),
19
+ );
20
+ return {
21
+ linkPickedFields: pickedFields,
22
+ linkNestedPickedFields: nestedPickedFields,
23
+ };
24
+ })()),
25
+ };
26
+ }
@@ -8,6 +8,7 @@ import { telemetry } from "@/apiClient";
8
8
  import { SlicePrimaryFieldSM } from "@/legacy/lib/models/common/Slice";
9
9
 
10
10
  import { getContentTypeForTracking } from "./getContentTypeForTracking";
11
+ import { getLinkTrackingProperties } from "./getLinkTrackingProperties";
11
12
 
12
13
  type TrackFieldAddedArgs = {
13
14
  id: string;
@@ -25,10 +26,6 @@ export function trackFieldAdded(args: TrackFieldAddedArgs) {
25
26
  type: field.type,
26
27
  isInAGroup,
27
28
  contentType: getContentTypeForTracking(window.location.pathname),
28
- ...(field.type === "Link" && {
29
- allowText: field.config?.allowText,
30
- repeat: field.config?.repeat,
31
- variants: field.config?.variants,
32
- }),
29
+ ...(field.type === "Link" && getLinkTrackingProperties(field)),
33
30
  });
34
31
  }
@@ -8,6 +8,7 @@ import { telemetry } from "@/apiClient";
8
8
  import { SlicePrimaryFieldSM } from "@/legacy/lib/models/common/Slice";
9
9
 
10
10
  import { getContentTypeForTracking } from "./getContentTypeForTracking";
11
+ import { getLinkTrackingProperties } from "./getLinkTrackingProperties";
11
12
 
12
13
  type TrackFieldUpdatedArgs = {
13
14
  id: string;
@@ -28,10 +29,6 @@ export function trackFieldUpdated(args: TrackFieldUpdatedArgs) {
28
29
  type: field.type,
29
30
  isInAGroup,
30
31
  contentType: getContentTypeForTracking(window.location.pathname),
31
- ...(field.type === "Link" && {
32
- allowText: field.config?.allowText,
33
- repeat: field.config?.repeat,
34
- variants: field.config?.variants,
35
- }),
32
+ ...(field.type === "Link" && getLinkTrackingProperties(field)),
36
33
  });
37
34
  }