@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.
- package/components/Datepicker/Datepicker.js +3 -60
- package/components/Datepicker/Datepicker.js.map +1 -1
- package/components/Datepicker/DatepickerFields.d.ts +8 -0
- package/components/Datepicker/DatepickerFields.js +60 -0
- package/components/Datepicker/DatepickerFields.js.map +1 -0
- package/components/Duration/durationUtils.d.ts +0 -1
- package/components/Duration/durationUtils.js.map +1 -1
- package/components/Duration/getDurationFromValue.d.ts +3 -2
- package/components/Duration/getDurationFromValue.js.map +1 -1
- package/components/shared/HOC/slottableComponent.d.ts +2 -0
- package/components/shared/HOC/slottableComponent.js.map +1 -1
- package/components/type.d.ts +2 -3
- package/esm/components/Datepicker/Datepicker.js +3 -60
- package/esm/components/Datepicker/Datepicker.js.map +1 -1
- package/esm/components/Datepicker/DatepickerFields.d.ts +8 -0
- package/esm/components/Datepicker/DatepickerFields.js +57 -0
- package/esm/components/Datepicker/DatepickerFields.js.map +1 -0
- package/esm/components/Duration/durationUtils.d.ts +0 -1
- package/esm/components/Duration/durationUtils.js.map +1 -1
- package/esm/components/Duration/getDurationFromValue.d.ts +3 -2
- package/esm/components/Duration/getDurationFromValue.js.map +1 -1
- package/esm/components/shared/HOC/slottableComponent.d.ts +2 -0
- package/esm/components/shared/HOC/slottableComponent.js.map +1 -1
- package/esm/components/type.d.ts +2 -3
- package/esm/use-lunatic/commons/fill-components/fill-components.d.ts +2 -0
- package/esm/use-lunatic/commons/fill-components/fill-components.js +1 -1
- package/esm/use-lunatic/commons/fill-components/fill-components.js.map +1 -1
- package/esm/use-lunatic/commons/variables/behaviours/resizing-behaviour.js +3 -3
- package/esm/use-lunatic/commons/variables/behaviours/resizing-behaviour.js.map +1 -1
- package/esm/use-lunatic/commons/variables/lunatic-variables-store.spec.js +17 -1
- package/esm/use-lunatic/commons/variables/lunatic-variables-store.spec.js.map +1 -1
- package/esm/use-lunatic/props/getComponentTypeProps.d.ts +1 -1
- package/esm/use-lunatic/props/propOptions.d.ts +2 -1
- package/esm/use-lunatic/props/propOptions.js +23 -3
- package/esm/use-lunatic/props/propOptions.js.map +1 -1
- package/esm/use-lunatic/props/propOptions.spec.js +46 -5
- package/esm/use-lunatic/props/propOptions.spec.js.map +1 -1
- package/esm/use-lunatic/type.d.ts +18 -4
- package/esm/use-lunatic/use-lunatic.js +2 -3
- package/esm/use-lunatic/use-lunatic.js.map +1 -1
- package/package.json +8 -1
- package/src/components/Datepicker/Datepicker.tsx +4 -117
- package/src/components/Datepicker/DatepickerFields.tsx +127 -0
- package/src/components/Duration/Duration.test.tsx +2 -2
- package/src/components/Duration/durationUtils.ts +0 -2
- package/src/components/Duration/getDurationFromValue.ts +4 -3
- package/src/components/shared/HOC/slottableComponent.tsx +4 -0
- package/src/components/type.ts +2 -2
- package/src/use-lunatic/commons/fill-components/fill-components.ts +4 -1
- package/src/use-lunatic/commons/variables/behaviours/resizing-behaviour.ts +2 -5
- package/src/use-lunatic/commons/variables/lunatic-variables-store.spec.ts +17 -1
- package/src/use-lunatic/props/propOptions.spec.ts +71 -5
- package/src/use-lunatic/props/propOptions.ts +23 -3
- package/src/use-lunatic/type.ts +18 -4
- package/src/use-lunatic/use-lunatic.test.ts +13 -23
- package/src/use-lunatic/use-lunatic.ts +2 -3
- package/tsconfig.build.tsbuildinfo +1 -1
- package/use-lunatic/commons/fill-components/fill-components.d.ts +2 -0
- package/use-lunatic/commons/fill-components/fill-components.js +1 -1
- package/use-lunatic/commons/fill-components/fill-components.js.map +1 -1
- package/use-lunatic/commons/variables/behaviours/resizing-behaviour.js +3 -3
- package/use-lunatic/commons/variables/behaviours/resizing-behaviour.js.map +1 -1
- package/use-lunatic/commons/variables/lunatic-variables-store.spec.js +17 -1
- package/use-lunatic/commons/variables/lunatic-variables-store.spec.js.map +1 -1
- package/use-lunatic/props/getComponentTypeProps.d.ts +1 -1
- package/use-lunatic/props/propOptions.d.ts +2 -1
- package/use-lunatic/props/propOptions.js +23 -3
- package/use-lunatic/props/propOptions.js.map +1 -1
- package/use-lunatic/props/propOptions.spec.js +46 -5
- package/use-lunatic/props/propOptions.spec.js.map +1 -1
- package/use-lunatic/type.d.ts +18 -4
- package/use-lunatic/use-lunatic.js +2 -3
- 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 {
|
|
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
|
|
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 {
|
|
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:
|
|
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:
|
|
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
|
|
package/src/components/type.ts
CHANGED
|
@@ -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
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
package/src/use-lunatic/type.ts
CHANGED
|
@@ -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
|
-
/**
|
|
337
|
-
|
|
338
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
292
|
+
result.current.handleChanges([
|
|
297
293
|
{
|
|
298
294
|
name: 'COMMENT',
|
|
299
295
|
value: 'Mon commentaire',
|
|
300
296
|
},
|
|
301
297
|
]);
|
|
302
|
-
result.current.
|
|
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.
|
|
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.
|
|
345
|
+
hookRef.current.handleChanges([
|
|
350
346
|
{ name: 'COMMENT', value: 'Mon commentaire' },
|
|
351
347
|
]);
|
|
352
|
-
hookRef.current.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
237
|
-
handleChanges,
|
|
238
|
-
},
|
|
237
|
+
handleChanges,
|
|
239
238
|
} satisfies LunaticState;
|
|
240
239
|
}
|