@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,275 @@
1
+ {
2
+ "$schema": "../../../../lunatic-schema.json",
3
+ "id": "activefilters",
4
+ "label": {
5
+ "type": "VTL|MD",
6
+ "value": "Story filters with external condition"
7
+ },
8
+ "modele": "ACTIVEFILTERS",
9
+ "maxPage": "5",
10
+ "resizing": {},
11
+ "variables": [
12
+ {
13
+ "name": "CADRE",
14
+ "variableType": "EXTERNAL",
15
+ "value": "OUI"
16
+ },
17
+ {
18
+ "name": "CADRE_ANSWER",
19
+ "values": {
20
+ "COLLECTED": null
21
+ },
22
+ "dimension": 0,
23
+ "variableType": "COLLECTED"
24
+ },
25
+ {
26
+ "name": "NAME",
27
+ "values": {
28
+ "COLLECTED": null
29
+ },
30
+ "dimension": 0,
31
+ "variableType": "COLLECTED"
32
+ },
33
+ {
34
+ "name": "AGE",
35
+ "values": {
36
+ "COLLECTED": null
37
+ },
38
+ "dimension": 0,
39
+ "variableType": "COLLECTED"
40
+ },
41
+ {
42
+ "name": "APPLE",
43
+ "values": {
44
+ "COLLECTED": null
45
+ },
46
+ "dimension": 0,
47
+ "variableType": "COLLECTED"
48
+ },
49
+ {
50
+ "name": "HAS_NAME",
51
+ "dimension": 0,
52
+ "expression": {
53
+ "type": "VTL",
54
+ "value": "not(isnull(NAME))"
55
+ },
56
+ "variableType": "CALCULATED"
57
+ }
58
+ ],
59
+ "components": [
60
+ {
61
+ "id": "seq-situation",
62
+ "page": "1",
63
+ "label": {
64
+ "type": "VTL",
65
+ "value": "\"I - \" || \"Votre situation\""
66
+ },
67
+ "description": {
68
+ "type": "VTL|MD",
69
+ "value": "\"The next question will be filtered if the external variable CADRE=NON, its filter is kept when activeFilters is EXTERNAL\""
70
+ },
71
+ "componentType": "Sequence"
72
+ },
73
+ {
74
+ "id": "question-cadre",
75
+ "page": "2",
76
+ "label": {
77
+ "type": "VTL|MD",
78
+ "value": "\"1\\. \" || \"Question filtrée si CADRE = NON \""
79
+ },
80
+ "components": [
81
+ {
82
+ "id": "radio-cadre",
83
+ "page": "2",
84
+ "options": [
85
+ {
86
+ "label": {
87
+ "type": "VTL|MD",
88
+ "value": "\"Oui\""
89
+ },
90
+ "value": "1"
91
+ },
92
+ {
93
+ "label": {
94
+ "type": "VTL|MD",
95
+ "value": "\"Non\""
96
+ },
97
+ "value": "2"
98
+ }
99
+ ],
100
+ "response": {
101
+ "name": "CADRE_ANSWER"
102
+ },
103
+ "isMandatory": false,
104
+ "orientation": "vertical",
105
+ "componentType": "Radio"
106
+ }
107
+ ],
108
+ "componentType": "Question",
109
+ "conditionFilter": {
110
+ "type": "VTL",
111
+ "value": "CADRE = \"OUI\"",
112
+ "variablesDependencies": {
113
+ "COLLECTED": [],
114
+ "EXTERNAL": ["CADRE"],
115
+ "CALCULATED": []
116
+ }
117
+ }
118
+ },
119
+ {
120
+ "id": "question-name",
121
+ "page": "3",
122
+ "label": {
123
+ "type": "VTL|MD",
124
+ "value": "\"2\\. \" || \"What is your name ?\""
125
+ },
126
+ "components": [
127
+ {
128
+ "id": "input-name",
129
+ "page": "3",
130
+ "response": {
131
+ "name": "NAME"
132
+ },
133
+ "isMandatory": false,
134
+ "maxLength": 249,
135
+ "componentType": "Input"
136
+ }
137
+ ],
138
+ "declarations": [
139
+ {
140
+ "id": "m48q03bn",
141
+ "label": {
142
+ "type": "VTL|MD",
143
+ "value": "\"Age filtered if name is not Paul, modality filtered if name is null\""
144
+ },
145
+ "position": "AFTER_QUESTION_TEXT",
146
+ "declarationType": "HELP"
147
+ }
148
+ ],
149
+ "componentType": "Question",
150
+ "conditionFilter": {
151
+ "type": "VTL",
152
+ "value": "true",
153
+ "variablesDependencies": {
154
+ "COLLECTED": [],
155
+ "EXTERNAL": [],
156
+ "CALCULATED": []
157
+ }
158
+ }
159
+ },
160
+ {
161
+ "id": "question-age",
162
+ "page": "4",
163
+ "label": {
164
+ "type": "VTL|MD",
165
+ "value": "\"3\\. \" || \"How old are you ?\""
166
+ },
167
+ "components": [
168
+ {
169
+ "id": "input-age",
170
+ "page": "4",
171
+ "max": 200,
172
+ "min": 1,
173
+ "response": {
174
+ "name": "AGE"
175
+ },
176
+ "isMandatory": false,
177
+ "decimals": 0,
178
+ "componentType": "InputNumber"
179
+ }
180
+ ],
181
+ "declarations": [
182
+ {
183
+ "id": "declaration-age",
184
+ "label": {
185
+ "type": "VTL|MD",
186
+ "value": "\"Filtered is NAME is not Paul\""
187
+ },
188
+ "position": "AFTER_QUESTION_TEXT",
189
+ "declarationType": "HELP"
190
+ }
191
+ ],
192
+ "componentType": "Question",
193
+ "conditionFilter": {
194
+ "type": "VTL",
195
+ "value": "NAME = \"Paul\"",
196
+ "variablesDependencies": {
197
+ "COLLECTED": ["NAME"],
198
+ "EXTERNAL": [],
199
+ "CALCULATED": []
200
+ }
201
+ }
202
+ },
203
+ {
204
+ "id": "question-apple",
205
+ "page": "5",
206
+ "label": {
207
+ "type": "VTL|MD",
208
+ "value": "\"4\\. \" || \"Do you like apple ?\""
209
+ },
210
+ "declarations": [
211
+ {
212
+ "id": "declaration-apple",
213
+ "label": {
214
+ "type": "VTL|MD",
215
+ "value": "\"Filtered if there is no name provided\""
216
+ },
217
+ "position": "AFTER_QUESTION_TEXT",
218
+ "declarationType": "HELP"
219
+ }
220
+ ],
221
+ "components": [
222
+ {
223
+ "id": "radio-apple",
224
+ "page": "5",
225
+ "options": [
226
+ {
227
+ "label": {
228
+ "type": "VTL|MD",
229
+ "value": "\"yes\""
230
+ },
231
+ "value": "1"
232
+ },
233
+ {
234
+ "label": {
235
+ "type": "VTL|MD",
236
+ "value": "\"no\""
237
+ },
238
+ "value": "2",
239
+ "conditionFilter": {
240
+ "type": "VTL",
241
+ "value": "HAS_NAME",
242
+ "variablesDependencies": {
243
+ "COLLECTED": [],
244
+ "EXTERNAL": [],
245
+ "CALCULATED": ["HAS_NAME"]
246
+ }
247
+ }
248
+ }
249
+ ],
250
+ "response": {
251
+ "name": "APPLE"
252
+ },
253
+ "isMandatory": false,
254
+ "orientation": "vertical",
255
+ "componentType": "Radio"
256
+ }
257
+ ],
258
+ "componentType": "Question",
259
+ "conditionFilter": {
260
+ "type": "VTL",
261
+ "value": "true",
262
+ "variablesDependencies": {
263
+ "COLLECTED": [],
264
+ "EXTERNAL": [],
265
+ "CALCULATED": []
266
+ }
267
+ }
268
+ }
269
+ ],
270
+ "pagination": "question",
271
+ "componentType": "Questionnaire",
272
+ "enoCoreVersion": "3.29.3-SNAPSHOT",
273
+ "generatingDate": "12-10-2026",
274
+ "lunaticModelVersion": "6.1.0"
275
+ }
@@ -21,7 +21,7 @@ import { Logger } from '../../utils/logger';
21
21
  import { OrchestratorOverview } from './OrchestratorOverview';
