Skip to content

Commit c7c811b

Browse files
Merge remote-tracking branch 'origin/feature/VB-499-resolved-variant-perms' into stage_v4
2 parents dfa1c7c + 0dc07d6 commit c7c811b

21 files changed

Lines changed: 462 additions & 25 deletions

src/visualBuilder/__test__/click/fields/multi-line.test.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ describe("When an element is clicked in visual builder mode", () => {
115115
},
116116
},
117117
});
118+
case VisualBuilderPostMessageEvents.GET_RESOLVED_VARIANT_PERMISSIONS:
119+
return Promise.resolve({
120+
update: true,
121+
});
118122
default:
119123
return Promise.resolve({});
120124
}
@@ -216,6 +220,10 @@ describe("When an element is clicked in visual builder mode", () => {
216220
},
217221
});
218222
}
223+
case VisualBuilderPostMessageEvents.GET_RESOLVED_VARIANT_PERMISSIONS:
224+
return Promise.resolve({
225+
update: true,
226+
});
219227
default:
220228
return Promise.resolve({});
221229
}

src/visualBuilder/__test__/click/fields/number.test.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,11 @@ describe("When an element is clicked in visual builder mode", () => {
223223
},
224224
});
225225
}
226+
else if (eventName === VisualBuilderPostMessageEvents.GET_RESOLVED_VARIANT_PERMISSIONS) {
227+
return Promise.resolve({
228+
update: true,
229+
});
230+
}
226231
return Promise.resolve({});
227232
}
228233
);

src/visualBuilder/__test__/click/fields/single-line.test.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ describe("When an element is clicked in visual builder mode", () => {
119119
},
120120
},
121121
});
122+
case VisualBuilderPostMessageEvents.GET_RESOLVED_VARIANT_PERMISSIONS:
123+
return Promise.resolve({
124+
update: true,
125+
});
122126
default:
123127
return Promise.resolve({});
124128
}
@@ -234,6 +238,10 @@ describe("When an element is clicked in visual builder mode", () => {
234238
},
235239
},
236240
});
241+
case VisualBuilderPostMessageEvents.GET_RESOLVED_VARIANT_PERMISSIONS:
242+
return Promise.resolve({
243+
update: true,
244+
});
237245
default:
238246
return Promise.resolve({});
239247
}

src/visualBuilder/components/FieldToolbar.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import { EntryPermissions } from "../utils/getEntryPermissions";
4343
import { FieldLocationAppList } from "./FieldLocationAppList";
4444
import { FieldLocationIcon } from "./FieldLocationIcon";
4545
import { WorkflowStageDetails } from "../utils/getWorkflowStageDetails";
46+
import { ResolvedVariantPermissions } from "../utils/getResolvedVariantPermissions";
4647

4748
export type FieldDetails = Pick<
4849
VisualBuilderCslpEventDetails,
@@ -57,6 +58,7 @@ interface MultipleFieldToolbarProps {
5758
isVariant?: boolean;
5859
entryPermissions?: EntryPermissions | undefined;
5960
entryWorkflowStageDetails?: WorkflowStageDetails | undefined;
61+
resolvedVariantPermissions?: ResolvedVariantPermissions | undefined;
6062
}
6163

6264
function handleReplaceAsset(fieldMetadata: CslpData) {
@@ -117,6 +119,7 @@ function FieldToolbarComponent(
117119
isVariant: isVariantOrParentOfVariant,
118120
entryPermissions,
119121
entryWorkflowStageDetails,
122+
resolvedVariantPermissions,
120123
} = props;
121124
const { fieldMetadata, editableElement: targetElement } = eventDetails;
122125
const [isFormLoading, setIsFormLoading] = useState(false);
@@ -157,8 +160,9 @@ function FieldToolbarComponent(
157160
editableElement: targetElement,
158161
fieldMetadata,
159162
},
163+
resolvedVariantPermissions,
160164
entryPermissions,
161-
entryWorkflowStageDetails
165+
entryWorkflowStageDetails,
162166
);
163167
disableFieldActions = isDisabled;
164168

