@inseefr/lunatic 3.14.0-rc-delay-sequence-cleaning.0 → 4.0.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.
Files changed (86) hide show
  1. package/esm/type.source.d.ts +10 -0
  2. package/esm/use-lunatic/commons/fill-components/fill-component.spec.js +7 -9
  3. package/esm/use-lunatic/commons/fill-components/fill-component.spec.js.map +1 -1
  4. package/esm/use-lunatic/commons/fill-components/fill-components.d.ts +2 -2
  5. package/esm/use-lunatic/commons/fill-components/fill-components.js +1 -2
  6. package/esm/use-lunatic/commons/fill-components/fill-components.js.map +1 -1
  7. package/esm/use-lunatic/commons/index.d.ts +1 -0
  8. package/esm/use-lunatic/commons/index.js +1 -0
  9. package/esm/use-lunatic/commons/index.js.map +1 -1
  10. package/esm/use-lunatic/commons/pagination/page.js +1 -1
  11. package/esm/use-lunatic/commons/pagination/page.js.map +1 -1
  12. package/esm/use-lunatic/commons/remove-condition-filters.d.ts +12 -0
  13. package/esm/use-lunatic/commons/remove-condition-filters.js +78 -0
  14. package/esm/use-lunatic/commons/remove-condition-filters.js.map +1 -0
  15. package/esm/use-lunatic/commons/remove-condition-filters.spec.d.ts +1 -0
  16. package/esm/use-lunatic/commons/remove-condition-filters.spec.js +111 -0
  17. package/esm/use-lunatic/commons/remove-condition-filters.spec.js.map +1 -0
  18. package/esm/use-lunatic/hooks/useOverview.d.ts +1 -1
  19. package/esm/use-lunatic/hooks/useOverview.js +5 -7
  20. package/esm/use-lunatic/hooks/useOverview.js.map +1 -1
  21. package/esm/use-lunatic/hooks/useOverview.spec.js +13 -4
  22. package/esm/use-lunatic/hooks/useOverview.spec.js.map +1 -1
  23. package/esm/use-lunatic/props/propOptions.d.ts +1 -2
  24. package/esm/use-lunatic/props/propOptions.js +3 -5
  25. package/esm/use-lunatic/props/propOptions.js.map +1 -1
  26. package/esm/use-lunatic/props/propOptions.spec.js +66 -57
  27. package/esm/use-lunatic/props/propOptions.spec.js.map +1 -1
  28. package/esm/use-lunatic/reducer/reducerInitializer.d.ts +2 -2
  29. package/esm/use-lunatic/reducer/reducerInitializer.js +19 -7
  30. package/esm/use-lunatic/reducer/reducerInitializer.js.map +1 -1
  31. package/esm/use-lunatic/type.d.ts +10 -6
  32. package/esm/use-lunatic/use-lunatic.js +3 -3
  33. package/esm/use-lunatic/use-lunatic.js.map +1 -1
  34. package/package.json +16 -1
  35. package/src/stories/behaviour/filter/filter.stories.tsx +24 -0
  36. package/src/stories/behaviour/filter/sourceActiveFilters.json +275 -0
  37. package/src/stories/utils/Orchestrator.tsx +6 -5
  38. package/src/type.source.ts +10 -0
  39. package/src/use-lunatic/commons/fill-components/fill-component.spec.ts +7 -9
  40. package/src/use-lunatic/commons/fill-components/fill-components.ts +8 -8
  41. package/src/use-lunatic/commons/index.ts +4 -0
  42. package/src/use-lunatic/commons/pagination/page.ts +1 -1
  43. package/src/use-lunatic/commons/remove-condition-filters.spec.ts +131 -0
  44. package/src/use-lunatic/commons/remove-condition-filters.ts +103 -0
  45. package/src/use-lunatic/hooks/useOverview.spec.ts +15 -4
  46. package/src/use-lunatic/hooks/useOverview.ts +5 -20
  47. package/src/use-lunatic/props/propOptions.spec.ts +83 -60
  48. package/src/use-lunatic/props/propOptions.ts +2 -8
  49. package/src/use-lunatic/reducer/reducerInitializer.tsx +23 -8
  50. package/src/use-lunatic/type.ts +27 -23
  51. package/src/use-lunatic/use-lunatic.test.ts +9 -9
  52. package/src/use-lunatic/use-lunatic.ts +6 -6
  53. package/tsconfig.build.tsbuildinfo +1 -1
  54. package/type.source.d.ts +10 -0
  55. package/use-lunatic/commons/fill-components/fill-component.spec.js +7 -9
  56. package/use-lunatic/commons/fill-components/fill-component.spec.js.map +1 -1
  57. package/use-lunatic/commons/fill-components/fill-components.d.ts +2 -2
  58. package/use-lunatic/commons/fill-components/fill-components.js +1 -2
  59. package/use-lunatic/commons/fill-components/fill-components.js.map +1 -1
  60. package/use-lunatic/commons/index.d.ts +1 -0
  61. package/use-lunatic/commons/index.js +4 -1
  62. package/use-lunatic/commons/index.js.map +1 -1
  63. package/use-lunatic/commons/pagination/page.js +1 -1
  64. package/use-lunatic/commons/pagination/page.js.map +1 -1
  65. package/use-lunatic/commons/remove-condition-filters.d.ts +12 -0
  66. package/use-lunatic/commons/remove-condition-filters.js +81 -0
  67. package/use-lunatic/commons/remove-condition-filters.js.map +1 -0
  68. package/use-lunatic/commons/remove-condition-filters.spec.d.ts +1 -0
  69. package/use-lunatic/commons/remove-condition-filters.spec.js +113 -0
  70. package/use-lunatic/commons/remove-condition-filters.spec.js.map +1 -0
  71. package/use-lunatic/hooks/useOverview.d.ts +1 -1
  72. package/use-lunatic/hooks/useOverview.js +5 -7
  73. package/use-lunatic/hooks/useOverview.js.map +1 -1
  74. package/use-lunatic/hooks/useOverview.spec.js +13 -4
  75. package/use-lunatic/hooks/useOverview.spec.js.map +1 -1
  76. package/use-lunatic/props/propOptions.d.ts +1 -2
  77. package/use-lunatic/props/propOptions.js +3 -5
  78. package/use-lunatic/props/propOptions.js.map +1 -1
  79. package/use-lunatic/props/propOptions.spec.js +66 -57
  80. package/use-lunatic/props/propOptions.spec.js.map +1 -1
  81. package/use-lunatic/reducer/reducerInitializer.d.ts +2 -2
  82. package/use-lunatic/reducer/reducerInitializer.js +19 -7
  83. package/use-lunatic/reducer/reducerInitializer.js.map +1 -1
  84. package/use-lunatic/type.d.ts +10 -6
  85. package/use-lunatic/use-lunatic.js +3 -3
  86. package/use-lunatic/use-lunatic.js.map +1 -1
