@griddo/ax 11.13.2 → 11.13.3-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.
Files changed (59) hide show
  1. package/config/jest/setup.js +10 -0
  2. package/package.json +2 -2
  3. package/src/GlobalStore.tsx +1 -1
  4. package/src/__tests__/components/Fields/AsyncCheckGroup/AsyncCheckGroup.test.tsx +276 -66
  5. package/src/__tests__/components/FloatingMenu/FloatingMenu.test.tsx +300 -99
  6. package/src/__tests__/modules/Settings/Social/Social.test.tsx +12 -4
  7. package/src/api/checkgroups.tsx +4 -3
  8. package/src/api/selects.tsx +12 -5
  9. package/src/components/ActionMenu/index.tsx +1 -3
  10. package/src/components/Browser/index.tsx +12 -3
  11. package/src/components/Browser/style.tsx +7 -0
  12. package/src/components/ConfigPanel/Form/index.tsx +47 -53
  13. package/src/components/Fields/AnalyticsField/PageAnalytics/atoms.tsx +9 -13
  14. package/src/components/Fields/AnalyticsField/PageAnalytics/index.tsx +37 -29
  15. package/src/components/Fields/AnalyticsField/StructuredDataAnalytics/atoms.tsx +9 -13
  16. package/src/components/Fields/AnalyticsField/StructuredDataAnalytics/index.tsx +17 -11
  17. package/src/components/Fields/AnalyticsField/index.tsx +1 -2
  18. package/src/components/Fields/AnalyticsField/utils.tsx +4 -4
  19. package/src/components/Fields/AsyncCheckGroup/index.tsx +97 -79
  20. package/src/components/Fields/AsyncSelect/index.tsx +33 -22
  21. package/src/components/Fields/DateField/DatePickerInput/index.tsx +2 -2
  22. package/src/components/Fields/DateField/index.tsx +3 -3
  23. package/src/components/Fields/IntegrationsField/SideModal/index.tsx +2 -2
  24. package/src/components/Fields/IntegrationsField/index.tsx +14 -10
  25. package/src/components/Fields/MultiCheckSelect/index.tsx +6 -6
  26. package/src/components/Fields/MultiCheckSelectGroup/index.tsx +39 -37
  27. package/src/components/Fields/MultiCheckSelectGroup/style.tsx +1 -1
  28. package/src/components/Fields/ReferenceField/ManualPanel/index.tsx +0 -2
  29. package/src/components/Fields/RichText/index.tsx +15 -7
  30. package/src/components/Fields/TextArea/index.tsx +9 -6
  31. package/src/components/FloatingMenu/index.tsx +32 -31
  32. package/src/components/FloatingMenu/style.tsx +23 -5
  33. package/src/components/Loader/components/SmallCircle.js +3 -3
  34. package/src/components/MainWrapper/AppBar/style.tsx +1 -0
  35. package/src/components/SideModal/index.tsx +1 -1
  36. package/src/components/TableFilters/CategoryFilter/index.tsx +14 -15
  37. package/src/containers/App/actions.tsx +7 -1
  38. package/src/containers/App/constants.tsx +2 -0
  39. package/src/containers/App/interfaces.tsx +5 -0
  40. package/src/containers/App/reducer.tsx +11 -2
  41. package/src/containers/Forms/actions.tsx +5 -7
  42. package/src/containers/Integrations/actions.tsx +1 -3
  43. package/src/containers/Navigation/Menu/actions.tsx +2 -2
  44. package/src/containers/PageEditor/actions.tsx +3 -2
  45. package/src/containers/Settings/DataPacks/actions.tsx +35 -29
  46. package/src/containers/Sites/actions.tsx +40 -33
  47. package/src/containers/StructuredData/actions.tsx +3 -9
  48. package/src/modules/ActivityLog/LogFilters/DateFilter/index.tsx +5 -4
  49. package/src/modules/Content/NewContentModal/PageImporter/index.tsx +1 -2
  50. package/src/modules/Content/index.tsx +8 -3
  51. package/src/modules/Content/style.tsx +7 -0
  52. package/src/modules/Navigation/Defaults/DefaultsEditor/index.tsx +58 -45
  53. package/src/modules/Navigation/Defaults/index.tsx +103 -104
  54. package/src/modules/PageEditor/index.tsx +9 -1
  55. package/src/modules/PublicPreview/index.tsx +2 -1
  56. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/index.tsx +60 -44
  57. package/src/modules/Settings/ContentTypes/DataPacks/Config/index.tsx +32 -37
  58. package/src/modules/Sites/index.tsx +3 -3
  59. package/src/modules/Users/UserList/index.tsx +1 -1