src/visualBuilder/components/__test__/fieldLabelWrapper.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ describe.skip("FieldLabelWrapperComponent", () => {
303303
expect(isFieldDisabled).toHaveBeenCalledWith(
304304
mockFieldSchema,
305305
mockEventDetails,
306+
undefined,
306307
{
307308
update: {
308309
create: true,

src/visualBuilder/components/fieldLabelWrapper.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,18 +157,20 @@ function FieldLabelWrapperComponent(
157157
return;
158158
}
159159

160-
const { acl: entryAcl, workflowStage: entryWorkflowStageDetails } =
160+
const { acl: entryAcl, workflowStage: entryWorkflowStageDetails, resolvedVariantPermissions } =
161161
await fetchEntryPermissionsAndStageDetails({
162162
entryUid: props.fieldMetadata.entry_uid,
163163
contentTypeUid: props.fieldMetadata.content_type_uid,
164164
locale: props.fieldMetadata.locale,
165165
variantUid: props.fieldMetadata.variant,
166+
fieldPathWithIndex: props.fieldMetadata.fieldPathWithIndex,
166167
});
167168
const { isDisabled: fieldDisabled, reason } = isFieldDisabled(
168169
fieldSchema,
169170
eventDetails,
171+
resolvedVariantPermissions,
170172
entryAcl,
171-
entryWorkflowStageDetails
173+
entryWorkflowStageDetails,
172174
);
173175

174176
const currentFieldDisplayName =

src/visualBuilder/eventManager/__test__/useVariantsPostMessageEvent.spec.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { FieldSchemaMap } from "../../../visualBuilder/utils/fieldSchemaMap";
2121
import { visualBuilderStyles } from "../../../visualBuilder/visualBuilder.style";
2222
import visualBuilderPostMessage from "../../../visualBuilder/utils/visualBuilderPostMessage";
2323
import { EventManager } from "@contentstack/advanced-post-message";
24+
import * as cslpdata from "../../../cslp/cslpdata";
2425

2526
const mockVisualBuilderPostMessage =
2627
visualBuilderPostMessage as MockedObject<EventManager>;
@@ -101,7 +102,7 @@ const mockQuerySelectorAll = vi.fn().mockImplementation((selector) => {
101102
return mockElements; // For onlyHighlighted=true case
102103
} else if (
103104
selector ===
104-
".visual-builder__disabled-variant-field, .visual-builder__variant-field, .visual-builder__base-field"
105+
".visual-builder__disabled-variant-field, .visual-builder__variant-field, .visual-builder__base-field, .visual-builder__lower-order-variant-field"
105106
) {
106107
return mockElements; // For onlyHighlighted=false case
107108
}
@@ -303,7 +304,7 @@ describe("useVariantFieldsPostMessageEvent", () => {
303304

304305
// Verify querySelectorAll was called with the correct selector
305306
expect(mockQuerySelectorAll).toHaveBeenCalledWith(
306-
".visual-builder__disabled-variant-field, .visual-builder__variant-field, .visual-builder__base-field"
307+
".visual-builder__disabled-variant-field, .visual-builder__variant-field, .visual-builder__base-field, .visual-builder__lower-order-variant-field"
307308
);
308309

309310
// Verify that classes were removed from elements correctly
@@ -312,7 +313,8 @@ describe("useVariantFieldsPostMessageEvent", () => {
312313
"visual-builder__disabled-variant-field",
313314
"visual-builder__variant-field",
314315
visualBuilderStyles()["visual-builder__variant-field"],
315-
"visual-builder__base-field"
316+
"visual-builder__base-field",
317+
"visual-builder__lower-order-variant-field"
316318
);
317319
});
318320
});
@@ -339,7 +341,7 @@ describe("addVariantFieldClass", () => {
339341
const variantUid = "variant-123";
340342
const highlightVariantFields = true;
341343

342-
addVariantFieldClass(variantUid, highlightVariantFields);
344+
addVariantFieldClass(variantUid, highlightVariantFields, []);
343345

344346
// Verify querySelectorAll was called with the correct selector
345347
expect(mockQuerySelectorAll).toHaveBeenCalledWith("[data-cslp]");
@@ -370,7 +372,7 @@ describe("addVariantFieldClass", () => {
370372
const variantUid = "variant-123";
371373
const highlightVariantFields = false;
372374

373-
addVariantFieldClass(variantUid, highlightVariantFields);
375+
addVariantFieldClass(variantUid, highlightVariantFields, []);
374376

375377
// First element has the variant ID but should not get highlight class
376378
expect(mockElements[0].getAttribute).toHaveBeenCalledWith("data-cslp");
@@ -381,6 +383,23 @@ describe("addVariantFieldClass", () => {
381383
"visual-builder__variant-field"
382384
);
383385
});
386+
387+
it("should handle lower order variant fields correctly", () => {
388+
// @ts-expect-error mocking only required properties
389+
vi.spyOn(cslpdata, "extractDetailsFromCslp").mockImplementation((cslpValue) => {
390+
return {
391+
variant: cslpValue.split(":")[1]
392+
}
393+
});
394+
const variantUid = "variant-456";
395+
const highlightVariantFields = false;
396+
const variantOrder = ["variant-123", "variant-456"];
397+
398+
addVariantFieldClass(variantUid, highlightVariantFields, variantOrder);
399+
400+
// Verify that classes were added to elements correctly
401+
expect(mockElements[0].classList.add).toHaveBeenCalledWith("visual-builder__variant-field", "visual-builder__lower-order-variant-field");
402+
});
384403
});
385404

386405
describe("removeVariantFieldClass", () => {
@@ -421,7 +440,7 @@ describe("removeVariantFieldClass", () => {
421440

422441
// Verify querySelectorAll was called with the correct selector
423442
expect(mockQuerySelectorAll).toHaveBeenCalledWith(
424-
".visual-builder__disabled-variant-field, .visual-builder__variant-field, .visual-builder__base-field"
443+
".visual-builder__disabled-variant-field, .visual-builder__variant-field, .visual-builder__base-field, .visual-builder__lower-order-variant-field"
425444
);
426445

427446
// Verify classes were removed
@@ -430,7 +449,8 @@ describe("removeVariantFieldClass", () => {
430449
"visual-builder__disabled-variant-field",
431450
"visual-builder__variant-field",
432451
visualBuilderStyles()["visual-builder__variant-field"],
433-
"visual-builder__base-field"
452+
"visual-builder__base-field",
453+
"visual-builder__lower-order-variant-field"
434454
);
435455
});
436456
});
@@ -440,7 +460,7 @@ describe("removeVariantFieldClass", () => {
440460

441461
// Verify querySelectorAll was called with the correct selector
442462
expect(mockQuerySelectorAll).toHaveBeenCalledWith(
443-
".visual-builder__disabled-variant-field, .visual-builder__variant-field, .visual-builder__base-field"
463+
".visual-builder__disabled-variant-field, .visual-builder__variant-field, .visual-builder__base-field, .visual-builder__lower-order-variant-field"
444464
);
445465
});
446466
});

src/visualBuilder/eventManager/useVariantsPostMessageEvent.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import { visualBuilderStyles } from "../visualBuilder.style";
33
import visualBuilderPostMessage from "../utils/visualBuilderPostMessage";
44
import { VisualBuilderPostMessageEvents } from "../utils/types/postMessage.types";
55
import { FieldSchemaMap } from "../utils/fieldSchemaMap";
6+
import { extractDetailsFromCslp } from "../../cslp/cslpdata";
67

78
interface VariantFieldsEvent {
89
data: {
910
variant_data: {
1011
variant: string;
1112
highlightVariantFields: boolean;
13+
variantOrder: string[];
1214
};
1315
};
1416
}
@@ -34,9 +36,24 @@ interface LocaleEvent {
3436
locale: string;
3537
};
3638
}
39+
40+
function isLowerOrderVariant(variant_uid: string, dataCslp: string, variantOrder: string[]): boolean {
41+
if(!variantOrder || variantOrder.length === 0) {
42+
return false;
43+
}
44+
const {variant: cslpVariant} = extractDetailsFromCslp(dataCslp);
45+
const indexOfCmsVariant = variantOrder.lastIndexOf(variant_uid);
46+
const indexOfCslpVariant = variantOrder.lastIndexOf(cslpVariant || "");
47+
if(indexOfCslpVariant < 0) {
48+
return false;
49+
}
50+
return indexOfCslpVariant < indexOfCmsVariant;
51+
}
52+
3753
export function addVariantFieldClass(
3854
variant_uid: string,
39-
highlightVariantFields: boolean
55+
highlightVariantFields: boolean,
56+
variantOrder: string[]
4057
): void {
4158
const elements = document.querySelectorAll(`[data-cslp]`);
4259
elements.forEach((element) => {
@@ -51,7 +68,11 @@ export function addVariantFieldClass(
5168
element.classList.add("visual-builder__variant-field");
5269
} else if (!dataCslp.startsWith("v2:")) {
5370
element.classList.add("visual-builder__base-field");
54-
} else {
71+
}
72+
else if (isLowerOrderVariant(variant_uid, dataCslp, variantOrder)) {
73+
element.classList.add("visual-builder__variant-field", "visual-builder__lower-order-variant-field");
74+
}
75+
else {
5576
element.classList.add("visual-builder__disabled-variant-field");
5677
}
5778
});
@@ -71,14 +92,15 @@ export function removeVariantFieldClass(
7192
});
7293
} else {
7394
const variantAndBaseFieldElements = document.querySelectorAll(
74-
".visual-builder__disabled-variant-field, .visual-builder__variant-field, .visual-builder__base-field"
95+
".visual-builder__disabled-variant-field, .visual-builder__variant-field, .visual-builder__base-field, .visual-builder__lower-order-variant-field"
7596
);
7697
variantAndBaseFieldElements.forEach((element) => {
7798
element.classList.remove(
7899
"visual-builder__disabled-variant-field",
79100
"visual-builder__variant-field",
80101
visualBuilderStyles()["visual-builder__variant-field"],
81-
"visual-builder__base-field"
102+
"visual-builder__base-field",
103+
"visual-builder__lower-order-variant-field"
82104
);
83105
});
84106
}
@@ -125,7 +147,8 @@ export function useVariantFieldsPostMessageEvent(): void {
125147
removeVariantFieldClass();
126148
addVariantFieldClass(
127149
event.data.variant_data.variant,
128-
event.data.variant_data.highlightVariantFields
150+
event.data.variant_data.highlightVariantFields,
151+
event.data.variant_data.variantOrder
129152
);
130153
}
131154
);

src/visualBuilder/generators/generateToolbar.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,13 @@ export async function appendFieldToolbar(
5151
) && !isHover
5252
)
5353
return;
54-
const { acl: entryPermissions, workflowStage: entryWorkflowStageDetails } =
54+
const { acl: entryPermissions, workflowStage: entryWorkflowStageDetails, resolvedVariantPermissions } =
5555
await fetchEntryPermissionsAndStageDetails({
5656
entryUid: eventDetails.fieldMetadata.entry_uid,
5757
contentTypeUid: eventDetails.fieldMetadata.content_type_uid,
5858
locale: eventDetails.fieldMetadata.locale,
5959
variantUid: eventDetails.fieldMetadata.variant,
60+
fieldPathWithIndex: eventDetails.fieldMetadata.fieldPathWithIndex,
6061
});
6162
const wrapper = document.createDocumentFragment();
6263
render(
@@ -66,6 +67,7 @@ export async function appendFieldToolbar(
6667
isVariant={isVariant}
6768
entryPermissions={entryPermissions}
6869
entryWorkflowStageDetails={entryWorkflowStageDetails}
70+
resolvedVariantPermissions={resolvedVariantPermissions}
6971
/>,
7072
wrapper
7173
);

src/visualBuilder/listeners/mouseClick.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,23 +310,26 @@ async function handleFieldSchemaAndIndividualFields(
310310
fieldPath,
311311
locale,
312312
variant: variantUid,
313+
fieldPathWithIndex,
313314
} = fieldMetadata;
314315
const fieldSchema = await FieldSchemaMap.getFieldSchema(
315316
content_type_uid,
316317
fieldPath
317318
);
318-
const { acl: entryAcl, workflowStage: entryWorkflowStageDetails } =
319+
const { acl: entryAcl, workflowStage: entryWorkflowStageDetails, resolvedVariantPermissions } =
319320
await fetchEntryPermissionsAndStageDetails({
320321
entryUid: entry_uid,
321322
contentTypeUid: content_type_uid,
322323
locale,
323324
variantUid,
325+
fieldPathWithIndex,
324326
});
325327

326328
if (fieldSchema) {
327329
const { isDisabled } = isFieldDisabled(
328330
fieldSchema,
329331
eventDetails,
332+
resolvedVariantPermissions,
330333
entryAcl,
331334
entryWorkflowStageDetails
332335
);

0 commit comments

Comments
 (0)