@inseefr/lunatic 3.8.0-rc.0 → 3.8.0-rc.ucq-options-variable.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.
- package/esm/type.source.d.ts +9 -3
- package/esm/use-lunatic/commons/fill-components/fill-component-expressions.d.ts +1 -1
- package/esm/use-lunatic/commons/fill-components/fill-component-expressions.js.map +1 -1
- package/esm/use-lunatic/commons/fill-components/fill-components.js +10 -2
- package/esm/use-lunatic/commons/fill-components/fill-components.js.map +1 -1
- package/esm/use-lunatic/props/propOptions.d.ts +9 -1
- package/esm/use-lunatic/props/propOptions.js +56 -1
- package/esm/use-lunatic/props/propOptions.js.map +1 -1
- package/esm/use-lunatic/props/propOptions.spec.js +220 -56
- package/esm/use-lunatic/props/propOptions.spec.js.map +1 -1
- package/package.json +4 -1
- package/src/stories/checkbox/checkbox.stories.tsx +13 -0
- package/src/stories/checkbox/sourceOneDynamicOptions.json +496 -0
- package/src/stories/dropdown/dropdown.stories.tsx +12 -0
- package/src/stories/dropdown/sourceDynamicOptions.json +496 -0
- package/src/stories/radio/radio.stories.tsx +13 -0
- package/src/stories/radio/sourceDynamicOptions.json +496 -0
- package/src/type.source.ts +9 -3
- package/src/use-lunatic/commons/fill-components/fill-component-expressions.ts +2 -1
- package/src/use-lunatic/commons/fill-components/fill-components.ts +9 -10
- package/src/use-lunatic/props/propOptions.spec.ts +217 -147
- package/src/use-lunatic/props/propOptions.ts +97 -8
- package/tsconfig.build.tsbuildinfo +1 -1
- package/type.source.d.ts +9 -3
- package/use-lunatic/commons/fill-components/fill-component-expressions.d.ts +1 -1
- package/use-lunatic/commons/fill-components/fill-component-expressions.js.map +1 -1
- package/use-lunatic/commons/fill-components/fill-components.js +9 -1
- package/use-lunatic/commons/fill-components/fill-components.js.map +1 -1
- package/use-lunatic/props/propOptions.d.ts +9 -1
- package/use-lunatic/props/propOptions.js +57 -2
- package/use-lunatic/props/propOptions.js.map +1 -1
- package/use-lunatic/props/propOptions.spec.js +217 -55
- package/use-lunatic/props/propOptions.spec.js.map +1 -1
|
@@ -1,46 +1,14 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
2
|
import { LunaticVariablesStore } from '../commons/variables/lunatic-variables-store';
|
|
3
|
-
import {
|
|
3
|
+
import { computeOptionsFromComponent, InterpretedOption } from './propOptions';
|
|
4
4
|
import type { DeepTranslateExpression } from '../commons/fill-components/fill-component-expressions';
|
|
5
5
|
import type {
|
|
6
6
|
LunaticChangesHandler,
|
|
7
7
|
LunaticComponentDefinition,
|
|
8
8
|
} from '../type';
|
|
9
9
|
|
|
10
|
-
describe('
|
|
10
|
+
describe('computeOptionsFromComponent', () => {
|
|
11
11
|
let variables: LunaticVariablesStore;
|
|
12
|
-
const checkboxGroupDefinition = {
|
|
13
|
-
id: 'CheckboxGroup',
|
|
14
|
-
componentType: 'CheckboxGroup',
|
|
15
|
-
responses: [
|
|
16
|
-
{
|
|
17
|
-
label: 'Option 1',
|
|
18
|
-
response: { name: 'O1' },
|
|
19
|
-
id: 'id1',
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
label: 'Option 2',
|
|
23
|
-
response: { name: 'O2' },
|
|
24
|
-
id: 'id2',
|
|
25
|
-
},
|
|
26
|
-
],
|
|
27
|
-
} satisfies DeepTranslateExpression<LunaticComponentDefinition>;
|
|
28
|
-
|
|
29
|
-
const radioDefinition = {
|
|
30
|
-
id: 'RadioGroup',
|
|
31
|
-
componentType: 'Radio',
|
|
32
|
-
response: { name: 'RADIO' },
|
|
33
|
-
options: [
|
|
34
|
-
{
|
|
35
|
-
label: 'Option 1',
|
|
36
|
-
value: 'id1',
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
label: 'Option 2',
|
|
40
|
-
value: 'id2',
|
|
41
|
-
},
|
|
42
|
-
],
|
|
43
|
-
} satisfies DeepTranslateExpression<LunaticComponentDefinition>;
|
|
44
12
|
|
|
45
13
|
let mockChange: LunaticChangesHandler;
|
|
46
14
|
const mockLogger = vi.fn();
|
|
@@ -50,69 +18,97 @@ describe('getOptionsProp()', () => {
|
|
|
50
18
|
variables = new LunaticVariablesStore();
|
|
51
19
|
});
|
|
52
20
|
|
|
53
|
-
describe('
|
|
21
|
+
describe('Options based on a fixed list', () => {
|
|
22
|
+
const checkboxGroupDefinition = {
|
|
23
|
+
id: 'CheckboxGroup',
|
|
24
|
+
componentType: 'CheckboxGroup',
|
|
25
|
+
responses: [
|
|
26
|
+
{
|
|
27
|
+
label: 'Option 1',
|
|
28
|
+
response: { name: 'O1' },
|
|
29
|
+
id: 'id1',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
label: 'Option 2',
|
|
33
|
+
response: { name: 'O2' },
|
|
34
|
+
id: 'id2',
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
} satisfies DeepTranslateExpression<LunaticComponentDefinition>;
|
|
38
|
+
|
|
39
|
+
const radioDefinition = {
|
|
40
|
+
id: 'RadioGroup',
|
|
41
|
+
componentType: 'Radio',
|
|
42
|
+
response: { name: 'RADIO' },
|
|
43
|
+
options: [
|
|
44
|
+
{
|
|
45
|
+
label: 'Option 1',
|
|
46
|
+
value: 'id1',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
label: 'Option 2',
|
|
50
|
+
value: 'id2',
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
} satisfies DeepTranslateExpression<LunaticComponentDefinition>;
|
|
54
|
+
|
|
54
55
|
it('should check boxes', () => {
|
|
55
56
|
variables.set('O2', false);
|
|
56
|
-
let options =
|
|
57
|
-
checkboxGroupDefinition,
|
|
57
|
+
let options = computeOptionsFromComponent(checkboxGroupDefinition, {
|
|
58
58
|
variables,
|
|
59
|
-
mockChange,
|
|
60
|
-
undefined,
|
|
61
|
-
undefined,
|
|
62
|
-
mockLogger
|
|
63
|
-
);
|
|
59
|
+
handleChanges: mockChange,
|
|
60
|
+
pagerIteration: undefined,
|
|
61
|
+
value: undefined,
|
|
62
|
+
logger: mockLogger,
|
|
63
|
+
});
|
|
64
64
|
expect(options[1].checked).toBe(false);
|
|
65
65
|
variables.set('O2', true);
|
|
66
|
-
options =
|
|
67
|
-
checkboxGroupDefinition,
|
|
66
|
+
options = computeOptionsFromComponent(checkboxGroupDefinition, {
|
|
68
67
|
variables,
|
|
69
|
-
mockChange,
|
|
70
|
-
undefined,
|
|
71
|
-
undefined,
|
|
72
|
-
mockLogger
|
|
73
|
-
);
|
|
68
|
+
handleChanges: mockChange,
|
|
69
|
+
pagerIteration: undefined,
|
|
70
|
+
value: undefined,
|
|
71
|
+
logger: mockLogger,
|
|
72
|
+
});
|
|
74
73
|
expect(options[1].checked).toBe(true);
|
|
75
74
|
});
|
|
76
75
|
it('should check boxes correctly within iteration', () => {
|
|
77
76
|
variables.set('O1', []);
|
|
78
77
|
variables.set('O2', []);
|
|
79
|
-
let options =
|
|
80
|
-
checkboxGroupDefinition,
|
|
78
|
+
let options = computeOptionsFromComponent(checkboxGroupDefinition, {
|
|
81
79
|
variables,
|
|
82
|
-
mockChange,
|
|
83
|
-
0,
|
|
84
|
-
undefined,
|
|
85
|
-
mockLogger
|
|
86
|
-
);
|
|
80
|
+
handleChanges: mockChange,
|
|
81
|
+
pagerIteration: 0,
|
|
82
|
+
value: undefined,
|
|
83
|
+
logger: mockLogger,
|
|
84
|
+
});
|
|
87
85
|
expect(
|
|
88
86
|
options.filter((o) => o.checked),
|
|
89
87
|
'Nothing checked when variable empty'
|
|
90
88
|
).toHaveLength(0);
|
|
91
89
|
|
|
92
90
|
variables.set('O1', [true, 0]);
|
|
93
|
-
options =
|
|
94
|
-
checkboxGroupDefinition,
|
|
91
|
+
options = computeOptionsFromComponent(checkboxGroupDefinition, {
|
|
95
92
|
variables,
|
|
96
|
-
mockChange,
|
|
97
|
-
0,
|
|
98
|
-
undefined,
|
|
99
|
-
mockLogger
|
|
100
|
-
);
|
|
93
|
+
handleChanges: mockChange,
|
|
94
|
+
pagerIteration: 0,
|
|
95
|
+
value: undefined,
|
|
96
|
+
logger: mockLogger,
|
|
97
|
+
});
|
|
101
98
|
expect(options[0].checked).toBe(true);
|
|
102
99
|
expect(options[1].checked).toBe(false);
|
|
103
100
|
});
|
|
104
101
|
it('should create handleChange correctly', () => {
|
|
105
102
|
variables.set('O1', [true, false]);
|
|
106
103
|
variables.set('O2', [false, true]);
|
|
107
|
-
const options =
|
|
108
|
-
checkboxGroupDefinition,
|
|
104
|
+
const options = computeOptionsFromComponent(checkboxGroupDefinition, {
|
|
109
105
|
variables,
|
|
110
|
-
mockChange,
|
|
111
|
-
1,
|
|
112
|
-
undefined,
|
|
113
|
-
mockLogger
|
|
114
|
-
);
|
|
115
|
-
options[1].onCheck(false);
|
|
106
|
+
handleChanges: mockChange,
|
|
107
|
+
pagerIteration: 1,
|
|
108
|
+
value: undefined,
|
|
109
|
+
logger: mockLogger,
|
|
110
|
+
});
|
|
111
|
+
options[1].onCheck?.(false);
|
|
116
112
|
expect(mockChange).toHaveBeenLastCalledWith([
|
|
117
113
|
{ name: 'O2', value: false },
|
|
118
114
|
]);
|
|
@@ -145,14 +141,13 @@ describe('getOptionsProp()', () => {
|
|
|
145
141
|
|
|
146
142
|
variables.set('DETAIL', true);
|
|
147
143
|
|
|
148
|
-
const options =
|
|
149
|
-
definition,
|
|
144
|
+
const options = computeOptionsFromComponent(definition, {
|
|
150
145
|
variables,
|
|
151
|
-
mockChange,
|
|
152
|
-
undefined,
|
|
153
|
-
undefined,
|
|
154
|
-
mockLogger
|
|
155
|
-
);
|
|
146
|
+
handleChanges: mockChange,
|
|
147
|
+
pagerIteration: undefined,
|
|
148
|
+
value: undefined,
|
|
149
|
+
logger: mockLogger,
|
|
150
|
+
});
|
|
156
151
|
|
|
157
152
|
expect(options).toHaveLength(2);
|
|
158
153
|
expect(options[0].detailLabel).toBe('Precize:');
|
|
@@ -188,14 +183,13 @@ describe('getOptionsProp()', () => {
|
|
|
188
183
|
|
|
189
184
|
variables.set('DETAIL', true);
|
|
190
185
|
|
|
191
|
-
const options =
|
|
192
|
-
definition,
|
|
186
|
+
const options = computeOptionsFromComponent(definition, {
|
|
193
187
|
variables,
|
|
194
|
-
mockChange,
|
|
195
|
-
undefined,
|
|
196
|
-
undefined,
|
|
197
|
-
mockLogger
|
|
198
|
-
);
|
|
188
|
+
handleChanges: mockChange,
|
|
189
|
+
pagerIteration: undefined,
|
|
190
|
+
value: undefined,
|
|
191
|
+
logger: mockLogger,
|
|
192
|
+
});
|
|
199
193
|
|
|
200
194
|
expect(options).toHaveLength(2);
|
|
201
195
|
expect(options[0].detailLabel).toBe('Precize:');
|
|
@@ -224,14 +218,13 @@ describe('getOptionsProp()', () => {
|
|
|
224
218
|
],
|
|
225
219
|
} satisfies DeepTranslateExpression<LunaticComponentDefinition>;
|
|
226
220
|
|
|
227
|
-
const options =
|
|
228
|
-
definition,
|
|
221
|
+
const options = computeOptionsFromComponent(definition, {
|
|
229
222
|
variables,
|
|
230
|
-
mockChange,
|
|
231
|
-
undefined,
|
|
232
|
-
undefined,
|
|
233
|
-
mockLogger
|
|
234
|
-
);
|
|
223
|
+
handleChanges: mockChange,
|
|
224
|
+
pagerIteration: undefined,
|
|
225
|
+
value: undefined,
|
|
226
|
+
logger: mockLogger,
|
|
227
|
+
});
|
|
235
228
|
|
|
236
229
|
// First option should be filtered out since its conditionFilter is evaluated to false
|
|
237
230
|
expect(options).toHaveLength(1);
|
|
@@ -254,14 +247,13 @@ describe('getOptionsProp()', () => {
|
|
|
254
247
|
],
|
|
255
248
|
} as any as DeepTranslateExpression<LunaticComponentDefinition>;
|
|
256
249
|
|
|
257
|
-
const options =
|
|
258
|
-
definition,
|
|
250
|
+
const options = computeOptionsFromComponent(definition, {
|
|
259
251
|
variables,
|
|
260
|
-
mockChange,
|
|
261
|
-
undefined,
|
|
262
|
-
undefined,
|
|
263
|
-
mockLogger
|
|
264
|
-
);
|
|
252
|
+
handleChanges: mockChange,
|
|
253
|
+
pagerIteration: undefined,
|
|
254
|
+
value: undefined,
|
|
255
|
+
logger: mockLogger,
|
|
256
|
+
});
|
|
265
257
|
|
|
266
258
|
// First option should be filtered out since its conditionFilter is evaluated to false
|
|
267
259
|
expect(options).toHaveLength(1);
|
|
@@ -285,14 +277,13 @@ describe('getOptionsProp()', () => {
|
|
|
285
277
|
throw new Error('Test error');
|
|
286
278
|
});
|
|
287
279
|
|
|
288
|
-
const options =
|
|
289
|
-
definition,
|
|
280
|
+
const options = computeOptionsFromComponent(definition, {
|
|
290
281
|
variables,
|
|
291
|
-
mockChange,
|
|
292
|
-
undefined,
|
|
293
|
-
undefined,
|
|
294
|
-
mockLogger
|
|
295
|
-
);
|
|
282
|
+
handleChanges: mockChange,
|
|
283
|
+
pagerIteration: undefined,
|
|
284
|
+
value: undefined,
|
|
285
|
+
logger: mockLogger,
|
|
286
|
+
});
|
|
296
287
|
|
|
297
288
|
// Ensure the option is not filtered
|
|
298
289
|
expect(options).toHaveLength(1);
|
|
@@ -315,14 +306,13 @@ describe('getOptionsProp()', () => {
|
|
|
315
306
|
throw new Error('Test error');
|
|
316
307
|
});
|
|
317
308
|
|
|
318
|
-
const options =
|
|
319
|
-
definition,
|
|
309
|
+
const options = computeOptionsFromComponent(definition, {
|
|
320
310
|
variables,
|
|
321
|
-
mockChange,
|
|
322
|
-
undefined,
|
|
323
|
-
undefined,
|
|
324
|
-
mockLogger
|
|
325
|
-
);
|
|
311
|
+
handleChanges: mockChange,
|
|
312
|
+
pagerIteration: undefined,
|
|
313
|
+
value: undefined,
|
|
314
|
+
logger: mockLogger,
|
|
315
|
+
});
|
|
326
316
|
|
|
327
317
|
// Ensure the option is not filtered
|
|
328
318
|
expect(options).toHaveLength(1);
|
|
@@ -346,15 +336,14 @@ describe('getOptionsProp()', () => {
|
|
|
346
336
|
return false;
|
|
347
337
|
});
|
|
348
338
|
|
|
349
|
-
const options =
|
|
350
|
-
definition,
|
|
339
|
+
const options = computeOptionsFromComponent(definition, {
|
|
351
340
|
variables,
|
|
352
|
-
mockChange,
|
|
353
|
-
undefined,
|
|
354
|
-
undefined,
|
|
355
|
-
mockLogger,
|
|
356
|
-
|
|
357
|
-
);
|
|
341
|
+
handleChanges: mockChange,
|
|
342
|
+
pagerIteration: undefined,
|
|
343
|
+
value: undefined,
|
|
344
|
+
logger: mockLogger,
|
|
345
|
+
disableFilters: true,
|
|
346
|
+
});
|
|
358
347
|
|
|
359
348
|
// Ensure the option is not filtered
|
|
360
349
|
expect(options).toHaveLength(1);
|
|
@@ -378,15 +367,14 @@ describe('getOptionsProp()', () => {
|
|
|
378
367
|
return false;
|
|
379
368
|
});
|
|
380
369
|
|
|
381
|
-
const options =
|
|
382
|
-
definition,
|
|
370
|
+
const options = computeOptionsFromComponent(definition, {
|
|
383
371
|
variables,
|
|
384
|
-
mockChange,
|
|
385
|
-
undefined,
|
|
386
|
-
undefined,
|
|
387
|
-
mockLogger,
|
|
388
|
-
|
|
389
|
-
);
|
|
372
|
+
handleChanges: mockChange,
|
|
373
|
+
pagerIteration: undefined,
|
|
374
|
+
value: undefined,
|
|
375
|
+
logger: mockLogger,
|
|
376
|
+
disableFilters: true,
|
|
377
|
+
});
|
|
390
378
|
|
|
391
379
|
// Ensure the option is not filtered
|
|
392
380
|
expect(options).toHaveLength(1);
|
|
@@ -406,16 +394,15 @@ describe('getOptionsProp()', () => {
|
|
|
406
394
|
],
|
|
407
395
|
} satisfies DeepTranslateExpression<LunaticComponentDefinition>;
|
|
408
396
|
|
|
409
|
-
const options =
|
|
410
|
-
definition,
|
|
397
|
+
const options = computeOptionsFromComponent(definition, {
|
|
411
398
|
variables,
|
|
412
|
-
mockChange,
|
|
413
|
-
undefined,
|
|
414
|
-
undefined,
|
|
415
|
-
mockLogger,
|
|
416
|
-
|
|
417
|
-
true
|
|
418
|
-
);
|
|
399
|
+
handleChanges: mockChange,
|
|
400
|
+
pagerIteration: undefined,
|
|
401
|
+
value: undefined,
|
|
402
|
+
logger: mockLogger,
|
|
403
|
+
disableFilters: true,
|
|
404
|
+
shouldParentBeFiltered: true,
|
|
405
|
+
});
|
|
419
406
|
|
|
420
407
|
// Ensure the option is not filtered
|
|
421
408
|
expect(options).toHaveLength(1);
|
|
@@ -434,16 +421,15 @@ describe('getOptionsProp()', () => {
|
|
|
434
421
|
],
|
|
435
422
|
} as any as DeepTranslateExpression<LunaticComponentDefinition>;
|
|
436
423
|
|
|
437
|
-
const options =
|
|
438
|
-
definition,
|
|
424
|
+
const options = computeOptionsFromComponent(definition, {
|
|
439
425
|
variables,
|
|
440
|
-
mockChange,
|
|
441
|
-
undefined,
|
|
442
|
-
undefined,
|
|
443
|
-
mockLogger,
|
|
444
|
-
|
|
445
|
-
true
|
|
446
|
-
);
|
|
426
|
+
handleChanges: mockChange,
|
|
427
|
+
pagerIteration: undefined,
|
|
428
|
+
value: undefined,
|
|
429
|
+
logger: mockLogger,
|
|
430
|
+
disableFilters: true,
|
|
431
|
+
shouldParentBeFiltered: true,
|
|
432
|
+
});
|
|
447
433
|
|
|
448
434
|
// Ensure the option is not filtered
|
|
449
435
|
expect(options).toHaveLength(1);
|
|
@@ -451,4 +437,88 @@ describe('getOptionsProp()', () => {
|
|
|
451
437
|
expect(options[0].shouldBeFiltered).toBe(true);
|
|
452
438
|
});
|
|
453
439
|
});
|
|
440
|
+
|
|
441
|
+
describe('Options based on a source variable', () => {
|
|
442
|
+
const radioOptionSourceDefinition = {
|
|
443
|
+
id: 'RadioGroupDynamic',
|
|
444
|
+
componentType: 'Radio',
|
|
445
|
+
response: { name: 'RADIO' },
|
|
446
|
+
optionSource: 'NAME',
|
|
447
|
+
} satisfies DeepTranslateExpression<LunaticComponentDefinition>;
|
|
448
|
+
|
|
449
|
+
it('should build options when the source variable is an array of strings', () => {
|
|
450
|
+
variables.set('NAME', ['Maëlle', 'Verso']);
|
|
451
|
+
const options = computeOptionsFromComponent(radioOptionSourceDefinition, {
|
|
452
|
+
variables,
|
|
453
|
+
handleChanges: mockChange,
|
|
454
|
+
pagerIteration: undefined,
|
|
455
|
+
value: undefined,
|
|
456
|
+
logger: mockLogger,
|
|
457
|
+
}) as InterpretedOption[]; // force type but it should infer type correctly
|
|
458
|
+
|
|
459
|
+
expect(options).toHaveLength(2);
|
|
460
|
+
expect(options[0].value).toBe('Maëlle');
|
|
461
|
+
expect(options[0].label).toBe('Maëlle');
|
|
462
|
+
expect(options[1].value).toBe('Verso');
|
|
463
|
+
expect(options[1].label).toBe('Verso');
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it('should build options when the source variable is an array of numbers', () => {
|
|
467
|
+
variables.set('NAME', [10, 20]);
|
|
468
|
+
const options = computeOptionsFromComponent(radioOptionSourceDefinition, {
|
|
469
|
+
variables,
|
|
470
|
+
handleChanges: mockChange,
|
|
471
|
+
pagerIteration: undefined,
|
|
472
|
+
value: undefined,
|
|
473
|
+
logger: mockLogger,
|
|
474
|
+
}) as InterpretedOption[]; // force type but it should infer type correctly
|
|
475
|
+
|
|
476
|
+
expect(options).toHaveLength(2);
|
|
477
|
+
expect(options[0].value).toBe(10);
|
|
478
|
+
expect(options[0].label).toBe('10');
|
|
479
|
+
expect(options[1].value).toBe(20);
|
|
480
|
+
expect(options[1].label).toBe('20');
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it('should set the response when selecting a dynamic option', () => {
|
|
484
|
+
variables.set('NAME', ['Maëlle', 'Verso']);
|
|
485
|
+
const options = computeOptionsFromComponent(radioOptionSourceDefinition, {
|
|
486
|
+
variables,
|
|
487
|
+
handleChanges: mockChange,
|
|
488
|
+
pagerIteration: undefined,
|
|
489
|
+
value: undefined,
|
|
490
|
+
logger: mockLogger,
|
|
491
|
+
}) as InterpretedOption[]; // force type but it should infer type correctly
|
|
492
|
+
|
|
493
|
+
options[0].onCheck?.();
|
|
494
|
+
expect(mockChange).toHaveBeenLastCalledWith([
|
|
495
|
+
{ name: 'RADIO', value: 'Maëlle' },
|
|
496
|
+
]);
|
|
497
|
+
|
|
498
|
+
options[1].onCheck?.();
|
|
499
|
+
expect(mockChange).toHaveBeenLastCalledWith([
|
|
500
|
+
{ name: 'RADIO', value: 'Verso' },
|
|
501
|
+
]);
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it('should filter options based on the optionFilter expression', () => {
|
|
505
|
+
const definition = {
|
|
506
|
+
...radioOptionSourceDefinition,
|
|
507
|
+
optionFilter: { type: 'VTL', value: 'AGE >= 18' },
|
|
508
|
+
} satisfies DeepTranslateExpression<LunaticComponentDefinition>;
|
|
509
|
+
|
|
510
|
+
variables.set('NAME', ['Maëlle', 'Verso', 'Aline']);
|
|
511
|
+
variables.set('AGE', [16, 30, 50]);
|
|
512
|
+
|
|
513
|
+
const options = computeOptionsFromComponent(definition, {
|
|
514
|
+
variables,
|
|
515
|
+
handleChanges: mockChange,
|
|
516
|
+
pagerIteration: undefined,
|
|
517
|
+
value: undefined,
|
|
518
|
+
logger: mockLogger,
|
|
519
|
+
}) as InterpretedOption[]; // force type but it should infer type correctly
|
|
520
|
+
|
|
521
|
+
expect(options.map((option) => option.value)).toEqual(['Verso', 'Aline']);
|
|
522
|
+
});
|
|
523
|
+
});
|
|
454
524
|
});
|
|
@@ -28,15 +28,25 @@ export type InterpretedOption = {
|
|
|
28
28
|
/**
|
|
29
29
|
* Compute options for checkboxes / radios / dropdown
|
|
30
30
|
*/
|
|
31
|
-
export function
|
|
31
|
+
export function computeOptionsFromComponent(
|
|
32
32
|
definition: DeepTranslateExpression<LunaticComponentDefinition>,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
{
|
|
34
|
+
variables,
|
|
35
|
+
handleChanges,
|
|
36
|
+
pagerIteration,
|
|
37
|
+
value,
|
|
38
|
+
logger,
|
|
39
|
+
disableFilters,
|
|
40
|
+
shouldParentBeFiltered,
|
|
41
|
+
}: {
|
|
42
|
+
variables: LunaticVariablesStore;
|
|
43
|
+
handleChanges: LunaticChangesHandler;
|
|
44
|
+
pagerIteration: LunaticState['pager']['iteration'];
|
|
45
|
+
value: unknown;
|
|
46
|
+
logger: LunaticLogger;
|
|
47
|
+
disableFilters?: boolean;
|
|
48
|
+
shouldParentBeFiltered?: boolean;
|
|
49
|
+
}
|
|
40
50
|
) {
|
|
41
51
|
const iteration = isNumber(pagerIteration) ? [pagerIteration] : undefined;
|
|
42
52
|
|
|
@@ -85,10 +95,27 @@ export function getOptionsProp(
|
|
|
85
95
|
}));
|
|
86
96
|
}
|
|
87
97
|
|
|
98
|
+
// options based on another variable
|
|
99
|
+
if ('optionSource' in definition && definition.optionSource) {
|
|
100
|
+
return computeOptionsFromSource(definition.optionSource, {
|
|
101
|
+
variables,
|
|
102
|
+
value,
|
|
103
|
+
handleChanges,
|
|
104
|
+
responseName: definition.response.name,
|
|
105
|
+
logger,
|
|
106
|
+
shouldParentBeFiltered,
|
|
107
|
+
optionFilter: definition.optionFilter,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
88
111
|
if (!('options' in definition)) {
|
|
89
112
|
return [];
|
|
90
113
|
}
|
|
91
114
|
|
|
115
|
+
if (!definition.options) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
|
|
92
119
|
return definition.options
|
|
93
120
|
.filter((option) => {
|
|
94
121
|
if (
|
|
@@ -144,6 +171,68 @@ export function getOptionsProp(
|
|
|
144
171
|
}));
|
|
145
172
|
}
|
|
146
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Get all options from a source variable, applying filters.
|
|
176
|
+
*/
|
|
177
|
+
function computeOptionsFromSource(
|
|
178
|
+
optionSource: string,
|
|
179
|
+
{
|
|
180
|
+
variables,
|
|
181
|
+
value,
|
|
182
|
+
handleChanges,
|
|
183
|
+
responseName,
|
|
184
|
+
logger,
|
|
185
|
+
shouldParentBeFiltered,
|
|
186
|
+
optionFilter,
|
|
187
|
+
}: {
|
|
188
|
+
variables: LunaticVariablesStore;
|
|
189
|
+
value: unknown;
|
|
190
|
+
handleChanges: LunaticChangesHandler;
|
|
191
|
+
responseName: string;
|
|
192
|
+
logger: LunaticLogger;
|
|
193
|
+
shouldParentBeFiltered?: boolean;
|
|
194
|
+
optionFilter?: VtlExpression;
|
|
195
|
+
}
|
|
196
|
+
): InterpretedOption[] {
|
|
197
|
+
// we don't know the type of the optionSource values (string, numbers, boolean)
|
|
198
|
+
const optionValues = variables.get<unknown>(optionSource);
|
|
199
|
+
if (!optionValues) {
|
|
200
|
+
return [];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const normalizedValues = Array.isArray(optionValues)
|
|
204
|
+
? optionValues
|
|
205
|
+
: [optionValues];
|
|
206
|
+
|
|
207
|
+
return normalizedValues
|
|
208
|
+
.filter((option, index) => {
|
|
209
|
+
// option is an empty value, we remove it from the options list
|
|
210
|
+
if (option === null || option === undefined) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
// no filter expression, we keep the option
|
|
214
|
+
if (!optionFilter) {
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
// apply filter expression on option (applied to its iteration)
|
|
218
|
+
return !isFilteredOutOption(variables, [index], logger, optionFilter);
|
|
219
|
+
})
|
|
220
|
+
.map((option) => {
|
|
221
|
+
return {
|
|
222
|
+
label: String(option),
|
|
223
|
+
value: option,
|
|
224
|
+
checked: value === option,
|
|
225
|
+
onCheck: () => {
|
|
226
|
+
handleChanges([{ name: responseName, value: option }]);
|
|
227
|
+
},
|
|
228
|
+
onUncheck: () => {
|
|
229
|
+
handleChanges([{ name: responseName, value: null }]);
|
|
230
|
+
},
|
|
231
|
+
shouldBeFiltered: shouldParentBeFiltered,
|
|
232
|
+
};
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
147
236
|
/**
|
|
148
237
|
* Check if an option should be filtered, depending on its conditionFilter.
|
|
149
238
|
*/
|