@inseefr/lunatic 3.8.0-rc.ucq-options-variable.0 → 3.8.1-rc.0

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 (65) hide show
  1. package/components/Suggester/Suggester.js +19 -7
  2. package/components/Suggester/Suggester.js.map +1 -1
  3. package/components/Suggester/Suggester.spec.js +8 -1
  4. package/components/Suggester/Suggester.spec.js.map +1 -1
  5. package/components/Suggester/useSuggestions.d.ts +21 -2
  6. package/components/Suggester/useSuggestions.js +26 -12
  7. package/components/Suggester/useSuggestions.js.map +1 -1
  8. package/esm/components/Suggester/Suggester.js +21 -8
  9. package/esm/components/Suggester/Suggester.js.map +1 -1
  10. package/esm/components/Suggester/Suggester.spec.js +8 -1
  11. package/esm/components/Suggester/Suggester.spec.js.map +1 -1
  12. package/esm/components/Suggester/useSuggestions.d.ts +21 -2
  13. package/esm/components/Suggester/useSuggestions.js +24 -10
  14. package/esm/components/Suggester/useSuggestions.js.map +1 -1
  15. package/esm/type.source.d.ts +3 -9
  16. package/esm/use-lunatic/commons/fill-components/fill-component-expressions.d.ts +1 -1
  17. package/esm/use-lunatic/commons/fill-components/fill-component-expressions.js.map +1 -1
  18. package/esm/use-lunatic/commons/fill-components/fill-components.js +2 -10
  19. package/esm/use-lunatic/commons/fill-components/fill-components.js.map +1 -1
  20. package/esm/use-lunatic/props/propOptions.d.ts +1 -9
  21. package/esm/use-lunatic/props/propOptions.js +1 -56
  22. package/esm/use-lunatic/props/propOptions.js.map +1 -1
  23. package/esm/use-lunatic/props/propOptions.spec.js +56 -220
  24. package/esm/use-lunatic/props/propOptions.spec.js.map +1 -1
  25. package/esm/utils/search/SearchInterface.d.ts +1 -0
  26. package/esm/utils/search/SearchMinisearch.d.ts +1 -0
  27. package/esm/utils/search/SearchMinisearch.js +6 -0
  28. package/esm/utils/search/SearchMinisearch.js.map +1 -1
  29. package/esm/utils/search/SuggestersDatabase.d.ts +7 -0
  30. package/esm/utils/search/SuggestersDatabase.js.map +1 -1
  31. package/package.json +1 -4
  32. package/src/components/Suggester/Suggester.spec.tsx +9 -1
  33. package/src/components/Suggester/Suggester.tsx +21 -8
  34. package/src/components/Suggester/useSuggestions.ts +39 -18
  35. package/src/stories/checkbox/checkbox.stories.tsx +0 -13
  36. package/src/stories/dropdown/dropdown.stories.tsx +0 -12
  37. package/src/stories/radio/radio.stories.tsx +0 -13
  38. package/src/type.source.ts +3 -9
  39. package/src/use-lunatic/commons/fill-components/fill-component-expressions.ts +1 -2
  40. package/src/use-lunatic/commons/fill-components/fill-components.ts +10 -9
  41. package/src/use-lunatic/props/propOptions.spec.ts +147 -217
  42. package/src/use-lunatic/props/propOptions.ts +8 -97
  43. package/src/utils/search/SearchInterface.ts +2 -0
  44. package/src/utils/search/SearchMinisearch.ts +7 -0
  45. package/src/utils/search/SuggestersDatabase.ts +10 -0
  46. package/tsconfig.build.tsbuildinfo +1 -1
  47. package/type.source.d.ts +3 -9
  48. package/use-lunatic/commons/fill-components/fill-component-expressions.d.ts +1 -1
  49. package/use-lunatic/commons/fill-components/fill-component-expressions.js.map +1 -1
  50. package/use-lunatic/commons/fill-components/fill-components.js +1 -9
  51. package/use-lunatic/commons/fill-components/fill-components.js.map +1 -1
  52. package/use-lunatic/props/propOptions.d.ts +1 -9
  53. package/use-lunatic/props/propOptions.js +2 -57
  54. package/use-lunatic/props/propOptions.js.map +1 -1
  55. package/use-lunatic/props/propOptions.spec.js +55 -217
  56. package/use-lunatic/props/propOptions.spec.js.map +1 -1
  57. package/utils/search/SearchInterface.d.ts +1 -0
  58. package/utils/search/SearchMinisearch.d.ts +1 -0
  59. package/utils/search/SearchMinisearch.js +6 -0
  60. package/utils/search/SearchMinisearch.js.map +1 -1
  61. package/utils/search/SuggestersDatabase.d.ts +7 -0
  62. package/utils/search/SuggestersDatabase.js.map +1 -1
  63. package/src/stories/checkbox/sourceOneDynamicOptions.json +0 -496
  64. package/src/stories/dropdown/sourceDynamicOptions.json +0 -496
  65. package/src/stories/radio/sourceDynamicOptions.json +0 -496
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
2
2
  import type { LunaticComponentProps } from '../type';
