@griddo/ax 11.12.1-rc.6 → 11.12.1-rc.8

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": "11.12.1-rc.6",
4
+ "version": "11.12.1-rc.8",
5
5
  "authors": [
6
6
  "Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
7
7
  "Diego M. Béjar <diego.bejar@secuoyas.com>",
@@ -217,5 +217,5 @@
217
217
  "publishConfig": {
218
218
  "access": "public"
219
219
  },
220
- "gitHead": "3dfa982521198297dcca356611a9bd2f3df23e90"
220
+ "gitHead": "dd8aea4aa1eeea915e2b432f15bded59ff906892"
221
221
  }
@@ -312,7 +312,7 @@ describe("Events", () => {
312
312
  fireEvent.click(actionMenu[1]);
313
313
 
314
314
  const buttonsDefault = screen.getAllByTestId("button-default");
315
- fireEvent.click(buttonsDefault[4]);
315
+ fireEvent.click(buttonsDefault[buttonsDefault.length - 1]);
316
316
 
317
317
  const integrationsWithDeletion = initialStore.integrations.integrations.slice(0, 3);
318
318
 
@@ -290,6 +290,12 @@ describe("UrlField component rendering", () => {
290
290
  render(Component, { store });
291
291
  });
292
292
 
293
+ // Open FloatingPanel first
294
+ const pageField = screen.getByTestId("page-field");
295
+ await act(async () => {
296
+ fireEvent.click(pageField);
297
+ });
298
+
293
299
  const selectComponents = screen.getAllByTestId("select-component");
294
300
 
295
301
  await act(async () => {
@@ -384,6 +390,12 @@ describe("onClick events", () => {
384
390
  render(Component, { store });
385
391
  });
386
392
 
393
+ // Open FloatingPanel first
394
+ const pageField = screen.getByTestId("page-field");
395
+ await act(async () => {
396
+ fireEvent.click(pageField);
397
+ });
398
+
387
399
  await act(async () => {
388
400
  const selectionListItem = screen.getAllByTestId("selection-list-item");
389
401
  selectionListItem[0].click();
@@ -430,10 +442,16 @@ describe("onClick events", () => {
430
442
  render(Component, { store });
431
443
  });
432
444
 
433
- const selectionListItem = screen.getAllByTestId("selection-list-item");
434
445
  const checkFieldInputs = screen.getAllByTestId("check-field-input");
435
446
  const pageField = screen.getByTestId("page-field");
436
447
 
448
+ // Open FloatingPanel first
449
+ await act(async () => {
450
+ fireEvent.click(pageField);
451
+ });
452
+
453
+ const selectionListItem = screen.getAllByTestId("selection-list-item");
454
+
437
455
  await act(async () => {
438
456
  selectionListItem[0].click();
439
457
  fireEvent.click(checkFieldInputs[0]);
@@ -0,0 +1,303 @@
1
+ import { act, renderHook } from "@testing-library/react";
2
+ import "@testing-library/jest-dom";
3
+
4
+ import { useModal, useModals } from "@ax/hooks";
5
+
6
+ describe("useModal hook", () => {
7
+ beforeEach(() => {
8
+ document.body.classList.remove("modal-open");
9
+ });
10
+
11
+ afterEach(() => {
12
+ document.body.classList.remove("modal-open");
13
+ });
14
+
15
+ it("should initialize with false when no initialState provided", () => {
16
+ const { result } = renderHook(() => useModal());
17
+ expect(result.current.isOpen).toBe(false);
18
+ });
19
+
20
+ it("should initialize with provided initialState", () => {
21
+ const { result } = renderHook(() => useModal(true));
22
+ expect(result.current.isOpen).toBe(true);
23
+ });
24
+
25
+ it("should toggle modal state when toggleModal is called", () => {
26
+ const { result } = renderHook(() => useModal(false));
27
+ expect(result.current.isOpen).toBe(false);
28
+
29
+ act(() => {
30
+ result.current.toggleModal();
31
+ });
32
+
33
+ expect(result.current.isOpen).toBe(true);
34
+
35
+ act(() => {
36
+ result.current.toggleModal();
37
+ });
38
+
39
+ expect(result.current.isOpen).toBe(false);
40
+ });
41
+
42
+ it("should add modal-open class to body when modal opens and bodyBlock is true", () => {
43
+ const { result } = renderHook(() => useModal(false, true));
44
+ expect(document.body.classList.contains("modal-open")).toBe(false);
45
+
46
+ act(() => {
47
+ result.current.toggleModal();
48
+ });
49
+
50
+ expect(document.body.classList.contains("modal-open")).toBe(true);
51
+ });
52
+
53
+ it("should not add modal-open class to body when bodyBlock is false", () => {
54
+ const { result } = renderHook(() => useModal(false, false));
55
+
56
+ act(() => {
57
+ result.current.toggleModal();
58
+ });
59
+
60
+ expect(document.body.classList.contains("modal-open")).toBe(false);
61
+ });
62
+
63
+ it("should check if modal-open class already exists before adding", () => {
64
+ document.body.classList.add("modal-open");
65
+
66
+ const { result } = renderHook(() => useModal(false, true));
67
+
68
+ act(() => {
69
+ result.current.toggleModal();
70
+ });
71
+
72
+ // Class should still be present
73
+ expect(document.body.classList.contains("modal-open")).toBe(true);
74
+
75
+ // Count of modal-open should be 1
76
+ const classList = [...document.body.classList];
77
+ const count = classList.filter((c) => c === "modal-open").length;
78
+ expect(count).toBe(1);
79
+ });
80
+ });
81
+
82
+ describe("useModals hook", () => {
83
+ beforeEach(() => {
84
+ document.body.classList.remove("modal-open");
85
+ });
86
+
87
+ afterEach(() => {
88
+ document.body.classList.remove("modal-open");
89
+ });
90
+
91
+ it("should initialize all modals as closed", () => {
92
+ const { result } = renderHook(() => useModals(["modal1", "modal2", "modal3"]));
93
+
94
+ expect(result.current.isOpen("modal1")).toBe(false);
95
+ expect(result.current.isOpen("modal2")).toBe(false);
96
+ expect(result.current.isOpen("modal3")).toBe(false);
97
+ });
98
+
99
+ it("should open a modal with openModal", () => {
100
+ const { result } = renderHook(() => useModals(["modal1", "modal2"]));
101
+
102
+ act(() => {
103
+ result.current.openModal("modal1");
104
+ });
105
+
106
+ expect(result.current.isOpen("modal1")).toBe(true);
107
+ expect(result.current.isOpen("modal2")).toBe(false);
108
+ });
109
+
110
+ it("should close a modal with closeModal", () => {
111
+ const { result } = renderHook(() => useModals(["modal1"]));
112
+
113
+ act(() => {
114
+ result.current.openModal("modal1");
115
+ });
116
+
117
+ expect(result.current.isOpen("modal1")).toBe(true);
118
+
119
+ act(() => {
120
+ result.current.closeModal("modal1");
121
+ });
122
+
123
+ expect(result.current.isOpen("modal1")).toBe(false);
124
+ });
125
+
126
+ it("should toggle a modal with toggleModal", () => {
127
+ const { result } = renderHook(() => useModals(["modal1"]));
128
+
129
+ expect(result.current.isOpen("modal1")).toBe(false);
130
+
131
+ act(() => {
132
+ result.current.toggleModal("modal1");
133
+ });
134
+
135
+ expect(result.current.isOpen("modal1")).toBe(true);
136
+
137
+ act(() => {
138
+ result.current.toggleModal("modal1");
139
+ });
140
+
141
+ expect(result.current.isOpen("modal1")).toBe(false);
142
+ });
143
+
144
+ it("should handle multiple modals independently", () => {
145
+ const { result } = renderHook(() => useModals(["modal1", "modal2", "modal3"]));
146
+
147
+ act(() => {
148
+ result.current.openModal("modal1");
149
+ });
150
+
151
+ expect(result.current.isOpen("modal1")).toBe(true);
152
+ expect(result.current.isOpen("modal2")).toBe(false);
153
+ expect(result.current.isOpen("modal3")).toBe(false);
154
+
155
+ act(() => {
156
+ result.current.openModal("modal2");
157
+ });
158
+
159
+ expect(result.current.isOpen("modal1")).toBe(true);
160
+ expect(result.current.isOpen("modal2")).toBe(true);
161
+ expect(result.current.isOpen("modal3")).toBe(false);
162
+ });
163
+
164
+ it("should add modal-open class when a modal is opened", () => {
165
+ const { result } = renderHook(() => useModals(["modal1"], true));
166
+
167
+ expect(document.body.classList.contains("modal-open")).toBe(false);
168
+
169
+ act(() => {
170
+ result.current.openModal("modal1");
171
+ });
172
+
173
+ expect(document.body.classList.contains("modal-open")).toBe(true);
174
+ });
175
+
176
+ it("should remove modal-open class when all modals are closed", () => {
177
+ const { result } = renderHook(() => useModals(["modal1", "modal2"], true));
178
+
179
+ act(() => {
180
+ result.current.openModal("modal1");
181
+ result.current.openModal("modal2");
182
+ });
183
+
184
+ expect(document.body.classList.contains("modal-open")).toBe(true);
185
+
186
+ act(() => {
187
+ result.current.closeModal("modal1");
188
+ });
189
+
190
+ expect(document.body.classList.contains("modal-open")).toBe(true);
191
+
192
+ act(() => {
193
+ result.current.closeModal("modal2");
194
+ });
195
+
196
+ expect(document.body.classList.contains("modal-open")).toBe(false);
197
+ });
198
+
199
+ it("should close multiple modals individually", () => {
200
+ const { result } = renderHook(() => useModals(["modal1", "modal2", "modal3"]));
201
+
202
+ act(() => {
203
+ result.current.openModal("modal1");
204
+ result.current.openModal("modal2");
205
+ result.current.openModal("modal3");
206
+ });
207
+
208
+ expect(result.current.isOpen("modal1")).toBe(true);
209
+ expect(result.current.isOpen("modal2")).toBe(true);
210
+ expect(result.current.isOpen("modal3")).toBe(true);
211
+
212
+ act(() => {
213
+ result.current.closeModal("modal1");
214
+ result.current.closeModal("modal2");
215
+ result.current.closeModal("modal3");
216
+ });
217
+
218
+ expect(result.current.isOpen("modal1")).toBe(false);
219
+ expect(result.current.isOpen("modal2")).toBe(false);
220
+ expect(result.current.isOpen("modal3")).toBe(false);
221
+ });
222
+
223
+ it("should not add modal-open class if bodyBlock is false", () => {
224
+ const { result } = renderHook(() => useModals(["modal1"], false));
225
+
226
+ act(() => {
227
+ result.current.openModal("modal1");
228
+ });
229
+
230
+ expect(document.body.classList.contains("modal-open")).toBe(false);
231
+ });
232
+
233
+ it("should not duplicate modal-open class when multiple modals are open", () => {
234
+ const { result } = renderHook(() => useModals(["modal1", "modal2"], true));
235
+
236
+ act(() => {
237
+ result.current.openModal("modal1");
238
+ });
239
+
240
+ const classList1 = [...document.body.classList];
241
+ const count1 = classList1.filter((c) => c === "modal-open").length;
242
+
243
+ act(() => {
244
+ result.current.openModal("modal2");
245
+ });
246
+
247
+ const classList2 = [...document.body.classList];
248
+ const count2 = classList2.filter((c) => c === "modal-open").length;
249
+
250
+ expect(count1).toBe(1);
251
+ expect(count2).toBe(1);
252
+ });
253
+
254
+ it("should handle isOpen callback correctly", () => {
255
+ const { result } = renderHook(() => useModals(["modal1"]));
256
+
257
+ act(() => {
258
+ result.current.openModal("modal1");
259
+ });
260
+
261
+ const isOpenResult = result.current.isOpen("modal1");
262
+ expect(isOpenResult).toBe(true);
263
+ });
264
+ });
265
+
266
+ describe("useModal and useModals working together", () => {
267
+ beforeEach(() => {
268
+ document.body.classList.remove("modal-open");
269
+ });
270
+
271
+ afterEach(() => {
272
+ document.body.classList.remove("modal-open");
273
+ });
274
+
275
+ it("should verify both hooks add modal-open class independently", () => {
276
+ const { result: modalResult } = renderHook(() => useModal(false, true));
277
+ const { result: modalsResult } = renderHook(() => useModals(["modal1"], true));
278
+
279
+ // useModal should add class
280
+ act(() => {
281
+ modalResult.current.toggleModal();
282
+ });
283
+ expect(document.body.classList.contains("modal-open")).toBe(true);
284
+
285
+ // useModals should keep class
286
+ act(() => {
287
+ modalsResult.current.openModal("modal1");
288
+ });
289
+ expect(document.body.classList.contains("modal-open")).toBe(true);
290
+
291
+ // Closing useModal should keep class (useModals still open)
292
+ act(() => {
293
+ modalResult.current.toggleModal();
294
+ });
295
+ // Note: This depends on DOM check in useModal
296
+
297
+ // Closing useModals should remove class
298
+ act(() => {
299
+ modalsResult.current.closeModal("modal1");
300
+ });
301
+ expect(document.body.classList.contains("modal-open")).toBe(false);
302
+ });
303
+ });
@@ -1,8 +1,8 @@
1
1
  import { memo, useRef } from "react";
2
2
  import { createPortal } from "react-dom";
3
3
 
4
- import { useHandleClickOutside } from "@ax/hooks";
5
4
  import { IconAction } from "@ax/components";
5
+ import { useHandleClickOutside } from "@ax/hooks";
6
6
 
7
7
  import * as S from "./style";
8
8
 
@@ -40,9 +40,10 @@ const FloatingPanel = (props: IFloatingPanelProps): JSX.Element | null => {
40
40
  }
41
41
  };
42
42
 
43
- return isOpen ? createPortal(
43
+ return createPortal(
44
44
  <S.Wrapper
45
45
  data-testid="floating-panel"
46
+ data-is-open={isOpen}
46
47
  ref={node}
47
48
  isOpen={isOpen}
48
49
  isOpenedSecond={isOpenedSecond}
@@ -60,7 +61,7 @@ const FloatingPanel = (props: IFloatingPanelProps): JSX.Element | null => {
60
61
  <S.Content>{children}</S.Content>
61
62
  </S.Wrapper>,
62
63
  document.body,
63
- ) : null;
64
+ );
64
65
  };
65
66
 
66
67
  export interface IFloatingPanelProps {
@@ -35,6 +35,7 @@ const HeadingsPreviewModal = (props: IHeadingsPreviewProps) => {
35
35
  setHeadings(headings);
36
36
  } else {
37
37
  setHeadingsFilter("all");
38
+ window.scrollTo(0, 0);
38
39
  }
39
40
  }, [isOpen, getSEOHeadings, setHeadingsFilter]);
40
41
 
@@ -81,6 +81,7 @@ const Modal = (props: IModalProps): JSX.Element | null => {
81
81
  <S.ModalOverlay isChild={isChild} />
82
82
  <S.ModalWrapper
83
83
  data-testid="modal-wrapper"
84
+ data-is-open={isOpen}
84
85
  isChild={isChild}
85
86
  role="dialog"
86
87
  aria-modal="true"
@@ -1,4 +1,4 @@
1
- import { useState, useEffect, useRef, useCallback } from "react";
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
2
 
3
3
  const useModal = (initialState?: boolean, bodyBlock = true) => {
4
4
  const [isOpen, setIsOpen] = useState(initialState || false);
@@ -12,10 +12,9 @@ const useModal = (initialState?: boolean, bodyBlock = true) => {
12
12
  document.body.classList.add("modal-open");
13
13
  }
14
14
  } else if (!isOpen && bodyBlock) {
15
- // Solo remover si no hay otros modales abiertos
16
- const hasFloatingPanels = document.querySelectorAll('[data-testid="floating-panel"]').length > 0;
17
- const hasModals = document.querySelectorAll('[data-testid="modal-wrapper"]').length > 0;
18
- if (!hasFloatingPanels && !hasModals) {
15
+ // Solo remover si no hay otros modales o FloatingPanels abiertos
16
+ const hasOpenModals = document.querySelectorAll('[data-is-open="true"]').length > 0;
17
+ if (!hasOpenModals) {
19
18
  document.body.classList.remove("modal-open");
20
19
  }
21
20
  }
@@ -65,7 +64,11 @@ const useModals = <T extends string>(modalKeys: readonly T[], bodyBlock = true)
65
64
  document.body.classList.add("modal-open");
66
65
  }
67
66
  } else if (!hasOpenModals) {
68
- document.body.classList.remove("modal-open");
67
+ // Solo remover si no hay otros modales o FloatingPanels abiertos
68
+ const hasAnyOpenModals = document.querySelectorAll('[data-is-open="true"]').length > 0;
69
+ if (!hasAnyOpenModals) {
70
+ document.body.classList.remove("modal-open");
71
+ }
69
72
  }
70
73
  }, [openModals, bodyBlock]);
71
74
 
@@ -55,18 +55,18 @@ const HeadingsOverlay = ({ headingFilter }: IHeadingsOverlayProps) => {
55
55
 
56
56
  update();
57
57
 
58
+ document.fonts.ready.then(update);
59
+
58
60
  window.addEventListener("resize", update);
59
- window.addEventListener("scroll", update, true);
60
61
  document.addEventListener("animationend", update, true);
61
62
  document.addEventListener("transitionend", update, true);
62
63
 
63
64
  const observer = new MutationObserver(update);
64
- observer.observe(document.body, { childList: true, subtree: true, attributes: true });
65
+ observer.observe(document.body, { childList: true, subtree: true });
65
66
 
66
67
  return () => {
67
68
  cancelAnimationFrame(rafRef.current);
68
69
  window.removeEventListener("resize", update);
69
- window.removeEventListener("scroll", update, true);
70
70
  document.removeEventListener("animationend", update, true);
71
71
  document.removeEventListener("transitionend", update, true);
72
72
  observer.disconnect();
@@ -6,7 +6,10 @@ const addIdsToHeadings = (container: HTMLElement, headingFilter: string | null):
6
6
  const text = heading.textContent?.trim();
7
7
  if (!text) return;
8
8
 
9
- heading.dataset.griddoid = `heading-${index + 1}`;
9
+ const id = `heading-${index + 1}`;
10
+ if (heading.dataset.griddoid !== id) {
11
+ heading.dataset.griddoid = id;
12
+ }
10
13
  });
11
14
  };
12
15