@inseefr/lunatic 3.5.0 → 3.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/components/Datepicker/Datepicker.js +3 -60
  2. package/components/Datepicker/Datepicker.js.map +1 -1
  3. package/components/Datepicker/DatepickerFields.d.ts +8 -0
  4. package/components/Datepicker/DatepickerFields.js +60 -0
  5. package/components/Datepicker/DatepickerFields.js.map +1 -0
  6. package/components/Duration/durationUtils.d.ts +0 -1
  7. package/components/Duration/durationUtils.js.map +1 -1
  8. package/components/Duration/getDurationFromValue.d.ts +3 -2
  9. package/components/Duration/getDurationFromValue.js.map +1 -1
  10. package/components/shared/HOC/slottableComponent.d.ts +2 -0
  11. package/components/shared/HOC/slottableComponent.js.map +1 -1
  12. package/components/type.d.ts +2 -3
  13. package/esm/components/Datepicker/Datepicker.js +3 -60
  14. package/esm/components/Datepicker/Datepicker.js.map +1 -1
  15. package/esm/components/Datepicker/DatepickerFields.d.ts +8 -0
  16. package/esm/components/Datepicker/DatepickerFields.js +57 -0
  17. package/esm/components/Datepicker/DatepickerFields.js.map +1 -0
  18. package/esm/components/Duration/durationUtils.d.ts +0 -1
  19. package/esm/components/Duration/durationUtils.js.map +1 -1
  20. package/esm/components/Duration/getDurationFromValue.d.ts +3 -2
  21. package/esm/components/Duration/getDurationFromValue.js.map +1 -1
  22. package/esm/components/shared/HOC/slottableComponent.d.ts +2 -0
  23. package/esm/components/shared/HOC/slottableComponent.js.map +1 -1
  24. package/esm/components/type.d.ts +2 -3
  25. package/esm/use-lunatic/commons/fill-components/fill-components.d.ts +2 -0
  26. package/esm/use-lunatic/commons/fill-components/fill-components.js +1 -1
  27. package/esm/use-lunatic/commons/fill-components/fill-components.js.map +1 -1
  28. package/esm/use-lunatic/commons/variables/behaviours/resizing-behaviour.js +3 -3
  29. package/esm/use-lunatic/commons/variables/behaviours/resizing-behaviour.js.map +1 -1
  30. package/esm/use-lunatic/commons/variables/lunatic-variables-store.spec.js +17 -1
  31. package/esm/use-lunatic/commons/variables/lunatic-variables-store.spec.js.map +1 -1
  32. package/esm/use-lunatic/props/getComponentTypeProps.d.ts +1 -1
  33. package/esm/use-lunatic/props/propOptions.d.ts +2 -1
  34. package/esm/use-lunatic/props/propOptions.js +23 -3
  35. package/esm/use-lunatic/props/propOptions.js.map +1 -1
  36. package/esm/use-lunatic/props/propOptions.spec.js +46 -5
  37. package/esm/use-lunatic/props/propOptions.spec.js.map +1 -1
  38. package/esm/use-lunatic/type.d.ts +18 -4
  39. package/esm/use-lunatic/use-lunatic.js +2 -3
  40. package/esm/use-lunatic/use-lunatic.js.map +1 -1
  41. package/package.json +8 -1
  42. package/src/components/Datepicker/Datepicker.tsx +4 -117
  43. package/src/components/Datepicker/DatepickerFields.tsx +127 -0
  44. package/src/components/Duration/Duration.test.tsx +2 -2
  45. package/src/components/Duration/durationUtils.ts +0 -2
  46. package/src/components/Duration/getDurationFromValue.ts +4 -3
  47. package/src/components/shared/HOC/slottableComponent.tsx +4 -0
  48. package/src/components/type.ts +2 -2
  49. package/src/use-lunatic/commons/fill-components/fill-components.ts +4 -1
  50. package/src/use-lunatic/commons/variables/behaviours/resizing-behaviour.ts +2 -5
  51. package/src/use-lunatic/commons/variables/lunatic-variables-store.spec.ts +17 -1
  52. package/src/use-lunatic/props/propOptions.spec.ts +71 -5
  53. package/src/use-lunatic/props/propOptions.ts +23 -3
  54. package/src/use-lunatic/type.ts +18 -4
  55. package/src/use-lunatic/use-lunatic.test.ts +13 -23
  56. package/src/use-lunatic/use-lunatic.ts +2 -3
  57. package/tsconfig.build.tsbuildinfo +1 -1
  58. package/use-lunatic/commons/fill-components/fill-components.d.ts +2 -0
  59. package/use-lunatic/commons/fill-components/fill-components.js +1 -1
  60. package/use-lunatic/commons/fill-components/fill-components.js.map +1 -1
  61. package/use-lunatic/commons/variables/behaviours/resizing-behaviour.js +3 -3
  62. package/use-lunatic/commons/variables/behaviours/resizing-behaviour.js.map +1 -1
  63. package/use-lunatic/commons/variables/lunatic-variables-store.spec.js +17 -1
  64. package/use-lunatic/commons/variables/lunatic-variables-store.spec.js.map +1 -1
  65. package/use-lunatic/props/getComponentTypeProps.d.ts +1 -1
  66. package/use-lunatic/props/propOptions.d.ts +2 -1
  67. package/use-lunatic/props/propOptions.js +23 -3
  68. package/use-lunatic/props/propOptions.js.map +1 -1
  69. package/use-lunatic/props/propOptions.spec.js +46 -5
  70. package/use-lunatic/props/propOptions.spec.js.map +1 -1
  71. package/use-lunatic/type.d.ts +18 -4
  72. package/use-lunatic/use-lunatic.js +2 -3
  73. package/use-lunatic/use-lunatic.js.map +1 -1