@@ -0,0 +1,103 @@
1
+ import type { ComponentDefinition } from '../../type.source';
2
+ import type { ActiveFilter, LunaticExpression, LunaticSource } from '../type';
3
+
4
+ /**
5
+ * Decide if a conditionFilter must be kept, depending on the active filters mode.
6
+ * - "ALL": Keep every filter
7
+ * - "EXTERNAL": Keep filters based only on external variables
8
+ * - "NONE": Ignore every filter
9
+ */
10
+ export function shouldKeepFilter(
11
+ filter: LunaticExpression | undefined,
12
+ activeFilters: ActiveFilter
13
+ ): boolean {
14
+ if (!filter) return true;
15
+ if (activeFilters === 'ALL') return true;
16
+ if (activeFilters === 'NONE') return false;
17
+ // For external active filter we have to check dependencies and remove any filter based on collected or calculated variables
18
+ const dependencies = filter.variablesDependencies;
19
+ return Boolean(
20
+ dependencies &&
21
+ (dependencies.COLLECTED?.length ?? 0) === 0 &&
22
+ (dependencies.CALCULATED?.length ?? 0) === 0
23
+ );
24
+ }
25
+
26
+ /**
27
+ * Compare the existing element against the outcome of the filtering function
28
+ * and remove it from the source if needed
29
+ */
30
+ const removeFilterIfChanged = <T>(
31
+ elements: T[],
32
+ cleanFilterFunction: (element: T) => T
33
+ ): T[] => {
34
+ const cleaned = elements.map((element) => cleanFilterFunction(element));
35
+ return cleaned.some((element, index) => element !== elements[index])
36
+ ? cleaned
37
+ : elements;
38
+ };
39
+
40
+ /**
41
+ * Remove filters from source based from activeFilters props
42
+ */
43
+ export function removeConditionFilters(
44
+ source: LunaticSource,
45
+ activeFilters: ActiveFilter
46
+ ): LunaticSource {
47
+ if (activeFilters === 'ALL') {
48
+ return source;
49
+ }
50
+
51
+ // Element that can have a condition filter
52
+ // Component is too dynamic to be typed
53
+ const cleanSingleElement = <T extends object>(element: T): T => {
54
+ if (
55
+ 'conditionFilter' in element &&
56
+ !shouldKeepFilter(
57
+ element.conditionFilter as LunaticExpression | undefined,
58
+ activeFilters
59
+ )
60
+ ) {
61
+ const rest = { ...element };
62
+ delete rest.conditionFilter;
63
+ return rest as T;
64
+ }
65
+
66
+ return element;
67
+ };
68
+
69
+ const cleanComponents = <T extends ComponentDefinition>(component: T): T => {
70
+ let current = cleanSingleElement(component);
71
+ if ('components' in current && Array.isArray(current.components)) {
72
+ const components = removeFilterIfChanged(
73
+ current.components,
74
+ cleanComponents
75
+ );
76
+ if (components !== current.components) {
77
+ current = { ...current, components } as T;
78
+ }
79
+ }
80
+ if ('responses' in current && Array.isArray(current.responses)) {
81
+ const currentResponses = current.responses;
82
+ const responses = removeFilterIfChanged(
83
+ currentResponses,
84
+ cleanSingleElement
85
+ );
86
+ if (responses !== currentResponses) {
87
+ current = { ...current, responses } as T;
88
+ }
89
+ }
90
+ if ('options' in current && Array.isArray(current.options)) {
91
+ const currentOptions = current.options
92
+ const options = removeFilterIfChanged(currentOptions, cleanSingleElement);
93
+ if (options !== currentOptions) {
94
+ current = { ...current, options } as T;
95
+ }
96
+ }
97
+ return current;
98
+ };
99
+ return {
100
+ ...source,
101
+ components: source.components.map(cleanComponents),
102
+ };
103
+ }
@@ -6,7 +6,7 @@ import { useLunatic } from '../use-lunatic';
6
6
  import { dataFromObject } from '../../utils/object';
