@griddo/ax 12.8.5-rc.0 → 12.9.0-rc.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@griddo/ax",
3
3
  "description": "Griddo Author Experience",
4
- "version": "12.8.5-rc.0",
4
+ "version": "12.9.0-rc.1",
5
5
  "authors": [
6
6
  "Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
7
7
  "Diego M. Béjar <diego.bejar@secuoyas.com>",
@@ -201,5 +201,5 @@
201
201
  "publishConfig": {
202
202
  "access": "public"
203
203
  },
204
- "gitHead": "c15b6cd2594c32e70932ea6e8e4895845064949e"
204
+ "gitHead": "6ca432142f7b2645958c4e65471bb9b8a6e7cc82"
205
205
  }
@@ -0,0 +1,344 @@
1
+ import { parseShortcut, type ShortcutConfig, shortcutEngine } from "@ax/hooks";
2
+
3
+ // ---------------------------------------------------------------------------
4
+ // Helpers
5
+ // ---------------------------------------------------------------------------
6
+
7
+ /** Dispatch a keydown on `window` and return the event. */
8
+ function keydown(init: KeyboardEventInit): KeyboardEvent {
9
+ const event = new KeyboardEvent("keydown", { ...init, bubbles: true, cancelable: true });
10
+ window.dispatchEvent(event);
11
+ return event;
12
+ }
13
+
14
+ /** Register a shortcut, track its cleanup automatically. */
15
+ let cleanups: Array<() => void> = [];
16
+
17
+ function register(
18
+ overrides: Partial<ShortcutConfig> & Pick<ShortcutConfig, "id" | "keys">,
19
+ target?: EventTarget,
20
+ ): ShortcutConfig {
21
+ const config: ShortcutConfig = { handler: jest.fn(), ...overrides };
22
+ const cleanup = shortcutEngine.register(config, target);
23
+ cleanups.push(cleanup);
24
+ return config;
25
+ }
26
+
27
+ afterEach(() => {
28
+ cleanups.forEach((fn) => {
29
+ fn();
30
+ });
31
+ cleanups = [];
32
+ });
33
+
34
+ // ---------------------------------------------------------------------------
35
+ // parseShortcut
36
+ // ---------------------------------------------------------------------------
37
+
38
+ describe("parseShortcut", () => {
39
+ it("parses a single key", () => {
40
+ expect(parseShortcut("escape")).toEqual([{ modifiers: [], key: "escape" }]);
41
+ });
42
+
43
+ it("parses key with one modifier", () => {
44
+ expect(parseShortcut("mod+s")).toEqual([{ modifiers: ["mod"], key: "s" }]);
45
+ });
46
+
47
+ it("parses key with multiple modifiers", () => {
48
+ expect(parseShortcut("mod+shift+z")).toEqual([{ modifiers: ["mod", "shift"], key: "z" }]);
49
+ });
50
+
51
+ it("parses chord (multi-step sequence)", () => {
52
+ expect(parseShortcut("g l")).toEqual([
53
+ { modifiers: [], key: "g" },
54
+ { modifiers: [], key: "l" },
55
+ ]);
56
+ });
57
+
58
+ it("parses chord with modifiers", () => {
59
+ expect(parseShortcut("mod+k mod+d")).toEqual([
60
+ { modifiers: ["mod"], key: "k" },
61
+ { modifiers: ["mod"], key: "d" },
62
+ ]);
63
+ });
64
+ });
65
+
66
+ // ---------------------------------------------------------------------------
67
+ // ShortcutEngine — basic dispatch
68
+ // ---------------------------------------------------------------------------
69
+ // In jsdom, isMac() → false, so "mod" maps to ctrlKey.
70
+
71
+ describe("ShortcutEngine", () => {
72
+ describe("basic dispatch", () => {
73
+ it("fires handler on matching key", () => {
74
+ const config = register({ id: "s1", keys: parseShortcut("mod+s") });
75
+ keydown({ key: "s", ctrlKey: true });
76
+ expect(config.handler).toHaveBeenCalledTimes(1);
77
+ });
78
+
79
+ it("ignores non-matching key", () => {
80
+ const config = register({ id: "s1", keys: parseShortcut("mod+s") });
81
+ keydown({ key: "a", ctrlKey: true });
82
+ expect(config.handler).not.toHaveBeenCalled();
83
+ });
84
+
85
+ it("ignores when required modifier is missing", () => {
86
+ const config = register({ id: "s1", keys: parseShortcut("mod+s") });
87
+ keydown({ key: "s" });
88
+ expect(config.handler).not.toHaveBeenCalled();
89
+ });
90
+
91
+ it("ignores when unexpected modifier is present", () => {
92
+ const config = register({ id: "s1", keys: parseShortcut("mod+s") });
93
+ keydown({ key: "s", ctrlKey: true, shiftKey: true });
94
+ expect(config.handler).not.toHaveBeenCalled();
95
+ });
96
+
97
+ it("matches shift+alt combo", () => {
98
+ const config = register({ id: "s1", keys: parseShortcut("shift+alt+n") });
99
+ keydown({ key: "n", shiftKey: true, altKey: true });
100
+ expect(config.handler).toHaveBeenCalledTimes(1);
101
+ });
102
+
103
+ it("ignores events with falsy event.key", () => {
104
+ const config = register({ id: "s1", keys: parseShortcut("mod+s") });
105
+ // KeyboardEvent with no key option defaults to ""
106
+ window.dispatchEvent(new KeyboardEvent("keydown", { ctrlKey: true, bubbles: true }));
107
+ expect(config.handler).not.toHaveBeenCalled();
108
+ });
109
+ });
110
+
111
+ // -----------------------------------------------------------------------
112
+ // preventDefault / stopPropagation
113
+ // -----------------------------------------------------------------------
114
+
115
+ describe("event control", () => {
116
+ it("calls preventDefault when handler does not return false", () => {
117
+ register({ id: "s1", keys: parseShortcut("mod+s") });
118
+ const event = keydown({ key: "s", ctrlKey: true });
119
+ expect(event.defaultPrevented).toBe(true);
120
+ });
121
+
122
+ it("skips preventDefault when handler returns false", () => {
123
+ register({ id: "s1", keys: parseShortcut("mod+s"), handler: () => false });
124
+ const event = keydown({ key: "s", ctrlKey: true });
125
+ expect(event.defaultPrevented).toBe(false);
126
+ });
127
+ });
128
+
129
+ // -----------------------------------------------------------------------
130
+ // enabled flag
131
+ // -----------------------------------------------------------------------
132
+
133
+ describe("enabled flag", () => {
134
+ it("skips disabled shortcuts", () => {
135
+ const config = register({ id: "s1", keys: parseShortcut("mod+s"), enabled: false });
136
+ keydown({ key: "s", ctrlKey: true });
137
+ expect(config.handler).not.toHaveBeenCalled();
138
+ });
139
+ });
140
+
141
+ // -----------------------------------------------------------------------
142
+ // Priority
143
+ // -----------------------------------------------------------------------
144
+
145
+ describe("priority", () => {
146
+ it("dispatches higher priority first", () => {
147
+ const order: string[] = [];
148
+ register({
149
+ id: "low",
150
+ keys: parseShortcut("mod+s"),
151
+ priority: 0,
152
+ handler: () => {
153
+ order.push("low");
154
+ },
155
+ });
156
+ register({
157
+ id: "high",
158
+ keys: parseShortcut("mod+s"),
159
+ priority: 10,
160
+ handler: () => {
161
+ order.push("high");
162
+ },
163
+ });
164
+
165
+ keydown({ key: "s", ctrlKey: true });
166
+ // "high" wins and stops propagation
167
+ expect(order).toEqual(["high"]);
168
+ });
169
+
170
+ it("falls through when higher priority handler returns false", () => {
171
+ const order: string[] = [];
172
+ register({
173
+ id: "high",
174
+ keys: parseShortcut("mod+s"),
175
+ priority: 10,
176
+ handler: () => {
177
+ order.push("high");
178
+ return false;
179
+ },
180
+ });
181
+ register({
182
+ id: "low",
183
+ keys: parseShortcut("mod+s"),
184
+ priority: 0,
185
+ handler: () => {
186
+ order.push("low");
187
+ },
188
+ });
189
+
190
+ keydown({ key: "s", ctrlKey: true });
191
+ expect(order).toEqual(["high", "low"]);
192
+ });
193
+ });
194
+
195
+ // -----------------------------------------------------------------------
196
+ // Input element filtering
197
+ // -----------------------------------------------------------------------
198
+
199
+ describe("input element filtering", () => {
200
+ let input: HTMLInputElement;
201
+
202
+ beforeEach(() => {
203
+ input = document.createElement("input");
204
+ document.body.appendChild(input);
205
+ });
206
+
207
+ afterEach(() => {
208
+ document.body.removeChild(input);
209
+ });
210
+
211
+ it("does not fire inside input by default", () => {
212
+ const config = register({ id: "s1", keys: parseShortcut("mod+s") });
213
+ input.dispatchEvent(new KeyboardEvent("keydown", { key: "s", ctrlKey: true, bubbles: true }));
214
+ expect(config.handler).not.toHaveBeenCalled();
215
+ });
216
+
217
+ it("fires inside input when allowInInput is true", () => {
218
+ const config = register({ id: "s1", keys: parseShortcut("mod+s"), allowInInput: true });
219
+ input.dispatchEvent(new KeyboardEvent("keydown", { key: "s", ctrlKey: true, bubbles: true }));
220
+ expect(config.handler).toHaveBeenCalledTimes(1);
221
+ });
222
+
223
+ it("Escape always fires inside input even without allowInInput", () => {
224
+ const config = register({ id: "s1", keys: parseShortcut("escape") });
225
+ input.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", bubbles: true }));
226
+ expect(config.handler).toHaveBeenCalledTimes(1);
227
+ });
228
+
229
+ it("does not fire inside textarea by default", () => {
230
+ const textarea = document.createElement("textarea");
231
+ document.body.appendChild(textarea);
232
+ const config = register({ id: "s1", keys: parseShortcut("mod+s") });
233
+ textarea.dispatchEvent(new KeyboardEvent("keydown", { key: "s", ctrlKey: true, bubbles: true }));
234
+ expect(config.handler).not.toHaveBeenCalled();
235
+ document.body.removeChild(textarea);
236
+ });
237
+
238
+ // Note: contentEditable filtering cannot be tested in jsdom
239
+ // (isContentEditable is not properly implemented).
240
+ });
241
+
242
+ // -----------------------------------------------------------------------
243
+ // Chords
244
+ // -----------------------------------------------------------------------
245
+
246
+ describe("chords", () => {
247
+ it("fires on completed chord sequence", () => {
248
+ const config = register({ id: "goto", keys: parseShortcut("g l") });
249
+ keydown({ key: "g" });
250
+ keydown({ key: "l" });
251
+ expect(config.handler).toHaveBeenCalledTimes(1);
252
+ });
253
+
254
+ it("does not fire on first key alone", () => {
255
+ const config = register({ id: "goto", keys: parseShortcut("g l") });
256
+ keydown({ key: "g" });
257
+ expect(config.handler).not.toHaveBeenCalled();
258
+ });
259
+
260
+ it("resets on wrong second key", () => {
261
+ const config = register({ id: "goto", keys: parseShortcut("g l") });
262
+ keydown({ key: "g" });
263
+ keydown({ key: "x" });
264
+ keydown({ key: "l" });
265
+ expect(config.handler).not.toHaveBeenCalled();
266
+ });
267
+
268
+ it("resets on timeout", () => {
269
+ jest.useFakeTimers();
270
+ const config = register({ id: "goto", keys: parseShortcut("g l") });
271
+ keydown({ key: "g" });
272
+ jest.advanceTimersByTime(1100);
273
+ keydown({ key: "l" });
274
+ expect(config.handler).not.toHaveBeenCalled();
275
+ jest.useRealTimers();
276
+ });
277
+
278
+ it("calls preventDefault on first key of chord", () => {
279
+ register({ id: "goto", keys: parseShortcut("g l") });
280
+ const event = keydown({ key: "g" });
281
+ expect(event.defaultPrevented).toBe(true);
282
+ });
283
+
284
+ it("does not fire single-key shortcuts while chord is advancing", () => {
285
+ const chordHandler = jest.fn();
286
+ const singleHandler = jest.fn();
287
+ register({ id: "chord", keys: parseShortcut("g l") });
288
+ register({ id: "single-g", keys: parseShortcut("g"), handler: singleHandler });
289
+
290
+ keydown({ key: "g" });
291
+ expect(singleHandler).not.toHaveBeenCalled();
292
+ });
293
+ });
294
+
295
+ // -----------------------------------------------------------------------
296
+ // Scoped target
297
+ // -----------------------------------------------------------------------
298
+
299
+ describe("scoped target", () => {
300
+ it("fires only on registered target", () => {
301
+ const div = document.createElement("div");
302
+ document.body.appendChild(div);
303
+ const config = register({ id: "scoped", keys: parseShortcut("mod+b") }, div);
304
+
305
+ // Event on window — should NOT trigger
306
+ keydown({ key: "b", ctrlKey: true });
307
+ expect(config.handler).not.toHaveBeenCalled();
308
+
309
+ // Event on the registered target — should trigger
310
+ div.dispatchEvent(new KeyboardEvent("keydown", { key: "b", ctrlKey: true, bubbles: true }));
311
+ expect(config.handler).toHaveBeenCalledTimes(1);
312
+
313
+ document.body.removeChild(div);
314
+ });
315
+ });
316
+
317
+ // -----------------------------------------------------------------------
318
+ // Cleanup
319
+ // -----------------------------------------------------------------------
320
+
321
+ describe("cleanup", () => {
322
+ it("unregisters shortcut when cleanup is called", () => {
323
+ const handler = jest.fn();
324
+ const cleanup = shortcutEngine.register({ id: "tmp", keys: parseShortcut("mod+s"), handler }, window);
325
+ cleanup();
326
+
327
+ keydown({ key: "s", ctrlKey: true });
328
+ expect(handler).not.toHaveBeenCalled();
329
+ });
330
+
331
+ it("removes listener when last shortcut on a target is unregistered", () => {
332
+ const div = document.createElement("div");
333
+ document.body.appendChild(div);
334
+ const spy = jest.spyOn(div, "removeEventListener");
335
+
336
+ const cleanup = shortcutEngine.register({ id: "tmp", keys: parseShortcut("mod+x"), handler: jest.fn() }, div);
337
+ cleanup();
338
+
339
+ expect(spy).toHaveBeenCalledWith("keydown", expect.any(Function));
340
+ spy.mockRestore();
341
+ document.body.removeChild(div);
342
+ });
343
+ });
344
+ });
@@ -0,0 +1,259 @@
1
+ import {
2
+ shortcut,
3
+ useEscapeShortcut,
4
+ useGlobalShortcuts,
5
+ useIframeShortcutBridge,
6
+ useSaveShortcut,
7
+ useShortcuts,
8
+ useUndoRedoShortcuts,
9
+ } from "@ax/hooks";
10
+
11
+ import { renderHook } from "@testing-library/react";
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Helpers
15
+ // ---------------------------------------------------------------------------
16
+
17
+ function keydown(init: KeyboardEventInit): KeyboardEvent {
18
+ const event = new KeyboardEvent("keydown", { ...init, bubbles: true, cancelable: true });
19
+ window.dispatchEvent(event);
20
+ return event;
21
+ }
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // shortcut builder
25
+ // ---------------------------------------------------------------------------
26
+
27
+ describe("shortcut()", () => {
28
+ it("creates a ShortcutConfig with parsed keys", () => {
29
+ const handler = jest.fn();
30
+ const config = shortcut("save", "mod+s", handler);
31
+ expect(config.id).toBe("save");
32
+ expect(config.keys).toEqual([{ modifiers: ["mod"], key: "s" }]);
33
+ expect(config.handler).toBe(handler);
34
+ });
35
+
36
+ it("applies overrides", () => {
37
+ const config = shortcut("save", "mod+s", jest.fn(), {
38
+ allowInInput: true,
39
+ priority: 5,
40
+ description: "Save document",
41
+ });
42
+ expect(config.allowInInput).toBe(true);
43
+ expect(config.priority).toBe(5);
44
+ expect(config.description).toBe("Save document");
45
+ });
46
+ });
47
+
48
+ // ---------------------------------------------------------------------------
49
+ // useShortcuts
50
+ // ---------------------------------------------------------------------------
51
+
52
+ describe("useShortcuts", () => {
53
+ it("registers and fires shortcut on matching key", () => {
54
+ const handler = jest.fn();
55
+ renderHook(() => useShortcuts([shortcut("s1", "mod+s", handler)]));
56
+ keydown({ key: "s", ctrlKey: true });
57
+ expect(handler).toHaveBeenCalledTimes(1);
58
+ });
59
+
60
+ it("cleans up shortcuts on unmount", () => {
61
+ const handler = jest.fn();
62
+ const { unmount } = renderHook(() => useShortcuts([shortcut("s1", "mod+s", handler)]));
63
+ unmount();
64
+ keydown({ key: "s", ctrlKey: true });
65
+ expect(handler).not.toHaveBeenCalled();
66
+ });
67
+
68
+ it("does not fire when enabled is false", () => {
69
+ const handler = jest.fn();
70
+ renderHook(() => useShortcuts([shortcut("s1", "mod+s", handler)], { enabled: false }));
71
+ keydown({ key: "s", ctrlKey: true });
72
+ expect(handler).not.toHaveBeenCalled();
73
+ });
74
+
75
+ it("always calls the latest handler (no stale closures)", () => {
76
+ let value = 0;
77
+ const { rerender } = renderHook(({ handler }) => useShortcuts([shortcut("s1", "mod+s", handler)]), {
78
+ initialProps: {
79
+ handler: () => {
80
+ value = 1;
81
+ },
82
+ },
83
+ });
84
+ rerender({
85
+ handler: () => {
86
+ value = 2;
87
+ },
88
+ });
89
+ keydown({ key: "s", ctrlKey: true });
90
+ expect(value).toBe(2);
91
+ });
92
+
93
+ it("scopes shortcuts to a ref element", () => {
94
+ const div = document.createElement("div");
95
+ document.body.appendChild(div);
96
+ const ref = { current: div } as React.RefObject<HTMLDivElement>;
97
+ const handler = jest.fn();
98
+
99
+ renderHook(() => useShortcuts([shortcut("s1", "mod+b", handler)], { ref }));
100
+
101
+ // Event on window — should NOT trigger
102
+ keydown({ key: "b", ctrlKey: true });
103
+ expect(handler).not.toHaveBeenCalled();
104
+
105
+ // Event on ref element — should trigger
106
+ div.dispatchEvent(new KeyboardEvent("keydown", { key: "b", ctrlKey: true, bubbles: true }));
107
+ expect(handler).toHaveBeenCalledTimes(1);
108
+
109
+ document.body.removeChild(div);
110
+ });
111
+ });
112
+
113
+ // ---------------------------------------------------------------------------
114
+ // Preset hooks
115
+ // ---------------------------------------------------------------------------
116
+
117
+ describe("useSaveShortcut", () => {
118
+ it("fires handler on mod+s", () => {
119
+ const handler = jest.fn();
120
+ renderHook(() => useSaveShortcut(handler));
121
+ keydown({ key: "s", ctrlKey: true });
122
+ expect(handler).toHaveBeenCalledTimes(1);
123
+ });
124
+
125
+ it("does not fire when disabled", () => {
126
+ const handler = jest.fn();
127
+ renderHook(() => useSaveShortcut(handler, false));
128
+ keydown({ key: "s", ctrlKey: true });
129
+ expect(handler).not.toHaveBeenCalled();
130
+ });
131
+
132
+ it("works inside input elements", () => {
133
+ const handler = jest.fn();
134
+ renderHook(() => useSaveShortcut(handler));
135
+ const input = document.createElement("input");
136
+ document.body.appendChild(input);
137
+ input.dispatchEvent(new KeyboardEvent("keydown", { key: "s", ctrlKey: true, bubbles: true }));
138
+ expect(handler).toHaveBeenCalledTimes(1);
139
+ document.body.removeChild(input);
140
+ });
141
+ });
142
+
143
+ describe("useEscapeShortcut", () => {
144
+ it("fires handler on Escape", () => {
145
+ const handler = jest.fn();
146
+ renderHook(() => useEscapeShortcut(handler));
147
+ keydown({ key: "Escape" });
148
+ expect(handler).toHaveBeenCalledTimes(1);
149
+ });
150
+
151
+ it("does not fire when disabled", () => {
152
+ const handler = jest.fn();
153
+ renderHook(() => useEscapeShortcut(handler, false));
154
+ keydown({ key: "Escape" });
155
+ expect(handler).not.toHaveBeenCalled();
156
+ });
157
+ });
158
+
159
+ describe("useUndoRedoShortcuts", () => {
160
+ it("fires onUndo on mod+z", () => {
161
+ const onUndo = jest.fn();
162
+ const onRedo = jest.fn();
163
+ renderHook(() => useUndoRedoShortcuts(onUndo, onRedo));
164
+ keydown({ key: "z", ctrlKey: true });
165
+ expect(onUndo).toHaveBeenCalledTimes(1);
166
+ expect(onRedo).not.toHaveBeenCalled();
167
+ });
168
+
169
+ it("fires onRedo on mod+shift+z", () => {
170
+ const onUndo = jest.fn();
171
+ const onRedo = jest.fn();
172
+ renderHook(() => useUndoRedoShortcuts(onUndo, onRedo));
173
+ keydown({ key: "z", ctrlKey: true, shiftKey: true });
174
+ expect(onRedo).toHaveBeenCalledTimes(1);
175
+ expect(onUndo).not.toHaveBeenCalled();
176
+ });
177
+ });
178
+
179
+ describe("useGlobalShortcuts", () => {
180
+ it("prevents browser default on mod+s", () => {
181
+ renderHook(() => useGlobalShortcuts());
182
+ const event = keydown({ key: "s", ctrlKey: true });
183
+ expect(event.defaultPrevented).toBe(true);
184
+ });
185
+
186
+ it("yields to higher-priority shortcut", () => {
187
+ const handler = jest.fn();
188
+ renderHook(() => useGlobalShortcuts());
189
+ renderHook(() => useSaveShortcut(handler));
190
+
191
+ keydown({ key: "s", ctrlKey: true });
192
+ // The preset (priority 0) wins over global (priority -1)
193
+ expect(handler).toHaveBeenCalledTimes(1);
194
+ });
195
+ });
196
+
197
+ // ---------------------------------------------------------------------------
198
+ // useIframeShortcutBridge
199
+ // ---------------------------------------------------------------------------
200
+
201
+ describe("useIframeShortcutBridge", () => {
202
+ it("re-dispatches same-origin shortcut messages as keydown", () => {
203
+ renderHook(() => useIframeShortcutBridge());
204
+ const spy = jest.fn();
205
+ window.addEventListener("keydown", spy);
206
+
207
+ window.dispatchEvent(
208
+ new MessageEvent("message", {
209
+ origin: window.location.origin,
210
+ data: {
211
+ type: "shortcut-keydown",
212
+ message: { key: "s", code: "KeyS", metaKey: false, ctrlKey: true, shiftKey: false, altKey: false },
213
+ },
214
+ }),
215
+ );
216
+
217
+ expect(spy).toHaveBeenCalled();
218
+ const event = spy.mock.calls[0][0] as KeyboardEvent;
219
+ expect(event.key).toBe("s");
220
+ expect(event.ctrlKey).toBe(true);
221
+
222
+ window.removeEventListener("keydown", spy);
223
+ });
224
+
225
+ it("ignores cross-origin messages", () => {
226
+ renderHook(() => useIframeShortcutBridge());
227
+ const spy = jest.fn();
228
+ window.addEventListener("keydown", spy);
229
+
230
+ window.dispatchEvent(
231
+ new MessageEvent("message", {
232
+ origin: "https://evil.example.com",
233
+ data: {
234
+ type: "shortcut-keydown",
235
+ message: { key: "s", code: "KeyS", metaKey: false, ctrlKey: true, shiftKey: false, altKey: false },
236
+ },
237
+ }),
238
+ );
239
+
240
+ expect(spy).not.toHaveBeenCalled();
241
+ window.removeEventListener("keydown", spy);
242
+ });
243
+
244
+ it("ignores messages with unrecognized type", () => {
245
+ renderHook(() => useIframeShortcutBridge());
246
+ const spy = jest.fn();
247
+ window.addEventListener("keydown", spy);
248
+
249
+ window.dispatchEvent(
250
+ new MessageEvent("message", {
251
+ origin: window.location.origin,
252
+ data: { type: "unrelated-message" },
253
+ }),
254
+ );
255
+
256
+ expect(spy).not.toHaveBeenCalled();
257
+ window.removeEventListener("keydown", spy);
258
+ });
259
+ });
@@ -2,7 +2,7 @@ import { getInnerFields } from "@ax/forms";
2
2
  import { FieldContainer, FieldGroup } from "@ax/components";
3
3
  import type { IErrorItem, IPage, ISite, ILanguage } from "@ax/types";
4
4
 
5
- const Field = (props: IFieldProps): JSX.Element => {
5
+ const Field = (props: IFieldProps) => {
6
6
  const {
7
7
  whiteList,
8
8
  objKey,