@inseefr/lunatic 3.12.3 → 3.13.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/esm/type.source.d.ts +1 -0
- package/esm/use-lunatic/commons/variables/behaviours/cleaning-behaviour.js +55 -16
- package/esm/use-lunatic/commons/variables/behaviours/cleaning-behaviour.js.map +1 -1
- package/esm/use-lunatic/commons/variables/lunatic-variables-store.spec.js +67 -0
- package/esm/use-lunatic/commons/variables/lunatic-variables-store.spec.js.map +1 -1
- package/package.json +2 -2
- package/src/stories/checkbox/sourceOneDynamicOptions.json +16 -1
- package/src/stories/dropdown/sourceDynamicOptions.json +16 -1
- package/src/stories/radio/sourceDynamicOptions.json +16 -1
- package/src/type.source.ts +1 -0
- package/src/use-lunatic/commons/variables/behaviours/cleaning-behaviour.ts +89 -30
- package/src/use-lunatic/commons/variables/lunatic-variables-store.spec.ts +85 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/type.source.d.ts +1 -0
- package/use-lunatic/commons/variables/behaviours/cleaning-behaviour.js +54 -16
- package/use-lunatic/commons/variables/behaviours/cleaning-behaviour.js.map +1 -1
- package/use-lunatic/commons/variables/lunatic-variables-store.spec.js +67 -0
- package/use-lunatic/commons/variables/lunatic-variables-store.spec.js.map +1 -1
|
@@ -7,6 +7,13 @@ import { depth, setAtIndex } from '../../../../utils/array';
|
|
|
7
7
|
import { castBool } from '../../../../utils/cast';
|
|
8
8
|
import { IterationLevel } from '../models';
|
|
9
9
|
|
|
10
|
+
type CleaningExpression = {
|
|
11
|
+
expression: string;
|
|
12
|
+
shapeFrom?: string;
|
|
13
|
+
isAggregatorUsed: boolean;
|
|
14
|
+
shouldCheckAllIterations?: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
10
17
|
/**
|
|
11
18
|
* Implements the cleaning behavior for the variable store.
|
|
12
19
|
* When a variable changes, this function determines which other variables
|
|
@@ -130,13 +137,7 @@ function shouldClean(
|
|
|
130
137
|
iteration,
|
|
131
138
|
isResizing,
|
|
132
139
|
}: {
|
|
133
|
-
expressions:
|
|
134
|
-
| string
|
|
135
|
-
| {
|
|
136
|
-
expression: string;
|
|
137
|
-
shapeFrom?: string;
|
|
138
|
-
isAggregatorUsed: boolean;
|
|
139
|
-
}[];
|
|
140
|
+
expressions: string | CleaningExpression[];
|
|
140
141
|
iteration?: number[];
|
|
141
142
|
isResizing: boolean;
|
|
142
143
|
}
|
|
@@ -156,10 +157,21 @@ function shouldClean(
|
|
|
156
157
|
expressions = expressions.filter((expr) => expr.isAggregatorUsed);
|
|
157
158
|
}
|
|
158
159
|
|
|
159
|
-
//
|
|
160
|
-
if (
|
|
160
|
+
// At least one expression requires to compute on each iteration
|
|
161
|
+
if (shouldCheckAtAllIterations(expressions)) {
|
|
162
|
+
const expressionsToCheckAtAllIterations: CleaningExpression[] = [];
|
|
163
|
+
const expressionsNotToCheckAtAllIterations: CleaningExpression[] = [];
|
|
164
|
+
|
|
165
|
+
for (const expression of expressions) {
|
|
166
|
+
if (expression.shouldCheckAllIterations) {
|
|
167
|
+
expressionsToCheckAtAllIterations.push(expression);
|
|
168
|
+
} else {
|
|
169
|
+
expressionsNotToCheckAtAllIterations.push(expression);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
161
173
|
const shapeFromVariable = store.get(
|
|
162
|
-
|
|
174
|
+
expressionsToCheckAtAllIterations[0].shapeFrom as string
|
|
163
175
|
) as Array<unknown>;
|
|
164
176
|
|
|
165
177
|
const shouldCleanArray = new Array(shapeFromVariable.length).fill(
|
|
@@ -167,27 +179,69 @@ function shouldClean(
|
|
|
167
179
|
) as Array<boolean>;
|
|
168
180
|
|
|
169
181
|
for (const [iterationIndex] of shouldCleanArray.entries()) {
|
|
170
|
-
shouldCleanArray[iterationIndex] =
|
|
182
|
+
shouldCleanArray[iterationIndex] =
|
|
183
|
+
// check if we need to clean each iteration according to expression
|
|
184
|
+
shouldCleanAtIteration(store, {
|
|
185
|
+
expressions: expressionsToCheckAtAllIterations,
|
|
186
|
+
iteration: [iterationIndex],
|
|
187
|
+
}) ||
|
|
188
|
+
shouldCleanAtIteration(store, {
|
|
189
|
+
expressions: expressionsNotToCheckAtAllIterations,
|
|
190
|
+
iteration,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return shouldCleanArray;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// If expressions have shapeFrom and we're at root level, we need to check each iteration individually
|
|
197
|
+
if (expressionsHaveShapeFrom(expressions) && !iteration) {
|
|
198
|
+
const shapeFrom = findFirstExpressionWithShapeFrom(expressions)?.shapeFrom;
|
|
199
|
+
const shapeFromVariable = store.get(shapeFrom as string) as Array<unknown>;
|
|
200
|
+
|
|
201
|
+
const shouldCleanArray = new Array(shapeFromVariable.length).fill(
|
|
202
|
+
false
|
|
203
|
+
) as Array<boolean>;
|
|
204
|
+
|
|
205
|
+
for (const [iterationIndex] of shouldCleanArray.entries()) {
|
|
206
|
+
shouldCleanArray[iterationIndex] = shouldCleanAtIteration(store, {
|
|
171
207
|
expressions,
|
|
172
208
|
iteration: [iterationIndex],
|
|
173
|
-
|
|
174
|
-
}) as boolean;
|
|
209
|
+
});
|
|
175
210
|
}
|
|
176
211
|
return shouldCleanArray;
|
|
177
|
-
}
|
|
178
|
-
// Clean the variable if any condition is false (variable is not visible),
|
|
179
|
-
for (const expression of expressions) {
|
|
180
|
-
if (
|
|
181
|
-
!store.run(expression.expression, {
|
|
182
|
-
iteration,
|
|
183
|
-
})
|
|
184
|
-
)
|
|
185
|
-
return true;
|
|
186
|
-
}
|
|
212
|
+
}
|
|
187
213
|
|
|
188
|
-
|
|
189
|
-
|
|
214
|
+
return shouldCleanAtIteration(store, { expressions, iteration });
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function shouldCheckAtAllIterations(expressions: CleaningExpression[]) {
|
|
218
|
+
return expressions.some(
|
|
219
|
+
(expression) => expression.shouldCheckAllIterations && expression.shapeFrom
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function shouldCleanAtIteration(
|
|
224
|
+
store: LunaticVariablesStore,
|
|
225
|
+
{
|
|
226
|
+
expressions,
|
|
227
|
+
iteration,
|
|
228
|
+
}: {
|
|
229
|
+
expressions: CleaningExpression[];
|
|
230
|
+
iteration?: number[];
|
|
231
|
+
}
|
|
232
|
+
) {
|
|
233
|
+
// Clean the variable if any condition is false (variable is not visible),
|
|
234
|
+
for (const expression of expressions) {
|
|
235
|
+
if (
|
|
236
|
+
!store.run(expression.expression, {
|
|
237
|
+
iteration,
|
|
238
|
+
})
|
|
239
|
+
)
|
|
240
|
+
return true;
|
|
190
241
|
}
|
|
242
|
+
|
|
243
|
+
// All conditions are true, no cleaning needed
|
|
244
|
+
return false;
|
|
191
245
|
}
|
|
192
246
|
|
|
193
247
|
/**
|
|
@@ -205,6 +259,10 @@ function getValueAtIteration(value: unknown, iteration?: number[]) {
|
|
|
205
259
|
return getValueAtIteration(value[iteration[0]], iteration.slice(1));
|
|
206
260
|
}
|
|
207
261
|
|
|
262
|
+
function hasShapeFrom(expression: CleaningExpression) {
|
|
263
|
+
return expression.shapeFrom !== null && expression.shapeFrom !== undefined;
|
|
264
|
+
}
|
|
265
|
+
|
|
208
266
|
/**
|
|
209
267
|
* Checks if all expressions in the array have a shapeFrom property
|
|
210
268
|
*
|
|
@@ -213,17 +271,18 @@ function getValueAtIteration(value: unknown, iteration?: number[]) {
|
|
|
213
271
|
*
|
|
214
272
|
* @returns true if all expressions have a non-null shapeFrom property
|
|
215
273
|
*/
|
|
216
|
-
function
|
|
274
|
+
function expressionsHaveShapeFrom(
|
|
217
275
|
expressions: {
|
|
218
276
|
expression: string;
|
|
219
277
|
shapeFrom?: string;
|
|
220
278
|
isAggregatorUsed: boolean;
|
|
221
279
|
}[]
|
|
222
280
|
) {
|
|
223
|
-
return expressions.every(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
281
|
+
return expressions.every((expression) => hasShapeFrom(expression));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function findFirstExpressionWithShapeFrom(expressions: CleaningExpression[]) {
|
|
285
|
+
return expressions.find((expression) => hasShapeFrom(expression));
|
|
227
286
|
}
|
|
228
287
|
|
|
229
288
|
/**
|
|
@@ -572,6 +572,91 @@ describe('lunatic-variables-store', () => {
|
|
|
572
572
|
variables.set('PRENOM', ['John', 'Jane']);
|
|
573
573
|
expect(variables.get('AGE')).toEqual([null, null]);
|
|
574
574
|
});
|
|
575
|
+
it('should clean but not at every iteration when configured from an iterated source change if there is no shapeFrom provided', () => {
|
|
576
|
+
// Given
|
|
577
|
+
variables.set('PRENOM', ['Marc', 'Marc', 'Marc']);
|
|
578
|
+
variables.set('QCU', 'A');
|
|
579
|
+
cleaningBehaviour(
|
|
580
|
+
variables,
|
|
581
|
+
{
|
|
582
|
+
PRENOM: {
|
|
583
|
+
QCU: [
|
|
584
|
+
{
|
|
585
|
+
expression: 'false',
|
|
586
|
+
isAggregatorUsed: false,
|
|
587
|
+
shouldCheckAllIterations: true,
|
|
588
|
+
},
|
|
589
|
+
],
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
{ PRENOM: [], QCU: null }
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
// When
|
|
596
|
+
variables.set('PRENOM', 'Patrick', { iteration: [0] });
|
|
597
|
+
|
|
598
|
+
// Then
|
|
599
|
+
expect(variables.get('QCU')).toEqual(null);
|
|
600
|
+
});
|
|
601
|
+
it('should check every iteration when configured from an iterated source change', () => {
|
|
602
|
+
// Given
|
|
603
|
+
variables.set('PRENOM', ['Marc', 'Marc', 'Marc']);
|
|
604
|
+
variables.set('QCU', ['A', 'B', 'B']);
|
|
605
|
+
cleaningBehaviour(
|
|
606
|
+
variables,
|
|
607
|
+
{
|
|
608
|
+
PRENOM: {
|
|
609
|
+
QCU: [
|
|
610
|
+
{
|
|
611
|
+
expression: 'PRENOM <> "Marc"',
|
|
612
|
+
shapeFrom: 'PRENOM',
|
|
613
|
+
isAggregatorUsed: false,
|
|
614
|
+
shouldCheckAllIterations: true,
|
|
615
|
+
},
|
|
616
|
+
],
|
|
617
|
+
},
|
|
618
|
+
},
|
|
619
|
+
{ PRENOM: [], QCU: [null] }
|
|
620
|
+
);
|
|
621
|
+
|
|
622
|
+
// When
|
|
623
|
+
variables.set('PRENOM', 'Patrick', { iteration: [0] });
|
|
624
|
+
|
|
625
|
+
// Then
|
|
626
|
+
expect(variables.get('QCU')).toEqual(['A', null, null]);
|
|
627
|
+
});
|
|
628
|
+
it('should handle simultaneously expressions that should check every iteration and expression that should not ', () => {
|
|
629
|
+
// Given
|
|
630
|
+
variables.set('PRENOM', ['Marc', 'Marc', 'Marc']);
|
|
631
|
+
variables.set('QCU', ['A', 'B', 'B']);
|
|
632
|
+
cleaningBehaviour(
|
|
633
|
+
variables,
|
|
634
|
+
{
|
|
635
|
+
PRENOM: {
|
|
636
|
+
QCU: [
|
|
637
|
+
{
|
|
638
|
+
expression: 'PRENOM <> ""',
|
|
639
|
+
shapeFrom: 'PRENOM',
|
|
640
|
+
isAggregatorUsed: false,
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
expression: 'PRENOM <> "Marc"',
|
|
644
|
+
shapeFrom: 'PRENOM',
|
|
645
|
+
isAggregatorUsed: false,
|
|
646
|
+
shouldCheckAllIterations: true,
|
|
647
|
+
},
|
|
648
|
+
],
|
|
649
|
+
},
|
|
650
|
+
},
|
|
651
|
+
{ PRENOM: [], QCU: [null] }
|
|
652
|
+
);
|
|
653
|
+
|
|
654
|
+
// When
|
|
655
|
+
variables.set('PRENOM', '', { iteration: [0] });
|
|
656
|
+
|
|
657
|
+
// Then
|
|
658
|
+
expect(variables.get('QCU')).toEqual([null, null, null]);
|
|
659
|
+
});
|
|
575
660
|
it('should clean pairwise in two directions', () => {
|
|
576
661
|
variables.set('LINKS', [
|
|
577
662
|
[null, '1', '3'],
|