@@ -1,15 +1,13 @@
1
- import React, { useRef } from "react";
1
+ import { useRef } from "react";
2
2
 
3
3
  import { ThemeProvider } from "styled-components";
4
4
  import { render, cleanup, screen, fireEvent } from "@testing-library/react";
5
5
  import { parseTheme } from "@ax/helpers";
6
6
  import "@testing-library/jest-dom";
7
7
 
8
- import FloatingMenu, { IFloatingProps } from "@ax/components/FloatingMenu";
8
+ import FloatingMenu, { type IFloatingProps } from "@ax/components/FloatingMenu";
9
9
  import globalTheme from "@ax/themes/theme.json";
10
10
 
11
- afterEach(cleanup);
12
-
13
11
  jest.mock("react", () => {
14
12
  const originReact = jest.requireActual("react");
15
13
  const mUseRef = jest.fn();
@@ -21,6 +19,13 @@ jest.mock("react", () => {
21
19
 
22
20
  const useMockRef = useRef as jest.MockedFunction<typeof useRef>;
23
21
 
22
+ afterEach(cleanup);
23
+ beforeEach(() => {
24
+ useMockRef.mockReset();
25
+ // Safe fallback for extra renders (useLayoutEffect, etc.)
26
+ useMockRef.mockImplementation(() => ({ current: null }));
27
+ });
28
+
24
29
  describe("FloatingMenu component rendering", () => {
25
30
  it("should render the component", () => {
26
31
  const buttonContent = () => (
@@ -35,7 +40,6 @@ describe("FloatingMenu component rendering", () => {
35
40
  isInAppBar: true,
36
41
  position: "left",
37
42
  closeOnSelect: false,
38
- isCheckGroup: true,
39
43
  reactiveToHover: true,
40
44
  offset: 10,
41
45
  };
@@ -47,7 +51,7 @@ describe("FloatingMenu component rendering", () => {
47
51
  );
48
52
 
49
53
  const floatingMenu = screen.getByTestId("floating-menu");
50
- expect(floatingMenu).toBeTruthy();
54
+ expect(floatingMenu).toBeInTheDocument();
51
55
  });
52
56
 
53
57
  it("should show the floating div when click", () => {
@@ -61,7 +65,6 @@ describe("FloatingMenu component rendering", () => {
61
65
  children: <div>hola</div>,
62
66
  Button: buttonContent,
63
67
  isInAppBar: true,
64
- isCheckGroup: true,
65
68
  reactiveToHover: true,
66
69
  offset: 10,
67
70
  };
@@ -113,11 +116,11 @@ describe("FloatingMenu component rendering", () => {
113
116
  );
114
117
 
115
118
  const floatingMenu = screen.getByTestId("floating-menu");
116
- expect(floatingMenu).toBeTruthy();
119
+ expect(floatingMenu).toBeInTheDocument();
117
120
 
118
121
  fireEvent.click(floatingMenu);
119
122
 
120
- expect(screen.getByTestId("floating-menu-wrapper")).toBeTruthy();
123
+ expect(screen.getByTestId("floating-menu-wrapper")).toBeInTheDocument();
121
124
 
122
125
  useMockRef.mockReturnValueOnce(wrapperRef);
123
126
 
@@ -127,7 +130,7 @@ describe("FloatingMenu component rendering", () => {
127
130
  </ThemeProvider>,
128
131
  );
129
132
  fireEvent.mouseDown(document);
130
- expect(screen.getByTestId("floating-menu-wrapper")).toBeTruthy();
133
+ expect(screen.getByTestId("floating-menu-wrapper")).toBeInTheDocument();
131
134
  });
132
135
 
133
136
  it("should close the floating div when click", () => {
@@ -141,7 +144,6 @@ describe("FloatingMenu component rendering", () => {
141
144
  children: <div>hola</div>,
142
145
  Button: buttonContent,
143
146
  isInAppBar: true,
144
- isCheckGroup: true,
145
147
  reactiveToHover: true,
146
148
  offset: 10,
147
149
  };
@@ -194,11 +196,11 @@ describe("FloatingMenu component rendering", () => {
194
196
  </ThemeProvider>,
195
197
  );
196
198
  const floatingMenu = screen.getByTestId("floating-menu");
197
- expect(floatingMenu).toBeTruthy();
199
+ expect(floatingMenu).toBeInTheDocument();
198
200
 
199
201
  fireEvent.click(floatingMenu);
200
202
 
201
- expect(screen.getByTestId("floating-menu-wrapper")).toBeTruthy();
203
+ expect(screen.getByTestId("floating-menu-wrapper")).toBeInTheDocument();
202
204
 
203
205
  Object.defineProperty(wrapperRef, "current", {
204
206
  set(_current) {
@@ -221,14 +223,14 @@ describe("FloatingMenu component rendering", () => {
221
223
 
222
224
  fireEvent.mouseDown(document);
223
225
 
224
- expect(screen.queryByTestId("floating-menu-wrapper")).not.toBeTruthy();
226
+ expect(screen.queryByTestId("floating-menu-wrapper")).not.toBeInTheDocument();
225
227
  });
226
228
 
227
- it("should close the floating div when select option", () => {
228
- const buttonContent = () => <input type="checkbox" data-testid="menu-children" />;
229
+ it("should close the floating div when checking a checkbox (isCheckGroup)", () => {
230
+ const buttonContent = () => <button type="button">menu</button>;
229
231
 
230
232
  const defaultProps: IFloatingProps = {
231
- children: <div>hola</div>,
233
+ children: <input type="checkbox" data-testid="menu-children" />,
232
234
  Button: buttonContent,
233
235
  isInAppBar: true,
234
236
  isCheckGroup: true,
@@ -284,11 +286,11 @@ describe("FloatingMenu component rendering", () => {
284
286
  </ThemeProvider>,
285
287
  );
286
288
  const floatingMenu = screen.getByTestId("floating-menu");
287
- expect(floatingMenu).toBeTruthy();
289
+ expect(floatingMenu).toBeInTheDocument();
288
290
 
289
291
  fireEvent.click(floatingMenu);
290
292
 
291
- expect(screen.getByTestId("floating-menu-wrapper")).toBeTruthy();
293
+ expect(screen.getByTestId("floating-menu-wrapper")).toBeInTheDocument();
292
294
 
293
295
  Object.defineProperty(wrapperRef, "current", {
294
296
  set(_current) {
@@ -336,17 +338,42 @@ describe("FloatingMenu component rendering", () => {
336
338
  const menuChildren = screen.getByTestId("menu-children");
337
339
  fireEvent.click(menuChildren);
338
340
 
339
- expect(screen.queryByTestId("floating-menu-wrapper")).not.toBeTruthy();
341
+ expect(screen.queryByTestId("floating-menu-wrapper")).not.toBeInTheDocument();
340
342
  });
341
343
 
342
- it("should close the floating div when select option and is not a checkgroup", () => {
344
+ it("should stay open when isCheckGroup and checkbox is unchecked", () => {
345
+ const buttonContent = () => <span>trigger</span>;
346
+
347
+ const defaultProps: IFloatingProps = {
348
+ children: <input type="checkbox" defaultChecked data-testid="menu-checkbox" />,
349
+ Button: buttonContent,
350
+ isCheckGroup: true,
351
+ };
352
+
353
+ render(
354
+ <ThemeProvider theme={parseTheme(globalTheme)}>
355
+ <FloatingMenu {...defaultProps} />
356
+ </ThemeProvider>,
357
+ );
358
+
359
+ const buttonWrapper = screen.getByTestId("floating-menu-button");
360
+ fireEvent.click(buttonWrapper);
361
+ expect(screen.getByTestId("floating-menu-wrapper")).toBeInTheDocument();
362
+
363
+ // Click an already-checked checkbox → becomes unchecked → menu must stay open
364
+ const checkbox = screen.getByTestId("menu-checkbox");
365
+ fireEvent.click(checkbox);
366
+
367
+ expect(screen.queryByTestId("floating-menu-wrapper")).toBeInTheDocument();
368
+ });
369
+
370
+ it("should close the floating div when select option (closeOnSelect=true)", () => {
343
371
  const buttonContent = () => <span data-testid="menu-children">texto</span>;
344
372
 
345
373
  const defaultProps: IFloatingProps = {
346
374
  children: <div>hola</div>,
347
375
  Button: buttonContent,
348
376
  isInAppBar: true,
349
- isCheckGroup: false,
350
377
  reactiveToHover: true,
351
378
  offset: 10,
352
379
  };
@@ -399,11 +426,11 @@ describe("FloatingMenu component rendering", () => {
399
426
  </ThemeProvider>,
400
427
  );
401
428
  const floatingMenu = screen.getByTestId("floating-menu");
402
- expect(floatingMenu).toBeTruthy();
429
+ expect(floatingMenu).toBeInTheDocument();
403
430
 
404
431
  fireEvent.click(floatingMenu);
405
432
 
406
- expect(screen.getByTestId("floating-menu-wrapper")).toBeTruthy();
433
+ expect(screen.getByTestId("floating-menu-wrapper")).toBeInTheDocument();
407
434
 
408
435
  Object.defineProperty(wrapperRef, "current", {
409
436
  set(_current) {
@@ -451,17 +478,48 @@ describe("FloatingMenu component rendering", () => {
451
478
  const menuChildren = screen.getByTestId("menu-children");
452
479
  fireEvent.click(menuChildren);
453
480
 
454
- expect(screen.queryByTestId("floating-menu-wrapper")).not.toBeTruthy();
481
+ expect(screen.queryByTestId("floating-menu-wrapper")).not.toBeInTheDocument();
455
482
  });
456
483
 
457
484
  it("shouldn't close the floating div when select option", () => {
485
+ // menu-children lives inside children (the options area), not inside Button,
486
+ // so clicking it is treated as an option selection, not a button click.
487
+ const buttonContent = () => <span>trigger</span>;
488
+
489
+ const defaultProps: IFloatingProps = {
490
+ children: <span data-testid="menu-children">option text</span>,
491
+ Button: buttonContent,
492
+ isInAppBar: true,
493
+ reactiveToHover: true,
494
+ offset: 10,
495
+ closeOnSelect: false,
496
+ };
497
+
498
+ render(
499
+ <ThemeProvider theme={parseTheme(globalTheme)}>
500
+ <FloatingMenu {...defaultProps} />
501
+ </ThemeProvider>,
502
+ );
503
+
504
+ // Open the menu by clicking the button wrapper
505
+ const buttonWrapper = screen.getByTestId("floating-menu-button");
506
+ fireEvent.click(buttonWrapper);
507
+ expect(screen.getByTestId("floating-menu-wrapper")).toBeInTheDocument();
508
+
509
+ // Click on a menu option — with closeOnSelect=false the menu must stay open
510
+ const menuChildren = screen.getByTestId("menu-children");
511
+ fireEvent.click(menuChildren);
512
+
513
+ expect(screen.queryByTestId("floating-menu-wrapper")).toBeInTheDocument();
514
+ });
515
+
516
+ it("should open the floating menu when handle over", () => {
458
517
  const buttonContent = () => <span data-testid="menu-children">texto</span>;
459
518
 
460
519
  const defaultProps: IFloatingProps = {
461
520
  children: <div>hola</div>,
462
521
  Button: buttonContent,
463
522
  isInAppBar: true,
464
- isCheckGroup: false,
465
523
  reactiveToHover: true,
466
524
  offset: 10,
467
525
  closeOnSelect: false,
@@ -509,75 +567,28 @@ describe("FloatingMenu component rendering", () => {
509
567
  useMockRef.mockReturnValueOnce(buttonRef);
510
568
  useMockRef.mockReturnValueOnce(menuOptionsRef);
511
569
 
512
- const { rerender } = render(
570
+ render(
513
571
  <ThemeProvider theme={parseTheme(globalTheme)}>
514
572
  <FloatingMenu {...defaultProps} />
515
573
  </ThemeProvider>,
516
574
  );
517
575
  const floatingMenu = screen.getByTestId("floating-menu");
518
- expect(floatingMenu).toBeTruthy();
519
-
520
- fireEvent.click(floatingMenu);
521
-
522
- expect(screen.getByTestId("floating-menu-wrapper")).toBeTruthy();
576
+ expect(floatingMenu).toBeInTheDocument();
523
577
 
524
- Object.defineProperty(wrapperRef, "current", {
525
- set(_current) {
526
- if (_current) {
527
- jest.spyOn(_current, "contains").mockReturnValue(true);
528
- }
529
- this._current = _current;
530
- },
531
- get() {
532
- return this._current;
533
- },
534
- });
535
- Object.defineProperty(buttonRef, "current", {
536
- set(_current) {
537
- if (_current) {
538
- jest.spyOn(_current, "contains").mockReturnValueOnce(false);
539
- }
540
- this._current = _current;
541
- },
542
- get() {
543
- return this._current;
544
- },
545
- });
546
- Object.defineProperty(menuOptionsRef, "current", {
547
- set(_current) {
548
- if (_current) {
549
- jest.spyOn(_current, "contains").mockReturnValueOnce(true);
550
- }
551
- this._current = _current;
552
- },
553
- get() {
554
- return this._current;
555
- },
556
- });
557
-
558
- useMockRef.mockReturnValueOnce(wrapperRef);
559
- useMockRef.mockReturnValueOnce(buttonRef);
560
- useMockRef.mockReturnValueOnce(menuOptionsRef);
561
- rerender(
562
- <ThemeProvider theme={parseTheme(globalTheme)}>
563
- <FloatingMenu {...defaultProps} />
564
- </ThemeProvider>,
565
- );
566
-
567
- const menuChildren = screen.getByTestId("menu-children");
568
- fireEvent.click(menuChildren);
569
-
570
- expect(screen.queryByTestId("floating-menu-wrapper")).toBeTruthy();
578
+ fireEvent.mouseEnter(floatingMenu);
579
+ expect(screen.getByTestId("floating-menu-wrapper")).toBeInTheDocument();
580
+ // If already open, a second mouseEnter must not close it
581
+ fireEvent.mouseEnter(floatingMenu);
582
+ expect(screen.getByTestId("floating-menu-wrapper")).toBeInTheDocument();
571
583
  });
572
584
 
573
- it("should open the floating menu when handle over", () => {
585
+ it("should close the floating menu when handle leave", () => {
574
586
  const buttonContent = () => <span data-testid="menu-children">texto</span>;
575
587
 
576
588
  const defaultProps: IFloatingProps = {
577
589
  children: <div>hola</div>,
578
590
  Button: buttonContent,
579
591
  isInAppBar: true,
580
- isCheckGroup: false,
581
592
  reactiveToHover: true,
582
593
  offset: 10,
583
594
  closeOnSelect: false,
@@ -631,25 +642,29 @@ describe("FloatingMenu component rendering", () => {
631
642
  </ThemeProvider>,
632
643
  );
633
644
  const floatingMenu = screen.getByTestId("floating-menu");
634
- expect(floatingMenu).toBeTruthy();
645
+ expect(floatingMenu).toBeInTheDocument();
635
646
 
636
647
  fireEvent.mouseEnter(floatingMenu);
637
- expect(screen.getByTestId("floating-menu-wrapper")).toBeTruthy();
638
- //Comprobación extra para testear que si ya está abierto, no gestiona el handleClick
639
- fireEvent.mouseEnter(floatingMenu);
640
- expect(screen.getByTestId("floating-menu-wrapper")).toBeTruthy();
648
+ expect(screen.getByTestId("floating-menu-wrapper")).toBeInTheDocument();
649
+
650
+ fireEvent.mouseLeave(floatingMenu);
651
+ expect(screen.queryByTestId("floating-menu-wrapper")).not.toBeInTheDocument();
641
652
  });
642
653
 
643
- it("should close the floating menu when handle leave", () => {
644
- const buttonContent = () => <span data-testid="menu-children">texto</span>;
654
+ it("should close the floating div when clicking an element with data-close-menu", () => {
655
+ const buttonContent = () => (
656
+ <div>
657
+ <span>trigger</span>
658
+ </div>
659
+ );
645
660
 
646
661
  const defaultProps: IFloatingProps = {
647
- children: <div>hola</div>,
662
+ children: (
663
+ <span data-testid="menu-children" data-close-menu="true">
664
+ Close
665
+ </span>
666
+ ),
648
667
  Button: buttonContent,
649
- isInAppBar: true,
650
- isCheckGroup: false,
651
- reactiveToHover: true,
652
- offset: 10,
653
668
  closeOnSelect: false,
654
669
  };
655
670
 
@@ -671,7 +686,8 @@ describe("FloatingMenu component rendering", () => {
671
686
  Object.defineProperty(buttonRef, "current", {
672
687
  set(_current) {
673
688
  if (_current) {
674
- jest.spyOn(_current, "contains").mockReturnValueOnce(true);
689
+ // true for button click (open), false for option click
690
+ jest.spyOn(_current, "contains").mockReturnValueOnce(true).mockReturnValueOnce(false);
675
691
  }
676
692
  this._current = _current;
677
693
  },
@@ -682,7 +698,8 @@ describe("FloatingMenu component rendering", () => {
682
698
  Object.defineProperty(menuOptionsRef, "current", {
683
699
  set(_current) {
684
700
  if (_current) {
685
- jest.spyOn(_current, "contains").mockReturnValueOnce(false);
701
+ // true when clicking menu-children (optionWasSelected)
702
+ jest.spyOn(_current, "contains").mockReturnValueOnce(true);
686
703
  }
687
704
  this._current = _current;
688
705
  },
@@ -695,18 +712,202 @@ describe("FloatingMenu component rendering", () => {
695
712
  useMockRef.mockReturnValueOnce(buttonRef);
696
713
  useMockRef.mockReturnValueOnce(menuOptionsRef);
697
714
 
698
- render(
715
+ const { rerender } = render(
699
716
  <ThemeProvider theme={parseTheme(globalTheme)}>
700
717
  <FloatingMenu {...defaultProps} />
701
718
  </ThemeProvider>,
702
719
  );
720
+
703
721
  const floatingMenu = screen.getByTestId("floating-menu");
704
- expect(floatingMenu).toBeTruthy();
722
+ fireEvent.click(floatingMenu);
723
+ expect(screen.getByTestId("floating-menu-wrapper")).toBeInTheDocument();
724
+
725
+ rerender(
726
+ <ThemeProvider theme={parseTheme(globalTheme)}>
727
+ <FloatingMenu {...defaultProps} />
728
+ </ThemeProvider>,
729
+ );
730
+
731
+ const menuChildren = screen.getByTestId("menu-children");
732
+ fireEvent.click(menuChildren);
733
+
734
+ expect(screen.queryByTestId("floating-menu-wrapper")).not.toBeInTheDocument();
735
+ });
736
+
737
+ it("should not open when disabled", () => {
738
+ const buttonContent = () => <span>trigger</span>;
739
+
740
+ const defaultProps: IFloatingProps = {
741
+ children: <div>content</div>,
742
+ Button: buttonContent,
743
+ disabled: true,
744
+ };
745
+
746
+ render(
747
+ <ThemeProvider theme={parseTheme(globalTheme)}>
748
+ <FloatingMenu {...defaultProps} />
749
+ </ThemeProvider>,
750
+ );
751
+
752
+ const buttonWrapper = screen.getByTestId("floating-menu-button");
753
+ fireEvent.click(buttonWrapper);
754
+
755
+ expect(screen.queryByTestId("floating-menu-wrapper")).not.toBeInTheDocument();
756
+ });
757
+
758
+ it("should not open on mouse enter when disabled", () => {
759
+ const buttonContent = () => <span>trigger</span>;
760
+
761
+ const defaultProps: IFloatingProps = {
762
+ children: <div>content</div>,
763
+ Button: buttonContent,
764
+ reactiveToHover: true,
765
+ disabled: true,
766
+ };
767
+
768
+ render(
769
+ <ThemeProvider theme={parseTheme(globalTheme)}>
770
+ <FloatingMenu {...defaultProps} />
771
+ </ThemeProvider>,
772
+ );
705
773
 
774
+ const floatingMenu = screen.getByTestId("floating-menu");
706
775
  fireEvent.mouseEnter(floatingMenu);
707
- expect(screen.getByTestId("floating-menu-wrapper")).toBeTruthy();
708
776
 
777
+ expect(screen.queryByTestId("floating-menu-wrapper")).not.toBeInTheDocument();
778
+ });
779
+
780
+ it("should call actionOnClose when clicking outside", () => {
781
+ const actionOnClose = jest.fn();
782
+ const buttonContent = () => <span>trigger</span>;
783
+
784
+ const defaultProps: IFloatingProps = {
785
+ children: <div>content</div>,
786
+ Button: buttonContent,
787
+ actionOnClose,
788
+ };
789
+
790
+ render(
791
+ <ThemeProvider theme={parseTheme(globalTheme)}>
792
+ <FloatingMenu {...defaultProps} />
793
+ </ThemeProvider>,
794
+ );
795
+
796
+ // Open the menu
797
+ const buttonWrapper = screen.getByTestId("floating-menu-button");
798
+ fireEvent.click(buttonWrapper);
799
+ expect(screen.getByTestId("floating-menu-wrapper")).toBeInTheDocument();
800
+
801
+ // Click outside — document is not inside wrapper
802
+ fireEvent.mouseDown(document);
803
+
804
+ expect(actionOnClose).toHaveBeenCalledTimes(1);
805
+ expect(screen.queryByTestId("floating-menu-wrapper")).not.toBeInTheDocument();
806
+ });
807
+
808
+ it("should call actionOnClose when closing via button toggle", () => {
809
+ const actionOnClose = jest.fn();
810
+ const buttonContent = () => <span>trigger</span>;
811
+
812
+ const defaultProps: IFloatingProps = {
813
+ children: <div>content</div>,
814
+ Button: buttonContent,
815
+ actionOnClose,
816
+ };
817
+
818
+ render(
819
+ <ThemeProvider theme={parseTheme(globalTheme)}>
820
+ <FloatingMenu {...defaultProps} />
821
+ </ThemeProvider>,
822
+ );
823
+
824
+ const buttonWrapper = screen.getByTestId("floating-menu-button");
825
+
826
+ fireEvent.click(buttonWrapper);
827
+ expect(screen.getByTestId("floating-menu-wrapper")).toBeInTheDocument();
828
+
829
+ fireEvent.click(buttonWrapper);
830
+
831
+ expect(actionOnClose).toHaveBeenCalledTimes(1);
832
+ expect(screen.queryByTestId("floating-menu-wrapper")).not.toBeInTheDocument();
833
+ });
834
+
835
+ it("should call actionOnClose when closing via mouseLeave", () => {
836
+ const actionOnClose = jest.fn();
837
+ const buttonContent = () => <span>trigger</span>;
838
+
839
+ const defaultProps: IFloatingProps = {
840
+ children: <div>content</div>,
841
+ Button: buttonContent,
842
+ reactiveToHover: true,
843
+ actionOnClose,
844
+ };
845
+
846
+ render(
847
+ <ThemeProvider theme={parseTheme(globalTheme)}>
848
+ <FloatingMenu {...defaultProps} />
849
+ </ThemeProvider>,
850
+ );
851
+
852
+ const buttonWrapper = screen.getByTestId("floating-menu-button");
853
+ fireEvent.click(buttonWrapper);
854
+ expect(screen.getByTestId("floating-menu-wrapper")).toBeInTheDocument();
855
+
856
+ const floatingMenu = screen.getByTestId("floating-menu");
857
+ fireEvent.mouseLeave(floatingMenu);
858
+
859
+ expect(actionOnClose).toHaveBeenCalledTimes(1);
860
+ expect(screen.queryByTestId("floating-menu-wrapper")).not.toBeInTheDocument();
861
+ });
862
+
863
+ it("should close the menu when clicking the button while open (toggle)", () => {
864
+ const buttonContent = () => <span>trigger</span>;
865
+
866
+ const defaultProps: IFloatingProps = {
867
+ children: <span data-testid="menu-option">option</span>,
868
+ Button: buttonContent,
869
+ };
870
+
871
+ render(
872
+ <ThemeProvider theme={parseTheme(globalTheme)}>
873
+ <FloatingMenu {...defaultProps} />
874
+ </ThemeProvider>,
875
+ );
876
+
877
+ const buttonWrapper = screen.getByTestId("floating-menu-button");
878
+
879
+ // First click: open
880
+ fireEvent.click(buttonWrapper);
881
+ expect(screen.getByTestId("floating-menu-wrapper")).toBeInTheDocument();
882
+
883
+ // Second click on button: close
884
+ fireEvent.click(buttonWrapper);
885
+ expect(screen.queryByTestId("floating-menu-wrapper")).not.toBeInTheDocument();
886
+ });
887
+
888
+ it("should not close on mouse leave when reactiveToHover is false", () => {
889
+ const buttonContent = () => <span>trigger</span>;
890
+
891
+ const defaultProps: IFloatingProps = {
892
+ children: <div>content</div>,
893
+ Button: buttonContent,
894
+ reactiveToHover: false,
895
+ };
896
+
897
+ render(
898
+ <ThemeProvider theme={parseTheme(globalTheme)}>
899
+ <FloatingMenu {...defaultProps} />
900
+ </ThemeProvider>,
901
+ );
902
+
903
+ const buttonWrapper = screen.getByTestId("floating-menu-button");
904
+ fireEvent.click(buttonWrapper);
905
+ expect(screen.getByTestId("floating-menu-wrapper")).toBeInTheDocument();
906
+
907
+ const floatingMenu = screen.getByTestId("floating-menu");
709
908
  fireEvent.mouseLeave(floatingMenu);
710
- expect(screen.queryByTestId("floating-menu-wrapper")).not.toBeTruthy();
909
+
910
+ // Still open — mouseLeave only works when reactiveToHover=true
911
+ expect(screen.queryByTestId("floating-menu-wrapper")).toBeInTheDocument();
711
912
  });
712
913
  });
@@ -1,16 +1,24 @@
1
+ import axios from "axios";
1
2
  import { Router } from "react-router-dom";
2
3
 
4
+ import configureStore from "redux-mock-store";
5
+ import thunk from "redux-thunk";
6
+ import { ThemeProvider } from "styled-components";
7
+
3
8
  import { parseTheme } from "@ax/helpers";
4
9
  import Social from "@ax/modules/Settings/Social";
5
10
  import history from "@ax/routes/history";
6
11
  import globalTheme from "@ax/themes/theme.json";
7
12
 
8
- import configureStore from "redux-mock-store";
9
- import thunk from "redux-thunk";
10
- import { ThemeProvider } from "styled-components";
11
-
12
13
  import { act, cleanup, render, screen } from "../../../../../config/jest/test-utils";
13
14
 
15
+ jest.mock("axios");
16
+ const mockedAxios = axios as jest.MockedFunction<typeof axios>;
17
+
18
+ beforeEach(() => {
19
+ mockedAxios.mockResolvedValue({ status: 200, data: {}, statusText: "OK", headers: {}, config: {} as any });
20
+ });
21
+
14
22
  afterEach(cleanup);
15
23
 
16
24
  const mockComponent = () => null;
@@ -1,6 +1,7 @@
1
- import { AxiosResponse } from "axios";
1
+ import type { AxiosResponse } from "axios";
2
+
2
3
  import { template } from "./config";
3
- import { sendRequest, IServiceConfig } from "./utils";
4
+ import { type IServiceConfig, sendRequest } from "./utils";
4
5
 
5
6
  const SERVICES: { [key: string]: IServiceConfig } = {
6
7
  GET_STRUCTURED_DATA: {
@@ -32,7 +33,7 @@ const getCheckGroupItems = async (
32
33
  } = SERVICES.GET_STRUCTURED_DATA;
33
34
 
34
35
  const allLanguagesQuery = allLanguages ? "&allLanguages=true" : "";
35
- const languagesIdsQuery = languagesIds && languagesIds.length ? `&languagesIds=${languagesIds}` : "";
36
+ const languagesIdsQuery = languagesIds?.length ? `&languagesIds=${languagesIds}` : "";
36
37
 
37
38
  SERVICES.GET_STRUCTURED_DATA.dynamicUrl = `${host}${prefix}${siteId}${infix}${source}${suffix}?groupingCategories=on&order=title-ASC${allLanguagesQuery}${languagesIdsQuery}`;
38
39
 
@@ -1,5 +1,5 @@
1
1
  import { template } from "./config";
2
- import { IServiceConfig, sendRequest } from "./utils";
2
+ import { type IServiceConfig, sendRequest } from "./utils";
3
3
 
4
4
  const SERVICES: { [key: string]: IServiceConfig } = {
5
5
  GET_SITE_ITEMS: {
@@ -19,22 +19,29 @@ const SERVICES: { [key: string]: IServiceConfig } = {
19
19
  },
20
20
  };
21
21
 
22
- const getSelectSiteItems = async (siteId: number, entity: string, params?: Record<string, any>, pageID?: number) => {
22
+ const getSelectSiteItems = async (
23
+ siteId: number,
24
+ entity: string,
25
+ params?: Record<string, any>,
26
+ pageID?: number,
27
+ forceLanguage?: number,
28
+ ) => {
23
29
  const { host, endpoint } = SERVICES.GET_SITE_ITEMS;
24
30
 
25
31
  let urlParams = "";
26
32
  if (params) {
27
33
  for (const prop in params) {
28
- urlParams = urlParams + `${prop}=${params[prop]}&`;
34
+ urlParams = `${urlParams}${prop}=${params[prop]}&`;
29
35
  }
30
- urlParams = urlParams.length ? "?" + urlParams.slice(0, -1) : "";
36
+ urlParams = urlParams.length ? `?${urlParams.slice(0, -1)}` : "";
31
37
  }
32
38
 
33
39
  SERVICES.GET_SITE_ITEMS.dynamicUrl = pageID
34
40
  ? `${host}${endpoint}${siteId}/${entity}/${pageID}${urlParams}`
35
41
  : `${host}${endpoint}${siteId}/${entity}${urlParams}`;
36
42
 
37
- return sendRequest(SERVICES.GET_SITE_ITEMS);
43
+ const headers = forceLanguage ? { lang: forceLanguage } : undefined;
44
+ return sendRequest(SERVICES.GET_SITE_ITEMS, undefined, headers);
38
45
  };
39
46
 
40
47
  const getSelectItems = async (entity: string, entityId?: number | string, lang?: number) => {
@@ -1,7 +1,5 @@
1
- import React from "react";
2
-
3
1
  import { FloatingMenu, Icon, IconAction, Tooltip } from "@ax/components";
4
- import { IActionMenuOption } from "@ax/types";
2
+ import type { IActionMenuOption } from "@ax/types";
5
3
 
6
4
  import * as S from "./style";
7
5