7
7
 
8
8
  describe('use-overview test with useLunatic()', () => {
9
- it('should initialize correctly with disableFilters: false (without data)', () => {
9
+ it('should initialize correctly with all filters active (without data)', () => {
10
10
  const params = [
11
11
  source as any,
12
12
  dataFromObject({}),
@@ -16,7 +16,7 @@ describe('use-overview test with useLunatic()', () => {
16
16
  expect(result.current.overview.length).toBe(3);
17
17
  });
18
18
 
19
- it('should initialize correctly with disableFilters: false (with data)', () => {
19
+ it('should initialize correctly with all filters active (with data)', () => {
20
20
  const params = [
21
21
  source as any,
22
22
  dataFromObject({
@@ -29,15 +29,26 @@ describe('use-overview test with useLunatic()', () => {
29
29
  expect(result.current.overview.length).toBe(9);
30
30
  });
31
31
 
32
- it('should initialize correctly with disableFilters: true (without data)', () => {
32
+ it('should show filtered items when activeFilters is NONE', () => {
33
33
  const params = [
34
34
  source as any,
35
35
  dataFromObject({}),
36
- { withOverview: true, disableFilters: true },
36
+ { withOverview: true, activeFilters: 'NONE' },
37
37
  ] as const;
38
38
 
39
39
  const { result } = renderHook(() => useLunatic(...params));
40
40
  // All elements have to be presents
41
41
  expect(result.current.overview.length).toBe(9);
42
42
  });
43
+
44
+ it('should show items without external-only filter dependencies when activeFilters is EXTERNAL', () => {
45
+ const params = [
46
+ source as any,
47
+ dataFromObject({}),
48
+ { withOverview: true, activeFilters: 'EXTERNAL' },
49
+ ] as const;
50
+
51
+ const { result } = renderHook(() => useLunatic(...params));
52
+ expect(result.current.overview.length).toBe(9);
53
+ });
43
54
  });
@@ -1,5 +1,4 @@
1
1
  import type {
2
- LunaticOptions,
3
2
  LunaticOverviewItem,
4
3
  LunaticReducerState,
5
4
  PageTag,
@@ -30,23 +29,13 @@ export const useOverview = (
30
29
  overview,
31
30
  executeExpression,
32
31
  pager,
33
- options,
34
- }: Pick<
35
- LunaticReducerState,
36
- 'executeExpression' | 'overview' | 'pager' | 'options'
37
- >,
32
+ }: Pick<LunaticReducerState, 'executeExpression' | 'overview' | 'pager'>,
38
33
  deps: DependencyList
39
34
  ) => {
40
35
  return useMemo(
41
- () =>
42
- interpretOverview(
43
- overview,
44
- executeExpression,
45
- pager,
46
- options.disableFilters
47
- ),
36
+ () => interpretOverview(overview, executeExpression, pager),
48
37
  // eslint-disable-next-line react-hooks/exhaustive-deps
49
- [...deps, overview, options.disableFilters]
38
+ [...deps, overview]
50
39
  );
51
40
  };
52
41
 
@@ -56,15 +45,13 @@ export const useOverview = (
56
45
  const interpretOverview = (
57
46
  overviewItems: LunaticOverviewItem[],
58
47
  executeExpression: LunaticReducerState['executeExpression'],
59
- pager?: LunaticReducerState['pager'],
60
- disableFilters?: LunaticOptions['disableFilters']
48
+ pager?: LunaticReducerState['pager']
61
49
  ) => {
62
50
  // Flat structure of the overview
63
51
  let items = overviewItems.reduce(
64
52
  (acc, item) =>
65
53
  interpretOverviewItem(acc, item, executeExpression, {
66
54
  pager,
67
- disableFilters,
68
55
  }),
69
56
  [] as InterpretedLunaticOverviewItem[]
70
57
  );
@@ -99,7 +86,6 @@ const interpretOverviewItem = (
99
86
  executeExpression: LunaticReducerState['executeExpression'],
100
87
  state: {
101
88
  pager?: LunaticReducerState['pager'];
102
- disableFilters?: LunaticOptions['disableFilters'];
103
89
  },
104
90
 
105
91
  iteration?: number
@@ -115,9 +101,8 @@ const interpretOverviewItem = (
115
101
  );
116
102
  }
117
103
 
118
- // if disableFilters is set to true, item are visible and reached by default
119
104
  const isVisible = (() => {
120
- if (state.disableFilters || !item.conditionFilter) return true;
105
+ if (!item.conditionFilter) return true;
121
106
  return Boolean(
122
107
  executeExpression(item.conditionFilter, {
123
108
  iteration: iteration,
@@ -5,7 +5,9 @@ import type { DeepTranslateExpression } from '../commons/fill-components/fill-co
5
5
  import type {
6
6
  LunaticChangesHandler,
7
7
  LunaticComponentDefinition,
8
+ LunaticSource,
8
9
  } from '../type';
10
+ import { removeConditionFilters } from '../commons/remove-condition-filters';
9
11
 
10
12
  describe('computeOptionsFromComponent', () => {
11
13
  let variables: LunaticVariablesStore;
@@ -318,7 +320,7 @@ describe('computeOptionsFromComponent', () => {
318
320
  expect(options).toHaveLength(1);
319
321
  expect(options[0].shouldBeFiltered).toBe(false);
320
322
  });
321
- it('should not filter any response (CheckboxGroup) when disableFilters is true', () => {
323
+ it('should keep responses after NONE filters have been removed during initialization', () => {
322
324
  const definition = {
323
325
  ...checkboxGroupDefinition,
324
326
  responses: [
@@ -331,26 +333,27 @@ describe('computeOptionsFromComponent', () => {
331
333
  ],
332
334
  } satisfies DeepTranslateExpression<LunaticComponentDefinition>;
333
335
 
334
- // ensure interpreted expression is false
335
- vi.spyOn(variables, 'run').mockImplementation(() => {
336
- return false;
337
- });
338
-
339
- const options = computeOptionsFromComponent(definition, {
336
+ const cleanedDefinition = removeConditionFilters(
337
+ {
338
+ variables: [],
339
+ components: [definition as any],
340
+ } as LunaticSource,
341
+ 'NONE'
342
+ )
343
+ .components[0] as unknown as DeepTranslateExpression<LunaticComponentDefinition>;
344
+
345
+ const options = computeOptionsFromComponent(cleanedDefinition, {
340
346
  variables,
341
347
  handleChanges: mockChange,
342
348
  pagerIteration: undefined,
343
349
  value: undefined,
344
350
  logger: mockLogger,
345
- disableFilters: true,
346
351
  });
347
352
 
348
- // Ensure the option is not filtered
349
353
  expect(options).toHaveLength(1);
350
- // the option should would have been filtered if we did not disable filters
351
- expect(options[0].shouldBeFiltered).toBe(true);
354
+ expect(options[0].shouldBeFiltered).toBe(false);
352
355
  });
353
- it('should not filter any option (Radio) when disableFilters is true', () => {
356
+ it('should keep options after NONE filters have been removed during initialization', () => {
354
357
  const definition = {
355
358
  ...radioDefinition,
356
359
  options: [
@@ -362,79 +365,99 @@ describe('computeOptionsFromComponent', () => {
362
365
  ],
363
366
  } as any as DeepTranslateExpression<LunaticComponentDefinition>;
364
367
 
365
- // ensure interpreted expression is false
366
- vi.spyOn(variables, 'run').mockImplementation(() => {
367
- return false;
368
- });
369
-
370
- const options = computeOptionsFromComponent(definition, {
368
+ const cleanedDefinition = removeConditionFilters(
369
+ {
370
+ variables: [],
371
+ components: [definition as any],
372
+ } as LunaticSource,
373
+ 'NONE'
374
+ )
375
+ .components[0] as unknown as DeepTranslateExpression<LunaticComponentDefinition>;
376
+
377
+ const options = computeOptionsFromComponent(cleanedDefinition, {
371
378
  variables,
372
379
  handleChanges: mockChange,
373
380
  pagerIteration: undefined,
374
381
  value: undefined,
375
382
  logger: mockLogger,
376
- disableFilters: true,
377
383
  });
378
384
 
379
- // Ensure the option is not filtered
380
385
  expect(options).toHaveLength(1);
381
- // the option should would have been filtered if we did not disable filters
382
- expect(options[0].shouldBeFiltered).toBe(true);
386
+ expect(options[0].shouldBeFiltered).toBe(false);
383
387
  });
384
- it('should set the response (CheckboxGroup) shouldBeFiltered=true when the parent component should be filtered', () => {
385
- const definition = {
386
- ...checkboxGroupDefinition,
387
- responses: [
388
- {
389
- label: 'Option 1',
390
- response: { name: 'O1' },
391
- id: 'id1',
392
- conditionFilter: { type: 'VTL', value: 'expression' },
393
- },
394
- ],
395
- } satisfies DeepTranslateExpression<LunaticComponentDefinition>;
396
-
397
- const options = computeOptionsFromComponent(definition, {
398
- variables,
399
- handleChanges: mockChange,
400
- pagerIteration: undefined,
401
- value: undefined,
402
- logger: mockLogger,
403
- disableFilters: true,
404
- shouldParentBeFiltered: true,
405
- });
406
388
 
407
- // Ensure the option is not filtered
408
- expect(options).toHaveLength(1);
409
- // the option would have been filtered if we did not disable filters because its parent would
410
- expect(options[0].shouldBeFiltered).toBe(true);
411
- });
412
- it('should set the option (Radio) shouldBeFiltered=true when the parent component should be filtered', () => {
389
+ it('should evaluate the external-only filters retained during initialization', () => {
413
390
  const definition = {
414
391
  ...radioDefinition,
415
392
  options: [
416
393
  {
417
- label: 'Option 1',
418
- value: 'id1',
419
- conditionFilter: { type: 'VTL', value: 'expression' },
394
+ label: 'External option',
395
+ value: 'external',
396
+ conditionFilter: {
397
+ type: 'VTL',
398
+ value: 'externalFilter',
399
+ variablesDependencies: {
400
+ EXTERNAL: ['CADRE'],
401
+ COLLECTED: [],
402
+ CALCULATED: [],
403
+ },
404
+ },
405
+ },
406
+ {
407
+ label: 'Collected option',
408
+ value: 'collected',
409
+ conditionFilter: {
410
+ type: 'VTL',
411
+ value: 'collectedFilter',
412
+ variablesDependencies: {
413
+ EXTERNAL: [],
414
+ COLLECTED: ['NAME'],
415
+ CALCULATED: [],
416
+ },
417
+ },
420
418
  },
421
419
  ],
422
420
  } as any as DeepTranslateExpression<LunaticComponentDefinition>;
423
421
 
424
- const options = computeOptionsFromComponent(definition, {
422
+ const cleanedDefinition = removeConditionFilters(
423
+ {
424
+ variables: [],
425
+ components: [definition as any],
426
+ } as LunaticSource,
427
+ 'EXTERNAL'
428
+ )
429
+ .components[0] as unknown as DeepTranslateExpression<LunaticComponentDefinition>;
430
+ expect((cleanedDefinition as any).options).toEqual([
431
+ {
432
+ label: 'External option',
433
+ value: 'external',
434
+ conditionFilter: {
435
+ type: 'VTL',
436
+ value: 'externalFilter',
437
+ variablesDependencies: {
438
+ EXTERNAL: ['CADRE'],
439
+ COLLECTED: [],
440
+ CALCULATED: [],
441
+ },
442
+ },
443
+ },
444
+ { label: 'Collected option', value: 'collected' },
445
+ ]);
446
+ vi.spyOn(variables, 'run').mockImplementation(
447
+ (expression) => expression !== 'externalFilter'
448
+ );
449
+
450
+ const options = computeOptionsFromComponent(cleanedDefinition, {
425
451
  variables,
426
452
  handleChanges: mockChange,
427
453
  pagerIteration: undefined,
428
454
  value: undefined,
429
455
  logger: mockLogger,
430
- disableFilters: true,
431
- shouldParentBeFiltered: true,
432
456
  });
433
457
 
434
- // Ensure the option is not filtered
435
- expect(options).toHaveLength(1);
436
- // the option would have been filtered if we did not disable filters because its parent would
437
- expect(options[0].shouldBeFiltered).toBe(true);
458
+ expect(
459
+ (options as InterpretedOption[]).map((option) => option.value)
460
+ ).toEqual(['collected']);
438
461
  });
439
462
  });
440
463
 
@@ -36,7 +36,6 @@ export function computeOptionsFromComponent(
36
36
  pagerIteration,
37
37
  value,
38
38
  logger,
39
- disableFilters,
40
39
  shouldParentBeFiltered,
41
40
  }: {
42
41
  variables: LunaticVariablesStore;
@@ -44,7 +43,6 @@ export function computeOptionsFromComponent(
44
43
  pagerIteration: LunaticState['pager']['iteration'];
45
44
  value: unknown;
46
45
  logger: LunaticLogger;
47
- disableFilters?: boolean;
48
46
  shouldParentBeFiltered?: boolean;
49
47
  }
50
48
  ) {
@@ -53,7 +51,7 @@ export function computeOptionsFromComponent(
53
51
  if (definition.componentType === 'CheckboxGroup') {
54
52
  return definition.responses
55
53
  .filter((response) => {
56
- if (disableFilters || !response.conditionFilter) {
54
+ if (!response.conditionFilter) {
57
55
  return true;
58
56
  }
59
57
  return !isFilteredOutOption(
@@ -118,11 +116,7 @@ export function computeOptionsFromComponent(
118
116
 
119
117
  return definition.options
120
118
  .filter((option) => {
121
- if (
122
- disableFilters ||
123
- !('conditionFilter' in option) ||
124
- !option.conditionFilter
125
- ) {
119
+ if (!('conditionFilter' in option) || !option.conditionFilter) {
126
120
  return true;
127
121
  }
128
122
  return !isFilteredOutOption(
@@ -6,6 +6,7 @@ import type {
6
6
  } from '../type';
7
7
  import { LunaticVariablesStore } from '../commons/variables/lunatic-variables-store';
8
8
  import { checkLoops, createMapPages } from '../commons';
9
+ import { removeConditionFilters } from '../commons/remove-condition-filters';
9
10
  import { getExpressionAsString, getExpressionType } from '../../utils/vtl';
10
11
  import { MD, VTL } from '../../utils/constants';
11
12
  import { MDLabel } from '../../components/shared/MDLabel/MDLabel';
@@ -35,7 +36,10 @@ const baseState = {
35
36
  overview: [],
36
37
  updateBindings: () => {},
37
38
  executeExpression: <T,>() => null as T,
38
- options: { disableFilters: false, disableFiltersDescription: true },
39
+ options: {
40
+ activeFilters: 'ALL',
41
+ disableFiltersDescription: true,
42
+ },
39
43
  } satisfies LunaticReducerState;
40
44
 
41
45
  const onChange = { current: () => {} };
@@ -47,8 +51,8 @@ export function reducerInitializer({
47
51
  initialPage = '1',
48
52
  lastReachedPage = undefined,
49
53
  withOverview = false,
50
- disableFilters = false,
51
54
  disableFiltersDescription = true,
55
+ activeFilters,
52
56
  getReferentiel,
53
57
  onVariableChange = onChange,
54
58
  logger = console.error,
@@ -59,18 +63,26 @@ export function reducerInitializer({
59
63
  initialPage?: LunaticOptions['initialPage'];
60
64
  lastReachedPage?: LunaticOptions['lastReachedPage'];
61
65
  withOverview?: LunaticOptions['withOverview'];
62
- disableFilters?: LunaticOptions['disableFilters'];
63
66
  disableFiltersDescription?: LunaticOptions['disableFiltersDescription'];
67
+ activeFilters?: LunaticOptions['activeFilters'];
64
68
  getReferentiel?: LunaticOptions['getReferentiel'];
65
69
  onVariableChange?: RefObject<LunaticOptions['onVariableChange']>;
66
70
  logger?: LunaticLogger;
67
71
  }): LunaticReducerState {
68
- const variables = LunaticVariablesStore.makeFromSource(source, data, {
72
+ const filteredSource =
73
+ activeFilters && activeFilters !== 'ALL'
74
+ ? removeConditionFilters(source, activeFilters)
75
+ : source;
76
+
77
+ // Cleaning is disabled if not all filters are active.
78
+ const disableCleaning =
79
+ activeFilters !== undefined && activeFilters !== 'ALL';
80
+ const variables = LunaticVariablesStore.makeFromSource(filteredSource, data, {
69
81
  autoCommit: false,
70
82
  changeHandler: onVariableChange,
71
- disableCleaning: disableFilters,
83
+ disableCleaning: disableCleaning,
72
84
  });
73
- const pages = checkLoops(createMapPages(source));
85
+ const pages = checkLoops(createMapPages(filteredSource));
74
86
 
75
87
  if (!source || !data) {
76
88
  return baseState;
@@ -151,10 +163,13 @@ export function reducerInitializer({
151
163
  pages,
152
164
  isInLoop: pager.subPage !== undefined,
153
165
  updatedAt: Date.now(),
154
- overview: withOverview ? buildOverview(source) : [],
166
+ overview: withOverview ? buildOverview(filteredSource) : [],
155
167
  updateBindings,
156
168
  executeExpression,
157
- options: { disableFilters, disableFiltersDescription },
169
+ options: {
170
+ activeFilters: activeFilters ?? 'ALL',
171
+ disableFiltersDescription,
172
+ },
158
173
  });
159
174
  }
160
175
 
@@ -59,6 +59,7 @@ export type LunaticError = Pick<
59
59
 
60
60
  export type VariableType = 'COLLECTED' | 'EXTERNAL' | 'CALCULATED';
61
61
  export type LunaticExpression = VTLExpression | VTLScalarExpression;
62
+ export type ActiveFilter = 'ALL' | 'EXTERNAL' | 'NONE';
62
63
 
63
64
  /**
64
65
  * Page numerotation.
@@ -142,22 +143,22 @@ export type LunaticReducerState = {
142
143
  pager: LunaticPager;
143
144
  pages: {
144
145
  [key: number | string]:
145
- | {
146
- components: LunaticSource['components'];
147
- isLoop: false;
148
- iterations?: undefined;
149
- loopDependencies?: undefined;
150
- subPages?: undefined;
151
- }
152
- | {
153
- components: LunaticSource['components'];
154
- isLoop: true;
155
- iterations: VTLScalarExpression;
156
- /** Variables affecting this loop. */
157
- loopDependencies: string[];
158
- /** List of child pages (ex: ['20.1', '20.2'] */
159
- subPages: string[];
160
- };
146
+ | {
147
+ components: LunaticSource['components'];
148
+ isLoop: false;
149
+ iterations?: undefined;
150
+ loopDependencies?: undefined;
151
+ subPages?: undefined;
152
+ }
153
+ | {
154
+ components: LunaticSource['components'];
155
+ isLoop: true;
156
+ iterations: VTLScalarExpression;
157
+ /** Variables affecting this loop. */
158
+ loopDependencies: string[];
159
+ /** List of child pages (ex: ['20.1', '20.2'] */
160
+ subPages: string[];
161
+ };
161
162
  };
162
163
  /** Run an expression using the value from the state. */
163
164
  executeExpression: <T = unknown>(
@@ -178,25 +179,28 @@ export type LunaticReducerState = {
178
179
  options: { iteration?: number[] }
179
180
  ) => unknown;
180
181
  options: {
181
- disableFilters?: boolean;
182
182
  disableFiltersDescription?: boolean;
183
+ activeFilters?: ActiveFilter;
183
184
  };
184
185
  };
185
186
 
186
187
  /** Specific behaviour options defined in the {@link useLunatic} hook. */
187
188
  export type LunaticOptions = {
188
- /**
189
- * Ignore filters.
190
- * @default false
191
- */
192
- disableFilters?: boolean;
193
-
194
189
  /**
195
190
  * Disable filters description.
196
191
  * @default true
197
192
  */
198
193
  disableFiltersDescription?: boolean;
199
194
 
195
+ /**
196
+ * Decides whether or not filters are active.
197
+ * - "ALL": Keep every filters
198
+ * - "EXTERNAL": Keep filter only if it is based on external variables only
199
+ * - "NONE": Ignore every filters
200
+ * @default 'ALL'
201
+ */
202
+ activeFilters?: ActiveFilter;
203
+
200
204
  /** Enable VTL and Markdown support. */
201
205
  features?: ('MD' | 'VTL')[];
202
206
  preferences?: ['COLLECTED'];
@@ -240,8 +240,8 @@ describe('use-lunatic()', () => {
240
240
  });
241
241
  });
242
242
 
243
- describe('disable filters', () => {
244
- const lunaticConfigurationWithoutDisableFilters = {
243
+ describe('active filters', () => {
244
+ const lunaticConfiguration = {
245
245
  management: false,
246
246
  activeControls: false,
247
247
  initialPage: '1' as PageTag,
@@ -258,29 +258,29 @@ describe('use-lunatic()', () => {
258
258
  useLunatic(
259
259
  sourceLogement,
260
260
  undefined,
261
- lunaticConfigurationWithoutDisableFilters
261
+ lunaticConfiguration
262
262
  )
263
263
  );
264
264
  act(() => result.current.goToPage({ page: '3' }));
265
265
  const currentPage = result.current.pageTag;
266
266
  expect(currentPage).not.toBe('3');
267
267
  });
268
- it('should filter out some components when false', function () {
268
+ it('should filter out some components when activeFilters is ALL', function () {
269
269
  const { result } = renderHook(() =>
270
270
  useLunatic(sourceLogement, undefined, {
271
- ...lunaticConfigurationWithoutDisableFilters,
272
- disableFilters: false,
271
+ ...lunaticConfiguration,
272
+ activeFilters: 'ALL',
273
273
  })
274
274
  );
275
275
  act(() => result.current.goToPage({ page: '3' }));
276
276
  const currentPage = result.current.pageTag;
277
277
  expect(currentPage).not.toBe('3');
278
278
  });
279
- it('should not filter any component when true', function () {
279
+ it('should not filter any component when activeFilters is NONE', function () {
280
280
  const { result } = renderHook(() =>
281
281
  useLunatic(sourceLogement, undefined, {
282
- ...lunaticConfigurationWithoutDisableFilters,
283
- disableFilters: true,
282
+ ...lunaticConfiguration,
283
+ activeFilters: 'NONE',
284
284
  })
285
285
  );
286
286
  act(() => result.current.goToPage({ page: '3' }));