@inseefr/lunatic 0.3.1-experimental → 0.3.2-experimental

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 (51) hide show
  1. package/lib/index.js +192 -189
  2. package/lib/index.js.map +1 -1
  3. package/package.json +2 -2
  4. package/src/components/component-wrapper/controls/validators/datepicker.js +25 -14
  5. package/src/components/component-wrapper/missing/component.js +37 -17
  6. package/src/components/datepicker/component.js +8 -12
  7. package/src/components/declarations/wrappers/input-declarations-wrapper.js +3 -2
  8. package/src/components/loop-constructor/block/index.js +1 -1
  9. package/src/components/loop-constructor/index.js +1 -1
  10. package/src/components/loop-constructor/roster/index.js +1 -1
  11. package/src/components/loop-constructor/wrapper/body-component.js +3 -0
  12. package/src/components/loop-constructor/wrapper/build-components.js +33 -33
  13. package/src/components/loop-constructor/wrapper/index.js +1 -1
  14. package/src/components/suggester/components/panel/option-container.js +1 -1
  15. package/src/components/suggester/suggester-wrapper.js +3 -3
  16. package/src/components/table/table.js +3 -1
  17. package/src/stories/loop-constructor/README.md +27 -27
  18. package/src/stories/loop-constructor/data-input-forced.json +64 -64
  19. package/src/stories/loop-constructor/data-input.json +100 -100
  20. package/src/stories/loop-constructor/data-loop-forced.json +66 -66
  21. package/src/stories/loop-constructor/data-loop-static-forced.json +66 -66
  22. package/src/stories/loop-constructor/data-loop-static.json +81 -81
  23. package/src/stories/loop-constructor/data-loop.json +81 -81
  24. package/src/stories/loop-constructor/data-roster-forced.json +68 -68
  25. package/src/stories/loop-constructor/data-roster.json +83 -83
  26. package/src/stories/loop-constructor/loop-constructor.stories.js +180 -180
  27. package/src/stories/questionnaire/arithmetic-management.json +47 -0
  28. package/src/stories/questionnaire/logement-queen.json +23390 -22706
  29. package/src/stories/questionnaire/questionnaire.stories.js +14 -14
  30. package/src/stories/suggester/data.json +4 -1
  31. package/src/stories/suggester/suggester-workers.stories.js +4 -1
  32. package/src/stories/utils/orchestrator-split.js +117 -0
  33. package/src/tests/utils/to-expose/handler/results/res-input-edited.json +1 -1
  34. package/src/tests/utils/to-expose/state/state.spec.js +59 -59
  35. package/src/utils/lib/index.js +1 -0
  36. package/src/utils/lib/pagination/navigation/shared.js +5 -5
  37. package/src/utils/lib/splitting.js +110 -0
  38. package/src/utils/suggester-workers/commons-tokenizer/create-entity-tokenizer.js +4 -2
  39. package/src/utils/suggester-workers/commons-tokenizer/filters/{filter-accents-to-lower.js → filter-accents.js} +2 -2
  40. package/src/utils/suggester-workers/commons-tokenizer/filters/{filter-accents-to-lower.spec.js → filter-accents.spec.js} +1 -1
  41. package/src/utils/suggester-workers/commons-tokenizer/filters/filter-synonyms.js +27 -1
  42. package/src/utils/suggester-workers/commons-tokenizer/filters/filter-to-lower.js +10 -0
  43. package/src/utils/suggester-workers/commons-tokenizer/filters/filter-to-lower.spec.js +12 -0
  44. package/src/utils/suggester-workers/commons-tokenizer/index.js +1 -1
  45. package/src/utils/to-expose/handler.js +47 -28
  46. package/src/utils/to-expose/hooks/filter-components.js +106 -106
  47. package/src/utils/to-expose/hooks/index.js +2 -1
  48. package/src/utils/to-expose/hooks/lunatic-split.js +407 -0
  49. package/src/utils/to-expose/hooks/lunatic.js +16 -2
  50. package/src/utils/to-expose/index.js +11 -11
  51. package/src/utils/to-expose/state.js +23 -15
