Skip to content

Commit 6a2c5d8

Browse files
authored
Merge pull request #422 from contentstack/VE-5361-no-update-on-no-input
fix(VE-5361): update field event is not sent when input event does not occur in inline editable fields
2 parents 9a7b64a + 8e6a56d commit 6a2c5d8

5 files changed

Lines changed: 83 additions & 16 deletions

File tree

src/visualBuilder/generators/generateOverlay.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import EventListenerHandlerParams from "../listeners/types";
88
import { FieldSchemaMap } from "../utils/fieldSchemaMap";
99
import { FieldDataType } from "../utils/types/index.types";
1010
import { getFieldType } from "../utils/getFieldType";
11-
import { CslpData } from "../../cslp/types/cslp.types";
1211
import { getMultilinePlaintext } from "../utils/getMultilinePlaintext";
1312
import { showAllHiddenHighlightedCommentIcons } from "./generateHighlightedComment";
1413

@@ -119,12 +118,16 @@ export function hideFocusOverlay(elements: HideOverlayParams): void {
119118
}
120119
});
121120

122-
if (!noTrigger) {
121+
if (
122+
!noTrigger &&
123+
// send update when focussed field has received input
124+
VisualBuilder.VisualBuilderGlobalState.value.focusFieldReceivedInput
125+
) {
123126
sendFieldEvent({
124127
visualBuilderContainer,
125128
eventType: VisualBuilderPostMessageEvents.UPDATE_FIELD,
126129
});
127-
} else {
130+
} else if (noTrigger) {
128131
const { previousSelectedEditableDOM, focusFieldValue } =
129132
VisualBuilder.VisualBuilderGlobalState.value || {};
130133
if (
@@ -136,6 +139,8 @@ export function hideFocusOverlay(elements: HideOverlayParams): void {
136139
}
137140
}
138141
VisualBuilder.VisualBuilderGlobalState.value.focusFieldValue = null;
142+
VisualBuilder.VisualBuilderGlobalState.value.focusFieldReceivedInput =
143+
false;
139144
cleanIndividualFieldResidual({
140145
overlayWrapper: visualBuilderOverlayWrapper,
141146
visualBuilderContainer: visualBuilderContainer,

src/visualBuilder/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ interface VisualBuilderGlobalStateImpl {
6262
previousHoveredTargetDOM: Element | null;
6363
previousEmptyBlockParents: Element[] | [];
6464
focusFieldValue: string | null;
65+
focusFieldReceivedInput: boolean;
6566
audienceMode: boolean;
6667
locale: string;
6768
variant: string | null;
@@ -82,6 +83,7 @@ export class VisualBuilder {
8283
previousHoveredTargetDOM: null,
8384
previousEmptyBlockParents: [],
8485
focusFieldValue: null,
86+
focusFieldReceivedInput: false,
8587
audienceMode: false,
8688
locale: Config.get().stackDetails.masterLocale || "en-us",
8789
variant: null,
@@ -208,9 +210,7 @@ export class VisualBuilder {
208210
);
209211

210212
const emptyBlockParents = Array.from(
211-
document.querySelectorAll(
212-
`.${VB_EmptyBlockParentClass}`
213-
)
213+
document.querySelectorAll(`.${VB_EmptyBlockParentClass}`)
214214
);
215215

216216
const previousEmptyBlockParents = VisualBuilder
@@ -417,6 +417,7 @@ export class VisualBuilder {
417417
previousHoveredTargetDOM: null,
418418
previousEmptyBlockParents: [],
419419
focusFieldValue: null,
420+
focusFieldReceivedInput: false,
420421
audienceMode: false,
421422
locale: "en-us",
422423
variant: null,

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ describe("hideFocusOverlay", () => {
156156
initUI({
157157
resizeObserver: mockResizeObserver,
158158
});
159+
VisualBuilder.VisualBuilderGlobalState.value.focusFieldReceivedInput =
160+
true;
159161
visualBuilderContainer = document.querySelector(
160162
".visual-builder__container"
161163
) as HTMLDivElement;
@@ -268,6 +270,32 @@ describe("hideFocusOverlay", () => {
268270
);
269271
});
270272

273+
test("should not send update field event when focusFieldReceivedInput is false", async () => {
274+
editedElement.setAttribute("contenteditable", "true");
275+
276+
// Set up global state
277+
VisualBuilder.VisualBuilderGlobalState.value.previousSelectedEditableDOM =
278+
editedElement;
279+
VisualBuilder.VisualBuilderGlobalState.value.focusFieldReceivedInput =
280+
false;
281+
282+
expect(focusOverlayWrapper.classList.contains("visible")).toBe(true);
283+
284+
hideFocusOverlay({
285+
visualBuilderContainer,
286+
visualBuilderOverlayWrapper: focusOverlayWrapper,
287+
focusedToolbar: document.querySelector(".visual-builder__toolbar"),
288+
resizeObserver: mockResizeObserver,
289+
noTrigger: false
290+
});
291+
292+
expect(focusOverlayWrapper.classList.contains("visible")).toBe(false);
293+
294+
await waitFor(() => {
295+
expect(visualBuilderPostMessage?.send).not.toHaveBeenCalled();
296+
});
297+
});
298+
271299
test("should run cleanup function", () => {
272300
// We"ll always click one of the overlays, so we can just grab the first one. Manually pointing the global state to the editedElement as we are not simulating mouse click on window here.
273301
VisualBuilder.VisualBuilderGlobalState.value.previousSelectedEditableDOM =

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

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,22 @@ import * as generateOverlay from "../../generators/generateOverlay";
66
import { VisualBuilderPostMessageEvents } from "../types/postMessage.types";
77
import { FieldDataType } from "../types/index.types";
88
import userEvent from "@testing-library/user-event";
9-
import { waitFor, screen } from "@testing-library/preact";
9+
import { waitFor } from "@testing-library/preact";
10+
import { VisualBuilder } from "../../index";
11+
12+
vi.mock("../../index", async () => ({
13+
VisualBuilder: {
14+
VisualBuilderGlobalState: {
15+
value: { focusFieldReceivedInput: false },
16+
},
17+
},
18+
}));
1019

1120
vi.mock("lodash-es", async () => ({
1221
...(await import("lodash-es")),
1322
throttle: vi.fn((fn) => fn),
14-
}))
23+
}));
24+
1525
describe("handle numeric field key down", () => {
1626
let h1: HTMLHeadingElement;
1727
let spiedPreventDefault: MockInstance<(e: []) => void> | undefined;
@@ -117,7 +127,7 @@ describe("handle numeric field key down", () => {
117127
});
118128

119129
test("should only accept characters like a number input", async () => {
120-
h1.innerHTML = '';
130+
h1.innerHTML = "";
121131
await userEvent.click(h1);
122132
await userEvent.keyboard("ab56c78e-h10");
123133

@@ -231,7 +241,10 @@ describe("handle single line field key down", () => {
231241
h1 = document.createElement("h1");
232242
h1.innerHTML = "2.2";
233243
h1.setAttribute("contenteditable", "true");
234-
h1.setAttribute(VISUAL_BUILDER_FIELD_TYPE_ATTRIBUTE_KEY, FieldDataType.SINGLELINE);
244+
h1.setAttribute(
245+
VISUAL_BUILDER_FIELD_TYPE_ATTRIBUTE_KEY,
246+
FieldDataType.SINGLELINE
247+
);
235248

236249
h1.addEventListener("keydown", (e) => {
237250
spiedPreventDefault = vi.spyOn(e, "preventDefault");
@@ -260,7 +273,7 @@ describe("handle single line field key down", () => {
260273

261274
expect(spiedPreventDefault).toHaveBeenCalledTimes(1);
262275
});
263-
})
276+
});
264277

265278
describe("`handleFieldInput`", () => {
266279
let h1: HTMLHeadingElement;
@@ -289,18 +302,30 @@ describe("`handleFieldInput`", () => {
289302
});
290303

291304
test("should call `sendFieldEvent` on input event", () => {
292-
const spiedSendFieldEvent = vi.spyOn(generateOverlay, "sendFieldEvent")
305+
const spiedSendFieldEvent = vi.spyOn(generateOverlay, "sendFieldEvent");
293306
const consoleError = vi.spyOn(console, "error");
294-
spiedSendFieldEvent.mockImplementation(() => {
295-
throw new Error("sendFieldEvent not implemented")
307+
spiedSendFieldEvent.mockImplementation(() => {
308+
throw new Error("sendFieldEvent not implemented");
296309
});
297310
const inputEvent = new InputEvent("input", {
298311
bubbles: true,
299312
});
300313
h1.dispatchEvent(inputEvent);
301314

302-
expect(spiedSendFieldEvent).toHaveBeenCalledWith({ visualBuilderContainer, eventType: VisualBuilderPostMessageEvents.SYNC_FIELD });
315+
expect(spiedSendFieldEvent).toHaveBeenCalledWith({
316+
visualBuilderContainer,
317+
eventType: VisualBuilderPostMessageEvents.SYNC_FIELD,
318+
});
303319
expect(consoleError).toHaveBeenCalled();
304320
});
305321

306-
})
322+
test("should set focusFieldReceivedInput to true", () => {
323+
const inputEvent = new InputEvent("input", {
324+
bubbles: true,
325+
});
326+
h1.dispatchEvent(inputEvent);
327+
expect(
328+
VisualBuilder.VisualBuilderGlobalState.value.focusFieldReceivedInput
329+
).toBe(true);
330+
});
331+
});

src/visualBuilder/utils/handleFieldMouseDown.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
import { FieldDataType } from "./types/index.types";
99
import { VisualBuilderPostMessageEvents } from "./types/postMessage.types";
1010
import { insertSpaceAtCursor } from "./insertSpaceAtCursor";
11+
import { VisualBuilder } from "..";
1112

1213
export function handleFieldInput(e: Event): void {
1314
const event = e as InputEvent;
@@ -19,6 +20,13 @@ export function handleFieldInput(e: Event): void {
1920
event.type === "input" &&
2021
ALLOWED_INLINE_EDITABLE_FIELD.includes(fieldType as FieldDataType)
2122
) {
23+
if (
24+
!VisualBuilder.VisualBuilderGlobalState.value
25+
.focusFieldReceivedInput
26+
) {
27+
VisualBuilder.VisualBuilderGlobalState.value.focusFieldReceivedInput =
28+
true;
29+
}
2230
throttledFieldSync();
2331
}
2432
}

0 commit comments

Comments
 (0)