@tsed/react-formio 3.0.0-alpha.10 → 3.0.0-alpha.12

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 (215) hide show
  1. package/dist/atoms/icon/Icon.js.map +1 -1
  2. package/dist/chunks/index.js +33 -30
  3. package/dist/chunks/index.js.map +1 -1
  4. package/dist/chunks/index.module.js +16 -16
  5. package/dist/chunks/index.module.js.map +1 -1
  6. package/dist/chunks/react-select-animated.esm.js +1204 -1101
  7. package/dist/chunks/react-select-animated.esm.js.map +1 -1
  8. package/dist/hooks/keyboard.constants.d.ts +38 -0
  9. package/dist/hooks/keyboard.constants.js +7 -0
  10. package/dist/hooks/keyboard.constants.js.map +1 -0
  11. package/dist/hooks/useKeyboardControls.d.ts +12 -0
  12. package/dist/hooks/useKeyboardControls.js +35 -0
  13. package/dist/hooks/useKeyboardControls.js.map +1 -0
  14. package/dist/hooks/useTooltip.js.map +1 -1
  15. package/dist/interfaces/Operation.d.ts +12 -2
  16. package/dist/molecules/alert/Alert.js.map +1 -1
  17. package/dist/molecules/button/Button.d.ts +18 -5
  18. package/dist/molecules/button/Button.js +22 -26
  19. package/dist/molecules/button/Button.js.map +1 -1
  20. package/dist/molecules/card/Card.js +7 -5
  21. package/dist/molecules/card/Card.js.map +1 -1
  22. package/dist/molecules/forms/form-control/FormControl.js.map +1 -1
  23. package/dist/molecules/forms/input-tags/InputTags.js +14 -14
  24. package/dist/molecules/forms/input-tags/InputTags.js.map +1 -1
  25. package/dist/molecules/forms/input-tags/components/ChoicesTags.js +26 -26
  26. package/dist/molecules/forms/input-tags/components/ChoicesTags.js.map +1 -1
  27. package/dist/molecules/forms/input-tags/components/ReactTags.js +289 -300
  28. package/dist/molecules/forms/input-tags/components/ReactTags.js.map +1 -1
  29. package/dist/molecules/forms/input-text/InputText.js +3 -3
  30. package/dist/molecules/forms/input-text/InputText.js.map +1 -1
  31. package/dist/molecules/forms/select/Select.js.map +1 -1
  32. package/dist/molecules/forms/select/components/ChoicesSelect.js +71 -73
  33. package/dist/molecules/forms/select/components/ChoicesSelect.js.map +1 -1
  34. package/dist/molecules/forms/select/components/HtmlSelect.js.map +1 -1
  35. package/dist/molecules/forms/select/components/ReactSelect.js +13 -14
  36. package/dist/molecules/forms/select/components/ReactSelect.js.map +1 -1
  37. package/dist/molecules/forms/select/components/choices.template.js +2340 -2257
  38. package/dist/molecules/forms/select/components/choices.template.js.map +1 -1
  39. package/dist/molecules/forms/select/hooks/useOptions.js.map +1 -1
  40. package/dist/molecules/loader/Loader.js.map +1 -1
  41. package/dist/molecules/modal/Modal.js +23 -24
  42. package/dist/molecules/modal/Modal.js.map +1 -1
  43. package/dist/molecules/pagination/Pagination.js +19 -19
  44. package/dist/molecules/pagination/Pagination.js.map +1 -1
  45. package/dist/molecules/pagination/PaginationButton.js.map +1 -1
  46. package/dist/molecules/pagination/utils/getPageNumbers.js.map +1 -1
  47. package/dist/molecules/table/Table.d.ts +11 -3
  48. package/dist/molecules/table/Table.js +31 -32
  49. package/dist/molecules/table/Table.js.map +1 -1
  50. package/dist/molecules/table/components/DefaultArrowSort.js.map +1 -1
  51. package/dist/molecules/table/components/DefaultCell.js.map +1 -1
  52. package/dist/molecules/table/components/DefaultCellFooter.js.map +1 -1
  53. package/dist/molecules/table/components/DefaultCellHeader.js.map +1 -1
  54. package/dist/molecules/table/components/DefaultCellOperations.d.ts +12 -4
  55. package/dist/molecules/table/components/DefaultCellOperations.js +13 -7
  56. package/dist/molecules/table/components/DefaultCellOperations.js.map +1 -1
  57. package/dist/molecules/table/components/DefaultFilter.js.map +1 -1
  58. package/dist/molecules/table/components/DefaultOperationButton.d.ts +12 -4
  59. package/dist/molecules/table/components/DefaultOperationButton.js +1 -1
  60. package/dist/molecules/table/components/DefaultOperationButton.js.map +1 -1
  61. package/dist/molecules/table/filters/RangeFilter.js +23 -24
  62. package/dist/molecules/table/filters/RangeFilter.js.map +1 -1
  63. package/dist/molecules/table/filters/SelectFilter.js +11 -13
  64. package/dist/molecules/table/filters/SelectFilter.js.map +1 -1
  65. package/dist/molecules/table/filters/TextFieldFilter.js.map +1 -1
  66. package/dist/molecules/table/hooks/useTable.d.ts +12 -4
  67. package/dist/molecules/table/hooks/useTable.js +7 -7
  68. package/dist/molecules/table/hooks/useTable.js.map +1 -1
  69. package/dist/molecules/table/hooks/useUniqValues.js.map +1 -1
  70. package/dist/molecules/table/utils/mapFormToColumns.js +20 -21
  71. package/dist/molecules/table/utils/mapFormToColumns.js.map +1 -1
  72. package/dist/molecules/tabs/Tab.d.ts +13 -0
  73. package/dist/molecules/tabs/Tab.js +66 -0
  74. package/dist/molecules/tabs/Tab.js.map +1 -0
  75. package/dist/molecules/tabs/TabList.d.ts +2 -0
  76. package/dist/molecules/tabs/TabList.js +23 -0
  77. package/dist/molecules/tabs/TabList.js.map +1 -0
  78. package/dist/molecules/tabs/TabPanel.d.ts +9 -0
  79. package/dist/molecules/tabs/TabPanel.js +27 -0
  80. package/dist/molecules/tabs/TabPanel.js.map +1 -0
  81. package/dist/molecules/tabs/Tabs.d.ts +4 -16
  82. package/dist/molecules/tabs/Tabs.js +7 -67
  83. package/dist/molecules/tabs/Tabs.js.map +1 -1
  84. package/dist/molecules/tabs/TabsBody.d.ts +1 -0
  85. package/dist/molecules/tabs/TabsBody.js +10 -0
  86. package/dist/molecules/tabs/TabsBody.js.map +1 -0
  87. package/dist/molecules/tabs/TabsLegacy.d.ts +17 -0
  88. package/dist/molecules/tabs/TabsLegacy.js +49 -0
  89. package/dist/molecules/tabs/TabsLegacy.js.map +1 -0
  90. package/dist/molecules/tabs/all.d.ts +5 -0
  91. package/dist/molecules/tabs/all.js +13 -0
  92. package/dist/molecules/tabs/all.js.map +1 -0
  93. package/dist/molecules/tabs/context/TabControl.d.ts +52 -0
  94. package/dist/molecules/tabs/context/TabControl.js +85 -0
  95. package/dist/molecules/tabs/context/TabControl.js.map +1 -0
  96. package/dist/molecules/tabs/hooks/tabControl.d.ts +44 -0
  97. package/dist/molecules/tabs/hooks/tabControl.js +34 -0
  98. package/dist/molecules/tabs/hooks/tabControl.js.map +1 -0
  99. package/dist/organisms/form/Form.js.map +1 -1
  100. package/dist/organisms/form/access/FormAccess.js +41 -41
  101. package/dist/organisms/form/access/FormAccess.js.map +1 -1
  102. package/dist/organisms/form/access/FormAccess.schema.js.map +1 -1
  103. package/dist/organisms/form/access/FormAccess.utils.js +2 -2
  104. package/dist/organisms/form/access/FormAccess.utils.js.map +1 -1
  105. package/dist/organisms/form/{action → actions}/FormAction.js +7 -8
  106. package/dist/organisms/form/actions/FormAction.js.map +1 -0
  107. package/dist/organisms/form/builder/FormBuilder.js.map +1 -1
  108. package/dist/organisms/form/builder/FormEdit.d.ts +3 -1
  109. package/dist/organisms/form/builder/FormEdit.js +38 -35
  110. package/dist/organisms/form/builder/FormEdit.js.map +1 -1
  111. package/dist/organisms/form/builder/FormEdit.reducer.js.map +1 -1
  112. package/dist/organisms/form/builder/FormEditCtas.js +34 -34
  113. package/dist/organisms/form/builder/FormEditCtas.js.map +1 -1
  114. package/dist/organisms/form/builder/FormParameters.js.map +1 -1
  115. package/dist/organisms/form/builder/useFormBuilder.js +41 -38
  116. package/dist/organisms/form/builder/useFormBuilder.js.map +1 -1
  117. package/dist/organisms/form/builder/useFormEdit.js +1 -1
  118. package/dist/organisms/form/builder/useFormEdit.js.map +1 -1
  119. package/dist/organisms/form/exports/FormExport.d.ts +5 -0
  120. package/dist/organisms/form/exports/FormExport.js +55 -0
  121. package/dist/organisms/form/exports/FormExport.js.map +1 -0
  122. package/dist/organisms/form/preview/FormPreview.d.ts +6 -0
  123. package/dist/organisms/form/preview/FormPreview.js +11 -0
  124. package/dist/organisms/form/preview/FormPreview.js.map +1 -0
  125. package/dist/organisms/form/settings/FormSettings.js +24 -24
  126. package/dist/organisms/form/settings/FormSettings.js.map +1 -1
  127. package/dist/organisms/form/settings/FormSettings.schema.js.map +1 -1
  128. package/dist/organisms/form/settings/FormSettings.utils.js.map +1 -1
  129. package/dist/organisms/form/useForm.js +664 -661
  130. package/dist/organisms/form/useForm.js.map +1 -1
  131. package/dist/organisms/modal/RemoveModal.js +16 -17
  132. package/dist/organisms/modal/RemoveModal.js.map +1 -1
  133. package/dist/organisms/table/actions/ActionsTable.js.map +1 -1
  134. package/dist/organisms/table/forms/FormsTable.js.map +1 -1
  135. package/dist/organisms/table/forms/components/FormsCell.js.map +1 -1
  136. package/dist/organisms/table/submissions/SubmissionsTable.d.ts +11 -3
  137. package/dist/organisms/table/submissions/SubmissionsTable.js +4 -1
  138. package/dist/organisms/table/submissions/SubmissionsTable.js.map +1 -1
  139. package/dist/organisms/views/FormViews.d.ts +24 -0
  140. package/dist/organisms/views/FormViews.js +96 -0
  141. package/dist/organisms/views/FormViews.js.map +1 -0
  142. package/dist/registries/components.js.map +1 -1
  143. package/dist/utils/getEventValue.js.map +1 -1
  144. package/dist/utils/iconClass.js.map +1 -1
  145. package/dist/utils/mapPagination.js.map +1 -1
  146. package/dist/utils/stopPropagationWrapper.js.map +1 -1
  147. package/package.json +7 -7
  148. package/src/atoms/icon/Icon.stories.tsx +1 -1
  149. package/src/hooks/keyboard.constants.ts +40 -0
  150. package/src/hooks/useKeyboardControls.spec.tsx +208 -0
  151. package/src/hooks/useKeyboardControls.ts +84 -0
  152. package/src/interfaces/Operation.ts +9 -3
  153. package/src/molecules/button/Button.stories.tsx +1 -1
  154. package/src/molecules/button/Button.tsx +43 -24
  155. package/src/molecules/card/Card.tsx +4 -0
  156. package/src/molecules/forms/form-control/FormControl.stories.tsx +1 -1
  157. package/src/molecules/forms/input-tags/InputTags.tsx +1 -1
  158. package/src/molecules/forms/input-tags/components/ChoicesTags.stories.tsx +1 -1
  159. package/src/molecules/forms/input-tags/components/ReactTags.stories.tsx +1 -1
  160. package/src/molecules/forms/input-text/InputText.stories.tsx +2 -2
  161. package/src/molecules/forms/select/components/ChoicesSelect.stories.tsx +2 -2
  162. package/src/molecules/forms/select/components/HtmlSelect.stories.tsx +2 -2
  163. package/src/molecules/forms/select/components/ReactSelect.stories.tsx +2 -2
  164. package/src/molecules/loader/Loader.stories.tsx +1 -1
  165. package/src/molecules/modal/Modal.stories.tsx +1 -1
  166. package/src/molecules/pagination/Pagination.stories.tsx +1 -8
  167. package/src/molecules/pagination/Pagination.tsx +0 -1
  168. package/src/molecules/table/Table.stories.tsx +36 -3
  169. package/src/molecules/table/Table.tsx +12 -6
  170. package/src/molecules/table/components/DefaultCellOperations.tsx +13 -7
  171. package/src/molecules/table/components/DefaultOperationButton.tsx +5 -4
  172. package/src/molecules/table/filters/SelectFilter.tsx +1 -1
  173. package/src/molecules/table/hooks/useTable.tsx +5 -5
  174. package/src/molecules/tabs/Tab.tsx +106 -0
  175. package/src/molecules/tabs/TabList.tsx +37 -0
  176. package/src/molecules/tabs/TabPanel.tsx +37 -0
  177. package/src/molecules/tabs/Tabs.spec.tsx +126 -73
  178. package/src/molecules/tabs/Tabs.stories.tsx +298 -65
  179. package/src/molecules/tabs/Tabs.tsx +10 -81
  180. package/src/molecules/tabs/TabsBody.tsx +11 -0
  181. package/src/molecules/tabs/TabsLegacy.stories.tsx +103 -0
  182. package/src/molecules/tabs/TabsLegacy.tsx +84 -0
  183. package/src/molecules/tabs/all.ts +5 -0
  184. package/src/molecules/tabs/context/TabControl.tsx +166 -0
  185. package/src/molecules/tabs/hooks/tabControl.spec.tsx +388 -0
  186. package/src/molecules/tabs/hooks/tabControl.ts +52 -0
  187. package/src/organisms/__fixtures__/form-firstname.fixture.json +1 -0
  188. package/src/organisms/__fixtures__/form.fixture.json +1 -0
  189. package/src/organisms/form/Form.stories.tsx +94 -118
  190. package/src/organisms/form/access/FormAccess.stories.tsx +2 -2
  191. package/src/organisms/form/actions/FormAction.stories.tsx +422 -0
  192. package/src/organisms/form/builder/FormBuilder.stories.tsx +4 -1
  193. package/src/organisms/form/builder/FormEdit.stories.tsx +1 -1
  194. package/src/organisms/form/builder/FormEdit.tsx +7 -1
  195. package/src/organisms/form/builder/useFormBuilder.ts +5 -1
  196. package/src/organisms/form/builder/useFormEdit.ts +1 -1
  197. package/src/organisms/form/exports/FormExport.stories.tsx +71 -0
  198. package/src/organisms/form/exports/FormExport.tsx +58 -0
  199. package/src/organisms/form/preview/FormPreview.stories.tsx +61 -0
  200. package/src/organisms/form/preview/FormPreview.tsx +21 -0
  201. package/src/organisms/modal/RemoveModal.stories.tsx +1 -1
  202. package/src/organisms/table/actions/ActionsTable.stories.tsx +38 -36
  203. package/src/organisms/table/submissions/SubmissionsTable.stories.tsx +103 -57
  204. package/src/organisms/table/submissions/SubmissionsTable.tsx +10 -4
  205. package/src/organisms/views/FormViews.stories.tsx +224 -0
  206. package/src/organisms/views/FormViews.tsx +146 -0
  207. package/vite.config.mts +2 -2
  208. package/dist/organisms/form/action/FormAction.js.map +0 -1
  209. package/src/organisms/form/action/FormAction.stories.tsx +0 -364
  210. package/tsconfig.app.json +0 -11
  211. package/tsconfig.json +0 -21
  212. package/tsconfig.node.json +0 -13
  213. package/tsconfig.spec.json +0 -14
  214. /package/dist/organisms/form/{action → actions}/FormAction.d.ts +0 -0
  215. /package/src/organisms/form/{action → actions}/FormAction.tsx +0 -0
