Skip to content

Commit abf08bf

Browse files
committed
Merge branch 'develop_v3' of https://github.com/contentstack/live-preview-sdk into VE-5061
2 parents bd71fb4 + a597a94 commit abf08bf

4 files changed

Lines changed: 174 additions & 11 deletions

File tree

src/visualBuilder/eventManager/useRecalculateVariantDataCSLPValues.ts

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,17 @@ function updateVariantClasses({
3535
const updateElementClasses = (
3636
element: HTMLElement,
3737
dataCslp: string,
38-
observer: MutationObserver
38+
observer?: MutationObserver
3939
) => {
4040
if (!dataCslp) return;
4141

4242
if (
4343
dataCslp.startsWith("v2:") &&
44-
element.classList.contains("visual-builder__base-field")
44+
!element.classList.contains("visual-builder__variant-field")
4545
) {
46-
element.classList.remove("visual-builder__base-field");
46+
if (element.classList.contains("visual-builder__base-field")) {
47+
element.classList.remove("visual-builder__base-field");
48+
}
4749
if (highlightVariantFields) {
4850
element.classList.add(
4951
visualBuilderStyles()["visual-builder__variant-field"],
@@ -73,7 +75,7 @@ function updateVariantClasses({
7375
);
7476
element.classList.add("visual-builder__disabled-variant-field");
7577
}
76-
78+
if (!observer) return;
7779
// Disconnect this observer after processing
7880
observer.disconnect();
7981
const index = observers.indexOf(observer);
@@ -82,20 +84,79 @@ function updateVariantClasses({
8284
}
8385
};
8486

87+
const addElementClasses = (element: HTMLElement) => {
88+
const dataCslp = element.getAttribute(DATA_CSLP_ATTR_SELECTOR);
89+
90+
if (!dataCslp) {
91+
//recursive call for child nodes
92+
element.childNodes.forEach((child) => {
93+
if (child instanceof HTMLElement) {
94+
addElementClasses(child);
95+
}
96+
});
97+
return;
98+
}
99+
//if element might have been updated by another observer
100+
if (
101+
dataCslp.startsWith("v2:") &&
102+
element.classList.contains("visual-builder__variant-field")
103+
) {
104+
return;
105+
}
106+
// if element has not given variant/base class
107+
if (
108+
dataCslp.startsWith("v2:") &&
109+
!element.classList.contains("visual-builder__variant-field")
110+
) {
111+
if (element.classList.contains("visual-builder__base-field")) {
112+
element.classList.remove("visual-builder__base-field");
113+
}
114+
if (highlightVariantFields) {
115+
element.classList.add(
116+
visualBuilderStyles()["visual-builder__variant-field"],
117+
"visual-builder__variant-field"
118+
);
119+
} else {
120+
element.classList.add("visual-builder__variant-field");
121+
}
122+
} else if (!dataCslp.startsWith("v2:")) {
123+
if (element.classList.contains("visual-builder__variant-field")) {
124+
element.classList.remove(
125+
visualBuilderStyles()["visual-builder__variant-field"],
126+
"visual-builder__variant-field"
127+
);
128+
}
129+
element.classList.add("visual-builder__base-field");
130+
}
131+
132+
//recursive call for child nodes
133+
element.childNodes.forEach((child) => {
134+
if (child instanceof HTMLElement) {
135+
addElementClasses(child);
136+
}
137+
});
138+
};
139+
85140
// Create a separate observer for each element
86141
const elementsWithCslp = document.querySelectorAll(
87142
`[${DATA_CSLP_ATTR_SELECTOR}]`
88143
);
89-
90144
elementsWithCslp.forEach((elementNode) => {
91145
const element = elementNode as HTMLElement;
92-
93146
const observer = new MutationObserver((mutations) => {
94147
mutations.forEach((mutation) => {
95148
if (
96-
mutation.type === "attributes" &&
97-
mutation.attributeName === DATA_CSLP_ATTR_SELECTOR
149+
(mutation.type === "attributes" &&
150+
mutation.attributeName === DATA_CSLP_ATTR_SELECTOR) ||
151+
mutation.type === "childList"
98152
) {
153+
if (mutation.addedNodes.length > 0) {
154+
mutation.addedNodes.forEach((node) => {
155+
if (node instanceof HTMLElement) {
156+
addElementClasses(node);
157+
}
158+
});
159+
}
99160
const dataCslp = element.getAttribute(
100161
DATA_CSLP_ATTR_SELECTOR
101162
);
@@ -105,7 +166,11 @@ function updateVariantClasses({
105166
});
106167

107168
observers.push(observer);
108-
observer.observe(element, { attributes: true });
169+
observer.observe(element, {
170+
attributes: true,
171+
childList: true, // Observe direct children
172+
subtree: true,
173+
});
109174
});
110175

111176
setTimeout(() => {

src/visualBuilder/listeners/mouseClick.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ async function handleBuilderInteraction(
7171
(eventTarget.hasAttribute("data-cslp") ||
7272
eventTarget.closest("[data-cslp]"));
7373

74+
// if the target element is a studio-ui element, return
75+
// this is currently used for the "Edit in Studio" button
76+
if (eventTarget?.dataset["studio-ui"] === "true") {
77+
return;
78+
}
7479
// prevent default behavior for anchor elements and elements with cslp attribute
7580
if (
7681
isAnchorElement ||

src/visualBuilder/utils/__test__/getChildrenDirection.test.ts

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ describe("getChildrenDirection", () => {
2525

2626
(getChildElements as any).mockReturnValue([null, null, vi.fn()]);
2727

28+
expect(parentElement.getAttribute("data-add-direction")).toBe(null);
2829
const result = getChildrenDirection(editableElement, "test");
2930
expect(result).toBe("none");
3031
});
@@ -67,6 +68,7 @@ describe("getChildrenDirection", () => {
6768
toJSON: () => ({}),
6869
});
6970

71+
expect(parentElement.getAttribute("data-add-direction")).toBe(null);
7072
const result = getChildrenDirection(editableElement, "test");
7173
expect(result).toBe("horizontal");
7274
});
@@ -84,6 +86,7 @@ describe("getChildrenDirection", () => {
8486
secondChildElement,
8587
vi.fn(),
8688
]);
89+
8790

8891
vi.spyOn(firstChildElement, "getBoundingClientRect").mockReturnValue({
8992
left: 0,
@@ -109,7 +112,82 @@ describe("getChildrenDirection", () => {
109112
toJSON: () => ({}),
110113
});
111114

115+
expect(parentElement.getAttribute("data-add-direction")).toBe(null);
112116
const result = getChildrenDirection(editableElement, "test");
113117
expect(result).toBe("vertical");
114118
});
115-
});
119+
120+
it("should return direction from parent's data-add-direction if it exists with valid value", () => {
121+
const editableElement = document.createElement("div");
122+
const parentElement = document.createElement("div");
123+
124+
vi.spyOn(editableElement, "closest").mockReturnValue(parentElement);
125+
126+
// Test vertical direction
127+
parentElement.setAttribute("data-add-direction", "vertical");
128+
const resultVertical = getChildrenDirection(editableElement, "test");
129+
expect(resultVertical).toBe("vertical");
130+
131+
// Test horizontal direction
132+
parentElement.setAttribute("data-add-direction", "horizontal");
133+
const resultHorizontal = getChildrenDirection(editableElement, "test");
134+
expect(resultHorizontal).toBe("horizontal");
135+
});
136+
137+
it("should calculate direction when parent's data-add-direction has invalid value", () => {
138+
const editableElement = document.createElement("div");
139+
const parentElement = document.createElement("div");
140+
const firstChildElement = document.createElement("div");
141+
const secondChildElement = document.createElement("div");
142+
143+
vi.spyOn(editableElement, "closest").mockReturnValue(parentElement);
144+
parentElement.setAttribute("data-add-direction", "invalid");
145+
146+
(getChildElements as any).mockReturnValue([
147+
firstChildElement,
148+
secondChildElement,
149+
vi.fn(),
150+
]);
151+
152+
vi.spyOn(firstChildElement, "getBoundingClientRect").mockReturnValue({
153+
left: 0, top: 0, right: 0, bottom: 0, width: 0, height: 0, x: 0, y: 0,
154+
toJSON: () => ({})
155+
});
156+
vi.spyOn(secondChildElement, "getBoundingClientRect").mockReturnValue({
157+
left: 10, top: 0, right: 10, bottom: 0, width: 0, height: 0, x: 10, y: 0,
158+
toJSON: () => ({})
159+
});
160+
161+
const result = getChildrenDirection(editableElement, "test");
162+
expect(result).toBe("horizontal");
163+
});
164+
165+
it("should calculate direction when parent's data-add-direction is empty string", () => {
166+
const editableElement = document.createElement("div");
167+
const parentElement = document.createElement("div");
168+
const firstChildElement = document.createElement("div");
169+
const secondChildElement = document.createElement("div");
170+
171+
vi.spyOn(editableElement, "closest").mockReturnValue(parentElement);
172+
parentElement.setAttribute("data-add-direction", "");
173+
174+
(getChildElements as any).mockReturnValue([
175+
firstChildElement,
176+
secondChildElement,
177+
vi.fn(),
178+
]);
179+
180+
vi.spyOn(firstChildElement, "getBoundingClientRect").mockReturnValue({
181+
left: 0, top: 0, right: 0, bottom: 0, width: 0, height: 0, x: 0, y: 0,
182+
toJSON: () => ({})
183+
});
184+
vi.spyOn(secondChildElement, "getBoundingClientRect").mockReturnValue({
185+
left: 0, top: 10, right: 0, bottom: 10, width: 0, height: 0, x: 0, y: 10,
186+
toJSON: () => ({})
187+
});
188+
189+
const result = getChildrenDirection(editableElement, "test");
190+
expect(result).toBe("vertical");
191+
});
192+
193+
});

src/visualBuilder/utils/getChildrenDirection.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import getChildElements from "./getChildElements";
22

3+
4+
const validPositions = ["vertical", "horizontal", "none"] as const;
5+
type ValidPositions = typeof validPositions[number];
6+
37
export default function getChildrenDirection(
48
editableElement: Element,
59
parentCslpValue: string
6-
): "none" | "horizontal" | "vertical" {
10+
): ValidPositions {
711
if (!editableElement) {
812
return "none";
913
}
@@ -16,6 +20,17 @@ export default function getChildrenDirection(
1620
return "none";
1721
}
1822

23+
const directionFromParentElement =
24+
parentElement.getAttribute("data-add-direction");
25+
26+
const isValidParentDirection = validPositions.includes(
27+
directionFromParentElement as ValidPositions
28+
);
29+
30+
31+
if (directionFromParentElement && isValidParentDirection) {
32+
return directionFromParentElement as ValidPositions;
33+
}
1934
const [firstChildElement, secondChildElement, removeClone] =
2035
getChildElements(parentElement, parentCslpValue);
2136

0 commit comments

Comments
 (0)