@inseefr/lunatic 3.5.6 → 3.5.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.
Files changed (245) hide show
  1. package/components/CheckboxOne/CheckboxOne.spec.js +3 -2
  2. package/components/CheckboxOne/CheckboxOne.spec.js.map +1 -1
  3. package/components/shared/Combobox/Combobox.stories.d.ts +3 -2
  4. package/components/shared/Combobox/Combobox.stories.js +26 -21
  5. package/components/shared/Combobox/Combobox.stories.js.map +1 -1
  6. package/components/type.d.ts +3 -0
  7. package/esm/components/CheckboxOne/CheckboxOne.spec.js +3 -2
  8. package/esm/components/CheckboxOne/CheckboxOne.spec.js.map +1 -1
  9. package/esm/components/shared/Combobox/Combobox.stories.d.ts +3 -2
  10. package/esm/components/shared/Combobox/Combobox.stories.js +28 -24
  11. package/esm/components/shared/Combobox/Combobox.stories.js.map +1 -1
  12. package/esm/components/type.d.ts +3 -0
  13. package/esm/hooks/useLocalStorage.d.ts +1 -0
  14. package/esm/hooks/useLocalStorage.js +31 -0
  15. package/esm/hooks/useLocalStorage.js.map +1 -0
  16. package/esm/type.source.d.ts +6 -0
  17. package/esm/use-lunatic/commons/compile-controls.js +22 -14
  18. package/esm/use-lunatic/commons/compile-controls.js.map +1 -1
  19. package/esm/use-lunatic/commons/fill-components/fill-component-expressions.js +4 -1
  20. package/esm/use-lunatic/commons/fill-components/fill-component-expressions.js.map +1 -1
  21. package/esm/use-lunatic/hooks/use-page-has-response.js +1 -1
  22. package/esm/use-lunatic/hooks/use-page-has-response.js.map +1 -1
  23. package/esm/use-lunatic/hooks/useOverview.spec.js +2 -2
  24. package/esm/use-lunatic/hooks/useOverview.spec.js.map +1 -1
  25. package/esm/use-lunatic/props/getComponentTypeProps.d.ts +3 -0
  26. package/esm/utils/number.d.ts +4 -0
  27. package/esm/utils/number.js +12 -0
  28. package/esm/utils/number.js.map +1 -1
  29. package/esm/utils/object.d.ts +10 -0
  30. package/esm/utils/object.js +26 -0
  31. package/esm/utils/object.js.map +1 -1
  32. package/hooks/useLocalStorage.d.ts +1 -0
  33. package/hooks/useLocalStorage.js +34 -0
  34. package/hooks/useLocalStorage.js.map +1 -0
  35. package/package.json +76 -137
  36. package/src/components/CheckboxOne/CheckboxOne.spec.tsx +3 -0
  37. package/src/components/shared/Combobox/Combobox.stories.tsx +48 -49
  38. package/src/components/type.ts +1 -0
  39. package/src/hooks/useLocalStorage.ts +37 -0
  40. package/src/json.d.ts +16 -0
  41. package/src/stories/accordion/accordion.stories.tsx +21 -0
  42. package/src/stories/behaviour/cleaning/cleaning.stories.tsx +40 -0
  43. package/src/stories/behaviour/controls/controls.stories.tsx +51 -0
  44. package/src/stories/behaviour/disabled/disabled.stories.tsx +29 -0
  45. package/src/stories/behaviour/filter/filter.stories.tsx +34 -0
  46. package/src/stories/behaviour/missing/missing.stories.tsx +31 -0
  47. package/src/stories/behaviour/overview/overview.stories.tsx +61 -0
  48. package/src/stories/{overview/sourceWithHierarchy.json → behaviour/overview/source.json} +1 -1
  49. package/src/stories/{overview → behaviour/overview}/sourceLoop.json +1 -1
  50. package/src/stories/behaviour/performance/performance.stories.tsx +29 -0
  51. package/src/stories/behaviour/resizing/resizing.stories.tsx +34 -0
  52. package/src/stories/behaviour/slots.stories.tsx +34 -0
  53. package/src/stories/checkbox/checkbox.stories.tsx +75 -0
  54. package/src/stories/{checkbox-group/sourceLoop.json → checkbox/sourceGroupLoop.json} +1 -1
  55. package/src/stories/datepicker/datepicker.stories.tsx +21 -0
  56. package/src/stories/declaration/declaration.stories.tsx +27 -0
  57. package/src/stories/dropdown/dropdown.stories.tsx +27 -0
  58. package/src/stories/duration/duration.stories.tsx +27 -0
  59. package/src/stories/filter-description/filter-description.stories.tsx +28 -0
  60. package/src/stories/input/input.stories.tsx +21 -0
  61. package/src/stories/input-number/input-number.stories.tsx +35 -0
  62. package/src/stories/loop/loop.stories.tsx +35 -0
  63. package/src/stories/loop/roster-for-loop.stories.tsx +59 -0
  64. package/src/stories/pairwise/pairwise.stories.tsx +30 -0
  65. package/src/stories/paste.stories.tsx +85 -0
  66. package/src/stories/question/question.stories.tsx +21 -0
  67. package/src/stories/questionnaires/logement/logement.stories.tsx +26 -0
  68. package/src/stories/questionnaires/recensement/recensement.stories.tsx +28 -0
  69. package/src/stories/questionnaires/rp/rp.stories.tsx +21 -0
  70. package/src/stories/questionnaires/simpsons/simpsons.stories.tsx +31 -0
  71. package/src/stories/radio/radio.stories.tsx +53 -0
  72. package/src/stories/roundabout/roundabout.stories.tsx +37 -0
  73. package/src/stories/roundabout/source.json +1 -0
  74. package/src/stories/sequence/sequence.stories.tsx +28 -0
  75. package/src/stories/suggester/source-option-responses.json +1 -1
  76. package/src/stories/suggester/suggester.stories.tsx +68 -0
  77. package/src/stories/summary/source.json +22 -1
  78. package/src/stories/summary/summary.stories.tsx +31 -0
  79. package/src/stories/table/table.stories.tsx +35 -0
  80. package/src/stories/text/text.stories.tsx +38 -0
  81. package/src/stories/textarea/textarea.stories.tsx +21 -0
  82. package/src/stories/utils/Orchestrator.tsx +310 -0
  83. package/src/stories/utils/OrchestratorData.tsx +176 -0
  84. package/src/stories/utils/OrchestratorOverview.tsx +70 -0
  85. package/src/stories/utils/OrchestratorSidebar.tsx +119 -0
  86. package/src/stories/utils/SchemaValidator.tsx +29 -0
  87. package/src/stories/utils/referentiel.ts +9 -0
  88. package/src/type.source.ts +6 -0
  89. package/src/use-lunatic/commons/compile-controls.ts +36 -18
  90. package/src/use-lunatic/commons/fill-components/fill-component-expressions.ts +4 -1
  91. package/src/use-lunatic/hooks/use-page-has-response.ts +1 -1
  92. package/src/use-lunatic/hooks/useOverview.spec.ts +3 -2
  93. package/src/use-lunatic/use-lunatic.test.ts +53 -40
  94. package/src/utils/number.ts +13 -0
  95. package/src/utils/object.ts +40 -0
  96. package/tsconfig.build.tsbuildinfo +1 -1
  97. package/type.source.d.ts +6 -0
  98. package/use-lunatic/commons/compile-controls.js +21 -13
  99. package/use-lunatic/commons/compile-controls.js.map +1 -1
  100. package/use-lunatic/commons/fill-components/fill-component-expressions.js +5 -2
  101. package/use-lunatic/commons/fill-components/fill-component-expressions.js.map +1 -1
  102. package/use-lunatic/hooks/use-page-has-response.js +2 -2
  103. package/use-lunatic/hooks/use-page-has-response.js.map +1 -1
  104. package/use-lunatic/hooks/useOverview.spec.js +8 -8
  105. package/use-lunatic/hooks/useOverview.spec.js.map +1 -1
  106. package/use-lunatic/props/getComponentTypeProps.d.ts +3 -0
  107. package/utils/number.d.ts +4 -0
  108. package/utils/number.js +13 -0
  109. package/utils/number.js.map +1 -1
  110. package/utils/object.d.ts +10 -0
  111. package/utils/object.js +30 -0
  112. package/utils/object.js.map +1 -1
  113. package/esm/stories/overview/sourceWithHierarchy.json +0 -5151
  114. package/esm/tests/utils/lunatic.d.ts +0 -15
  115. package/esm/tests/utils/lunatic.js +0 -27
  116. package/esm/tests/utils/lunatic.js.map +0 -1
  117. package/esm/use-lunatic/replace-component-sequence.d.ts +0 -36
  118. package/esm/use-lunatic/replace-component-sequence.js +0 -19
  119. package/esm/use-lunatic/replace-component-sequence.js.map +0 -1
  120. package/esm/use-lunatic/test.utils.d.ts +0 -2
  121. package/esm/use-lunatic/test.utils.js +0 -13
  122. package/esm/use-lunatic/test.utils.js.map +0 -1
  123. package/esm/utils/is-object.d.ts +0 -4
  124. package/esm/utils/is-object.js +0 -7
  125. package/esm/utils/is-object.js.map +0 -1
  126. package/esm/utils/to-number.d.ts +0 -4
  127. package/esm/utils/to-number.js +0 -13
  128. package/esm/utils/to-number.js.map +0 -1
  129. package/src/stories/accordion/accordion.stories.jsx +0 -17
  130. package/src/stories/behaviour/cleaning/cleaning.stories.jsx +0 -69
  131. package/src/stories/behaviour/controls/controls.stories.jsx +0 -81
  132. package/src/stories/behaviour/filter/dataLoop.json +0 -14
  133. package/src/stories/behaviour/filter/filter.stories.jsx +0 -36
  134. package/src/stories/behaviour/missing/missing.stories.jsx +0 -69
  135. package/src/stories/behaviour/others/V2_DeclarationsSimples.json +0 -908
  136. package/src/stories/behaviour/others/V2_MinMaxSum_Boucles.json +0 -489
  137. package/src/stories/behaviour/others/V2_QuestSimple_Boucles.json +0 -3919
  138. package/src/stories/behaviour/others/V2_TCMRallyeGames.json +0 -2760
  139. package/src/stories/behaviour/others/test-dylan.json +0 -538
  140. package/src/stories/behaviour/others/test.stories.jsx +0 -78
  141. package/src/stories/behaviour/paste/source.json +0 -32
  142. package/src/stories/behaviour/paste/test.stories.jsx +0 -62
  143. package/src/stories/behaviour/performance/performance.stories.jsx +0 -26
  144. package/src/stories/behaviour/resizing/resizing.stories.jsx +0 -60
  145. package/src/stories/behaviour/slots.stories.jsx +0 -32
  146. package/src/stories/checkbox-boolean/checkboxBoolean.stories.jsx +0 -17
  147. package/src/stories/checkbox-group/checkbox-group.stories.jsx +0 -77
  148. package/src/stories/checkbox-one/checkboxOne.stories.jsx +0 -53
  149. package/src/stories/date-picker/data.json +0 -3
  150. package/src/stories/date-picker/datepicker.stories.jsx +0 -26
  151. package/src/stories/declaration/data.json +0 -1
  152. package/src/stories/declaration/input.stories.jsx +0 -18
  153. package/src/stories/disabled/data.json +0 -16
  154. package/src/stories/disabled/disabled.stories.jsx +0 -18
  155. package/src/stories/dropdown/data.json +0 -8
  156. package/src/stories/dropdown/dropdown.stories.jsx +0 -25
  157. package/src/stories/duration/duration.stories.jsx +0 -25
  158. package/src/stories/filter-description/filter-description.stories.jsx +0 -37
  159. package/src/stories/input/data.json +0 -1
  160. package/src/stories/input/input.stories.jsx +0 -18
  161. package/src/stories/input-number/input-number.stories.jsx +0 -23
  162. package/src/stories/loop/loop.stories.jsx +0 -29
  163. package/src/stories/loop/roster-for-loop.stories.jsx +0 -46
  164. package/src/stories/markdown/markdown.stories.jsx +0 -20
  165. package/src/stories/overview/data.json +0 -1
  166. package/src/stories/overview/dataLoop.json +0 -93
  167. package/src/stories/overview/overview.stories.jsx +0 -44
  168. package/src/stories/overview/source.json +0 -25
  169. package/src/stories/pairwise/data.json +0 -12
  170. package/src/stories/pairwise/pairwise-links.stories.jsx +0 -48
  171. package/src/stories/question/question.stories.jsx +0 -16
  172. package/src/stories/questionnaires/logement/logement.stories.jsx +0 -59
  173. package/src/stories/questionnaires/recensement/data.json +0 -12
  174. package/src/stories/questionnaires/recensement/recensement.stories.jsx +0 -35
  175. package/src/stories/questionnaires/rp/data.json +0 -5
  176. package/src/stories/questionnaires/rp/rp.stories.jsx +0 -23
  177. package/src/stories/questionnaires/simpsons/simpsons.stories.jsx +0 -246
  178. package/src/stories/radio/radio.stories.jsx +0 -78
  179. package/src/stories/roundabout/data1.json +0 -13
  180. package/src/stories/roundabout/data2.json +0 -16
  181. package/src/stories/roundabout/roundabout.stories.jsx +0 -32
  182. package/src/stories/sequence/sequence.stories.jsx +0 -29
  183. package/src/stories/suggester/suggester.stories.jsx +0 -71
  184. package/src/stories/summary/data.json +0 -16
  185. package/src/stories/summary/summary.stories.jsx +0 -23
  186. package/src/stories/switch/README.md +0 -29
  187. package/src/stories/switch/data-forced.json +0 -40
  188. package/src/stories/switch/source.json +0 -64
  189. package/src/stories/switch/switch.stories.jsx +0 -17
  190. package/src/stories/table/data.json +0 -1
  191. package/src/stories/table/table.stories.jsx +0 -30
  192. package/src/stories/text/data-roster.json +0 -5
  193. package/src/stories/text/text.stories.jsx +0 -20
  194. package/src/stories/textarea/data.json +0 -1
  195. package/src/stories/textarea/textarea.stories.jsx +0 -18
  196. package/src/stories/utils/SchemaValidator.jsx +0 -40
  197. package/src/stories/utils/default-arg-types.js +0 -39
  198. package/src/stories/utils/default-args.js +0 -3
  199. package/src/stories/utils/options.js +0 -19
  200. package/src/stories/utils/orchestrator.jsx +0 -267
  201. package/src/stories/utils/orchestrator.scss +0 -66
  202. package/src/stories/utils/overview.jsx +0 -39
  203. package/src/stories/utils/overview.scss +0 -37
  204. package/src/stories/utils/referentiel.js +0 -7
  205. package/src/tests/utils/e2e.js +0 -91
  206. package/src/tests/utils/lunatic.ts +0 -33
  207. package/src/use-lunatic/replace-component-sequence.ts +0 -25
  208. package/src/use-lunatic/test.utils.ts +0 -17
  209. package/src/utils/is-object.ts +0 -6
  210. package/src/utils/to-number.ts +0 -12
  211. package/stories/overview/sourceWithHierarchy.json +0 -5151
  212. package/tests/utils/lunatic.d.ts +0 -15
  213. package/tests/utils/lunatic.js +0 -31
  214. package/tests/utils/lunatic.js.map +0 -1
  215. package/use-lunatic/replace-component-sequence.d.ts +0 -36
  216. package/use-lunatic/replace-component-sequence.js +0 -22
  217. package/use-lunatic/replace-component-sequence.js.map +0 -1
  218. package/use-lunatic/test.utils.d.ts +0 -2
  219. package/use-lunatic/test.utils.js +0 -17
  220. package/use-lunatic/test.utils.js.map +0 -1
  221. package/utils/is-object.d.ts +0 -4
  222. package/utils/is-object.js +0 -10
  223. package/utils/is-object.js.map +0 -1
  224. package/utils/to-number.d.ts +0 -4
  225. package/utils/to-number.js +0 -16
  226. package/utils/to-number.js.map +0 -1
  227. /package/src/stories/behaviour/cleaning/{loop.json → source-loop-scopes.json} +0 -0
  228. /package/src/stories/behaviour/controls/{boucles-n.json → source-boucles-n.json} +0 -0
  229. /package/src/stories/behaviour/controls/{loop.json → source-loop.json} +0 -0
  230. /package/src/stories/behaviour/controls/{roundabout.json → source-roundabout.json} +0 -0
  231. /package/src/stories/behaviour/controls/{simple-numeric.json → source-simple-numeric.json} +0 -0
  232. /package/src/stories/behaviour/controls/{simple.json → source-simple.json} +0 -0
  233. /package/src/stories/{disabled → behaviour/disabled}/source.json +0 -0
  234. /package/src/stories/{checkbox-boolean → checkbox}/source.json +0 -0
  235. /package/src/stories/{checkbox-group/source.json → checkbox/sourceGroup.json} +0 -0
  236. /package/src/stories/{checkbox-group/sourceCondition.json → checkbox/sourceGroupCondition.json} +0 -0
  237. /package/src/stories/{checkbox-group/sourceDetail.json → checkbox/sourceGroupDetail.json} +0 -0
  238. /package/src/stories/{checkbox-one/source.json → checkbox/sourceOne.json} +0 -0
  239. /package/src/stories/{checkbox-one/sourceDetail.json → checkbox/sourceOneDetail.json} +0 -0
  240. /package/src/stories/{date-picker → datepicker}/source.json +0 -0
  241. /package/src/stories/{markdown/source.json → declaration/sourceMarkdown.json} +0 -0
  242. /package/src/stories/duration/{mois.json → sourceMonths.json} +0 -0
  243. /package/src/stories/duration/{time.json → sourceTime.json} +0 -0
  244. /package/src/stories/filter-description/{source-options.json → sourceOptions.json} +0 -0
  245. /package/src/stories/table/{table-dynamique.json → source-dynamic.json} +0 -0