@@ -0,0 +1,407 @@
1
+ import { useState, useEffect, useCallback, useMemo } from 'react';
2
+ import { mergeQuestionnaireAndData } from '../init-questionnaire';
3
+ import { getBindings } from '../state';
4
+ import { updateQuestionnaire, updateExternals } from '../handler';
5
+ import {
6
+ getPage,
7
+ FLOW_NEXT,
8
+ FLOW_PREVIOUS,
9
+ getControls,
10
+ isDev,
11
+ getSplitQuestionnaireSource,
12
+ mergeStateData,
13
+ getRootPageInSources,
14
+ } from '../../lib';
15
+ import { COLLECTED } from '../../../constants';
16
+ import { useFilterComponents } from './filter-components';
17
+ import { loadSuggesters } from '../../store-tools/auto-load';
18
+ import { getCollectedStateByValueType, getState } from '..';
19
+
20
+ const useLunaticSplit = (
21
+ source,
22
+ data,
23
+ {
24
+ savingType = COLLECTED,
25
+ preferences = [COLLECTED],
26
+ features = ['VTL'],
27
+ management = false,
28
+ pagination = false,
29
+ modalForControls = false,
30
+ initialPage = null,
31
+ logFunction = null,
32
+ autoSuggesterLoading = false,
33
+ suggesterFetcher,
34
+ suggesters,
35
+ }
36
+ ) => {
37
+ if (isDev) {
38
+ console.log('useLunaticSplit');
39
+ var start = new Date().getTime();
40
+ }
41
+ const sources = useMemo(() => getSplitQuestionnaireSource(source), [source]);
42
+ const rootPagesOfSource = useMemo(
43
+ () => getRootPageInSources(sources),
44
+ [sources]
45
+ );
46
+
47
+ const [sourceIndice, setSourceIndice] = useState(0);
48
+ const [lunaticData, setLunaticData] = useState(data || {});
49
+
50
+ const [initPage, setInitPage] = useState(false);
51
+ const featuresWithoutMD = features.filter((f) => f !== 'MD');
52
+
53
+ const [questionnaire, setQuestionnaire] = useState(() =>
54
+ mergeQuestionnaireAndData(sources[sourceIndice])(lunaticData)
55
+ );
56
+ const [bindings, setBindings] = useState(() => getBindings(questionnaire));
57
+ const [allBindings, setAllBindings] = useState(bindings);
58
+
59
+ const [wantedPage, setWantedPage] = useState(null);
60
+
61
+ // updating current source
62
+ useEffect(() => {
63
+ setInitPage(false);
64
+ const newQ = mergeQuestionnaireAndData(sources[sourceIndice])(lunaticData);
65
+ setQuestionnaire(newQ);
66
+ const bind = getBindings(newQ);
67
+ setBindings(bind);
68
+ setAllBindings({ ...allBindings, ...bind });
69
+
70
+ // eslint-disable-next-line react-hooks/exhaustive-deps
71
+ }, [sources, sourceIndice]);
72
+
73
+ const getBindingsSplit = (quest) => {
74
+ const bind = getBindings(quest);
75
+ return { ...allBindings, ...bind };
76
+ };
77
+
78
+ function getStateSplit(quest) {
79
+ const lastState = getState(quest);
80
+ return mergeStateData(lunaticData, lastState);
81
+ }
82
+ const getCollectedStateSplit = (quest) => {
83
+ const lastState = getStateSplit(quest);
84
+ return lastState[COLLECTED];
85
+ };
86
+ const getCollectedStateByValueTypeSplit =
87
+ (quest) => (valueType, displayNull) => {
88
+ return getCollectedStateByValueType(null, getCollectedStateSplit(quest))(
89
+ valueType,
90
+ displayNull
91
+ );
92
+ };
93
+
94
+ const [page, setPage] = useState(questionnaire.firstPage);
95
+
96
+ const [todo, setTodo] = useState({});
97
+ const [todoExternals, setTodoExternals] = useState({});
98
+ const [modalContent, setModalContent] = useState(null);
99
+
100
+ const components = useFilterComponents({
101
+ questionnaire,
102
+ management,
103
+ bindings,
104
+ features: featuresWithoutMD,
105
+ page,
106
+ pagination,
107
+ todo: { ...todo, ...todoExternals },
108
+ });
109
+
110
+ const { suggesters: suggestersToLoad } = source;
111
+
112
+ useEffect(() => {
113
+ const init = async () => {
114
+ if (
115
+ autoSuggesterLoading &&
116
+ typeof suggesters === 'object' &&
117
+ Object.values(suggesters).length > 0
118
+ ) {
119
+ const s = suggestersToLoad.reduce(function (current, storeInfo) {
120
+ const { name } = storeInfo;
121
+ if (!suggesters[name]) return current;
122
+ return {
123
+ ...current,
124
+ [name]: {
125
+ ...storeInfo,
126
+ url: suggesters[name].url,
127
+ stopWords: suggesters[name].stopWords,
128
+ },
129
+ };
130
+ }, {});
131
+ loadSuggesters(suggesterFetcher)(s);
132
+ }
133
+ };
134
+ init();
135
+ }, [autoSuggesterLoading, suggesterFetcher, suggesters, suggestersToLoad]);
136
+
137
+ const [flow, setFlow] = useState(null);
138
+
139
+ // TODO: dynamic because of filters (kind of last accessible page)
140
+ const { firstPage, maxPage } = questionnaire;
141
+
142
+ const isFirstPage = page === firstPage;
143
+ const isLastPage = page === maxPage;
144
+ const isFirstSource = sourceIndice === 0;
145
+ const isLastSource = sourceIndice === sources.length - 1;
146
+
147
+ const goSplitNext = () => {
148
+ if (!isLastSource) {
149
+ const stateData = getState(questionnaire);
150
+ setLunaticData(mergeStateData(lunaticData, stateData));
151
+ setSourceIndice(sourceIndice + 1);
152
+ }
153
+ };
154
+
155
+ // First param is the onClick event, useless for us but we have to keep it safe into
156
+ // function signature to avoid confusing with customBindings
157
+ const goNext = (_, customBindings) => {
158
+ if (!(isLastPage && isLastSource)) {
159
+ if (flow !== FLOW_NEXT) {
160
+ setFlow(FLOW_NEXT);
161
+ }
162
+ const nextPage = getPage({
163
+ components: questionnaire.components,
164
+ bindings: customBindings || bindings,
165
+ currentPage: page,
166
+ featuresWithoutMD,
167
+ flow: FLOW_NEXT,
168
+ management,
169
+ });
170
+
171
+ const nextFunction = nextPage ? () => setPage(nextPage) : goSplitNext;
172
+
173
+ if (modalForControls) {
174
+ const controls = getControls({
175
+ page,
176
+ features: featuresWithoutMD,
177
+ components,
178
+ bindings,
179
+ preferences,
180
+ });
181
+ if (controls.length > 0)
182
+ setModalContent({ confirm: nextFunction, controls });
183
+ else nextFunction();
184
+ } else nextFunction();
185
+ }
186
+ };
187
+
188
+ const goSplitPrevious = () => {
189
+ if (!isFirstSource) {
190
+ const stateData = getState(questionnaire);
191
+ setLunaticData(mergeStateData(lunaticData, stateData));
192
+ setSourceIndice(sourceIndice - 1);
193
+ }
194
+ };
195
+
196
+ const goPrevious = () => {
197
+ if (!(isFirstPage && isFirstSource)) {
198
+ if (flow !== FLOW_PREVIOUS) {
199
+ setFlow(FLOW_PREVIOUS);
200
+ }
201
+ const previousPage = getPage({
202
+ components: questionnaire.components,
203
+ bindings,
204
+ currentPage: page,
205
+ featuresWithoutMD,
206
+ flow: FLOW_PREVIOUS,
207
+ management,
208
+ });
209
+
210
+ const previousFunction = previousPage
211
+ ? () => setPage(previousPage)
212
+ : goSplitPrevious;
213
+
214
+ if (modalForControls) {
215
+ const controls = getControls({
216
+ page,
217
+ features: featuresWithoutMD,
218
+ components,
219
+ bindings,
220
+ preferences,
221
+ });
222
+ if (controls.length > 0)
223
+ setModalContent({
224
+ confirm: previousFunction,
225
+ controls,
226
+ });
227
+ else previousFunction();
228
+ } else previousFunction();
229
+ }
230
+ };
231
+
232
+ const exportedSetPage = useCallback(
233
+ (newPage) => {
234
+ const p = newPage?.split('.')[0];
235
+ const index = rootPagesOfSource.findIndex((pages) => pages.includes(p));
236
+ if (index === sourceIndice) {
237
+ setPage(newPage);
238
+ setWantedPage(null);
239
+ } else {
240
+ // change source
241
+ setWantedPage(newPage);
242
+ const stateData = getState(questionnaire);
243
+ setLunaticData(mergeStateData(lunaticData, stateData));
244
+ setSourceIndice(index);
245
+ }
246
+ },
247
+ [lunaticData, questionnaire, rootPagesOfSource, sourceIndice]
248
+ );
249
+
250
+ useEffect(() => {
251
+ if (!initPage) {
252
+ if (wantedPage) {
253
+ setPage(wantedPage);
254
+ setWantedPage(null);
255
+ setInitPage(true);
256
+ } else {
257
+ const getNewInitPage = () => {
258
+ if (flow === FLOW_NEXT) {
259
+ const tempPage =
260
+ sourceIndice - 1 > 0
261
+ ? sources[sourceIndice - 1].maxPage
262
+ : sources[0].maxPage;
263
+ return getPage({
264
+ components: questionnaire.components,
265
+ bindings: bindings,
266
+ currentPage: tempPage,
267
+ featuresWithoutMD,
268
+ flow: FLOW_NEXT,
269
+ management,
270
+ });
271
+ } else if (flow === FLOW_PREVIOUS) {
272
+ const tempPage =
273
+ sourceIndice + 1 < sources.length - 1
274
+ ? sources[sourceIndice + 1].firstPage
275
+ : sources[sources.length - 1].firstPage;
276
+ return getPage({
277
+ components: questionnaire.components,
278
+ bindings: bindings,
279
+ currentPage: tempPage,
280
+ featuresWithoutMD,
281
+ flow: flow,
282
+ management,
283
+ });
284
+ }
285
+ return null;
286
+ };
287
+
288
+ if (!flow) exportedSetPage(initialPage || sources[0].firstPage);
289
+ else {
290
+ const newPage = getNewInitPage();
291
+ if (!newPage) {
292
+ if (flow === FLOW_NEXT) setSourceIndice(sourceIndice + 1);
293
+ else setSourceIndice(sourceIndice - 1);
294
+ } else setPage(newPage);
295
+ }
296
+ setInitPage(true);
297
+ }
298
+ }
299
+ }, [
300
+ initPage,
301
+ isFirstSource,
302
+ components,
303
+ questionnaire.components,
304
+ bindings,
305
+ page,
306
+ featuresWithoutMD,
307
+ management,
308
+ sourceIndice,
309
+ sources,
310
+ flow,
311
+ wantedPage,
312
+ exportedSetPage,
313
+ initialPage,
314
+ ]);
315
+
316
+ const handleChange = useCallback((updatedValue) => {
317
+ setTodo((t) => ({ ...t, ...updatedValue }));
318
+ }, []);
319
+
320
+ const handleExternals = useCallback((externals) => {
321
+ setTodoExternals((t) => ({ ...t, ...externals }));
322
+ }, []);
323
+
324
+ useEffect(() => {
325
+ if (Object.keys(todo).length !== 0) {
326
+ const newQ = updateQuestionnaire(savingType)(questionnaire)(
327
+ preferences,
328
+ logFunction
329
+ )(todo);
330
+
331
+ const bind = getBindings(newQ);
332
+ setBindings(bind);
333
+ setAllBindings({ ...allBindings, ...bind });
334
+ setQuestionnaire(newQ);
335
+ setTodo({});
336
+ }
337
+ // eslint-disable-next-line react-hooks/exhaustive-deps
338
+ }, [
339
+ todo,
340
+ preferences,
341
+ logFunction,
342
+ questionnaire,
343
+ savingType,
344
+ features,
345
+ management,
346
+ ]);
347
+
348
+ useEffect(() => {
349
+ if (Object.keys(todoExternals).length !== 0) {
350
+ const newQ = updateExternals(questionnaire)(logFunction)(todoExternals);
351
+ setQuestionnaire(newQ);
352
+ setTodoExternals({});
353
+ }
354
+ }, [todoExternals, logFunction, questionnaire]);
355
+
356
+ const cancelModal = () => {
357
+ setModalContent(null);
358
+ };
359
+
360
+ const validateModal = () => {
361
+ const { confirm } = modalContent;
362
+ if (confirm) confirm();
363
+ setModalContent(null);
364
+ };
365
+
366
+ const componentsToDiplay =
367
+ pagination && modalContent
368
+ ? [
369
+ {
370
+ componentType: 'Modal',
371
+ controls: modalContent.controls,
372
+ validateModal,
373
+ cancelModal,
374
+ },
375
+ ...components,
376
+ ]
377
+ : components;
378
+
379
+ if (isDev)
380
+ console.log(`End useLunaticSplit: ${new Date().getTime() - start} ms`);
381
+
382
+ return {
383
+ questionnaire,
384
+ handleChange,
385
+ handleExternals,
386
+ components: componentsToDiplay,
387
+ bindings,
388
+ allBindings,
389
+ state: {
390
+ getState: getStateSplit,
391
+ getCollectedState: getCollectedStateSplit,
392
+ getCollectedStateByValueType: getCollectedStateByValueTypeSplit,
393
+ getBindings: getBindingsSplit,
394
+ },
395
+ pagination: {
396
+ page: page,
397
+ maxPage: sources[sources.length - 1].maxPage,
398
+ goNext,
399
+ goPrevious,
400
+ isFirstPage: isFirstPage && isFirstSource,
401
+ isLastPage: isLastPage && isLastSource,
402
+ setPage: exportedSetPage,
403
+ },
404
+ };
405
+ };
406
+
407
+ export default useLunaticSplit;
@@ -2,7 +2,13 @@ import { useState, useEffect, useCallback } from 'react';
2
2
  import { mergeQuestionnaireAndData } from '../init-questionnaire';