@@ -1,87 +1,140 @@
1
- import { fireEvent, render, screen } from "@testing-library/react";
1
+ import { render, screen, waitFor } from "@testing-library/react";
2
+ import { userEvent } from "@testing-library/user-event";
2
3
  import { expect, vi } from "vitest";
3
4
 
4
- import { Tabs } from "./Tabs";
5
- import { Sandbox } from "./Tabs.stories";
5
+ import { Tab } from "./Tab.js";
6
+ import { TabList } from "./TabList.js";
7
+ import { TabPanel } from "./TabPanel.js";
8
+ import { Tabs } from "./Tabs.js";
9
+ import { TabsBody } from "./TabsBody.js";
10
+
11
+ const props = {
12
+ reverse: false,
13
+ items: [
14
+ {
15
+ action: "edit",
16
+ exact: true,
17
+ icon: "edit",
18
+ label: "Edit",
19
+ children: <div className='bg-red-100 p-5'>Edit</div>
20
+ },
21
+ {
22
+ action: "submissions",
23
+ exact: false,
24
+ icon: "data",
25
+ label: "Data",
26
+ children: <div className='bg-orange-100 p-5'>Data</div>
27
+ },
28
+ {
29
+ action: "preview",
30
+ exact: true,
31
+ icon: "test-tube",
32
+ label: "Preview",
33
+ children: <div className='bg-yellow-100 p-5'>Preview</div>
34
+ },
35
+ {
36
+ action: "actions",
37
+ exact: false,
38
+ icon: "paper-plane",
39
+ label: "Actions",
40
+ children: <div className='bg-green-100 p-5'>Actions</div>
41
+ },
42
+ {
43
+ action: "access",
44
+ exact: true,
45
+ icon: "lock",
46
+ label: "Access",
47
+ children: <div className='bg-blue-100 p-5'>Access</div>
48
+ },
49
+ {
50
+ action: "export",
51
+ exact: true,
52
+ icon: "download",
53
+ label: "Export",
54
+ children: <div className='bg-purple-100 p-5'>Export</div>
55
+ },
56
+ {
57
+ action: "delete",
58
+ exact: true,
59
+ icon: "trash",
60
+ label: "Delete",
61
+ roles: ["administrator", "owner"],
62
+ children: <div className='bg-gray-100 p-5'>Trash</div>
63
+ }
64
+ ],
65
+ onClick: vi.fn()
66
+ };
67
+
68
+ function TestComponent({
69
+ selected,
70
+ onClick,
71
+ reverse,
72
+ items
73
+ }: {
74
+ selected?: number;
75
+ onClick?: (item: any) => void;
76
+ reverse?: boolean;
77
+ items: any[];
78
+ }) {
79
+ return (
80
+ <Tabs selected={selected}>
81
+ <TabList>
82
+ {items.map((item, index) => {
83
+ return (
84
+ <Tab onClick={() => onClick?.(item)} key={index} icon={item.icon} value={index} className={reverse ? "-reverse" : ""}>
85
+ {item.label}
86
+ </Tab>
87
+ );
88
+ })}
89
+ </TabList>
90
+ <TabsBody>
91
+ {items.map((item, index) => {
92
+ return (
93
+ <TabPanel key={index} value={index}>
94
+ {item.children}
95
+ </TabPanel>
96
+ );
97
+ })}
98
+ </TabsBody>
99
+ </Tabs>
100
+ );
101
+ }
6
102
 