@@ -0,0 +1,38 @@
1
+ import {
2
+ type Orchestrator,
3
+ OrchestratorMeta,
4
+ type OrchestratorStory,
5
+ } from '../utils/Orchestrator';
6
+ import source from './source.json';
7
+ import sourceRoster from './source-roster.json';
8
+ import sourceTable from './source-table.json';
9
+
10
+ import { Meta } from '@storybook/react';
11
+
12
+ const meta: Meta<typeof Orchestrator> = {
13
+ title: 'Components/Text',
14
+ ...OrchestratorMeta,
15
+ };
16
+
17
+ export default meta;
18
+
19
+ export const Default: OrchestratorStory = {
20
+ args: {
21
+ source,
22
+ },
23
+ };
24
+ export const Roster: OrchestratorStory = {
25
+ args: {
26
+ source: sourceRoster,
27
+ data: {
28
+ EXTERNAL: {
29
+ VARIABLE_EXT: ['TRUC', 'BIDULE', 'CHOSE'],
30
+ },
31
+ },
32
+ },
33
+ };
34
+ export const Table: OrchestratorStory = {
35
+ args: {
36
+ source: sourceTable,
37
+ },
38
+ };
@@ -0,0 +1,21 @@
1
+ import {
2
+ type Orchestrator,
3
+ OrchestratorMeta,
4
+ type OrchestratorStory,
5
+ } from '../utils/Orchestrator';
6
+ import source from './source.json';
7
+
8
+ import { Meta } from '@storybook/react';
9
+
10
+ const meta: Meta<typeof Orchestrator> = {
11
+ title: 'Components/Textarea',
12
+ ...OrchestratorMeta,
13
+ };
14
+
15
+ export default meta;
16
+
17
+ export const Default: OrchestratorStory = {
18
+ args: {
19
+ source,
20
+ },
21
+ };
@@ -0,0 +1,310 @@
1
+ import {
2
+ LunaticComponents,
3
+ type LunaticData,
4
+ type LunaticError,
5
+ type LunaticSlotComponents,
6
+ type LunaticSource,
7
+ ModalControls,
8
+ useLunatic,
9
+ } from '../..';
10
+ import React, {
11
+ memo,
12
+ type ReactNode,
13
+ useCallback,
14
+ useEffect,
15
+ useMemo,
16
+ useState,
17
+ } from 'react';
18
+ import Ajv from 'ajv/dist/2020.js';
19
+ import LunaticSchema from '../../../lunatic-schema.json';
20
+ import { Logger } from '../../utils/logger';
21
+ import { OrchestratorOverview } from './OrchestratorOverview';
22
+ import { SchemaValidator } from './SchemaValidator';
23
+ import { OrchestratorData } from './OrchestratorData';
24
+ import type { PageTag } from '../../use-lunatic/type';
25
+ import type { IndexEntry } from '../../utils/search/SearchInterface';
26
+ import { OrchestratorSidebar } from './OrchestratorSidebar';
27
+ import type { Meta, StoryObj } from '@storybook/react';
28
+
29
+ type Props = {
30
+ source: LunaticSource;
31
+ data: LunaticData;
32
+ // Starting page
33
+ initialPage?: PageTag;
34
+ // Function used to load a list of option in a suggester
35
+ getReferentiel: (name: string) => Promise<IndexEntry[]>;
36
+ // Display detail input even if the corresponding checkbox is not checked
37
+ detailAlwaysDisplayed?: boolean;
38
+ // Enable missing buttons
39
+ missing?: boolean;
40
+ // Readonly mode
41
+ readOnly: boolean;
42
+ // List of custom components
43
+ slots: Partial<LunaticSlotComponents>;
44
+ // Last reached paged (used in the overview)
45
+ lastReachedPage: PageTag;
46
+ // Preload suggester data at the start of the form
47
+ autoSuggesterLoading: boolean;
48
+ disableFiltersDescription?: boolean;
49
+ disableFilters?: boolean;
50
+ management?: boolean;
51
+ shortcut?: boolean;
52
+ activeControls?: boolean;
53
+ missingStrategy?: () => void;
54
+ showOverview?: boolean;
55
+ disabled?: boolean;
56
+ extraTabs?: TabEntry[];
57
+ };
58
+
59
+ type TabEntry = { label: ReactNode; children: ReactNode };
60
+
61
+ function OrchestratorForStories(props: Readonly<Props>) {
62
+ const {
63
+ source,
64
+ data,
65
+ disableFilters,
66
+ disableFiltersDescription,
67
+ management,
68
+ shortcut,
69
+ activeControls,
70
+ initialPage,
71
+ missing,
72
+ missingStrategy,
73
+ slots,
74
+ showOverview,
75
+ getReferentiel,
76
+ readOnly,
77
+ disabled,
78
+ detailAlwaysDisplayed,
79
+ autoSuggesterLoading,
80
+ extraTabs = [],
81
+ } = props;
82
+
83
+ const componentsOptions = { detailAlwaysDisplayed };
84
+
85
+ const {
86
+ getComponents,
87
+ goPreviousPage,
88
+ goNextPage,
89
+ goToPage,
90
+ pager,
91
+ pageTag,
92
+ isFirstPage,
93
+ isLastPage,
94
+ overview,
95
+ compileControls,
96
+ getData,
97
+ Provider,
98
+ hasPageResponse,
99
+ } = useLunatic(source, data, {
100
+ initialPage,
101
+ disableFilters,
102
+ disableFiltersDescription,
103
+ onChange: onLogChange,
104
+ getReferentiel,
105
+ management,
106
+ missing,
107
+ missingStrategy,
108
+ lastReachedPage: props.lastReachedPage,
109
+ missingShortcut: { dontKnow: 'f2', refused: 'f4' },
110
+ shortcut,
111
+ activeControls,
112
+ withOverview: showOverview,
113
+ autoSuggesterLoading,
114
+ componentsOptions,
115
+ });
116
+
117
+ const components = getComponents();
118
+
119
+ const [errorActive, setErrorActive] = useState<
120
+ Record<PageTag, LunaticError[]>
121
+ >({});
122
+ const [errorsForModal, setErrorsForModal] = useState<null | ReturnType<
123
+ typeof compileControls
124
+ >>(null);
125
+
126
+ const skip = useCallback(() => {
127
+ setErrorsForModal(null);
128
+ goNextPage();
129
+ }, [goNextPage]);
130
+
131
+ const closeModal = useCallback(() => setErrorsForModal(null), []);
132
+
133
+ const handleGoNext = useCallback(() => {
134
+ const { currentErrors, isCritical } = compileControls();
135
+ setErrorActive((v) => ({ ...v, [pageTag]: currentErrors || {} }));
136
+ if (currentErrors && Object.keys(currentErrors).length > 0) {
137
+ setErrorsForModal({ currentErrors, isCritical });
138
+ } else {
139
+ goNextPage();
140
+ }
141
+ }, [compileControls, goNextPage, pageTag]);
142
+
143
+ // Allow PageDown / PageUp shortcut to ease navigation
144
+ useEffect(() => {
145
+ const listener = (e: KeyboardEvent) => {
146
+ let stopPropagation = false;
147
+ if (e.key === 'PageDown') {
148
+ handleGoNext();
149
+ stopPropagation = true;
150
+ }
151
+ if (e.key === 'PageUp') {
152
+ goPreviousPage();
153
+ stopPropagation = true;
154
+ }
155
+ if (stopPropagation) {
156
+ e.preventDefault();
157
+ e.stopPropagation();
158
+ }
159
+ };
160
+ document.addEventListener('keydown', listener);
161
+ return () => {
162
+ document.removeEventListener('keydown', listener);
163
+ };
164
+ }, [handleGoNext, goPreviousPage]);
165
+
166
+ // Check that the source is valid against the schema
167
+ const errors = useMemo(() => {
168
+ const ajv = new Ajv({
169
+ removeAdditional: true,
170
+ strict: false,
171
+ allErrors: true,
172
+ discriminator: true,
173
+ });
174
+ const validator = ajv.compile(LunaticSchema);
175
+ const isSourceValid = validator(structuredClone(source)); // ajv mutate the object, send a clone
176
+ if (!isSourceValid) {
177
+ return validator.errors;
178
+ }
179
+ return [];
180
+ }, [source]);
181
+
182
+ const [tab, setTab] = useState(0);
183
+
184
+ const tabs = [
185
+ {
186
+ label: 'Form',
187
+ children: (
188
+ <LunaticComponents
189
+ slots={slots}
190
+ autoFocusKey={pageTag}
191
+ components={components}
192
+ componentProps={(p) => ({
193
+ errors: errorActive[pageTag],
194
+ disabled: disabled,
195
+ readOnly: 'readOnly' in p ? p.readOnly : readOnly,
196
+ })}
197
+ />
198
+ ),
199
+ },
200
+ {
201
+ label: 'Data',
202
+ children: <OrchestratorData getData={getData} source={source} />,
203
+ },
204
+ ...extraTabs,
205
+ ] as TabEntry[];
206
+
207
+ if (errors && errors.length > 0) {
208
+ tabs.push({
209
+ label: (
210
+ <>
211
+ Errors{' '}
212
+ <span className="badge badge-xs badge-error text-white">
213
+ {errors.length}
214
+ </span>
215
+ </>
216
+ ),
217
+ children: <SchemaValidator errors={errors} />,
218
+ });
219
+ }
220
+
221
+ return (
222
+ <Provider>
223
+ <div className="container grid grid-cols-[1fr_300px] gap-4">
224
+ <div>
225
+ <div tabIndex={-1} role="tablist" className="tabs tabs-box mb-4">
226
+ {tabs.map((t, k) => (
227
+ <button
228
+ tabIndex={-1}
229
+ key={JSON.stringify(t.label)}
230
+ role="tab"
231
+ onClick={() => setTab(k)}
232
+ className={`tab gap-2 ${tab === k ? 'tab-active' : ''}`}
233
+ >
234
+ {t.label}
235
+ </button>
236
+ ))}
237
+ </div>
238
+ <div>{tabs[tab].children}</div>
239
+ </div>
240
+ <OrchestratorSidebar
241
+ goPreviousPage={goPreviousPage}
242
+ goNextPage={handleGoNext}
243
+ goToPage={goToPage}
244
+ isLastPage={isLastPage}
245
+ isFirstPage={isFirstPage}
246
+ pageTag={pageTag}
247
+ pager={pager}
248
+ hasPageResponse={hasPageResponse()}
249
+ onLogData={() => console.log('Data', getData(true))}
250
+ onLogComponents={() => console.log('Components', components)}
251
+ >
252
+ {showOverview && (
253
+ <OrchestratorOverview overview={overview} goToPage={goToPage} />
254
+ )}
255
+ </OrchestratorSidebar>
256
+ {errorsForModal?.currentErrors && (
257
+ <ModalControls
258
+ errors={errorsForModal.currentErrors}
259
+ goNext={skip}
260
+ onClose={closeModal}
261
+ isCritical={errorsForModal.isCritical}
262
+ />
263
+ )}
264
+ </div>
265
+ </Provider>
266
+ );
267
+ }
268
+
269
+ function onLogChange(args: unknown) {
270
+ Logger.log('onChange', args);
271
+ }
272
+
273
+ function logMissingStrategy() {
274
+ Logger.log('no missing strategy');
275
+ }
276
+
277
+ export const Orchestrator = memo(OrchestratorForStories);
278
+
279
+ export const OrchestratorMeta: Meta<typeof Orchestrator> = {
280
+ component: Orchestrator,
281
+ argTypes: {
282
+ // Do not display controls for these args
283
+ source: { table: { disable: true } },
284
+ data: { table: { disable: true } },
285
+ getReferentiel: { table: { disable: true } },
286
+ initialPage: { table: { disable: true } },
287
+ slots: { table: { disable: true } },
288
+ lastReachedPage: { table: { disable: true } },
289
+ autoSuggesterLoading: { table: { disable: true } },
290
+ missingStrategy: { table: { disable: true } },
291
+ extraTabs: { table: { disable: true } },
292
+ },
293
+ args: {
294
+ disableFilters: false,
295
+ disableFiltersDescription: false,
296
+ management: false,
297
+ shortcut: false,
298
+ activeControls: true,
299
+ initialPage: '1',
300
+ missing: false,
301
+ missingStrategy: logMissingStrategy,
302
+ showOverview: false,
303
+ readOnly: false,
304
+ disabled: false,
305
+ detailAlwaysDisplayed: false,
306
+ autoSuggesterLoading: false,
307
+ },
308
+ };
309
+
310
+ export type OrchestratorStory = StoryObj<typeof Orchestrator>;
@@ -0,0 +1,176 @@
1
+ import { useMemo, useState } from 'react';
2
+ import type { LunaticSource, LunaticState } from '../../use-lunatic/type';
3
+ import { isObject, objectFilter, objectKeys } from '../../utils/object';
4
+
5
+ type Props = {
6
+ getData: LunaticState['getData'];
7
+ source: LunaticSource;
8
+ };
9
+
10
+ export function OrchestratorData({ getData, source }: Readonly<Props>) {
11
+ const data = useMemo(() => getData(true), [getData]);
12
+ const [search, setSearch] = useState('');
13
+ const [tab, setTab] = useState(0);
14
+
15
+ const tabs = [
16
+ {
17
+ label: 'Collected',
18
+ variables: objectFilter(data.COLLECTED ?? {}, (k) =>
19
+ search ? k.toLowerCase().includes(search.toLowerCase()) : true
20
+ ),
21
+ },
22
+ {
23
+ label: 'Calculated',
24
+ variables: objectFilter(data.CALCULATED ?? {}, (k) =>
25
+ search ? k.startsWith(search) : true
26
+ ),
27
+ },
28
+ ];
29
+
30
+ const variables = tabs[tab].variables;
31
+ return (
32
+ <div>
33
+ <div className="flex justify-between">
34
+ <div role="tablist" className="tabs tabs-border mb-4">
35
+ {tabs.map((t, k) => (
36
+ <button
37
+ key={`variables-${t.label}`}
38
+ role="tab"
39
+ onClick={() => setTab(k)}
40
+ className={`tab gap-2 ${tab === k ? 'tab-active' : ''}`}
41
+ >
42
+ {t.label}
43
+ </button>
44
+ ))}
45
+ </div>
46
+ <input
47
+ type="text"
48
+ className="input"
49
+ placeholder="Rechercher"
50
+ onInput={(e) => setSearch(e.currentTarget.value)}
51
+ value={search}
52
+ />
53
+ </div>
54
+ <div className="overflow-x-auto rounded-box border border-base-content/5 bg-base-100">
55
+ <table className="table table-zebra">
56
+ <thead>
57
+ <tr>
58
+ <th>Variable</th>
59
+ <th>Infos</th>
60
+ <th>Value</th>
61
+ </tr>
62
+ </thead>
63
+ <tbody>
64
+ {Object.entries(variables).map(([key, value]) => (
65
+ <tr key={key}>
66
+ <td>{key}</td>
67
+ <td>
68
+ <div className="flex gap-2 flex-wrap">
69
+ {source.resizing &&
70
+ key in source.resizing &&
71
+ 'variables' in source.resizing[key] ? (
72
+ <ResizeInfo resizing={source.resizing[key]} />
73
+ ) : null}
74
+ {source.cleaning && key in source.cleaning ? (
75
+ <CleaningInfo cleaning={source.cleaning[key]} />
76
+ ) : null}
77
+ </div>
78
+ </td>
79
+ <td>
80
+ {JSON.stringify(
81
+ isObject(value) && 'COLLECTED' in value
82
+ ? value.COLLECTED
83
+ : value
84
+ )}
85
+ </td>
86
+ </tr>
87
+ ))}
88
+ </tbody>
89
+ </table>
90
+ </div>
91
+ </div>
92
+ );
93
+ }
94
+
95
+ const ResizeInfo = ({
96
+ resizing,
97
+ }: {
98
+ resizing: {
99
+ size: string;
100
+ variables: string[];
101
+ };
102
+ }) => {
103
+ const [expanded, setExpanded] = useState(false);
104
+ const toggle = () => setExpanded((v) => !v);
105
+ return (
106
+ <div className="space-y-2">
107
+ <button
108
+ className="flex gap-1 btn btn-primary rounded-full badge-sm whitespace-nowrap"
109
+ onClick={toggle}
110
+ >
111
+ {resizing.variables.length} resize(s)
112
+ <svg
113
+ className="size-4"
114
+ xmlns="http://www.w3.org/2000/svg"
115
+ viewBox="0 0 24 24"
116
+ fill="currentColor"
117
+ >
118
+ <path d="M12 15.0006L7.75732 10.758L9.17154 9.34375L12 12.1722L14.8284 9.34375L16.2426 10.758L12 15.0006Z"></path>
119
+ </svg>
120
+ </button>
121
+ {expanded && (
122
+ <div className="flex flex-wrap gap-2">
123
+ {resizing.variables.map((variable) => (
124
+ <div key={variable} className="kbd">
125
+ {variable}
126
+ </div>
127
+ ))}
128
+ </div>
129
+ )}
130
+ </div>
131
+ );
132
+ };
133
+
134
+ const CleaningInfo = ({
135
+ cleaning,
136
+ }: {
137
+ cleaning: {
138
+ [p: string]:
139
+ | string
140
+ | {
141
+ expression: string;
142
+ shapeFrom?: string;
143
+ isAggregatorUsed: boolean;
144
+ }[];
145
+ };
146
+ }) => {
147
+ const [expanded, setExpanded] = useState(false);
148
+ const toggle = () => setExpanded((v) => !v);
149
+ return (
150
+ <div className="space-y-2">
151
+ <button
152
+ className="flex gap-1 btn btn-secondary rounded-full badge-sm whitespace-nowrap"
153
+ onClick={toggle}
154
+ >
155
+ {Object.keys(cleaning).length} cleaning(s)
156
+ <svg
157
+ className="size-4"
158
+ xmlns="http://www.w3.org/2000/svg"
159
+ viewBox="0 0 24 24"
160
+ fill="currentColor"
161
+ >
162
+ <path d="M12 15.0006L7.75732 10.758L9.17154 9.34375L12 12.1722L14.8284 9.34375L16.2426 10.758L12 15.0006Z"></path>
163
+ </svg>
164
+ </button>
165
+ {expanded && (
166
+ <div className="flex flex-wrap gap-2">
167
+ {objectKeys(cleaning).map((k) => (
168
+ <div key={k} className="kbd">
169
+ {k}
170
+ </div>
171
+ ))}
172
+ </div>
173
+ )}
174
+ </div>
175
+ );
176
+ };
@@ -0,0 +1,70 @@
1
+ import type { InterpretedLunaticOverviewItem } from '../../use-lunatic/hooks/useOverview';
2
+ import type { useLunatic } from '../../use-lunatic/use-lunatic';
3
+
4
+ type Props = {
5
+ overview: InterpretedLunaticOverviewItem[];
6
+ goToPage: ReturnType<typeof useLunatic>['goToPage'];
7
+ depth?: number;
8
+ };
9
+
10
+ export const OrchestratorOverview = ({
11
+ overview: stateOverview,
12
+ goToPage,
13
+ depth = 0,
14
+ }: Props) => {
15
+ return (
16
+ <div>
17
+ {depth === 0 && <h3 className="text-lg font-bold mb-2">Overview</h3>}
18
+ <ol style={{ paddingLeft: `${depth * 1.25}rem` }}>
19
+ {stateOverview.map((entry) => (
20
+ <OverviewItem
21
+ key={`view-${entry.id}-${entry.page}`}
22
+ overviewEntry={entry}
23
+ goToPage={goToPage}
24
+ depth={depth}
25
+ />
26
+ ))}
27
+ </ol>
28
+ </div>
29
+ );
30
+ };
31
+
32
+ type OverviewItemProps = {
33
+ overviewEntry: InterpretedLunaticOverviewItem;
34
+ goToPage: Props['goToPage'];
35
+ depth: number;
36
+ };
37
+
38
+ const OverviewItem = ({
39
+ overviewEntry,
40
+ goToPage,
41
+ depth,
42
+ }: OverviewItemProps) => {
43
+ let color = 'text-base-content/50';
44
+ if (overviewEntry.reached) {
45
+ color = 'text-base-content';
46
+ }
47
+ if (overviewEntry.current) {
48
+ color = 'text-success';
49
+ }
50
+ return (
51
+ <li className={`${color}`}>
52
+ <button
53
+ style={{ display: 'contents' }}
54
+ onClick={() => goToPage({ page: overviewEntry.page })}
55
+ >
56
+ <div className="flex">
57
+ <span>{overviewEntry.label}</span>
58
+ <span className="dot-leader">{overviewEntry.page}</span>
59
+ </div>
60
+ </button>
61
+ {overviewEntry.children.length > 0 && (
62
+ <OrchestratorOverview
63
+ overview={overviewEntry.children}
64
+ goToPage={goToPage}
65
+ depth={depth + 1}
66
+ />
67
+ )}
68
+ </li>
69
+ );
70
+ };