@@ -0,0 +1,127 @@
1
+ import { useState } from 'react';
2
+ import { slottableComponent } from '../shared/HOC/slottableComponent';
3
+ import type { LunaticComponentProps } from '../type';
4
+ import { DatepickerField } from './DatepickerField';
5
+ import { LunaticError } from '../../use-lunatic/type';
6
+
7
+ type CustomProps = Omit<
8
+ LunaticComponentProps<'Datepicker'>,
9
+ 'response' | 'handleChanges' | 'errors'
10
+ > & {
11
+ onChange: (s: string | null) => void;
12
+ errors?: LunaticError[];
13
+ };
14
+
15
+ export const CustomDatepickerFields = slottableComponent<CustomProps>(
16
+ 'DatepickerFields',
17
+ (props) => {
18
+ const {
19
+ disabled,
20
+ readOnly,
21
+ value = '',
22
+ dateFormat = 'YYYY-MM-DD',
23
+ id,
24
+ onChange,
25
+ } = props;
26
+
27
+ const showDay = dateFormat.includes('DD');
28
+ const showMonth = dateFormat.includes('MM');
29
+
30
+ // Raw state, we allow invalid dates to be typed
31
+ const [numbers, setNumbers] = useState(() =>
32
+ numbersFromDateString(value ?? undefined)
33
+ );
34
+ const setNumber = (index: number) => (value: number) => {
35
+ const newNumbers = [...numbers] as typeof numbers;
36
+ newNumbers[index] = value;
37
+ setNumbers(newNumbers);
38
+ onNumbersChange(newNumbers);
39
+ };
40
+
41
+ const onNumbersChange = (numbers: [number, number, number]) => {
42
+ const formatParts = dateFormat.split('-');
43
+ const hasNaNIndex = numbers.findIndex((v) => Number.isNaN(v));
44
+
45
+ // Date is not valid, or date has a missing part
46
+ if (
47
+ (dateFormat === 'YYYY-MM-DD' && !isDateValid(numbers)) ||
48
+ (hasNaNIndex > -1 && hasNaNIndex <= formatParts.length - 1)
49
+ ) {
50
+ onChange(null);
51
+ return;
52
+ }
53
+
54
+ const result = formatParts
55
+ .map((v, k) => numbers[k].toString().padStart(v.length, '0'))
56
+ .join('-');
57
+ onChange(result);
58
+ };
59
+
60
+ const extraProps = {
61
+ readOnly,
62
+ disabled,
63
+ };
64
+
65
+ return (
66
+ <div className="lunaticDatepickerFields">
67
+ {showDay && (
68
+ <DatepickerField
69
+ id={id + 'day'}
70
+ label="Jour"
71
+ description="Exemple: 14"
72
+ max={31}
73
+ value={numbers[2]}
74
+ onChange={setNumber(2)}
75
+ {...extraProps}
76
+ />
77
+ )}
78
+ {showMonth && (
79
+ <DatepickerField
80
+ id={id + 'month'}
81
+ label="Mois"
82
+ description="Exemple: 7"
83
+ max={12}
84
+ value={numbers[1]}
85
+ onChange={setNumber(1)}
86
+ {...extraProps}
87
+ />
88
+ )}
89
+ <DatepickerField
90
+ id={id + 'year'}
91
+ label="Année"
92
+ description="Exemple: 2023"
93
+ value={numbers[0]}
94
+ max={9999}
95
+ onChange={setNumber(0)}
96
+ {...extraProps}
97
+ />
98
+ </div>
99
+ );
100
+ }
101
+ );
102
+
103
+ function numbersFromDateString(s?: string): [number, number, number] {
104
+ if (!s) {
105
+ return [NaN, NaN, NaN];
106
+ }
107
+ const [year, month, day] = s.split('-').map((part) => parseInt(part, 10));
108
+ return [year, month, day];
109
+ }
110
+
111
+ /**
112
+ * Check if the date provided by the user is valid (e.g. not 2001/02/29)
113
+ */
114
+ function isDateValid(dateArray: [number, number, number]) {
115
+ const [year, month, day] = dateArray;
116
+
117
+ // do not set the date directly on new Date(), to avoid transformation on year between 0 and 99.
118
+ //See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date#year
119
+ const date = new Date();
120
+ date.setFullYear(year, month - 1, day);
121
+
122
+ return (
123
+ date.getFullYear() === year &&
124
+ date.getMonth() === month - 1 &&
125
+ date.getDate() === day
126
+ );
127
+ }
@@ -1,7 +1,7 @@
1
1
  import { afterEach, describe, expect, it, vi } from 'vitest';
