@inseefr/lunatic 3.5.4 → 3.5.6
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/Duration/durationUtils.js +1 -1
- package/components/Duration/durationUtils.js.map +1 -1
- package/components/Sequence/Sequence.d.ts +1 -1
- package/components/Subsequence/Subsequence.d.ts +1 -1
- package/components/library.d.ts +2 -2
- package/components/type.d.ts +4 -3
- package/esm/components/Duration/durationUtils.js +1 -1
- package/esm/components/Duration/durationUtils.js.map +1 -1
- package/esm/components/Sequence/Sequence.d.ts +1 -1
- package/esm/components/Subsequence/Subsequence.d.ts +1 -1
- package/esm/components/library.d.ts +2 -2
- package/esm/components/type.d.ts +4 -3
- package/esm/use-lunatic/actions.d.ts +2 -0
- package/esm/use-lunatic/actions.js.map +1 -1
- package/esm/use-lunatic/commons/fill-components/fill-component.spec.js +105 -0
- package/esm/use-lunatic/commons/fill-components/fill-component.spec.js.map +1 -1
- package/esm/use-lunatic/commons/fill-components/fill-components.d.ts +3 -2
- package/esm/use-lunatic/commons/fill-components/fill-components.js +12 -4
- package/esm/use-lunatic/commons/fill-components/fill-components.js.map +1 -1
- package/esm/use-lunatic/commons/variables/lunatic-variables-store.d.ts +7 -2
- package/esm/use-lunatic/commons/variables/lunatic-variables-store.js +11 -4
- package/esm/use-lunatic/commons/variables/lunatic-variables-store.js.map +1 -1
- package/esm/use-lunatic/commons/variables/lunatic-variables-store.spec.js +11 -0
- package/esm/use-lunatic/commons/variables/lunatic-variables-store.spec.js.map +1 -1
- package/esm/use-lunatic/props/getComponentTypeProps.d.ts +3 -3
- package/esm/use-lunatic/props/getComponentTypeProps.js +1 -1
- package/esm/use-lunatic/props/getComponentTypeProps.js.map +1 -1
- package/esm/use-lunatic/props/propOptions.d.ts +3 -1
- package/esm/use-lunatic/props/propOptions.js +22 -24
- package/esm/use-lunatic/props/propOptions.js.map +1 -1
- package/esm/use-lunatic/props/propOptions.spec.js +46 -0
- package/esm/use-lunatic/props/propOptions.spec.js.map +1 -1
- package/esm/use-lunatic/reducer/reducer.js +1 -1
- package/esm/use-lunatic/reducer/reducer.js.map +1 -1
- package/esm/use-lunatic/type.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/Duration/durationUtils.ts +1 -1
- package/src/components/type.ts +9 -2
- package/src/use-lunatic/actions.ts +2 -0
- package/src/use-lunatic/commons/fill-components/fill-component.spec.ts +126 -0
- package/src/use-lunatic/commons/fill-components/fill-components.ts +19 -4
- package/src/use-lunatic/commons/variables/lunatic-variables-store.spec.ts +12 -0
- package/src/use-lunatic/commons/variables/lunatic-variables-store.ts +20 -5
- package/src/use-lunatic/props/getComponentTypeProps.ts +6 -1
- package/src/use-lunatic/props/propOptions.spec.ts +64 -0
- package/src/use-lunatic/props/propOptions.ts +50 -22
- package/src/use-lunatic/reducer/reducer.ts +1 -1
- package/src/use-lunatic/type.ts +1 -0
- package/src/use-lunatic/use-lunatic.test.ts +39 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/use-lunatic/actions.d.ts +2 -0
- package/use-lunatic/actions.js.map +1 -1
- package/use-lunatic/commons/fill-components/fill-component.spec.js +105 -0
- package/use-lunatic/commons/fill-components/fill-component.spec.js.map +1 -1
- package/use-lunatic/commons/fill-components/fill-components.d.ts +3 -2
- package/use-lunatic/commons/fill-components/fill-components.js +12 -4
- package/use-lunatic/commons/fill-components/fill-components.js.map +1 -1
- package/use-lunatic/commons/variables/lunatic-variables-store.d.ts +7 -2
- package/use-lunatic/commons/variables/lunatic-variables-store.js +11 -4
- package/use-lunatic/commons/variables/lunatic-variables-store.js.map +1 -1
- package/use-lunatic/commons/variables/lunatic-variables-store.spec.js +11 -0
- package/use-lunatic/commons/variables/lunatic-variables-store.spec.js.map +1 -1
- package/use-lunatic/props/getComponentTypeProps.d.ts +3 -3
- package/use-lunatic/props/getComponentTypeProps.js +1 -1
- package/use-lunatic/props/getComponentTypeProps.js.map +1 -1
- package/use-lunatic/props/propOptions.d.ts +3 -1
- package/use-lunatic/props/propOptions.js +22 -24
- package/use-lunatic/props/propOptions.js.map +1 -1
- package/use-lunatic/props/propOptions.spec.js +46 -0
- package/use-lunatic/props/propOptions.spec.js.map +1 -1
- package/use-lunatic/reducer/reducer.js +1 -1
- package/use-lunatic/reducer/reducer.js.map +1 -1
- package/use-lunatic/type.d.ts +1 -0
|
@@ -66,6 +66,7 @@ describe('fillComponents', () => {
|
|
|
66
66
|
expect(input.required).toBe(true);
|
|
67
67
|
expect(input.maxLength).toBe(15);
|
|
68
68
|
expect(input.conditionFilter).toBe(true);
|
|
69
|
+
expect(input.shouldBeFiltered).toBe(false);
|
|
69
70
|
});
|
|
70
71
|
|
|
71
72
|
it('should fill a Radio component correctly with options', () => {
|
|
@@ -136,6 +137,7 @@ describe('fillComponents', () => {
|
|
|
136
137
|
expect(radio.options[0].label).toBe('"oui"');
|
|
137
138
|
expect(radio.options[1].label).toBe('"non"');
|
|
138
139
|
expect(radio.conditionFilter).toBe(true);
|
|
140
|
+
expect(radio.shouldBeFiltered).toBe(false);
|
|
139
141
|
expect(radio.response.name).toBe('TESTRADIO');
|
|
140
142
|
});
|
|
141
143
|
|
|
@@ -187,6 +189,7 @@ describe('fillComponents', () => {
|
|
|
187
189
|
expect(question.id).toBe('question-m8ilvkbt');
|
|
188
190
|
expect(question.label).toBe('"Question label"');
|
|
189
191
|
expect(question.conditionFilter).toBe(true);
|
|
192
|
+
expect(question.shouldBeFiltered).toBe(false);
|
|
190
193
|
expect(question.components.length).toBe(1);
|
|
191
194
|
|
|
192
195
|
const input = question.components[0];
|
|
@@ -258,12 +261,16 @@ describe('fillComponents', () => {
|
|
|
258
261
|
expect(input.componentType).toBe('Input');
|
|
259
262
|
expect(input.response.name).toBe('TESTINPUT');
|
|
260
263
|
expect(input.maxLength).toBe(100);
|
|
264
|
+
expect(input.conditionFilter).toBe(true);
|
|
265
|
+
expect(input.shouldBeFiltered).toBe(false);
|
|
261
266
|
|
|
262
267
|
const radio = filledComponents[1];
|
|
263
268
|
expect(radio.componentType).toBe('Radio');
|
|
264
269
|
expect(radio.response.name).toBe('TESTRADIO');
|
|
265
270
|
expect(radio.options[0].label).toBe('"Yes"');
|
|
266
271
|
expect(radio.options[1].label).toBe('"No"');
|
|
272
|
+
expect(radio.conditionFilter).toBe(true);
|
|
273
|
+
expect(radio.shouldBeFiltered).toBe(false);
|
|
267
274
|
});
|
|
268
275
|
|
|
269
276
|
it('should filter out FilterDescription components if disableFiltersDescription is true', () => {
|
|
@@ -453,6 +460,7 @@ describe('fillComponents', () => {
|
|
|
453
460
|
const input = filledComponents[0];
|
|
454
461
|
expect(input.id).toBe('input1');
|
|
455
462
|
expect(input.conditionFilter).toBe(false);
|
|
463
|
+
expect(input.shouldBeFiltered).toBe(true);
|
|
456
464
|
});
|
|
457
465
|
|
|
458
466
|
it('should transform components into Text with empty label when conditionFilter is false and parentType is RosterForLoop', () => {
|
|
@@ -507,6 +515,8 @@ describe('fillComponents', () => {
|
|
|
507
515
|
expect(input.componentType).toBe('Text');
|
|
508
516
|
expect(input.label).toBe('');
|
|
509
517
|
expect(input.id).toBe('input1');
|
|
518
|
+
expect(input.conditionFilter).toBe(false);
|
|
519
|
+
expect(input.shouldBeFiltered).toBe(true);
|
|
510
520
|
|
|
511
521
|
// Check the second component that has conditionFilter: true
|
|
512
522
|
const radio = filledComponents[1];
|
|
@@ -569,6 +579,8 @@ describe('fillComponents', () => {
|
|
|
569
579
|
// The component should remain unchanged
|
|
570
580
|
expect(input.componentType).toBe('Input');
|
|
571
581
|
expect(input.label).toBe('"Input label"');
|
|
582
|
+
expect(input.conditionFilter).toBe(false);
|
|
583
|
+
expect(input.shouldBeFiltered).toBe(true);
|
|
572
584
|
|
|
573
585
|
// Check the second component that has conditionFilter: true
|
|
574
586
|
const radio = filledComponents[1];
|
|
@@ -578,4 +590,118 @@ describe('fillComponents', () => {
|
|
|
578
590
|
expect(radio.options[0].label).toBe('"Yes"');
|
|
579
591
|
expect(radio.options[1].label).toBe('"No"');
|
|
580
592
|
});
|
|
593
|
+
|
|
594
|
+
it('should tag children components with shouldBeFiltered=true if the parent component should be filtered', () => {
|
|
595
|
+
const components = [
|
|
596
|
+
{
|
|
597
|
+
id: 'question-m8ilvkbt',
|
|
598
|
+
componentType: 'Question',
|
|
599
|
+
page: '1',
|
|
600
|
+
label: {
|
|
601
|
+
value: '"Question label"',
|
|
602
|
+
type: 'VTL|MD',
|
|
603
|
+
},
|
|
604
|
+
conditionFilter: {
|
|
605
|
+
type: 'VTL',
|
|
606
|
+
// value should be string, but did not find how to execute correctly with mocks
|
|
607
|
+
// for having a false conditionFilter at the end
|
|
608
|
+
value: false,
|
|
609
|
+
},
|
|
610
|
+
components: [
|
|
611
|
+
{
|
|
612
|
+
id: 'm8ilvkbt',
|
|
613
|
+
componentType: 'Input',
|
|
614
|
+
page: '1',
|
|
615
|
+
maxLength: 249,
|
|
616
|
+
response: {
|
|
617
|
+
name: 'TESTTEXTE',
|
|
618
|
+
},
|
|
619
|
+
},
|
|
620
|
+
],
|
|
621
|
+
},
|
|
622
|
+
];
|
|
623
|
+
|
|
624
|
+
const mockVariables = LunaticVariablesStore.makeFromObject({
|
|
625
|
+
TESTTEXTE: 'some value',
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
const mockState = {
|
|
629
|
+
...defaultMockState,
|
|
630
|
+
disableFilters: true,
|
|
631
|
+
variables: mockVariables,
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
const filledComponents = fillComponents(
|
|
635
|
+
components as LunaticComponentDefinition[],
|
|
636
|
+
mockState as unknown as FillComponentArgs
|
|
637
|
+
) as any;
|
|
638
|
+
|
|
639
|
+
const question = filledComponents[0];
|
|
640
|
+
|
|
641
|
+
expect(question.componentType).toBe('Question');
|
|
642
|
+
expect(question.conditionFilter).toBe(false);
|
|
643
|
+
expect(question.shouldBeFiltered).toBe(true);
|
|
644
|
+
|
|
645
|
+
const input = question.components[0];
|
|
646
|
+
expect(input.componentType).toBe('Input');
|
|
647
|
+
expect(input.conditionFilter).toBe(undefined);
|
|
648
|
+
expect(input.shouldBeFiltered).toBe(true);
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
it('should tag options with shouldBeFiltered=true if the component should be filtered', () => {
|
|
652
|
+
const components = [
|
|
653
|
+
{
|
|
654
|
+
id: 'radio1',
|
|
655
|
+
componentType: 'Radio',
|
|
656
|
+
page: '1',
|
|
657
|
+
label: {
|
|
658
|
+
type: 'VTL|MD',
|
|
659
|
+
value: '"Radio label"',
|
|
660
|
+
},
|
|
661
|
+
options: [
|
|
662
|
+
{ value: 'yes', label: { value: '"Yes"', type: 'VTL|MD' } },
|
|
663
|
+
{ value: 'no', label: { value: '"No"', type: 'VTL|MD' } },
|
|
664
|
+
],
|
|
665
|
+
response: {
|
|
666
|
+
name: 'TESTRADIO',
|
|
667
|
+
},
|
|
668
|
+
conditionFilter: {
|
|
669
|
+
type: 'VTL',
|
|
670
|
+
// value should be string, but did not find how to execute correctly with mocks
|
|
671
|
+
// for having a false conditionFilter at the end
|
|
672
|
+
value: false,
|
|
673
|
+
},
|
|
674
|
+
},
|
|
675
|
+
];
|
|
676
|
+
|
|
677
|
+
const mockVariables = LunaticVariablesStore.makeFromObject({
|
|
678
|
+
TESTINPUT: 'Filled input',
|
|
679
|
+
TESTRADIO: 'yes',
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
const mockState = {
|
|
683
|
+
...defaultMockState,
|
|
684
|
+
disableFilters: true,
|
|
685
|
+
variables: mockVariables,
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
const filledComponents = fillComponents(
|
|
689
|
+
components as LunaticComponentDefinition[],
|
|
690
|
+
mockState as unknown as FillComponentArgs
|
|
691
|
+
) as any;
|
|
692
|
+
|
|
693
|
+
const radio = filledComponents[0];
|
|
694
|
+
|
|
695
|
+
expect(radio.componentType).toBe('Radio');
|
|
696
|
+
expect(radio.conditionFilter).toBe(false);
|
|
697
|
+
expect(radio.shouldBeFiltered).toBe(true);
|
|
698
|
+
|
|
699
|
+
expect(radio.options[0].label).toBe('"Yes"');
|
|
700
|
+
expect(radio.options[0].conditionFilter).toBe(undefined);
|
|
701
|
+
expect(radio.options[0].shouldBeFiltered).toBe(true);
|
|
702
|
+
|
|
703
|
+
expect(radio.options[1].label).toBe('"No"');
|
|
704
|
+
expect(radio.options[1].conditionFilter).toBe(undefined);
|
|
705
|
+
expect(radio.options[1].shouldBeFiltered).toBe(true);
|
|
706
|
+
});
|
|
581
707
|
});
|
|
@@ -13,6 +13,7 @@ import { getValueProp } from '../../props/propValue';
|
|
|
13
13
|
import { getIterationsProp } from '../../props/propIterations';
|
|
14
14
|
import { getOptionsProp } from '../../props/propOptions';
|
|
15
15
|
import { LunaticLogger } from '../../logger/type';
|
|
16
|
+
import { VTLScalarExpression } from '../../../type.source';
|
|
16
17
|
|
|
17
18
|
export type FillComponentArgs = {
|
|
18
19
|
disableFilters?: boolean;
|
|
@@ -35,9 +36,20 @@ export type FillComponentArgs = {
|
|
|
35
36
|
*/
|
|
36
37
|
export const fillComponent = (
|
|
37
38
|
component: LunaticComponentDefinition,
|
|
38
|
-
state: FillComponentArgs
|
|
39
|
+
state: FillComponentArgs,
|
|
40
|
+
// the given parentConditionFilter is typed as VTLScalarExpression, but it's actually a boolean or undefined
|
|
41
|
+
parentConditionFilter?: any
|
|
39
42
|
): LunaticComponentProps & { conditionFilter?: boolean } => {
|
|
40
43
|
const interpretedProps = fillComponentExpressions(component, state);
|
|
44
|
+
|
|
45
|
+
const shouldParentBeFiltered = parentConditionFilter === false;
|
|
46
|
+
|
|
47
|
+
const shouldBeFiltered =
|
|
48
|
+
shouldParentBeFiltered ||
|
|
49
|
+
('conditionFilter' in interpretedProps
|
|
50
|
+
? !interpretedProps.conditionFilter
|
|
51
|
+
: false);
|
|
52
|
+
|
|
41
53
|
const value = getValueProp(component, state);
|
|
42
54
|
return {
|
|
43
55
|
...interpretedProps,
|
|
@@ -48,6 +60,7 @@ export const fillComponent = (
|
|
|
48
60
|
shortcut: state.shortcut,
|
|
49
61
|
goNextPage: state.goNextPage,
|
|
50
62
|
goPreviousPage: state.goPreviousPage,
|
|
63
|
+
shouldBeFiltered: shouldBeFiltered,
|
|
51
64
|
iteration: state.pager.iteration,
|
|
52
65
|
required: 'isMandatory' in component ? component.isMandatory : false,
|
|
53
66
|
value: value,
|
|
@@ -61,7 +74,8 @@ export const fillComponent = (
|
|
|
61
74
|
state.pager.iteration,
|
|
62
75
|
value,
|
|
63
76
|
state.logger,
|
|
64
|
-
state.disableFilters
|
|
77
|
+
state.disableFilters,
|
|
78
|
+
shouldBeFiltered
|
|
65
79
|
),
|
|
66
80
|
...getComponentTypeProps(interpretedProps, state),
|
|
67
81
|
// This is too dynamic to be typed correctly, so we allow any here
|
|
@@ -74,7 +88,8 @@ export const fillComponent = (
|
|
|
74
88
|
export function fillComponents(
|
|
75
89
|
components: LunaticComponentDefinition[],
|
|
76
90
|
state: FillComponentArgs,
|
|
77
|
-
parentType?: LunaticComponentDefinition['componentType']
|
|
91
|
+
parentType?: LunaticComponentDefinition['componentType'],
|
|
92
|
+
parentConditionFilter?: VTLScalarExpression
|
|
78
93
|
): LunaticComponentProps[] {
|
|
79
94
|
// Flatmap to directly remove FilterDescription components if disableFiltersDescription is true
|
|
80
95
|
const filledComponents = components.flatMap((component) => {
|
|
@@ -85,7 +100,7 @@ export function fillComponents(
|
|
|
85
100
|
return [];
|
|
86
101
|
}
|
|
87
102
|
|
|
88
|
-
return [fillComponent(component, state)];
|
|
103
|
+
return [fillComponent(component, state, parentConditionFilter)];
|
|
89
104
|
});
|
|
90
105
|
|
|
91
106
|
if (state.disableFilters) {
|
|
@@ -124,6 +124,18 @@ describe('lunatic-variables-store', () => {
|
|
|
124
124
|
expect(variables.get('FIRSTNAME')).toEqual('Jane');
|
|
125
125
|
});
|
|
126
126
|
|
|
127
|
+
it('should ignore iteration when updating a scalar value', () => {
|
|
128
|
+
variables.set('FIRSTNAME', 'John'); // Start with a scalar value
|
|
129
|
+
expect(variables.get('FIRSTNAME')).toEqual('John');
|
|
130
|
+
variables.set('FIRSTNAME', 'Jane', {
|
|
131
|
+
iteration: [1],
|
|
132
|
+
ignoreIterationOnScalar: true,
|
|
133
|
+
});
|
|
134
|
+
expect(variables.get('FIRSTNAME')).toEqual('Jane');
|
|
135
|
+
variables.set('FIRSTNAME', 'Marc', { iteration: [1] });
|
|
136
|
+
expect(variables.get('FIRSTNAME')).toEqual([null, 'Marc']);
|
|
137
|
+
});
|
|
138
|
+
|
|
127
139
|
describe('event listener', () => {
|
|
128
140
|
it('should trigger onChange', () => {
|
|
129
141
|
variables.set('FIRSTNAME', 'John');
|
|
@@ -35,6 +35,8 @@ export type EventArgs = {
|
|
|
35
35
|
iteration?: IterationLevel | undefined;
|
|
36
36
|
/** What triggered this change. */
|
|
37
37
|
cause?: 'resizing' | 'cleaning';
|
|
38
|
+
/** Force a vector when an iteration is set and the value was a scalar **/
|
|
39
|
+
ignoreIterationOnScalar?: boolean;
|
|
38
40
|
/** Extra sent when setting the variable. */
|
|
39
41
|
[extra: string]: unknown;
|
|
40
42
|
};
|
|
@@ -168,7 +170,10 @@ export class LunaticVariablesStore {
|
|
|
168
170
|
public set(
|
|
169
171
|
name: string,
|
|
170
172
|
value: unknown,
|
|
171
|
-
args: Pick<
|
|
173
|
+
args: Pick<
|
|
174
|
+
EventArgs['change'],
|
|
175
|
+
'iteration' | 'cause' | 'ignoreIterationOnScalar'
|
|
176
|
+
> = {}
|
|
172
177
|
): LunaticVariable {
|
|
173
178
|
if (!this.dictionary.has(name)) {
|
|
174
179
|
this.dictionary.set(
|
|
@@ -188,7 +193,7 @@ export class LunaticVariablesStore {
|
|
|
188
193
|
);
|
|
189
194
|
}
|
|
190
195
|
const variable = this.dictionary.get(name)!;
|
|
191
|
-
if (variable.setValue(value, args
|
|
196
|
+
if (variable.setValue(value, args)) {
|
|
192
197
|
this.eventTarget.dispatchEvent(
|
|
193
198
|
new CustomEvent('change', {
|
|
194
199
|
detail: {
|
|
@@ -390,7 +395,9 @@ class LunaticVariable {
|
|
|
390
395
|
// this.calculatedCount++;
|
|
391
396
|
// Remember the value
|
|
392
397
|
try {
|
|
393
|
-
this.setValue(interpretVTL(this.expression, bindings),
|
|
398
|
+
this.setValue(interpretVTL(this.expression, bindings), {
|
|
399
|
+
iteration: iteration,
|
|
400
|
+
});
|
|
394
401
|
} catch {
|
|
395
402
|
throw new VTLInterpretationError(this.expression!, bindings);
|
|
396
403
|
}
|
|
@@ -401,7 +408,11 @@ class LunaticVariable {
|
|
|
401
408
|
/**
|
|
402
409
|
* Set the value and returns true if the variable is touched
|
|
403
410
|
*/
|
|
404
|
-
setValue(
|
|
411
|
+
setValue(
|
|
412
|
+
value: unknown,
|
|
413
|
+
opts: { iteration?: IterationLevel; ignoreIterationOnScalar?: boolean }
|
|
414
|
+
): boolean {
|
|
415
|
+
const { iteration, ignoreIterationOnScalar } = opts;
|
|
405
416
|
if (value === this.getSavedValue(iteration)) {
|
|
406
417
|
return false;
|
|
407
418
|
}
|
|
@@ -411,6 +422,10 @@ class LunaticVariable {
|
|
|
411
422
|
}
|
|
412
423
|
// We want to save a value at a specific iteration, but the value is not an array yet
|
|
413
424
|
if (iteration !== undefined && !Array.isArray(this.value)) {
|
|
425
|
+
// Ignore the iteration since the value is not an array
|
|
426
|
+
if (ignoreIterationOnScalar) {
|
|
427
|
+
return this.setValue(value, {});
|
|
428
|
+
}
|
|
414
429
|
this.value = [];
|
|
415
430
|
}
|
|
416
431
|
this.value = !Array.isArray(iteration)
|
|
@@ -445,7 +460,7 @@ class LunaticVariable {
|
|
|
445
460
|
// Update every item of the array and look if we changed one item
|
|
446
461
|
const oneValueChanged =
|
|
447
462
|
times(Math.max(oldSize, newSize), (k) =>
|
|
448
|
-
this.setValue(value[k], [k])
|
|
463
|
+
this.setValue(value[k], { iteration: [k] })
|
|
449
464
|
).find((v) => v) !== undefined;
|
|
450
465
|
// New array is smaller, shorten the array
|
|
451
466
|
if (oldSize > newSize && Array.isArray(this.value)) {
|
|
@@ -45,7 +45,12 @@ function getChildComponents(
|
|
|
45
45
|
state: State
|
|
46
46
|
) {
|
|
47
47
|
return {
|
|
48
|
-
components: fillComponents(
|
|
48
|
+
components: fillComponents(
|
|
49
|
+
component.components,
|
|
50
|
+
state,
|
|
51
|
+
undefined,
|
|
52
|
+
component.conditionFilter
|
|
53
|
+
),
|
|
49
54
|
};
|
|
50
55
|
}
|
|
51
56
|
|
|
@@ -208,6 +208,7 @@ describe('getOptionsProp()', () => {
|
|
|
208
208
|
|
|
209
209
|
// Ensure the option is not filtered
|
|
210
210
|
expect(options).toHaveLength(1);
|
|
211
|
+
expect(options[0].shouldBeFiltered).toBe(false);
|
|
211
212
|
});
|
|
212
213
|
it('should not filter option (radio) when its conditionFilter evaluation fails', () => {
|
|
213
214
|
const definition = {
|
|
@@ -237,6 +238,7 @@ describe('getOptionsProp()', () => {
|
|
|
237
238
|
|
|
238
239
|
// Ensure the option is not filtered
|
|
239
240
|
expect(options).toHaveLength(1);
|
|
241
|
+
expect(options[0].shouldBeFiltered).toBe(false);
|
|
240
242
|
});
|
|
241
243
|
it('should not filter any response (CheckboxGroup) when disableFilters is true', () => {
|
|
242
244
|
const definition = {
|
|
@@ -268,6 +270,8 @@ describe('getOptionsProp()', () => {
|
|
|
268
270
|
|
|
269
271
|
// Ensure the option is not filtered
|
|
270
272
|
expect(options).toHaveLength(1);
|
|
273
|
+
// the option should would have been filtered if we did not disable filters
|
|
274
|
+
expect(options[0].shouldBeFiltered).toBe(true);
|
|
271
275
|
});
|
|
272
276
|
it('should not filter any option (Radio) when disableFilters is true', () => {
|
|
273
277
|
const definition = {
|
|
@@ -296,7 +300,67 @@ describe('getOptionsProp()', () => {
|
|
|
296
300
|
true // disableFilters = true
|
|
297
301
|
);
|
|
298
302
|
|
|
303
|
+
// Ensure the option is not filtered
|
|
304
|
+
expect(options).toHaveLength(1);
|
|
305
|
+
// the option should would have been filtered if we did not disable filters
|
|
306
|
+
expect(options[0].shouldBeFiltered).toBe(true);
|
|
307
|
+
});
|
|
308
|
+
it('should set the response (CheckboxGroup) shouldBeFiltered=true when the parent component should be filtered', () => {
|
|
309
|
+
const definition = {
|
|
310
|
+
...checkboxGroupDefinition,
|
|
311
|
+
responses: [
|
|
312
|
+
{
|
|
313
|
+
label: 'Option 1',
|
|
314
|
+
response: { name: 'O1' },
|
|
315
|
+
id: 'id1',
|
|
316
|
+
conditionFilter: { type: 'VTL', value: 'expression' },
|
|
317
|
+
},
|
|
318
|
+
],
|
|
319
|
+
} satisfies DeepTranslateExpression<LunaticComponentDefinition>;
|
|
320
|
+
|
|
321
|
+
const options = getOptionsProp(
|
|
322
|
+
definition,
|
|
323
|
+
variables,
|
|
324
|
+
mockChange,
|
|
325
|
+
undefined,
|
|
326
|
+
undefined,
|
|
327
|
+
mockLogger,
|
|
328
|
+
true, // disableFilters = true
|
|
329
|
+
true // parent component should be filtered
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
// Ensure the option is not filtered
|
|
333
|
+
expect(options).toHaveLength(1);
|
|
334
|
+
// the option would have been filtered if we did not disable filters because its parent would
|
|
335
|
+
expect(options[0].shouldBeFiltered).toBe(true);
|
|
336
|
+
});
|
|
337
|
+
it('should set the option (Radio) shouldBeFiltered=true when the parent component should be filtered', () => {
|
|
338
|
+
const definition = {
|
|
339
|
+
...radioDefinition,
|
|
340
|
+
options: [
|
|
341
|
+
{
|
|
342
|
+
label: 'Option 1',
|
|
343
|
+
value: 'id1',
|
|
344
|
+
conditionFilter: { type: 'VTL', value: 'expression' },
|
|
345
|
+
},
|
|
346
|
+
],
|
|
347
|
+
} as any as DeepTranslateExpression<LunaticComponentDefinition>;
|
|
348
|
+
|
|
349
|
+
const options = getOptionsProp(
|
|
350
|
+
definition,
|
|
351
|
+
variables,
|
|
352
|
+
mockChange,
|
|
353
|
+
undefined,
|
|
354
|
+
undefined,
|
|
355
|
+
mockLogger,
|
|
356
|
+
true, // disableFilters = true
|
|
357
|
+
true // parent component should be filtered
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
// Ensure the option is not filtered
|
|
299
361
|
expect(options).toHaveLength(1);
|
|
362
|
+
// the option would have been filtered if we did not disable filters because its parent would
|
|
363
|
+
expect(options[0].shouldBeFiltered).toBe(true);
|
|
300
364
|
});
|
|
301
365
|
});
|
|
302
366
|
});
|
|
@@ -8,6 +8,7 @@ import type { DeepTranslateExpression } from '../commons/fill-components/fill-co
|
|
|
8
8
|
import { isNumber } from '../../utils/number';
|
|
9
9
|
import type { LunaticVariablesStore } from '../commons/variables/lunatic-variables-store';
|
|
10
10
|
import { LunaticLogger } from '../logger/type';
|
|
11
|
+
import { VtlExpression } from '../../components/type';
|
|
11
12
|
|
|
12
13
|
/* Used for radio option and checkbox one option */
|
|
13
14
|
export type InterpretedOption = {
|
|
@@ -32,10 +33,10 @@ export function getOptionsProp(
|
|
|
32
33
|
pagerIteration: LunaticState['pager']['iteration'],
|
|
33
34
|
value: unknown,
|
|
34
35
|
logger: LunaticLogger,
|
|
35
|
-
disableFilters?: boolean
|
|
36
|
+
disableFilters?: boolean,
|
|
37
|
+
shouldParentBeFiltered?: boolean
|
|
36
38
|
) {
|
|
37
39
|
const iteration = isNumber(pagerIteration) ? [pagerIteration] : undefined;
|
|
38
|
-
//const iteration = pagerIteration ? [pagerIteration] : undefined;
|
|
39
40
|
|
|
40
41
|
if (definition.componentType === 'CheckboxGroup') {
|
|
41
42
|
return definition.responses
|
|
@@ -43,16 +44,12 @@ export function getOptionsProp(
|
|
|
43
44
|
if (disableFilters || !response.conditionFilter) {
|
|
44
45
|
return true;
|
|
45
46
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
error: e as Error,
|
|
53
|
-
});
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
47
|
+
return !isFilteredOutOption(
|
|
48
|
+
variables,
|
|
49
|
+
iteration,
|
|
50
|
+
logger,
|
|
51
|
+
response.conditionFilter
|
|
52
|
+
);
|
|
56
53
|
})
|
|
57
54
|
.map((response) => ({
|
|
58
55
|
label: response.label,
|
|
@@ -74,6 +71,14 @@ export function getOptionsProp(
|
|
|
74
71
|
]);
|
|
75
72
|
}
|
|
76
73
|
: undefined,
|
|
74
|
+
shouldBeFiltered:
|
|
75
|
+
shouldParentBeFiltered ||
|
|
76
|
+
isFilteredOutOption(
|
|
77
|
+
variables,
|
|
78
|
+
iteration,
|
|
79
|
+
logger,
|
|
80
|
+
response.conditionFilter
|
|
81
|
+
),
|
|
77
82
|
}));
|
|
78
83
|
}
|
|
79
84
|
|
|
@@ -90,16 +95,12 @@ export function getOptionsProp(
|
|
|
90
95
|
) {
|
|
91
96
|
return true;
|
|
92
97
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
error: e as Error,
|
|
100
|
-
});
|
|
101
|
-
return true;
|
|
102
|
-
}
|
|
98
|
+
return !isFilteredOutOption(
|
|
99
|
+
variables,
|
|
100
|
+
iteration,
|
|
101
|
+
logger,
|
|
102
|
+
option.conditionFilter
|
|
103
|
+
);
|
|
103
104
|
})
|
|
104
105
|
.map((option) => ({
|
|
105
106
|
label: option.label,
|
|
@@ -126,5 +127,32 @@ export function getOptionsProp(
|
|
|
126
127
|
handleChanges([{ name: option.detail!.response.name, value }]);
|
|
127
128
|
}
|
|
128
129
|
: null,
|
|
130
|
+
shouldBeFiltered:
|
|
131
|
+
shouldParentBeFiltered ||
|
|
132
|
+
('conditionFilter' in option &&
|
|
133
|
+
isFilteredOutOption(
|
|
134
|
+
variables,
|
|
135
|
+
iteration,
|
|
136
|
+
logger,
|
|
137
|
+
option.conditionFilter
|
|
138
|
+
)),
|
|
129
139
|
}));
|
|
130
140
|
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Check if an option should be filtered, depending on its conditionFilter.
|
|
144
|
+
*/
|
|
145
|
+
function isFilteredOutOption(
|
|
146
|
+
variables: LunaticVariablesStore,
|
|
147
|
+
iteration: number[] | undefined,
|
|
148
|
+
logger: LunaticLogger,
|
|
149
|
+
conditionFilter?: VtlExpression
|
|
150
|
+
): boolean {
|
|
151
|
+
if (!conditionFilter) return false;
|
|
152
|
+
try {
|
|
153
|
+
return !variables.run(conditionFilter.value, { iteration });
|
|
154
|
+
} catch (e) {
|
|
155
|
+
logger({ type: 'ERROR', error: e as Error });
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -5,7 +5,7 @@ import { reduceHandleChanges } from './reduce-handle-changes';
|
|
|
5
5
|
import { reduceGoPreviousPage } from './reduce-go-previous-page';
|
|
6
6
|
import { reduceGoToPage } from './reduce-go-to-page';
|
|
7
7
|
|
|
8
|
-
//
|
|
8
|
+
// Actions that trigger a change in the store
|
|
9
9
|
const commitActions: ActionKind[] = [
|
|
10
10
|
ActionKind.GO_PREVIOUS_PAGE,
|
|
11
11
|
ActionKind.GO_NEXT_PAGE,
|
package/src/use-lunatic/type.ts
CHANGED
|
@@ -59,6 +59,45 @@ describe('use-lunatic()', () => {
|
|
|
59
59
|
expect(components[0].id).toBe('kiq5xw5p');
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
+
describe('handleChange()', () => {
|
|
63
|
+
it('should change variable value', () => {
|
|
64
|
+
const { result } = renderHook(() => useLunatic(...defaultParams));
|
|
65
|
+
act(() => {
|
|
66
|
+
result.current.handleChanges([
|
|
67
|
+
{
|
|
68
|
+
name: 'COMMENT',
|
|
69
|
+
value: 'Mon commentaire',
|
|
70
|
+
},
|
|
71
|
+
]);
|
|
72
|
+
});
|
|
73
|
+
act(() => {
|
|
74
|
+
expect(
|
|
75
|
+
result.current.getData(false, ['COMMENT']).COLLECTED!.COMMENT
|
|
76
|
+
.COLLECTED
|
|
77
|
+
).toBe('Mon commentaire');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
it('should ignore iteration for scalar value', () => {
|
|
81
|
+
const { result } = renderHook(() => useLunatic(...defaultParams));
|
|
82
|
+
act(() => {
|
|
83
|
+
result.current.handleChanges([
|
|
84
|
+
{
|
|
85
|
+
name: 'COMMENT',
|
|
86
|
+
value: 'Mon commentaire 2',
|
|
87
|
+
iteration: [1],
|
|
88
|
+
ignoreIterationOnScalar: true,
|
|
89
|
+
},
|
|
90
|
+
]);
|
|
91
|
+
});
|
|
92
|
+
act(() => {
|
|
93
|
+
expect(
|
|
94
|
+
result.current.getData(false, ['COMMENT']).COLLECTED!.COMMENT
|
|
95
|
+
.COLLECTED
|
|
96
|
+
).toBe('Mon commentaire 2');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
62
101
|
describe('Provider', () => {
|
|
63
102
|
it('should not generate a new Provider every render', () => {
|
|
64
103
|
const { result } = renderHook(() => {
|