Skip to content

Commit 0619529

Browse files
Merge pull request #504 from contentstack/VB-277/truncate-handlling-develop_v4
VB-277 :fix(VB-277/truncate-handlling):function added to remove and store styles
2 parents 0df93a5 + b225a71 commit 0619529

9 files changed

Lines changed: 531 additions & 22 deletions

File tree

src/visualBuilder/generators/generateOverlay.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import { FieldDataType } from "../utils/types/index.types";
1010
import { getFieldType } from "../utils/getFieldType";
1111
import { getMultilinePlaintext } from "../utils/getMultilinePlaintext";
1212
import { showAllHiddenHighlightedCommentIcons } from "./generateHighlightedComment";
13+
import {
14+
restoreTruncateStyles,
15+
hasStoredLineClampStyles,
16+
} from "../utils/truncateHandler";
1317

1418
/**
1519
* Adds a focus overlay to the target element.
@@ -224,6 +228,18 @@ export function hideOverlay(params: HideOverlayParams): void {
224228
VisualBuilder.VisualBuilderGlobalState.value.focusElementObserver =
225229
null;
226230
}
231+
232+
// Restore truncate styles for the previously selected element before hiding overlay
233+
const previousSelectedElement =
234+
VisualBuilder.VisualBuilderGlobalState.value
235+
.previousSelectedEditableDOM;
236+
if (
237+
previousSelectedElement &&
238+
hasStoredLineClampStyles(previousSelectedElement)
239+
) {
240+
restoreTruncateStyles(previousSelectedElement);
241+
}
242+
227243
hideFocusOverlay({
228244
visualBuilderContainer: params.visualBuilderContainer,
229245
visualBuilderOverlayWrapper: params.visualBuilderOverlayWrapper,

src/visualBuilder/listeners/mouseClick.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ import { fixSvgXPath } from "../utils/collabUtils";
3232
import { v4 as uuidV4 } from "uuid";
3333
import { CslpData } from "../../cslp/types/cslp.types";
3434
import { fetchEntryPermissionsAndStageDetails } from "../utils/fetchEntryPermissionsAndStageDetails";
35+
import {
36+
removeTruncateStyles,
37+
restoreTruncateStyles,
38+
hasStoredLineClampStyles,
39+
} from "../utils/truncateHandler";
3540

3641
export type HandleBuilderInteractionParams = Omit<
3742
EventListenerHandlerParams,
@@ -243,6 +248,14 @@ function cleanResidualsIfNeeded(
243248
previousSelectedElement !== editableElement) ||
244249
params.reEvaluate
245250
) {
251+
// Restore truncate styles for the previously selected element
252+
if (
253+
previousSelectedElement &&
254+
hasStoredLineClampStyles(previousSelectedElement)
255+
) {
256+
restoreTruncateStyles(previousSelectedElement);
257+
}
258+
246259
cleanIndividualFieldResidual({
247260
overlayWrapper: params.overlayWrapper!,
248261
visualBuilderContainer: params.visualBuilderContainer,
@@ -277,6 +290,10 @@ function addOverlayAndToolbar(
277290
isVariant: boolean
278291
) {
279292
VisualBuilder.VisualBuilderGlobalState.value.isFocussed = true;
293+
294+
// Remove truncate styles from the focused element
295+
removeTruncateStyles(editableElement);
296+
280297
addOverlay({
281298
overlayWrapper: params.overlayWrapper,
282299
resizeObserver: params.resizeObserver,
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
import { clearVisibilityStyles, restoreVisibilityStyles } from "../clearStyles";
2+
3+
describe("clearStyles", () => {
4+
let element: HTMLDivElement;
5+
6+
beforeEach(() => {
7+
element = document.createElement("div");
8+
document.body.appendChild(element);
9+
});
10+
11+
afterEach(() => {
12+
document.body.removeChild(element);
13+
});
14+
15+
describe("clearVisibilityStyles", () => {
16+
it("should hide element and disable transitions/animations", () => {
17+
// Set initial styles
18+
element.style.visibility = "visible";
19+
element.style.transition = "all 0.3s ease";
20+
element.style.animation = "fadeIn 1s";
21+
22+
clearVisibilityStyles(element);
23+
24+
expect(element.style.visibility).toBe("hidden");
25+
expect(element.style.transition).toBe("none");
26+
expect(element.style.animation).toBe("none");
27+
});
28+
29+
it("should handle element with no initial styles", () => {
30+
clearVisibilityStyles(element);
31+
32+
expect(element.style.visibility).toBe("hidden");
33+
expect(element.style.transition).toBe("none");
34+
expect(element.style.animation).toBe("none");
35+
});
36+
37+
it("should store original style values", () => {
38+
// Set initial styles
39+
element.style.visibility = "visible";
40+
element.style.transition = "opacity 0.5s";
41+
element.style.animation = "slideIn 2s";
42+
43+
clearVisibilityStyles(element);
44+
45+
// Verify styles are cleared
46+
expect(element.style.visibility).toBe("hidden");
47+
expect(element.style.transition).toBe("none");
48+
expect(element.style.animation).toBe("none");
49+
50+
// Verify we can restore (this indirectly tests that values were stored)
51+
restoreVisibilityStyles(element);
52+
expect(element.style.visibility).toBe("visible");
53+
expect(element.style.transition).toBe("opacity 0.5s");
54+
expect(element.style.animation).toBe("slideIn 2s");
55+
});
56+
57+
it("should handle multiple calls on the same element", () => {
58+
element.style.visibility = "visible";
59+
element.style.transition = "all 0.3s";
60+
61+
// First call
62+
clearVisibilityStyles(element);
63+
expect(element.style.visibility).toBe("hidden");
64+
65+
// Second call should not overwrite stored values
66+
element.style.visibility = "hidden"; // Simulate current state
67+
clearVisibilityStyles(element);
68+
69+
// Restore should still work with original values
70+
restoreVisibilityStyles(element);
71+
expect(element.style.visibility).toBe("visible");
72+
expect(element.style.transition).toBe("all 0.3s");
73+
});
74+
75+
it("should handle empty string style values", () => {
76+
element.style.visibility = "";
77+
element.style.transition = "";
78+
element.style.animation = "";
79+
80+
clearVisibilityStyles(element);
81+
restoreVisibilityStyles(element);
82+
83+
expect(element.style.visibility).toBe("");
84+
expect(element.style.transition).toBe("");
85+
expect(element.style.animation).toBe("");
86+
});
87+
});
88+
89+
describe("restoreVisibilityStyles", () => {
90+
it("should restore original styles after clearing", () => {
91+
const originalVisibility = "visible";
92+
const originalTransition = "all 0.3s ease-in-out";
93+
const originalAnimation = "bounce 1s infinite";
94+
95+
element.style.visibility = originalVisibility;
96+
element.style.transition = originalTransition;
97+
element.style.animation = originalAnimation;
98+
99+
clearVisibilityStyles(element);
100+
restoreVisibilityStyles(element);
101+
102+
expect(element.style.visibility).toBe(originalVisibility);
103+
expect(element.style.transition).toBe(originalTransition);
104+
expect(element.style.animation).toBe(originalAnimation);
105+
});
106+
107+
it("should handle restore without prior clear (no stored values)", () => {
108+
element.style.visibility = "visible";
109+
element.style.transition = "all 0.3s";
110+
111+
// Call restore without clearing first
112+
restoreVisibilityStyles(element);
113+
114+
// Should not modify styles when no stored values exist
115+
expect(element.style.visibility).toBe("visible");
116+
expect(element.style.transition).toBe("all 0.3s");
117+
});
118+
119+
it("should remove stored values after restoration", () => {
120+
element.style.visibility = "visible";
121+
clearVisibilityStyles(element);
122+
restoreVisibilityStyles(element);
123+
124+
// Second restore call should not affect styles
125+
element.style.visibility = "hidden";
126+
restoreVisibilityStyles(element);
127+
expect(element.style.visibility).toBe("hidden"); // Should remain unchanged
128+
});
129+
130+
it("should restore empty string values correctly", () => {
131+
// Start with empty styles (which is the default)
132+
clearVisibilityStyles(element);
133+
restoreVisibilityStyles(element);
134+
135+
expect(element.style.visibility).toBe("");
136+
expect(element.style.transition).toBe("");
137+
expect(element.style.animation).toBe("");
138+
});
139+
140+
it("should handle partial style restoration", () => {
141+
element.style.visibility = "visible";
142+
element.style.transition = "opacity 0.5s";
143+
// animation is not set (empty)
144+
145+
clearVisibilityStyles(element);
146+
restoreVisibilityStyles(element);
147+
148+
expect(element.style.visibility).toBe("visible");
149+
expect(element.style.transition).toBe("opacity 0.5s");
150+
expect(element.style.animation).toBe("");
151+
});
152+
});
153+
154+
describe("WeakMap storage behavior", () => {
155+
it("should handle multiple elements independently", () => {
156+
const element1 = document.createElement("div");
157+
const element2 = document.createElement("div");
158+
document.body.appendChild(element1);
159+
document.body.appendChild(element2);
160+
161+
element1.style.visibility = "visible";
162+
element1.style.transition = "all 0.3s";
163+
element2.style.visibility = "hidden";
164+
element2.style.animation = "fadeIn 1s";
165+
166+
clearVisibilityStyles(element1);
167+
clearVisibilityStyles(element2);
168+
169+
restoreVisibilityStyles(element1);
170+
restoreVisibilityStyles(element2);
171+
172+
expect(element1.style.visibility).toBe("visible");
173+
expect(element1.style.transition).toBe("all 0.3s");
174+
expect(element1.style.animation).toBe(""); // Was not set originally
175+
176+
expect(element2.style.visibility).toBe("hidden");
177+
expect(element2.style.transition).toBe(""); // Was not set originally
178+
expect(element2.style.animation).toBe("fadeIn 1s");
179+
180+
document.body.removeChild(element1);
181+
document.body.removeChild(element2);
182+
});
183+
184+
it("should not leak memory after element removal", () => {
185+
const tempElement = document.createElement("div");
186+
tempElement.style.visibility = "visible";
187+
188+
clearVisibilityStyles(tempElement);
189+
190+
// Element is not in DOM and has no references
191+
// WeakMap should allow garbage collection
192+
// This test mainly documents the expected behavior
193+
expect(tempElement.style.visibility).toBe("hidden");
194+
});
195+
});
196+
197+
describe("CSS property handling edge cases", () => {
198+
it("should handle complex transition values", () => {
199+
element.style.transition =
200+
"opacity 0.3s ease-in-out, transform 0.5s linear";
201+
202+
clearVisibilityStyles(element);
203+
expect(element.style.transition).toBe("none");
204+
205+
restoreVisibilityStyles(element);
206+
expect(element.style.transition).toBe(
207+
"opacity 0.3s ease-in-out, transform 0.5s linear"
208+
);
209+
});
210+
211+
it("should handle complex animation values", () => {
212+
element.style.animation =
213+
"slideIn 0.5s ease-out, fadeIn 1s linear infinite";
214+
215+
clearVisibilityStyles(element);
216+
expect(element.style.animation).toBe("none");
217+
218+
restoreVisibilityStyles(element);
219+
expect(element.style.animation).toBe(
220+
"slideIn 0.5s ease-out, fadeIn 1s linear infinite"
221+
);
222+
});
223+
224+
it("should handle inherit and initial values", () => {
225+
element.style.visibility = "inherit";
226+
element.style.transition = "initial";
227+
element.style.animation = "inherit";
228+
229+
clearVisibilityStyles(element);
230+
restoreVisibilityStyles(element);
231+
232+
expect(element.style.visibility).toBe("inherit");
233+
expect(element.style.transition).toBe("initial");
234+
expect(element.style.animation).toBe("inherit");
235+
});
236+
});
237+
});

0 commit comments

Comments
 (0)