7
103
  describe("<Tabs>", () => {
8
- it("should display the tabs component and children", () => {
9
- const items = [
10
- {
11
- action: "back",
12
- exact: true,
13
- icon: "chevron-left",
14
- back: true
15
- },
16
- {
17
- action: "edit",
18
- exact: true,
19
- icon: "edit",
20
- label: "Edit"
21
- }
22
- ];
104
+ it("should display the tabs component and children", async () => {
105
+ render(<TestComponent {...props} />);
23
106
 
24
- render(<Tabs {...Sandbox.args} items={items} />);
107
+ // Vérifie que tous les onglets sont présents
108
+ expect(screen.getByRole("tab", { name: "Edit" })).toBeInTheDocument();
109
+ expect(screen.getByRole("tab", { name: "Data" })).toBeInTheDocument();
110
+ expect(screen.getByRole("tab", { name: "Preview" })).toBeInTheDocument();
111
+ expect(screen.getByRole("tab", { name: "Actions" })).toBeInTheDocument();
112
+ expect(screen.getByRole("tab", { name: "Access" })).toBeInTheDocument();
113
+ expect(screen.getByRole("tab", { name: "Export" })).toBeInTheDocument();
114
+ expect(screen.getByRole("tab", { name: "Delete" })).toBeInTheDocument();
25
115
 
26
- const tabsComponent = screen.getByTestId("tabs-comp");
116
+ await waitFor(() => expect(screen.getByRole("tabpanel")).toHaveTextContent("Edit"));
27
117
 
28
- const buttonsTabWrapper = screen.getAllByTitle("button-wrapper");
29
- const chevronLeftButtonTabWrapper = buttonsTabWrapper[0];
30
- const editButtonTabWrapper = buttonsTabWrapper[1];
118
+ // Clique sur l'onglet "Preview" et vérifie le contenu
119
+ await userEvent.click(screen.getByRole("tab", { name: "Preview" }));
31
120
 
32
- const buttonsTab = screen.getAllByTitle("button-tab");
33
- const chevronLeftButtonTab = buttonsTab[0];
34
- const editButtonTab = buttonsTab[1];
121
+ expect(props.onClick).toHaveBeenCalledWith(
122
+ expect.objectContaining({
123
+ action: "preview"
124
+ })
125
+ );
35
126
 
36
- const fontAwsomeChevronLeftIcon = "fa fa-chevron-left";
37
- const fontAwsomeEditIcon = "fa fa-edit";
127
+ await waitFor(() => expect(screen.getByRole("tabpanel")).toHaveTextContent("Preview"));
38
128
 
39
- expect(tabsComponent).toBeInTheDocument();
129
+ // Clique sur l'onglet "Delete" et vérifie le contenu
130
+ await userEvent.click(screen.getByRole("tab", { name: "Delete" }));
40
131
 
41
- expect(chevronLeftButtonTabWrapper).toContainElement(chevronLeftButtonTab);
42
- expect(chevronLeftButtonTabWrapper).toContainHTML("-back");
43
- expect(chevronLeftButtonTab).toBeInTheDocument();
44
- expect(chevronLeftButtonTab).toContainHTML(fontAwsomeChevronLeftIcon);
45
- expect(chevronLeftButtonTab).toHaveTextContent("");
132
+ expect(props.onClick).toHaveBeenCalledWith(
133
+ expect.objectContaining({
134
+ action: "delete"
135
+ })
136
+ );
46
137
 
47
- expect(editButtonTabWrapper).toContainElement(editButtonTab);
48
- expect(editButtonTabWrapper).not.toContainHTML("-back");
49
- expect(editButtonTab).toBeInTheDocument();
50
- expect(editButtonTab).toContainHTML(fontAwsomeEditIcon);
51
- expect(editButtonTab).toHaveTextContent("Edit");
52
- });
53
-
54
- it("should call dispatcher when clicking on a button tab", () => {
55
- const items = [
56
- {
57
- action: "back",
58
- exact: true,
59
- icon: "chevron-left",
60
- back: true
61
- },
62
- {
63
- action: "edit",
64
- exact: true,
65
- icon: "edit",
66
- label: "Edit"
67
- }
68
- ];
69
- const onClick = vi.fn();
70
-
71
- render(<Tabs items={items} onClick={onClick} />);
72
-
73
- const buttonsTab = screen.getAllByTitle("button-tab");
74
- const chevronLeftButtonTab = buttonsTab[0];
75
- const editButtonTab = buttonsTab[1];
76
-
77
- fireEvent.click(chevronLeftButtonTab);
78
-
79
- expect(onClick).toHaveBeenCalledTimes(1);
80
- expect(onClick).toHaveBeenCalledWith(items[0]);
81
-
82
- fireEvent.click(editButtonTab);
83
-
84
- expect(onClick).toHaveBeenCalledTimes(2);
85
- expect(onClick).toHaveBeenCalledWith(items[1]);
138
+ await waitFor(() => expect(screen.getByRole("tabpanel")).toHaveTextContent("Trash"));
86
139
  });
87
140
  });
@@ -1,141 +1,374 @@
1
- import { Meta, StoryObj } from "@storybook/react";
2
- import { useState } from "react";
1
+ import "./all.js";
3
2
 
4
- import { Tabs } from "./Tabs";
3
+ import { Meta, StoryObj } from "@storybook/react-vite";
4
+ import { expect, fn, userEvent, waitFor, within } from "storybook/test";
5
5
 
6
- const useTabs = (args: any) => {
7
- const [current, onClick] = useState(args.value);
8
-
9
- return {
10
- ...args,
11
- current,
12
- onClick
13
- };
14
- };
6
+ import { Icon } from "../../atoms/icon/Icon.js";
7
+ import { Button } from "../button/Button.js";
8
+ import { Tab } from "./Tab.js";
9
+ import { TabList } from "./TabList.js";
10
+ import { TabPanel } from "./TabPanel.js";
11
+ import { Tabs } from "./Tabs.js";
12
+ import { TabsBody } from "./TabsBody.js";
15
13
 
16
14
  /**
17
- * Tabs component
15
+ * Tabs component.
16
+ *
17
+ * You can import this component and use it like:
18
+ *
19
+ * ```tsx
20
+ * import {Tabs} from "@tsed/react-formio/molecules/tabs/all"
21
+ *
22
+ * or
23
+ *
24
+ * import {Tabs} from "@tsed/react-formio/molecules/tabs/Tabs";
25
+ * ```
26
+ *
27
+ * Tabs component support DI container and can be used with custom component. Here is the list of components that you can override:
28
+ *
29
+ * - Tab
30
+ * - TabList
31
+ * - TabPanel
32
+ * - Tabs
33
+ * - TabsBody
18
34
  *
19
35
  * ```tsx
20
- * import {Modal} from "@tsed/react-formio/molecules/tabs/Tabs";
36
+ * function CustomTab() {
37
+ *
38
+ * }
39
+ *
40
+ * registerComponent("Tab", CustomTab);
21
41
  * ```
22
42
  */
23
43
  export default {
24
44
  title: "Tabs",
25
45
  component: Tabs,
26
- argTypes: {},
46
+ argTypes: {
47
+ reverse: {
48
+ control: "boolean",
49
+ description: "Reverse the order of the tabs"
50
+ },
51
+ selected: {
52
+ control: { type: "number", min: 0, max: 10 },
53
+ description: "Selected tab index"
54
+ },
55
+ items: {
56
+ control: "object",
57
+ description: "Array of tab items with label, icon, and content"
58
+ }
59
+ },
27
60
  parameters: {},
28
- render: (args: any) => {
29
- // eslint-disable-next-line react-hooks/rules-of-hooks
30
- const tabs = useTabs(args);
61
+ args: {}
62
+ } satisfies Meta<typeof Tabs>;
63
+
64
+ type Story = StoryObj<typeof Tabs>;
65
+
66
+ export const Usage: Story = {
67
+ args: {
68
+ reverse: false,
69
+ items: [
70
+ {
71
+ action: "edit",
72
+ exact: true,
73
+ icon: "edit",
74
+ label: "Edit",
75
+ children: <div className='bg-red-100 p-5'>Edit</div>
76
+ },
77
+ {
78
+ action: "submissions",
79
+ exact: false,
80
+ icon: "data",
81
+ label: "Data",
82
+ children: <div className='bg-orange-100 p-5'>Data</div>
83
+ },
84
+ {
85
+ action: "preview",
86
+ exact: true,
87
+ icon: "test-tube",
88
+ label: "Preview",
89
+ children: <div className='bg-yellow-100 p-5'>Preview</div>
90
+ },
91
+ {
92
+ action: "actions",
93
+ exact: false,
94
+ icon: "paper-plane",
95
+ label: "Actions",
96
+ children: <div className='bg-green-100 p-5'>Actions</div>
97
+ },
98
+ {
99
+ action: "access",
100
+ exact: true,
101
+ icon: "lock",
102
+ label: "Access",
103
+ children: <div className='bg-blue-100 p-5'>Access</div>
104
+ },
105
+ {
106
+ action: "export",
107
+ exact: true,
108
+ icon: "download",
109
+ label: "Export",
110
+ children: <div className='bg-purple-100 p-5'>Export</div>
111
+ },
112
+ {
113
+ action: "delete",
114
+ exact: true,
115
+ icon: "trash",
116
+ label: "Delete",
117
+ roles: ["administrator", "owner"],
118
+ children: <div className='bg-gray-100 p-5'>Trash</div>
119
+ }
120
+ ],
121
+ onClick: fn()
122
+ },
123
+
124
+ play: async ({ canvasElement, args }) => {
125
+ const canvas = within(canvasElement);
126
+
127
+ // Vérifie que tous les onglets sont présents
128
+ await expect(canvas.getByRole("tab", { name: "Edit" })).toBeInTheDocument();
129
+ await expect(canvas.getByRole("tab", { name: "Data" })).toBeInTheDocument();
130
+ await expect(canvas.getByRole("tab", { name: "Preview" })).toBeInTheDocument();
131
+ await expect(canvas.getByRole("tab", { name: "Actions" })).toBeInTheDocument();
132
+ await expect(canvas.getByRole("tab", { name: "Access" })).toBeInTheDocument();
133
+ await expect(canvas.getByRole("tab", { name: "Export" })).toBeInTheDocument();
134
+ await expect(canvas.getByRole("tab", { name: "Delete" })).toBeInTheDocument();
135
+
136
+ await waitFor(() => expect(canvas.getByRole("tabpanel")).toHaveTextContent("Edit"));
137
+
138
+ // Clique sur l'onglet "Preview" et vérifie le contenu
139
+ await userEvent.click(canvas.getByRole("tab", { name: "Preview" }));
140
+
141
+ await expect(args.onClick).toHaveBeenCalledWith(
142
+ expect.objectContaining({
143
+ action: "preview"
144
+ })
145
+ );
146
+
147
+ await waitFor(() => expect(canvas.getByRole("tabpanel")).toHaveTextContent("Preview"));
148
+
149
+ // Clique sur l'onglet "Delete" et vérifie le contenu
150
+ await userEvent.click(canvas.getByRole("tab", { name: "Delete" }));
151
+ await expect(args.onClick).toHaveBeenCalledWith(
152
+ expect.objectContaining({
153
+ action: "delete"
154
+ })
155
+ );
156
+
157
+ await waitFor(() => expect(canvas.getByRole("tabpanel")).toHaveTextContent("Trash"));
158
+ },
159
+
160
+ render: (args) => {
161
+ const items: any[] = args.items.filter((item: any) => item.label || item.icon);
31
162
 
32
163
  return (
33
164
  <div className={"border-gray-300 border-1 shadow"}>
34
- <Tabs {...tabs} i18n={(f) => f}>
35
- <div className={"p-5"}>{tabs?.current?.action}</div>
165
+ <Tabs selected={args.selected}>
166
+ <TabList>
167
+ {items.map((item, index) => {
168
+ return (
169
+ <Tab
170
+ onClick={() => args.onClick(item)}
171
+ key={index}
172
+ icon={item.icon}
173
+ value={index}
174
+ className={args.reverse ? "-reverse" : ""}
175
+ >
176
+ {item.label}
177
+ </Tab>
178
+ );
179
+ })}
180
+ </TabList>
181
+ <TabsBody>
182
+ {items.map((item, index) => {
183
+ return (
184
+ <TabPanel key={index} value={index}>
185
+ {item.children}
186
+ </TabPanel>
187
+ );
188
+ })}
189
+ </TabsBody>
36
190
  </Tabs>
37
191
  </div>
38
192
  );
39
193
  }
40
- } satisfies Meta<typeof Tabs>;
41
-
42
- type Story = StoryObj<typeof Tabs>;
194
+ };
43
195
 
44
- export const Sandbox: Story = {
196
+ export const WithSelectedTab: Story = {
45
197
  args: {
198
+ selected: 2,
199
+ reverse: false,
46
200
  items: [
47
- {
48
- action: "back",
49
- exact: true,
50
- icon: "chevron-left",
51
- back: true
52
- },
53
201
  {
54
202
  action: "edit",
55
203
  exact: true,
56
204
  icon: "edit",
57
- label: "Edit"
205
+ label: "Edit",
206
+ children: <div className='bg-red-100 p-5'>Edit</div>
58
207
  },
59
208
  {
60
209
  action: "submissions",
61
210
  exact: false,
62
211
  icon: "data",
63
- label: "Data"
212
+ label: "Data",
213
+ children: <div className='bg-orange-100 p-5'>Data</div>
64
214
  },
65
215
  {
66
216
  action: "preview",
67
217
  exact: true,
68
218
  icon: "test-tube",
69
- label: "Preview"
219
+ label: "Preview",
220
+ children: <div className='bg-yellow-100 p-5'>Preview</div>
70
221
  },
71
222
  {
72
223
  action: "actions",
73
224
  exact: false,
74
225
  icon: "paper-plane",
75
- label: "Actions"
226
+ label: "Actions",
227
+ children: <div className='bg-green-100 p-5'>Actions</div>
76
228
  },
77
229
  {
78
230
  action: "access",
79
231
  exact: true,
80
232
  icon: "lock",
81
- label: "Access"
233
+ label: "Access",
234
+ children: <div className='bg-blue-100 p-5'>Access</div>
82
235
  },
83
236
  {
84
237
  action: "export",
85
238
  exact: true,
86
239
  icon: "download",
87
- label: "Export"
240
+ label: "Export",
241
+ children: <div className='bg-purple-100 p-5'>Export</div>
88
242
  },
89
243
  {
90
244
  action: "delete",
91
245
  exact: true,
92
246
  icon: "trash",
93
247
  label: "Delete",
94
- roles: ["administrator", "owner"]
248
+ roles: ["administrator", "owner"],
249
+ children: <div className='bg-gray-100 p-5'>Trash</div>
95
250
  }
96
251
  ]
97
- }
98
- };
99
-
100
- function AddButton({ onCreate }: any) {
101
- return (
102
- <div>
103
- <button onClick={onCreate}>+</button>
104
- </div>
105
- );
106
- }
107
-
108
- function HeaderChildren() {
109
- return <div className={"px-3 bg-gray-100 border-b-1 border-gray-light"}>test</div>;
110
- }
111
-
112
- export const WithCloseable = {
113
- render: (args: any) => {
114
- args.value = args.value === undefined ? 0 : args.value;
115
- // eslint-disable-next-line react-hooks/rules-of-hooks
116
- const tabs = useTabs(args);
252
+ },
253
+ render: (args) => {
254
+ const items: any[] = args.items.filter((item: any) => item.label || item.icon);
117
255
 
118
256
  return (
119
257
  <div className={"border-gray-300 border-1 shadow"}>
120
- <Tabs {...tabs} i18n={(f) => f} AddButton={AddButton} HeaderChildren={HeaderChildren}>
121
- <div className={"p-5"}>{tabs?.current?.action}</div>
258
+ <Tabs selected={args.selected}>
259
+ <TabList>
260
+ {items.map((item, index) => {
261
+ return (
262
+ <Tab key={index} icon={item.icon} value={index}>
263
+ {item.label}
264
+ </Tab>
265
+ );
266
+ })}
267
+ </TabList>
268
+ <TabsBody>
269
+ {items.map((item, index) => {
270
+ return (
271
+ <TabPanel key={index} value={index}>
272
+ {item.children}
273
+ </TabPanel>
274
+ );
275
+ })}
276
+ </TabsBody>
122
277
  </Tabs>
123
278
  </div>
124
279
  );
125
- },
280
+ }
281
+ };
282
+
283
+ export const WithExtraControls: Story = {
126
284
  args: {
127
- reverse: true,
285
+ reverse: false,
128
286
  items: [
129
287
  {
288
+ action: "edit",
130
289
  exact: true,
131
- action: 0,
132
- label: "Test (0)"
290
+ icon: "edit",
291
+ label: "Edit",
292
+ children: <div className='bg-red-100 p-5'>Edit</div>
133
293
  },
134
294
  {
295
+ action: "submissions",
296
+ exact: false,
297
+ icon: "data",
298
+ label: "Data",
299
+ children: <div className='bg-orange-100 p-5'>Data</div>
300
+ },
301
+ {
302
+ action: "preview",
135
303
  exact: true,
136
- action: 1,
137
- label: "Test (1)"
304
+ icon: "test-tube",
305
+ label: "Preview",
306
+ children: <div className='bg-yellow-100 p-5'>Preview</div>
307
+ },
308
+ {
309
+ action: "actions",
310
+ exact: false,
311
+ icon: "paper-plane",
312
+ label: "Actions",
313
+ children: <div className='bg-green-100 p-5'>Actions</div>
314
+ },
315
+ {
316
+ action: "access",
317
+ exact: true,
318
+ icon: "lock",
319
+ label: "Access",
320
+ children: <div className='bg-blue-100 p-5'>Access</div>
321
+ },
322
+ {
323
+ action: "export",
324
+ exact: true,
325
+ icon: "download",
326
+ label: "Export",
327
+ children: <div className='bg-purple-100 p-5'>Export</div>
328
+ },
329
+ {
330
+ action: "delete",
331
+ exact: true,
332
+ icon: "trash",
333
+ label: "Delete",
334
+ roles: ["administrator", "owner"],
335
+ children: <div className='bg-gray-100 p-5'>Trash</div>
138
336
  }
139
337
  ]
338
+ },
339
+ render: (args) => {
340
+ const items: any[] = args.items.filter((item: any) => item.label || item.icon);
341
+
342
+ return (
343
+ <div className={"border-gray-300 border-1 shadow"}>
344
+ <Tabs selected={args.selected}>
345
+ <TabList>
346
+ <Button variant='link' aria-label='Previous tab'>
347
+ <Icon name='chevron-left' />
348
+ </Button>
349
+
350
+ {items.map((item, index) => {
351
+ return (
352
+ <Tab key={index} icon={item.icon} value={index} className={args.reverse ? "-reverse" : ""}>
353
+ {item.label}
354
+ </Tab>
355
+ );
356
+ })}
357
+ <Button variant='link' aria-label='Add tab'>
358
+ <Icon name='plus' />
359
+ </Button>
360
+ </TabList>
361
+ <TabsBody>
362
+ {items.map((item, index) => {
363
+ return (
364
+ <TabPanel key={index} value={index}>
365
+ {item.children}
366
+ </TabPanel>
367
+ );
368
+ })}
369
+ </TabsBody>
370
+ </Tabs>
371
+ </div>
372
+ );
140
373
  }
141
374
  };