3
3
  import { getBindings } from '../state';
4
4
  import { updateQuestionnaire, updateExternals } from '../handler';
5
- import { getPage, FLOW_NEXT, FLOW_PREVIOUS, getControls } from '../../lib';
5
+ import {
6
+ getPage,
7
+ FLOW_NEXT,
8
+ FLOW_PREVIOUS,
9
+ getControls,
10
+ isDev,
11
+ } from '../../lib';
6
12
  import { COLLECTED } from '../../../constants';
7
13
  import { useFilterComponents } from './filter-components';
8
14
  import { loadSuggesters } from '../../store-tools/auto-load';
@@ -24,12 +30,17 @@ const useLunatic = (
24
30
  suggesters,
25
31
  }
26
32
  ) => {
33
+ if (isDev) {
34
+ console.log('useLunatic');
35
+ var start = new Date().getTime();
36
+ }
27
37
  const [initPage, setInitPage] = useState(false);
28
38
  const featuresWithoutMD = features.filter((f) => f !== 'MD');
29
39
  const [questionnaire, setQuestionnaire] = useState(() =>
30
40
  mergeQuestionnaireAndData(source)(data || {})
31
41
  );
32
- const bindings = getBindings(questionnaire);
42
+ const [bindings, setBindings] = useState(() => getBindings(questionnaire));
43
+
33
44
  const [page, setPage] = useState(initialPage);
34
45
 
35
46
  const [todo, setTodo] = useState({});
@@ -185,6 +196,7 @@ const useLunatic = (
185
196
  preferences,
186
197
  logFunction
187
198
  )(todo);
199
+ setBindings(getBindings(newQ));
188
200
  setQuestionnaire(newQ);
189
201
  setTodo({});
190
202
  }