22
22
  import { SchemaValidator } from './SchemaValidator';
23
23
  import { OrchestratorData } from './OrchestratorData';
24
- import type { PageTag } from '../../use-lunatic/type';
24
+ import type { ActiveFilter, PageTag } from '../../use-lunatic/type';
25
25
  import type { IndexEntry } from '../../utils/search/SearchInterface';
26
26
  import { OrchestratorSidebar } from './OrchestratorSidebar';
27
27
  import type { Meta, StoryObj } from '@storybook/react';
@@ -49,7 +49,7 @@ type Props = {
49
49
  // Preload suggester data at the start of the form
50
50
  autoSuggesterLoading: boolean;
51
51
  disableFiltersDescription?: boolean;
52
- disableFilters?: boolean;
52
+ activeFilters?: ActiveFilter;
53
53
  management?: boolean;
54
54
  shortcut?: boolean;
55
55
  activeControls?: boolean;
@@ -66,8 +66,8 @@ function OrchestratorForStories(props: Readonly<Props>) {
66
66
  const {
67
67
  source,
68
68
  data,
69
- disableFilters,
70
69
  disableFiltersDescription,
70
+ activeFilters,
71
71
  management,
72
72
  shortcut,
73
73
  activeControls,
@@ -107,8 +107,8 @@ function OrchestratorForStories(props: Readonly<Props>) {
107
107
  getMultimode,
108
108
  } = useLunatic(source, data, {
109
109
  initialPage,
110
- disableFilters,
111
110
  disableFiltersDescription,
111
+ activeFilters,
112
112
  onChange: onLogChange,
113
113
  getReferentiel,
114
114
  management,
@@ -305,10 +305,11 @@ export const OrchestratorMeta: Meta<typeof Orchestrator> = {
305
305
  autoSuggesterLoading: { table: { disable: true } },
306
306
  missingStrategy: { table: { disable: true } },
307
307
  extraTabs: { table: { disable: true } },
308
+ activeFilters: { control: 'select', options: ['ALL', 'EXTERNAL', 'NONE'] },
308
309
  },
309
310
  args: {
310
- disableFilters: false,
311
311
  disableFiltersDescription: false,
312
+ activeFilters: 'ALL',
312
313
  management: false,
313
314
  shortcut: false,
314
315
  activeControls: true,
@@ -390,8 +390,17 @@ export type VTLExpression = {
390
390
  * Variables used in the expression
391
391
  */
392
392
  bindingDependencies?: string[];
393
+ variablesDependencies?: VariablesDependencies;
393
394
  type: 'VTL' | 'VTL|MD' | 'TXT';
394
395
  };
396
+ /**
397
+ * Variables grouped by type (collected, external and calculated)
398
+ */
399
+ export type VariablesDependencies = {
400
+ COLLECTED?: string[];
401
+ EXTERNAL?: string[];
402
+ CALCULATED?: string[];
403
+ };
395
404
  export type ComponentDefinitionBase = {
396
405
  label?: VTLExpression;
397
406
  description?: VTLExpression;
@@ -428,6 +437,7 @@ export type VTLScalarExpression = {
428
437
  * Variables used in the expression
429
438
  */
430
439
  bindingDependencies?: string[];
440
+ variablesDependencies?: VariablesDependencies;
431
441
  type: 'VTL';
432
442
  shapeFrom?: string;
433
443
  };
@@ -378,7 +378,7 @@ describe('fillComponents', () => {
378
378
  expect(filledComponents[1].componentType).toBe('Input');
379
379
  });
380
380
 
381
- it('should filter out components with conditionFilter=false when disableFilters is false or undefined', () => {
381
+ it('should filter out components with conditionFilter=false by default', () => {
382
382
  const components = [
383
383
  {
384
384
  id: 'input1',
@@ -407,7 +407,6 @@ describe('fillComponents', () => {
407
407
  const mockState = {
408
408
  ...defaultMockState,
409
409
  variables: mockVariables,
410
- disableFilters: false,
411
410
  };
412
411
 
413
412
  const filledComponents = fillComponents(
@@ -418,7 +417,7 @@ describe('fillComponents', () => {
418
417
  expect(filledComponents).toHaveLength(0);
419
418
  });
420
419
 
421
- it('should not filter out components with conditionFilter=false when disableFilters is true', () => {
420
+ it('should not filter out components with conditionFilter=false when activeFilters is NONE', () => {
422
421
  const components = [
423
422
  {
424
423
  id: 'input1',
@@ -447,7 +446,7 @@ describe('fillComponents', () => {
447
446
  const mockState = {
448
447
  ...defaultMockState,
449
448
  variables: mockVariables,
450
- disableFilters: true,
449
+ activeFilters: 'NONE',
451
450
  };
452
451
 
453
452
  const filledComponents = fillComponents(
@@ -527,7 +526,7 @@ describe('fillComponents', () => {
527
526
  expect(radio.options[1].label).toBe('"No"');
528
527
  });
529
528
 
530
- it('should never transform components into Text when disableFilters is true', () => {
529
+ it('should never transform components into Text when activeFilters is NONE', () => {
531
530
  const components = [
532
531
  {
533
532
  id: 'input1',
@@ -562,7 +561,7 @@ describe('fillComponents', () => {
562
561
 
563
562
  const mockState = {
564
563
  ...defaultMockState,
565
- disableFilters: true,
564
+ activeFilters: 'NONE',
566
565
  variables: mockVariables,
567
566
  };
568
567
 
@@ -627,7 +626,7 @@ describe('fillComponents', () => {
627
626
 
628
627
  const mockState = {
629
628
  ...defaultMockState,
630
- disableFilters: true,
629
+ activeFilters: 'NONE',
631
630
  variables: mockVariables,
632
631
  };
633
632
 
@@ -681,7 +680,7 @@ describe('fillComponents', () => {
681
680
 
682
681
  const mockState = {
683
682
  ...defaultMockState,
684
- disableFilters: true,
683
+ activeFilters: 'NONE',
685
684
  variables: mockVariables,
686
685
  };
687
686
 
@@ -739,7 +738,6 @@ describe('fillComponents', () => {
739
738
 
740
739
  const mockState = {
741
740
  ...defaultMockState,
742
- disableFilters: true,
743
741
  variables: mockVariables,
744
742
  };
745
743
 
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ ActiveFilter,
2
3
  LunaticChangesHandler,
3
4
  LunaticComponentDefinition,
4
5
  LunaticOptions,
@@ -16,8 +17,8 @@ import { LunaticLogger } from '../../logger/type';
16
17
  import { VTLScalarExpression } from '../../../type.source';
17
18
 
18
19
  export type FillComponentArgs = {
19
- disableFilters?: boolean;
20
20
  disableFiltersDescription?: true;
21
+ activeFilters?: ActiveFilter;
21
22
  handleChanges: LunaticChangesHandler;
22
23
  executeExpression: LunaticReducerState['executeExpression'];
23
24
  goToPage: LunaticState['goToPage'];
@@ -86,7 +87,6 @@ export const fillComponent = (
86
87
  pagerIteration: state.pager.iteration,
87
88
  value,
88
89
  logger: state.logger,
89
- disableFilters: state.disableFilters,
90
90
  shouldParentBeFiltered: shouldBeFiltered,
91
91
  }),
92
92
  ...getComponentTypeProps(interpretedProps, state),
@@ -118,7 +118,7 @@ export function fillComponents(
118
118
  ];
119
119
  });
120
120
 
121
- if (state.disableFilters) {
121
+ if (state.activeFilters === 'NONE') {
122
122
  return filledComponents;
123
123
  }
124
124
 
@@ -128,11 +128,11 @@ export function fillComponents(
128
128
  (filledComponent.conditionFilter ?? true)
129
129
  ? filledComponent
130
130
  : // Replace the component by an empty text component
131
- ({
132
- ...filledComponent,
133
- label: '',
134
- componentType: 'Text',
135
- } as LunaticComponentProps)
131
+ ({
132
+ ...filledComponent,
133
+ label: '',
134
+ componentType: 'Text',
135
+ } as LunaticComponentProps)
136
136
  );
137
137
  }
138
138
 
@@ -1,6 +1,10 @@
1
1
  export { default as checkLoops } from './pagination/check-loops';
2
2
  export { default as createMapPages } from './pagination/create-map-pages';
3
3
  export { default as executeConditionFilter } from './execute-condition-filter';
4
+ export {
5
+ removeConditionFilters,
6
+ shouldKeepFilter,
7
+ } from './remove-condition-filters';
4
8
  export { default as getCompatibleVTLExpression } from './get-compatible-vtl-expression';
5
9
  export { default as isFirstLastPage } from './pagination/is-First-last-page';
6
10
  export {
@@ -94,7 +94,7 @@ export function isPageEmpty(state: LunaticReducerState): boolean {
94
94
  const components = getComponentsFromState(state, true);
95
95
 
96
96
  const visibleComponents = components.filter((component) => {
97
- if (options.disableFilters) {
97
+ if (options.activeFilters === 'NONE') {
98
98
  return true;
99
99
  }
100
100
 
@@ -0,0 +1,131 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import type { LunaticSource } from '../type';
3
+ import { removeConditionFilters } from './remove-condition-filters';
4
+
5
+ const externalFilter = {
6
+ type: 'VTL',
7
+ value: 'CADRE = "OUI"',
8
+ variablesDependencies: { EXTERNAL: ['CADRE'], COLLECTED: [], CALCULATED: [] },
9
+ };
10
+
11
+ const collectedFilter = {
12
+ type: 'VTL',
13
+ value: 'NAME = "Paul"',
14
+ variablesDependencies: { EXTERNAL: [], COLLECTED: ['NAME'], CALCULATED: [] },
15
+ };
16
+
17
+ const calculatedFilter = {
18
+ type: 'VTL',
19
+ value: 'HAS_NAME',
20
+ variablesDependencies: {
21
+ EXTERNAL: [],
22
+ COLLECTED: [],
23
+ CALCULATED: ['HAS_NAME'],
24
+ },
25
+ };
26
+
27
+ const source = {
28
+ variables: [],
29
+ components: [
30
+ {
31
+ id: 'outside-loop',
32
+ page: '1',
33
+ componentType: 'Question',
34
+ conditionFilter: externalFilter,
35
+ components: [
36
+ {
37
+ id: 'radio',
38
+ componentType: 'Radio',
39
+ response: { name: 'ANSWER' },
40
+ options: [
41
+ { value: 'yes', label: 'Yes', conditionFilter: externalFilter },
42
+ { value: 'no', label: 'No', conditionFilter: collectedFilter },
43
+ ],
44
+ },
45
+ {
46
+ id: 'checkboxes',
47
+ componentType: 'CheckboxGroup',
48
+ responses: [
49
+ {
50
+ id: 'external-response',
51
+ label: 'External',
52
+ response: { name: 'EXTERNAL_RESPONSE' },
53
+ conditionFilter: externalFilter,
54
+ },
55
+ {
56
+ id: 'calculated-response',
57
+ label: 'Calculated',
58
+ response: { name: 'CALCULATED_RESPONSE' },
59
+ conditionFilter: calculatedFilter,
60
+ },
61
+ ],
62
+ },
63
+ ],
64
+ },
65
+ {
66
+ id: 'loop',
67
+ page: '2',
68
+ componentType: 'Loop',
69
+ paginatedLoop: false,
70
+ iterations: { type: 'VTL', value: '2' },
71
+ conditionFilter: collectedFilter,
72
+ components: [
73
+ {
74
+ id: 'loop-occurrence',
75
+ componentType: 'Question',
76
+ conditionFilter: collectedFilter,
77
+ components: [],
78
+ },
79
+ ],
80
+ },
81
+ ],
82
+ } as unknown as LunaticSource;
83
+
84
+ describe('removeConditionFilters', () => {
85
+ it('keeps only external-only filters throughout components, options, and responses', () => {
86
+ const cleaned = removeConditionFilters(source, 'EXTERNAL') as any;
87
+
88
+ expect(cleaned.components[0].conditionFilter).toEqual(externalFilter);
89
+ expect(cleaned.components[0].components[0].options).toEqual([
90
+ { value: 'yes', label: 'Yes', conditionFilter: externalFilter },
91
+ { value: 'no', label: 'No' },
92
+ ]);
93
+ expect(cleaned.components[0].components[1].responses).toEqual([
94
+ {
95
+ id: 'external-response',
96
+ label: 'External',
97
+ response: { name: 'EXTERNAL_RESPONSE' },
98
+ conditionFilter: externalFilter,
99
+ },
100
+ {
101
+ id: 'calculated-response',
102
+ label: 'Calculated',
103
+ response: { name: 'CALCULATED_RESPONSE' },
104
+ },
105
+ ]);
106
+ expect(cleaned.components[1].conditionFilter).toBeUndefined();
107
+ expect(cleaned.components[1].components[0].conditionFilter).toBeUndefined();
108
+ });
109
+
110
+ it('removes filters recursively when activeFilters is NONE and preserves the source for ALL', () => {
111
+ const withoutFilters = removeConditionFilters(source, 'NONE') as any;
112
+
113
+ expect(withoutFilters.components[0].conditionFilter).toBeUndefined();
114
+ expect(
115
+ withoutFilters.components[0].components[0].options.every(
116
+ (option: { conditionFilter?: unknown }) =>
117
+ !('conditionFilter' in option)
118
+ )
119
+ ).toBe(true);
120
+ expect(
121
+ withoutFilters.components[0].components[1].responses.every(
122
+ (response: { conditionFilter?: unknown }) =>
123
+ !('conditionFilter' in response)
124
+ )
125
+ ).toBe(true);
126
+ expect(
127
+ withoutFilters.components[1].components[0].conditionFilter
128
+ ).toBeUndefined();
129
+ expect(removeConditionFilters(source, 'ALL')).toBe(source);
130
+ });
131
+ });