2
2
  import { Duration } from './Duration';
3
3
  import { render } from '@testing-library/react';
4
- import { Formats } from './durationUtils';
4
+ import { DurationFormat } from '../type';
5
5
 
6
6
  describe('Duration', () => {
7
7
  const mockOnChange = vi.fn();
@@ -24,7 +24,7 @@ describe('Duration', () => {
24
24
  const baseProps = {
25
25
  handleChanges: mockOnChange,
26
26
  response: { name: 'demo' },
27
- format: 'PTnHnM' as Formats,
27
+ format: 'PTnHnM' as DurationFormat,
28
28
  value: 'PT5H1M',
29
29
  id: 'duration',
30
30
  label: 'label',
@@ -5,8 +5,6 @@ export type DurationValue = {
5
5
  months?: number | null;
6
6
  };
7
7
 
8
- export type Formats = 'PTnHnM' | 'PnYnM';
9
-
10
8
  export const propsByUnit = {
11
9
  hours: { min: 0, max: 99, size: 2, style: { width: '2.5em' } },
12
10
  minutes: { min: 0, max: 59, size: 2, style: { width: '2.5em' } },
@@ -1,4 +1,5 @@
1
- import { type Formats, type DurationValue } from './durationUtils';
1
+ import { DurationFormat } from '../type';
2
+ import { type DurationValue } from './durationUtils';
2
3
 
3
4
  /**
4
5
  * Convert a string into a duration
@@ -8,7 +9,7 @@ import { type Formats, type DurationValue } from './durationUtils';
8
9
  */
9
10
  export const getDurationFromValue = (
10
11
  value: string | null,
11
- format: Formats
12
+ format: DurationFormat
12
13
  ): DurationValue => {
13
14
  // Handle nulls value
14
15
  if (value === null && format === 'PTnHnM') {
@@ -30,7 +31,7 @@ export const getDurationFromValue = (
30
31
  * ## Example :
31
32
  * - "P12Y3M" => [12, 3]
32
33
  */
33
- const matchFromFormat = (value: string, format: Formats): number[] => {
34
+ const matchFromFormat = (value: string, format: DurationFormat): number[] => {
34
35
  const regex = new RegExp(format.replaceAll('n', '(\\d+)'));
35
36
  const match = value.match(regex);
36
37
  if (!match) {
@@ -43,6 +43,7 @@ import type { SummaryResponses, SummaryTitle } from '../../Summary/Summary';
43
43
  import type { LunaticComponentProps } from '../../type';
44
44
  import type { MarkdownLink } from '../MDLabel/MarkdownLink';
45
45
  import type { Accordion } from '../../Accordion/Accordion';
46
+ import type { CustomDatepickerFields } from '../../Datepicker/DatepickerFields';
46
47
 
47
48
  /**
48
49
  * Contain the type of every customizable components.
@@ -80,6 +81,9 @@ export type LunaticSlotComponents = {
80
81
  ComboboxClearButton: typeof ComboboxClearButton;
81
82
  ComboboxLabelSelection: typeof ComboboxLabelSelection;
82
83
 
84
+ // Datepicker
85
+ DatepickerFields: typeof CustomDatepickerFields;
86
+
83
87
  // Roundabout
84
88
  Roundabout: typeof CustomRoundabout;
85
89
 
@@ -9,7 +9,7 @@ import type {
9
9
  } from '../use-lunatic/type';
10
10
  import type { InterpretedOption } from '../use-lunatic/props/propOptions';
11
11
 
12
- type Formats = 'PTnHnM' | 'PnYnM';
12
+ export type DurationFormat = 'PTnHnM' | 'PnYnM';
13
13
  export type VtlExpression = {
14
14
  value: string;
15
15
  type: 'VTL' | 'VTL|MD' | 'TXT';
@@ -78,7 +78,7 @@ export type ComponentPropsByType = {
78
78
  };
79
79
  Duration: LunaticBaseProps<string | null> &
80
80
  LunaticExtraProps & {
81
- format: Formats;
81
+ format: DurationFormat;
82
82
  response: { name: string };
83
83
  componentType?: 'Duration';
84
84
  };
@@ -12,6 +12,7 @@ import { getMissingResponseProp } from '../../props/propMissingResponse';
12
12
  import { getValueProp } from '../../props/propValue';
13
13
  import { getIterationsProp } from '../../props/propIterations';
14
14
  import { getOptionsProp } from '../../props/propOptions';
15
+ import { LunaticLogger } from '../../logger/type';
15
16
 
16
17
  type FillComponentArgs = {
17
18
  disableFilters?: boolean;
@@ -25,6 +26,7 @@ type FillComponentArgs = {
25
26
  preferences: LunaticOptions['preferences'];
26
27
  pager: LunaticReducerState['pager'];
27
28
  variables: LunaticReducerState['variables'];
29
+ logger: LunaticLogger;
28
30
  };
29
31
 
30
32
  /**
@@ -56,7 +58,8 @@ export const fillComponent = (
56
58
  state.variables,
57
59
  state.handleChanges,
58
60
  state.pager.iteration,
59
- value
61
+ value,
62
+ state.logger
60
63
  ),
61
64
  ...getComponentTypeProps(interpretedProps, state),
62
65
  // This is too dynamic to be typed correctly, so we allow any here
@@ -29,7 +29,7 @@ export function resizingBehaviour(
29
29
 
30
30
  // Pairwise resizing
31
31
  if ('sizeForLinksVariables' in resizingInfo) {
32
- resizePairwise(store, resizingInfo, e.detail);
32
+ resizePairwise(store, resizingInfo);
33
33
  if (!('size' in resizingInfo)) {
34
34
  return;
35
35
  }
@@ -58,9 +58,6 @@ function resizePairwise(
58
58
  | [string, string]
59
59
  | { xAxisSize: string; yAxisSize: string };
60
60
  linksVariables: string[];
61
- },
62
- args: {
63
- iteration?: number[];
64
61
  }
65
62
  ) {
66
63
  // Handle expression being sent as an array or an object (ensure backward compatibility)
@@ -77,7 +74,7 @@ function resizePairwise(
77
74
  return forceInt(store.run(getExpressionAsString(expression)));
78
75
  });
79
76
  resizingInfo.linksVariables.forEach((variable) => {
80
- const value = store.get(variable, args.iteration);
77
+ const value = store.get(variable);
81
78
  const resizedValue = resizeArray(
82
79
  // The value is not an array, force an array
83
80
  Array.isArray(value) ? value.map((i) => resizeArray(i, ySize, null)) : [],
@@ -298,7 +298,10 @@ describe('lunatic-variables-store', () => {
298
298
  variables.set('LINKS', [[]]);
299
299
  resizingBehaviour(variables, {
300
300
  PRENOM: {
301
- sizeForLinksVariables: ['count(PRENOM)', 'count(PRENOM)'],
301
+ sizeForLinksVariables: {
302
+ xAxisSize: 'count(PRENOM)',
303
+ yAxisSize: 'count(PRENOM)',
304
+ },
302
305
  linksVariables: ['LINKS'],
303
306
  },
304
307
  });
@@ -308,6 +311,19 @@ describe('lunatic-variables-store', () => {
308
311
  [null, null, null],
309
312
  [null, null, null],
310
313
  ]);
314
+ // Adding a person should not reset links
315
+ variables.set('LINKS', [
316
+ [null, '1', '3'],
317
+ ['1', null, '3'],
318
+ ['2', '2', null],
319
+ ]);
320
+ variables.set('PRENOM', 'Marie', { iteration: [3] });
321
+ expect(variables.get('LINKS') as string[][]).toEqual([
322
+ [null, '1', '3', null],
323
+ ['1', null, '3', null],
324
+ ['2', '2', null, null],
325
+ [null, null, null, null],
326
+ ]);
311
327
  });
312
328
  it('should resize pairwise with the object syntax', () => {
313
329
  variables.set('PRENOM', []);
@@ -26,6 +26,7 @@ describe('getOptionsProp()', () => {
26
26
  ],
27
27
  } satisfies DeepTranslateExpression<LunaticComponentDefinition>;
28
28
  let mockChange: LunaticChangesHandler;
29
+ const mockLogger = vi.fn();
29
30
 
30
31
  beforeEach(() => {
31
32
  mockChange = vi.fn();
@@ -40,7 +41,8 @@ describe('getOptionsProp()', () => {
40
41
  variables,
41
42
  mockChange,
42
43
  undefined,
43
- undefined
44
+ undefined,
45
+ mockLogger
44
46
  );
45
47
  expect(options[1].checked).toBe(false);
46
48
  variables.set('O2', true);
@@ -49,7 +51,8 @@ describe('getOptionsProp()', () => {
49
51
  variables,
50
52
  mockChange,
51
53
  undefined,
52
- undefined
54
+ undefined,
55
+ mockLogger
53
56
  );
54
57
  expect(options[1].checked).toBe(true);
55
58
  });
@@ -61,7 +64,8 @@ describe('getOptionsProp()', () => {
61
64
  variables,
62
65
  mockChange,
63
66
  0,
64
- undefined
67
+ undefined,
68
+ mockLogger
65
69
  );
66
70
  expect(
67
71
  options.filter((o) => o.checked),
@@ -74,7 +78,8 @@ describe('getOptionsProp()', () => {
74
78
  variables,
75
79
  mockChange,
76
80
  0,
77
- undefined
81
+ undefined,
82
+ mockLogger
78
83
  );
79
84
  expect(options[0].checked).toBe(true);
80
85
  expect(options[1].checked).toBe(false);
@@ -87,12 +92,73 @@ describe('getOptionsProp()', () => {
87
92
  variables,
88
93
  mockChange,
89
94
  1,
90
- undefined
95
+ undefined,
96
+ mockLogger
91
97
  );
92
98
  options[1].onCheck(false);
93
99
  expect(mockChange).toHaveBeenLastCalledWith([
94
100
  { name: 'O2', value: false },
95
101
  ]);
96
102
  });
103
+ it('should not filter response (checkboxGroup) when its conditionFilter evaluation fails', () => {
104
+ const definition = {
105
+ ...checkboxGroupDefinition,
106
+ responses: [
107
+ {
108
+ label: 'Option 1',
109
+ response: { name: 'O1' },
110
+ id: 'id1',
111
+ conditionFilter: { type: 'VTL', value: 'invalid expression' },
112
+ },
113
+ ],
114
+ } satisfies DeepTranslateExpression<LunaticComponentDefinition>;
115
+
116
+ // mock variables.run for having an error interpreting a variable
117
+ vi.spyOn(variables, 'run').mockImplementation(() => {
118
+ throw new Error('Test error');
119
+ });
120
+
121
+ const options = getOptionsProp(
122
+ definition,
123
+ variables,
124
+ mockChange,
125
+ undefined,
126
+ undefined,
127
+ mockLogger
128
+ );
129
+
130
+ // Ensure the option is not filtered
131
+ expect(options).toHaveLength(1);
132
+ });
133
+ it('should not filter option (radio) when its conditionFilter evaluation fails', () => {
134
+ const definition = {
135
+ id: 'RadioGroup',
136
+ componentType: 'Radio',
137
+ options: [
138
+ {
139
+ label: 'Option 1',
140
+ value: 'id1',
141
+ conditionFilter: { type: 'VTL', value: 'invalid expression' },
142
+ },
143
+ ],
144
+ } as any as DeepTranslateExpression<LunaticComponentDefinition>;
145
+
146
+ // Mock `variables.run` to throw an error
147
+ vi.spyOn(variables, 'run').mockImplementation(() => {
148
+ throw new Error('Test error');
149
+ });
150
+
151
+ const options = getOptionsProp(
152
+ definition,
153
+ variables,
154
+ mockChange,
155
+ undefined,
156
+ undefined,
157
+ mockLogger
158
+ );
159
+
160
+ // Ensure the option is not filtered
161
+ expect(options).toHaveLength(1);
162
+ });
97
163
  });
98
164
  });
@@ -7,6 +7,7 @@ import type { ReactNode } from 'react';
7
7
  import type { DeepTranslateExpression } from '../commons/fill-components/fill-component-expressions';
8
8
  import { isNumber } from '../../utils/number';
9
9
  import type { LunaticVariablesStore } from '../commons/variables/lunatic-variables-store';
10
+ import { LunaticLogger } from '../logger/type';
10
11
 
11
12
  /* Used for radio option and checkbox one option */
12
13
  export type InterpretedOption = {
@@ -29,7 +30,8 @@ export function getOptionsProp(
29
30
  variables: LunaticVariablesStore,
30
31
  handleChanges: LunaticChangesHandler,
31
32
  pagerIteration: LunaticState['pager']['iteration'],
32
- value: unknown
33
+ value: unknown,
34
+ logger: LunaticLogger
33
35
  ) {
34
36
  const iteration = isNumber(pagerIteration) ? [pagerIteration] : undefined;
35
37
  //const iteration = pagerIteration ? [pagerIteration] : undefined;
@@ -40,7 +42,16 @@ export function getOptionsProp(
40
42
  if (!response.conditionFilter) {
41
43
  return true;
42
44
  }
43
- return variables.run(response.conditionFilter.value, { iteration });
45
+ try {
46
+ return variables.run(response.conditionFilter.value, { iteration });
47
+ } catch (e) {
48
+ // If there is an error interpreting a variable, we do not filter
49
+ logger({
50
+ type: 'ERROR',
51
+ error: e as Error,
52
+ });
53
+ return true;
54
+ }
44
55
  })
45
56
  .map((response) => ({
46
57
  label: response.label,
@@ -74,7 +85,16 @@ export function getOptionsProp(
74
85
  if (!('conditionFilter' in option) || !option.conditionFilter) {
75
86
  return true;
76
87
  }
77
- return variables.run(option.conditionFilter.value, { iteration });
88
+ try {
89
+ return variables.run(option.conditionFilter.value, { iteration });
90
+ } catch (e) {
91
+ // If there is an error interpreting a variable, we do not filter
92
+ logger({
93
+ type: 'ERROR',
94
+ error: e as Error,
95
+ });
96
+ return true;
97
+ }
78
98
  })
79
99
  .map((option) => ({
80
100
  label: option.label,
@@ -333,10 +333,24 @@ export type LunaticState = {
333
333
  resetChangedData: () => void;
334
334
  /** Return `true` as soon as the current page has at least one answer. */
335
335
  hasPageResponse: () => boolean;
336
- /** Used for testing purpose only. */
337
- testing: {
338
- handleChanges: LunaticChangesHandler;
339
- };
336
+ /**
337
+ * Change several variable values.
338
+ *
339
+ * Be careful when using this function.
340
+ *
341
+ * In most cases, you don't need this function.
342
+ * It's used directly by the components (and available as a props.)
343
+ *
344
+ * - With only one change or with serveral changes
345
+ * @example
346
+ * handleChanges([{name: "MY_VAR", value: "new value"}])
347
+ *
348
+ * handleChanges([
349
+ * {name: "MY_VAR", value: "new value"},
350
+ * {name: "MY_VAR_2", value: "new value 2"}
351
+ * ])
352
+ */
353
+ handleChanges: LunaticChangesHandler;
340
354
  };
341
355
 
342
356
  /** Function taking as arguments the various changes the user has made. */
@@ -233,7 +233,7 @@ describe('use-lunatic()', () => {
233
233
  useLunatic(sourceCleaningLoop as any, undefined, {})
234
234
  );
235
235
  act(() => {
236
- result.current.testing.handleChanges([
236
+ result.current.handleChanges([
237
237
  {
238
238
  name: 'PRENOM',
239
239
  value: ['John', 'Doe', 'Marc'],
@@ -251,7 +251,7 @@ describe('use-lunatic()', () => {
251
251
  };
252
252
  expectCollectedAgeToEqual([18, 18, 18]);
253
253
  act(() => {
254
- result.current.testing.handleChanges([
254
+ result.current.handleChanges([
255
255
  {
256
256
  name: 'HIDE_AGE',
257
257
  value: true,
@@ -271,15 +271,11 @@ describe('use-lunatic()', () => {
271
271
  onChange: spy,
272
272
  })
273
273
  );
274
- act(() =>
275
- result.current.testing.handleChanges([{ name: 'NB', value: 3 }])
276
- );
274
+ act(() => result.current.handleChanges([{ name: 'NB', value: 3 }]));
277
275
  expect(
278
276
  (result.current.getData(true).COLLECTED?.PRENOMS as any).COLLECTED
279
277
  ).toEqual([null, null, null]);
280
- act(() =>
281
- result.current.testing.handleChanges([{ name: 'NB', value: 2 }])
282
- );
278
+ act(() => result.current.handleChanges([{ name: 'NB', value: 2 }]));
283
279
  expect(
284
280
  (result.current.getData(true).COLLECTED?.PRENOMS as any).COLLECTED
285
281
  ).toEqual([null, null]);
@@ -293,13 +289,13 @@ describe('use-lunatic()', () => {
293
289
  useLunatic(sourceSimpsons as any, undefined, {})
294
290
  );
295
291
  act(() => {
296
- result.current.testing.handleChanges([
292
+ result.current.handleChanges([
297
293
  {
298
294
  name: 'COMMENT',
299
295
  value: 'Mon commentaire',
300
296
  },
301
297
  ]);
302
- result.current.testing.handleChanges([{ name: 'READY', value: true }]);
298
+ result.current.handleChanges([{ name: 'READY', value: true }]);
303
299
  });
304
300
  hookRef = result;
305
301
  });
@@ -328,7 +324,7 @@ describe('use-lunatic()', () => {
328
324
  });
329
325
  it('should return changes since the last update', () => {
330
326
  act(() => {
331
- hookRef.current.testing.handleChanges([
327
+ hookRef.current.handleChanges([
332
328
  { name: 'COMMENT', value: 'Mon commentaire' },
333
329
  { name: 'READY', value: true },
334
330
  ]);
@@ -346,10 +342,10 @@ describe('use-lunatic()', () => {
346
342
  });
347
343
  it('should reset changes with true parameter', () => {
348
344
  act(() => {
349
- hookRef.current.testing.handleChanges([
345
+ hookRef.current.handleChanges([
350
346
  { name: 'COMMENT', value: 'Mon commentaire' },
351
347
  ]);
352
- hookRef.current.testing.handleChanges([{ name: 'READY', value: true }]);
348
+ hookRef.current.handleChanges([{ name: 'READY', value: true }]);
353
349
  });
354
350
  const data = hookRef.current.getChangedData(true);
355
351
  expect(data).toMatchObject({
@@ -366,7 +362,7 @@ describe('use-lunatic()', () => {
366
362
  });
367
363
  it('should reset changes with resetChanges()', () => {
368
364
  act(() => {
369
- hookRef.current.testing.handleChanges([
365
+ hookRef.current.handleChanges([
370
366
  {
371
367
  name: 'COMMENT',
372
368
  value: 'Mon commentaire',
@@ -377,9 +373,7 @@ describe('use-lunatic()', () => {
377
373
  hookRef.current.resetChangedData();
378
374
  expect(hookRef.current.getChangedData().COLLECTED).toEqual({});
379
375
  act(() => {
380
- hookRef.current.testing.handleChanges([
381
- { name: 'READY', value: false },
382
- ]);
376
+ hookRef.current.handleChanges([{ name: 'READY', value: false }]);
383
377
  });
384
378
  expect(hookRef.current.getChangedData().COLLECTED).toMatchObject({
385
379
  READY: {
@@ -395,9 +389,7 @@ describe('use-lunatic()', () => {
395
389
  useLunatic(sourceCheckboxGroup as any, undefined, {})
396
390
  );
397
391
  act(() => {
398
- result.current.testing.handleChanges([
399
- { name: 'NATIO1N1', value: true },
400
- ]);
392
+ result.current.handleChanges([{ name: 'NATIO1N1', value: true }]);
401
393
  });
402
394
  expect(result.current.hasPageResponse()).toBeTruthy();
403
395
  });
@@ -406,9 +398,7 @@ describe('use-lunatic()', () => {
406
398
  useLunatic(sourceCheckboxGroup as any, undefined, {})
407
399
  );
408
400
  act(() => {
409
- result.current.testing.handleChanges([
410
- { name: 'NATIO1N1', value: false },
411
- ]);
401
+ result.current.handleChanges([{ name: 'NATIO1N1', value: false }]);
412
402
  });
413
403
  expect(result.current.hasPageResponse()).toBeFalsy();
414
404
  });
@@ -205,6 +205,7 @@ export function useLunatic(
205
205
  goNextPage,
206
206
  goPreviousPage,
207
207
  management,
208
+ logger,
208
209
  ...state,
209
210
  });
210
211
 
@@ -233,8 +234,6 @@ export function useLunatic(
233
234
  hasPageResponse: usePageHasResponse(components, state.executeExpression),
234
235
  // Components
235
236
  Provider,
236
- testing: {
237
- handleChanges,
238
- },
237
+ handleChanges,
239
238
  } satisfies LunaticState;
240
239
  }