@@ -228,6 +240,8 @@ const useLunatic = (
228
240
  ]
229
241
  : components;
230
242
 
243
+ if (isDev) console.log(`End useLunatic: ${new Date().getTime() - start} ms`);
244
+
231
245
  return {
232
246
  questionnaire,
233
247
  handleChange,
@@ -1,11 +1,11 @@
1
- // updateQuestionnaire is useless since 2.0 optimization efforts
2
- // export { updateQuestionnaire } from './handler';
3
- export { mergeQuestionnaireAndData } from './init-questionnaire';
4
- export {
5
- getState,
6
- getCollectedState,
7
- getCollectedStateByValueType,
8
- getBindings,
9
- } from './state';
10
- export { interpret } from './interpret';
11
- export { default as useLunatic } from './hooks';
1
+ // updateQuestionnaire is useless since 2.0 optimization efforts
2
+ // export { updateQuestionnaire } from './handler';
3
+ export { mergeQuestionnaireAndData } from './init-questionnaire';
4
+ export {
5
+ getState,
6
+ getCollectedState,
7
+ getCollectedStateByValueType,
8
+ getBindings,
9
+ } from './state';
10
+ export { interpret } from './interpret';
11
+ export { useLunatic, useLunaticSplit } from './hooks';
@@ -1,4 +1,5 @@
1
1
  import * as C from '../../constants';
2
+ import { isDev } from '../lib';
2
3
 
3
4
  export const getState = (questionnaire) => {
4
5
  const { variables } = questionnaire;
@@ -33,26 +34,33 @@ const getCalculatedFromVariables = (variables) =>
33
34
  const getExternalFromVariables = (variables) =>
34
35
  (variables && variables[C.EXTERNAL]) || {};
35
36
 
36
- export const getCollectedStateByValueType = (questionnaire) => (
37
- valueType,
38
- displayNull
39
- ) =>
40
- ['PREVIOUS', 'COLLECTED', 'FORCED', 'EDITED', 'INPUTED'].includes(valueType)
41
- ? Object.entries(getCollectedState(questionnaire)).reduce((_, v) => {
42
- if (displayNull || v[1][valueType] !== null)
43
- return {
44
- ..._,
45
- [v[0]]: v[1][valueType],
46
- };
47
- return _;
48
- }, {})
49
- : {};
37
+ export const getCollectedStateByValueType =
38
+ (questionnaire, collectedState) => (valueType, displayNull) =>
39
+ ['PREVIOUS', 'COLLECTED', 'FORCED', 'EDITED', 'INPUTED'].includes(valueType)
40
+ ? Object.entries(
41
+ collectedState || getCollectedState(questionnaire)
42
+ ).reduce((_, v) => {
43
+ if (displayNull || v[1][valueType] !== null)
44
+ return {
45
+ ..._,
46
+ [v[0]]: v[1][valueType],
47
+ };
48
+ return _;
49
+ }, {})
50
+ : {};
50
51
 
51
52
  export const getBindings = (questionnaire) => {
53
+ if (isDev) {
54
+ console.log('Get bindings');
55
+ var start = new Date().getTime();
56
+ }
52
57
  const { variables } = questionnaire;
53
- return {
58
+ const bindings = {
54
59
  ...getCollectedStateByValueType(questionnaire)('COLLECTED', true),
55
60
  ...getCalculatedFromVariables(variables),
56
61
  ...getExternalFromVariables(variables),
57
62
  };
63
+ if (isDev)
64
+ console.log(`End get bindings: ${new Date().getTime() - start} ms`);
65
+ return bindings;
58
66
  };