3
3
  import { CustomSuggester } from './CustomSuggester';
4
4
  import { getComponentErrors } from '../shared/ComponentErrors/ComponentErrors';
5
- import { OTHER_VALUE, useSuggestions } from './useSuggestions';
5
+ import { OTHER_VALUE, useStore, useSuggestions } from './useSuggestions';
6
6
  import D from '../../i18n';
7
7
  import type { SuggesterOptionType } from './SuggesterType';
8
8
 
@@ -36,6 +36,10 @@ export function WrappedSuggester({
36
36
  arbitrary,
37
37
  arbitraryValue,
38
38
  }: LunaticComponentProps<'Suggester'>) {
39
+ const { store, storeState, setStoreState, getLabelById } = useStore({
40
+ storeName,
41
+ });
42
+
39
43
  // Default options should not change between render
40
44
  // so we can break the rule of hooks here
41
45
  const computeSelectedOptions = (): [SuggesterOptionType] | [] => {
@@ -47,7 +51,7 @@ export function WrappedSuggester({
47
51
  }
48
52
  const labelResponse = optionResponses?.find((o) => o.attribute === 'label');
49
53
  if (!labelResponse) {
50
- return [{ id: value, label: value, value: value }];
54
+ return [{ id: value, label: getLabelById(value), value: value }];
51
55
  }
52
56
  const label = executeExpression(
53
57
  { value: labelResponse.name, type: 'VTL' },
@@ -73,7 +77,9 @@ export function WrappedSuggester({
73
77
 
74
78
  const { state, options, search, setSearch, onFocus, onBlur } = useSuggestions(
75
79
  {
76
- storeName: storeName,
80
+ store,
81
+ storeState,
82
+ setStoreState,
77
83
  allowArbitrary: !!arbitrary,
78
84
  selectedOptions: selectedOptions,
79
85
  }
@@ -110,7 +116,7 @@ export function WrappedSuggester({
110
116
  { name: response.name, value: null },
111
117
  ];
112
118
  // User chose an arbitrary option or clear the value
113
- if (arbitrary && arbitrary.response) {
119
+ if (arbitrary?.response) {
114
120
  newResponses.push({
115
121
  name: arbitrary.response.name,
116
122
  value: v?.id === OTHER_VALUE ? search : null,
@@ -145,11 +151,18 @@ export function WrappedSuggester({
145
151
  setSearch('');
146
152
  };
147
153
 
148
- // Fix display issue (when handleChanges is called outside this component (in management mode, return to FORCED value by example) )
149
- // We have to re-compute actual selection
150
154
  useEffect(() => {
151
- if (value) {
152
- setSelectedOptions(computeSelectedOptions());
155
+ // Fix display issue (when handleChanges is called outside this component (in management mode, return to FORCED value by example)
156
+ // "value" does'nt match selectedOption's "id"
157
+ if (value && selectedOptions[0]?.id !== value) {
158
+ const actualSelection = computeSelectedOptions();
159
+ const selectedOptionsWithLabel = [
160
+ {
161
+ ...actualSelection[0],
162
+ label: getLabelById(actualSelection[0]?.id),
163
+ },
164
+ ] as [SuggesterOptionType];
165
+ setSelectedOptions(selectedOptionsWithLabel);
153
166
  }
154
167
  // eslint-disable-next-line react-hooks/exhaustive-deps
155
168
  }, [value]);
@@ -1,11 +1,16 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import type { SuggesterOptionType } from './SuggesterType';
3
3
  import { useEffectDebounced } from '../../hooks/useDebounce';
4
- import { getSearchForStore } from '../../utils/search/SuggestersDatabase';
4
+ import {
5
+ getSearchForStore,
6
+ SearchStore,
7
+ } from '../../utils/search/SuggestersDatabase';
5
8
  import { useRefSync } from '../../hooks/useRefSync';
6
9
 
7
10
  type Props = {
8
- storeName: string;
11
+ store: SearchStore;
12
+ storeState: State;
13
+ setStoreState: (s: State) => any;
9
14
  selectedOptions: SuggesterOptionType[];
10
15
  allowArbitrary: boolean;
11
16
  };
@@ -14,20 +19,13 @@ export const OTHER_VALUE = 'OTHER';
14
19
 
15
20
  type State = 'success' | 'loading' | 'error';
16
21
 
17
- export function useSuggestions({
18
- storeName,
19
- selectedOptions,
20
- allowArbitrary,
21
- }: Props) {
22
- const [searchQuery, setSearchQuery] = useState('');
22
+ export function useStore({ storeName }: { storeName: string }) {
23
23
  const store = getSearchForStore(storeName);
24
24
  const searchIndexRef = useRefSync(store.index);
25
- // eslint-disable-next-line prefer-const
26
- let [options, setOptions] = useState(selectedOptions);
27
25
  const [state, setState] = useState<State>(
28
26
  store.error !== null
29
27
  ? 'error'
30
- : store.search.isIndexed()
28
+ : store?.search.isIndexed()
31
29
  ? 'success'
32
30
  : 'loading'
33
31
  );
@@ -47,19 +45,42 @@ export function useSuggestions({
47
45
  });
48
46
  }, [searchIndexRef]);
49
47
 
48
+ return {
49
+ store,
50
+ storeState: state,
51
+ setStoreState: setState,
52
+ getLabelById: (id: any) => {
53
+ if (!id) return '';
54
+ return store.search?.getFieldsById(id).label ?? '';
55
+ },
56
+ };
57
+ }
58
+
59
+ export function useSuggestions({
60
+ store,
61
+ storeState: state,
62
+ setStoreState: setState,
63
+ selectedOptions,
64
+ allowArbitrary,
65
+ }: Props) {
66
+ const [searchQuery, setSearchQuery] = useState('');
67
+ // eslint-disable-next-line prefer-const
68
+ let [options, setOptions] = useState(selectedOptions);
69
+
50
70
  useEffectDebounced(
51
71
  () => {
52
72
  // Do not reset search for empty search
53
73
  if (!searchQuery) {
54
74
  return;
55
75
  }
56
- store.search
57
- ?.search(searchQuery)
58
- .then((r) => {
59
- setOptions(r);
60
- setState('success');
61
- })
62
- .catch(() => setState('error'));
76
+ if (store.error === null)
77
+ store.search
78
+ ?.search(searchQuery)
79
+ .then((r) => {
80
+ setOptions(r);
81
+ setState('success');
82
+ })
83
+ .catch(() => setState('error'));
63
84
  },
64
85
  [searchQuery],
65
86
  300
@@ -1,4 +1,3 @@
1
- import { dataFromObject } from '../../utils/object';
2
1
  import {
3
2
  type Orchestrator,
4
3
  OrchestratorMeta,
@@ -11,7 +10,6 @@ import sourceGroupDetail from './sourceGroupDetail.json';
11
10
  import sourceGroupLoop from './sourceGroupLoop.json';
12
11
  import sourceOne from './sourceOne.json';
13
12
  import sourceOneDetail from './sourceOneDetail.json';
14
- import sourceOneDynamicOptions from './sourceOneDynamicOptions.json';
15
13
 
16
14
  import { Meta } from '@storybook/react';
17
15
 
@@ -75,14 +73,3 @@ export const CheckboxOneWithDetail: OrchestratorStory = {
75
73
  source: sourceOneDetail,
76
74
  },
77
75
  };
78
-
79
- export const CheckboxOneDynamicOptions: OrchestratorStory = {
80
- args: {
81
- source: sourceOneDynamicOptions,
82
- data: dataFromObject({
83
- NBHAB: 3,
84
- PRENOM: ['Verso', 'Maëlle', 'Aline'],
85
- AGE: [30, 16, 50],
86
- }),
87
- },
88
- };
@@ -4,7 +4,6 @@ import {
4
4
  type OrchestratorStory,
5
5
  } from '../utils/Orchestrator';
6
6
  import source from './source.json';
7
- import sourceDynamicOptions from './sourceDynamicOptions.json';
8
7
 
9
8
  import { Meta } from '@storybook/react';
10
9
 
@@ -26,14 +25,3 @@ export const Default: OrchestratorStory = {
26
25
  }),
27
26
  },
28
27
  };
29
-
30
- export const DynamicOptions: OrchestratorStory = {
31
- args: {
32
- source: sourceDynamicOptions,
33
- data: dataFromObject({
34
- NBHAB: 3,
35
- PRENOM: ['Verso', 'Maëlle', 'Aline'],
36
- AGE: [30, 16, 50],
37
- }),
38
- },
39
- };
@@ -7,10 +7,8 @@ import source from './source.json';
7
7
  import sourceHorizontal from './sourceHorizontal.json';
8
8
  import sourceDetail from './sourceDetail.json';
9
9
  import sourceCondition from './sourceCondition.json';
10
- import sourceDynamicOptions from './sourceDynamicOptions.json';
11
10
 
12
11
  import { Meta } from '@storybook/react';
13
- import { dataFromObject } from '../../utils/object';
14
12
 
15
13
  const meta: Meta<typeof Orchestrator> = {
16
14
  title: 'Components/Radio',
@@ -53,14 +51,3 @@ export const WithDetail: OrchestratorStory = {
53
51
  source: sourceDetail,
54
52
  },
55
53
  };
56
-
57
- export const DynamicOptions: OrchestratorStory = {
58
- args: {
59
- source: sourceDynamicOptions,
60
- data: dataFromObject({
61
- NBHAB: 3,
62
- PRENOM: ['Verso', 'Maëlle', 'Aline'],
63
- AGE: [30, 16, 50],
64
- }),
65
- },
66
- };
@@ -159,9 +159,7 @@ export type ComponentCheckboxBooleanDefinition =
159
159
  export type ComponentRadioDefinition = ComponentDefinitionBaseWithResponse & {
160
160
  componentType: 'Radio';
161
161
  orientation?: 'horizontal' | 'vertical';
162
- options?: OptionsWithDetail;
163
- optionSource?: string;
164
- optionFilter?: VTLExpression;
162
+ options: OptionsWithDetail;
165
163
  };
166
164
  export type OptionsWithDetail = {
167
165
  value: string | boolean;
@@ -178,9 +176,7 @@ export type OptionsWithDetail = {
178
176
  export type ComponentDropdownDefinition =
179
177
  ComponentDefinitionBaseWithResponse & {
180
178
  componentType: 'Dropdown';
181
- options?: Options;
182
- optionSource?: string;
183
- optionFilter?: VTLExpression;
179
+ options: Options;
184
180
  };
185
181
  export type ComponentQuestionDefinition = ComponentDefinitionBase & {
186
182
  componentType: 'Question';
@@ -189,9 +185,7 @@ export type ComponentQuestionDefinition = ComponentDefinitionBase & {
189
185
  export type ComponentCheckboxOneDefinition =
190
186
  ComponentDefinitionBaseWithResponse & {
191
187
  componentType: 'CheckboxOne';
192
- options?: OptionsWithDetail;
193
- optionSource?: string;
194
- optionFilter?: VTLExpression;
188
+ options: OptionsWithDetail;
195
189
  };
196
190
  export type ComponentSuggesterDefinition =
197
191
  ComponentDefinitionBaseWithResponse & {
@@ -91,8 +91,7 @@ type UntranslatedProperties =
91
91
  | 'controls'
92
92
  | 'conditionFilter'
93
93
  | 'conditionReadOnly'
94
- | 'components'
95
- | 'optionFilter';
94
+ | 'components';
96
95
  export type DeepTranslateExpression<T> = T extends LunaticExpression
97
96
  ? ReactNode
98
97
  : T extends (infer ElementType)[]
@@ -11,7 +11,7 @@ import type { LunaticComponentProps } from '../../../components/type';
11
11
  import { getMissingResponseProp } from '../../props/propMissingResponse';
12
12
  import { getValueProp } from '../../props/propValue';
13
13
  import { getIterationsProp } from '../../props/propIterations';
14
- import { computeOptionsFromComponent } from '../../props/propOptions';
14
+ import { getOptionsProp } from '../../props/propOptions';
15
15
  import { LunaticLogger } from '../../logger/type';
16
16
  import { VTLScalarExpression } from '../../../type.source';
17
17
 
@@ -80,15 +80,16 @@ export const fillComponent = (
80
80
  missingResponse: getMissingResponseProp(component, state),
81
81
  management: state.management,
82
82
  iterations: getIterationsProp(component, state),
83
- options: computeOptionsFromComponent(interpretedProps, {
84
- variables: state.variables,
85
- handleChanges: state.handleChanges,
86
- pagerIteration: state.pager.iteration,
83
+ options: getOptionsProp(
84
+ interpretedProps,
85
+ state.variables,
86
+ state.handleChanges,
87
+ state.pager.iteration,
87
88
  value,
88
- logger: state.logger,
89
- disableFilters: state.disableFilters,
90
- shouldParentBeFiltered: shouldBeFiltered,
91
- }),
89
+ state.logger,
90
+ state.disableFilters,
91
+ shouldBeFiltered
92
+ ),
92
93
  ...getComponentTypeProps(interpretedProps, state),
93
94
  // This is too dynamic to be typed correctly, so we allow any here
94
95
  } as any;