@inseefr/lunatic 3.13.5-rc.3 → 3.13.5
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/getDurationFromValue.d.ts +2 -2
- package/components/Duration/getDurationFromValue.js +7 -6
- package/components/Duration/getDurationFromValue.js.map +1 -1
- package/esm/components/Duration/getDurationFromValue.d.ts +2 -2
- package/esm/components/Duration/getDurationFromValue.js +7 -6
- package/esm/components/Duration/getDurationFromValue.js.map +1 -1
- package/esm/hooks/useMultimode.js +21 -10
- package/esm/hooks/useMultimode.js.map +1 -1
- package/esm/use-lunatic/commons/compile-controls.d.ts +5 -0
- package/esm/use-lunatic/commons/compile-controls.js +6 -1
- package/esm/use-lunatic/commons/compile-controls.js.map +1 -1
- package/esm/use-lunatic/commons/compile-controls.spec.d.ts +1 -0
- package/esm/use-lunatic/commons/compile-controls.spec.js +121 -0
- package/esm/use-lunatic/commons/compile-controls.spec.js.map +1 -0
- package/esm/utils/getArticulationState.js +12 -6
- package/esm/utils/getArticulationState.js.map +1 -1
- package/hooks/useMultimode.js +20 -8
- package/hooks/useMultimode.js.map +1 -1
- package/package.json +11 -1
- package/src/components/Duration/getDurationFromValue.ts +10 -6
- package/src/hooks/useMultimode.ts +27 -13
- package/src/stories/questionnaires/SRCV/data.json +116 -0
- package/src/stories/questionnaires/SRCV/source.json +69256 -0
- package/src/stories/questionnaires/SRCV/srcv.stories.tsx +23 -0
- package/src/use-lunatic/commons/compile-controls.spec.ts +136 -0
- package/src/use-lunatic/commons/compile-controls.ts +7 -1
- package/src/utils/getArticulationState.ts +16 -8
- package/tsconfig.build.tsbuildinfo +1 -1
- package/use-lunatic/commons/compile-controls.d.ts +5 -0
- package/use-lunatic/commons/compile-controls.js +6 -0
- package/use-lunatic/commons/compile-controls.js.map +1 -1
- package/use-lunatic/commons/compile-controls.spec.d.ts +1 -0
- package/use-lunatic/commons/compile-controls.spec.js +123 -0
- package/use-lunatic/commons/compile-controls.spec.js.map +1 -0
- package/utils/getArticulationState.js +12 -6
- package/utils/getArticulationState.js.map +1 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Orchestrator,
|
|
3
|
+
OrchestratorMeta,
|
|
4
|
+
type OrchestratorStory,
|
|
5
|
+
} from '../../utils/Orchestrator';
|
|
6
|
+
import source from './source.json';
|
|
7
|
+
import interrogation from './data.json';
|
|
8
|
+
import { Meta } from '@storybook/react';
|
|
9
|
+
|
|
10
|
+
const meta: Meta<typeof Orchestrator> = {
|
|
11
|
+
title: 'Questionnaires/SRCV',
|
|
12
|
+
...OrchestratorMeta,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default meta;
|
|
16
|
+
|
|
17
|
+
export const Default: OrchestratorStory = {
|
|
18
|
+
args: {
|
|
19
|
+
source,
|
|
20
|
+
data: interrogation.data,
|
|
21
|
+
initialPage: '179',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import type { LunaticReducerState } from '../type';
|
|
3
|
+
import type { ComponentDefinition, InterpretedComponent } from './component';
|
|
4
|
+
import { computeIterations } from './compile-controls';
|
|
5
|
+
|
|
6
|
+
// Mock de la fonction executeExpression
|
|
7
|
+
const mockExecuteExpression =
|
|
8
|
+
vi.fn() as unknown as LunaticReducerState['executeExpression'] &
|
|
9
|
+
ReturnType<typeof vi.fn>;
|
|
10
|
+
|
|
11
|
+
describe('computeIterations', () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
vi.resetAllMocks();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should return iterations directly if it is a number', () => {
|
|
17
|
+
const component = {
|
|
18
|
+
id: 'test',
|
|
19
|
+
componentType: 'Loop',
|
|
20
|
+
iterations: 5,
|
|
21
|
+
} as InterpretedComponent;
|
|
22
|
+
expect(computeIterations(component, mockExecuteExpression)).toBe(5);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should execute VTL expression for iterations if it is an object', () => {
|
|
26
|
+
const component = {
|
|
27
|
+
id: 'test',
|
|
28
|
+
componentType: 'Loop',
|
|
29
|
+
iterations: { type: 'VTL', value: 'count(ARRAY)' },
|
|
30
|
+
} as ComponentDefinition;
|
|
31
|
+
mockExecuteExpression.mockReturnValue(3);
|
|
32
|
+
expect(computeIterations(component, mockExecuteExpression)).toBe(3);
|
|
33
|
+
expect(mockExecuteExpression).toHaveBeenCalledWith({
|
|
34
|
+
type: 'VTL',
|
|
35
|
+
value: 'count(ARRAY)',
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should return array length if component has a response with an array value', () => {
|
|
40
|
+
const component = {
|
|
41
|
+
id: 'test',
|
|
42
|
+
componentType: 'Question',
|
|
43
|
+
response: { name: 'ARRAY' },
|
|
44
|
+
} as unknown as ComponentDefinition;
|
|
45
|
+
mockExecuteExpression.mockReturnValue(['a', 'b', 'c', 'd']);
|
|
46
|
+
expect(computeIterations(component, mockExecuteExpression)).toBe(4);
|
|
47
|
+
expect(mockExecuteExpression).toHaveBeenCalledWith({
|
|
48
|
+
type: 'VTL',
|
|
49
|
+
value: 'ARRAY',
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should return 0 if response value is not an array', () => {
|
|
54
|
+
const component: ComponentDefinition = {
|
|
55
|
+
id: 'test',
|
|
56
|
+
componentType: 'Question',
|
|
57
|
+
response: { name: 'VAR' },
|
|
58
|
+
} as unknown as ComponentDefinition;
|
|
59
|
+
mockExecuteExpression.mockReturnValue('not an array');
|
|
60
|
+
expect(computeIterations(component, mockExecuteExpression)).toBe(0);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should return max iterations of children if component has components', () => {
|
|
64
|
+
const component: ComponentDefinition = {
|
|
65
|
+
id: 'parent',
|
|
66
|
+
componentType: 'Loop',
|
|
67
|
+
components: [
|
|
68
|
+
{ id: 'child1', componentType: 'Question', iterations: 2 },
|
|
69
|
+
{ id: 'child2', componentType: 'Question', iterations: 5 },
|
|
70
|
+
],
|
|
71
|
+
} as unknown as ComponentDefinition;
|
|
72
|
+
expect(computeIterations(component, mockExecuteExpression)).toBe(5);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should return 0 if no iterations, response, or components are present', () => {
|
|
76
|
+
const component: ComponentDefinition = {
|
|
77
|
+
id: 'test',
|
|
78
|
+
componentType: 'Question',
|
|
79
|
+
} as unknown as ComponentDefinition;
|
|
80
|
+
expect(computeIterations(component, mockExecuteExpression)).toBe(0);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should return 0 if iterations is null or undefined', () => {
|
|
84
|
+
const component1: ComponentDefinition = {
|
|
85
|
+
id: 'test1',
|
|
86
|
+
componentType: 'Loop',
|
|
87
|
+
iterations: null as unknown as number,
|
|
88
|
+
} as unknown as ComponentDefinition;
|
|
89
|
+
const component2: ComponentDefinition = {
|
|
90
|
+
id: 'test2',
|
|
91
|
+
componentType: 'Loop',
|
|
92
|
+
iterations: undefined as unknown as number,
|
|
93
|
+
} as unknown as ComponentDefinition;
|
|
94
|
+
expect(computeIterations(component1, mockExecuteExpression)).toBe(0);
|
|
95
|
+
expect(computeIterations(component2, mockExecuteExpression)).toBe(0);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should return 0 if all children have no iterations', () => {
|
|
99
|
+
const component: ComponentDefinition = {
|
|
100
|
+
id: 'parent',
|
|
101
|
+
componentType: 'Loop',
|
|
102
|
+
components: [
|
|
103
|
+
{ id: 'child1', componentType: 'Question' },
|
|
104
|
+
{ id: 'child2', componentType: 'Question' },
|
|
105
|
+
],
|
|
106
|
+
} as unknown as ComponentDefinition;
|
|
107
|
+
expect(computeIterations(component, mockExecuteExpression)).toBe(0);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should prioritize iterations over response if both are present', () => {
|
|
111
|
+
const component: ComponentDefinition = {
|
|
112
|
+
id: 'test',
|
|
113
|
+
componentType: 'Loop',
|
|
114
|
+
iterations: 3,
|
|
115
|
+
response: { name: 'ARRAY' },
|
|
116
|
+
} as unknown as ComponentDefinition;
|
|
117
|
+
mockExecuteExpression.mockReturnValue(['a', 'b', 'c', 'd', 'e']);
|
|
118
|
+
expect(computeIterations(component, mockExecuteExpression)).toBe(3);
|
|
119
|
+
expect(mockExecuteExpression).not.toHaveBeenCalled();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should prioritize iterations expression over response if both are present', () => {
|
|
123
|
+
const component: ComponentDefinition = {
|
|
124
|
+
id: 'test',
|
|
125
|
+
componentType: 'Loop',
|
|
126
|
+
iterations: { type: 'VTL', value: 'count(ARRAY)' },
|
|
127
|
+
response: { name: 'ANOTHER_ARRAY' },
|
|
128
|
+
} as unknown as ComponentDefinition;
|
|
129
|
+
mockExecuteExpression.mockReturnValue(7);
|
|
130
|
+
expect(computeIterations(component, mockExecuteExpression)).toBe(7);
|
|
131
|
+
expect(mockExecuteExpression).toHaveBeenCalledWith({
|
|
132
|
+
type: 'VTL',
|
|
133
|
+
value: 'count(ARRAY)',
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
});
|
|
@@ -115,10 +115,14 @@ function checkControls(
|
|
|
115
115
|
/**
|
|
116
116
|
* Figure out the number of iterations of a component.
|
|
117
117
|
*/
|
|
118
|
-
function computeIterations(
|
|
118
|
+
export function computeIterations(
|
|
119
119
|
component: InterpretedComponent | ComponentDefinition,
|
|
120
120
|
executeExpression: LunaticReducerState['executeExpression']
|
|
121
121
|
): number {
|
|
122
|
+
// iterations is present and it's already computed, return the actual value (case of InterpretedComponent)
|
|
123
|
+
if ('iterations' in component && typeof component.iterations === 'number')
|
|
124
|
+
return component.iterations;
|
|
125
|
+
// iteratons is present and it's an expression, compute it
|
|
122
126
|
if (
|
|
123
127
|
'iterations' in component &&
|
|
124
128
|
component.iterations &&
|
|
@@ -127,6 +131,8 @@ function computeIterations(
|
|
|
127
131
|
) {
|
|
128
132
|
return executeExpression<number>(component.iterations);
|
|
129
133
|
}
|
|
134
|
+
|
|
135
|
+
// Fallback to variable collectes by this component, return it's length
|
|
130
136
|
if ('response' in component) {
|
|
131
137
|
const value = executeExpression({
|
|
132
138
|
type: 'VTL',
|
|
@@ -9,6 +9,7 @@ import { type ReactNode } from 'react';
|
|
|
9
9
|
import { times } from './array';
|
|
10
10
|
import { forceInt } from './number';
|
|
11
11
|
import { isRoundaboutComponent } from '../use-lunatic/commons/component';
|
|
12
|
+
import { castNumber } from './cast';
|
|
12
13
|
|
|
13
14
|
export enum ArticulationState {
|
|
14
15
|
COMPLETED = 1,
|
|
@@ -70,13 +71,26 @@ export function getArticulationState(
|
|
|
70
71
|
source: LunaticSource & { articulation: Articulation },
|
|
71
72
|
data: LunaticData
|
|
72
73
|
): { items: Item[] } {
|
|
74
|
+
if (!source.articulation) {
|
|
75
|
+
return {
|
|
76
|
+
items: [],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
73
80
|
const roundabout = findComponentById(
|
|
74
81
|
source.components,
|
|
75
82
|
source.articulation.source
|
|
76
83
|
);
|
|
84
|
+
|
|
85
|
+
if (!roundabout) {
|
|
86
|
+
return {
|
|
87
|
+
items: [],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
77
91
|
const { variables } = reducerInitializer({ source, data });
|
|
78
|
-
const iterations =
|
|
79
|
-
variables.run(roundabout
|
|
92
|
+
const iterations = castNumber(
|
|
93
|
+
variables.run(roundabout.iterations.value ?? '0')
|
|
80
94
|
);
|
|
81
95
|
|
|
82
96
|
const rows = times(iterations, (k) =>
|
|
@@ -86,12 +100,6 @@ export function getArticulationState(
|
|
|
86
100
|
}))
|
|
87
101
|
);
|
|
88
102
|
|
|
89
|
-
if (!roundabout) {
|
|
90
|
-
return {
|
|
91
|
-
items: [],
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
103
|
return {
|
|
96
104
|
items: rows.map((row, k) => ({
|
|
97
105
|
cells: row,
|