@inseefr/lunatic 3.4.20 → 3.4.21

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 (46) hide show
  1. package/components/Loop/Loop.js +1 -1
  2. package/components/Loop/Loop.js.map +1 -1
  3. package/components/Loop/Loop.spec.d.ts +1 -0
  4. package/components/Loop/Loop.spec.js +39 -0
  5. package/components/Loop/Loop.spec.js.map +1 -0
  6. package/esm/components/Loop/Loop.js +1 -1
  7. package/esm/components/Loop/Loop.js.map +1 -1
  8. package/esm/components/Loop/Loop.spec.d.ts +1 -0
  9. package/esm/components/Loop/Loop.spec.js +37 -0
  10. package/esm/components/Loop/Loop.spec.js.map +1 -0
  11. package/esm/type.source.js +0 -1
  12. package/esm/type.source.js.map +1 -1
  13. package/esm/use-lunatic/commons/variables/lunatic-variables-store.d.ts +1 -1
  14. package/esm/use-lunatic/commons/variables/lunatic-variables-store.js +4 -2
  15. package/esm/use-lunatic/commons/variables/lunatic-variables-store.js.map +1 -1
  16. package/esm/use-lunatic/commons/variables/lunatic-variables-store.spec.js +71 -0
  17. package/esm/use-lunatic/commons/variables/lunatic-variables-store.spec.js.map +1 -1
  18. package/esm/use-lunatic/props/propValue.js +3 -2
  19. package/esm/use-lunatic/props/propValue.js.map +1 -1
  20. package/esm/use-lunatic/props/propValue.spec.js +48 -0
  21. package/esm/use-lunatic/props/propValue.spec.js.map +1 -1
  22. package/esm/use-lunatic/reducer/reducerInitializer.js +1 -1
  23. package/esm/use-lunatic/reducer/reducerInitializer.js.map +1 -1
  24. package/package.json +8 -1
  25. package/src/components/Loop/Loop.spec.tsx +77 -0
  26. package/src/components/Loop/Loop.tsx +1 -1
  27. package/src/type.source.ts +0 -1
  28. package/src/use-lunatic/commons/variables/lunatic-variables-store.spec.ts +79 -0
  29. package/src/use-lunatic/commons/variables/lunatic-variables-store.ts +5 -2
  30. package/src/use-lunatic/props/propValue.spec.ts +52 -0
  31. package/src/use-lunatic/props/propValue.ts +5 -2
  32. package/src/use-lunatic/reducer/reducerInitializer.tsx +2 -1
  33. package/tsconfig.build.tsbuildinfo +1 -1
  34. package/type.source.js +0 -1
  35. package/type.source.js.map +1 -1
  36. package/use-lunatic/commons/variables/lunatic-variables-store.d.ts +1 -1
  37. package/use-lunatic/commons/variables/lunatic-variables-store.js +4 -2
  38. package/use-lunatic/commons/variables/lunatic-variables-store.js.map +1 -1
  39. package/use-lunatic/commons/variables/lunatic-variables-store.spec.js +94 -0
  40. package/use-lunatic/commons/variables/lunatic-variables-store.spec.js.map +1 -1
  41. package/use-lunatic/props/propValue.js +3 -2
  42. package/use-lunatic/props/propValue.js.map +1 -1
  43. package/use-lunatic/props/propValue.spec.js +48 -0
  44. package/use-lunatic/props/propValue.spec.js.map +1 -1
  45. package/use-lunatic/reducer/reducerInitializer.js +1 -1
  46. package/use-lunatic/reducer/reducerInitializer.js.map +1 -1
@@ -0,0 +1,77 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import { Loop } from './Loop';
4
+ import type { LunaticComponentProps } from '../type';
5
+
6
+ describe('Loop', () => {
7
+ const mockOnChange = vi.fn();
8
+
9
+ beforeEach(() => {
10
+ mockOnChange.mockClear();
11
+ });
12
+
13
+ const getComponents = (iteration: number) => [
14
+ {
15
+ componentType: 'Input',
16
+ maxLength: 249,
17
+ id: 'nameId',
18
+ response: {
19
+ name: 'name',
20
+ },
21
+ iteration: iteration,
22
+ value: 'Jonathan Doe',
23
+ } as LunaticComponentProps<'Input'>,
24
+ ];
25
+
26
+ it('render the right number of lines by default', () => {
27
+ render(
28
+ <Loop
29
+ value={{ name: ['John Doe', 'Jane Doe'] }}
30
+ handleChanges={mockOnChange}
31
+ label="Ceci est un test"
32
+ id="table"
33
+ lines={{ min: 4, max: 10 }}
34
+ iterations={2}
35
+ getComponents={getComponents}
36
+ executeExpression={() => null as any}
37
+ />
38
+ );
39
+ expect(screen.getAllByRole('textbox')).toHaveLength(4);
40
+ });
41
+
42
+ it('disables the remove row button when the minimum number of rows is reached', () => {
43
+ render(
44
+ <Loop
45
+ value={{ name: ['John Doe', 'Jane Doe', 'Alice', 'Bob'] }}
46
+ handleChanges={mockOnChange}
47
+ label="Ceci est un test"
48
+ id="table"
49
+ lines={{ min: 4, max: 10 }}
50
+ iterations={4}
51
+ getComponents={getComponents}
52
+ executeExpression={() => null as any}
53
+ />
54
+ );
55
+
56
+ const Button = screen.getByRole('button', { name: /remove/i });
57
+ expect(Button).toBeDisabled();
58
+ });
59
+
60
+ it('enables the add row button when the maximum number of rows is not reached', () => {
61
+ render(
62
+ <Loop
63
+ value={{ name: ['John Doe', 'Jane Doe', 'Alice'] }}
64
+ handleChanges={mockOnChange}
65
+ label="Ceci est un test"
66
+ id="table"
67
+ lines={{ min: 2, max: 10 }}
68
+ iterations={3}
69
+ getComponents={getComponents}
70
+ executeExpression={() => null as any}
71
+ />
72
+ );
73
+
74
+ const Button = screen.getByRole('button', { name: /add/i });
75
+ expect(Button).not.toBeDisabled();
76
+ });
77
+ });
@@ -60,7 +60,7 @@ export function Loop({
60
60
  {...props}
61
61
  errors={getComponentErrors(errors, props.id)}
62
62
  addRow={nbRows === max ? undefined : addRow}
63
- removeRow={nbRows === 1 ? undefined : removeRow}
63
+ removeRow={nbRows === 1 || nbRows === min ? undefined : removeRow}
64
64
  canControlRows={min !== max && Number.isFinite(max)}
65
65
  >
66
66
  {times(nbRows, (n) => (
@@ -1,4 +1,3 @@
1
- /* eslint-disable */
2
1
  /**
3
2
  * This file was automatically generated by json-schema-to-typescript.
4
3
  * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
@@ -1,5 +1,6 @@
1
1
  import { beforeEach, describe, expect, it, vi } from 'vitest';
2
2
  import { cleaningBehaviour } from './behaviours/cleaning-behaviour';
3
+ import * as cleaningModule from './behaviours/cleaning-behaviour';
3
4
  import { missingBehaviour } from './behaviours/missing-behaviour';
4
5
  import { resizingBehaviour } from './behaviours/resizing-behaviour';
5
6
  import { LunaticVariablesStore } from './lunatic-variables-store';
@@ -480,5 +481,83 @@ describe('lunatic-variables-store', () => {
480
481
  store.set('NOM', 'Doe');
481
482
  expect(store.get('PRENOM')).toEqual('John');
482
483
  });
484
+ it('should enable cleaning when disableCleaning = false', () => {
485
+ const cleaningSpy = vi.spyOn(cleaningModule, 'cleaningBehaviour');
486
+ LunaticVariablesStore.makeFromSource(
487
+ {
488
+ components: [],
489
+ variables: [
490
+ {
491
+ name: 'PRENOM',
492
+ values: {
493
+ COLLECTED: 'John',
494
+ },
495
+ variableType: 'COLLECTED',
496
+ },
497
+ {
498
+ name: 'NOM',
499
+ values: {
500
+ COLLECTED: '',
501
+ },
502
+ variableType: 'COLLECTED',
503
+ },
504
+ ],
505
+ cleaning: {
506
+ NOM: {
507
+ PRENOM: 'false',
508
+ },
509
+ },
510
+ },
511
+ {
512
+ COLLECTED: {
513
+ PRENOM: {
514
+ COLLECTED: 'Jane',
515
+ },
516
+ },
517
+ },
518
+ { current: () => {} },
519
+ false // enable cleaning
520
+ );
521
+ expect(cleaningSpy).toHaveBeenCalled();
522
+ });
523
+ it('should disable cleaning when disableCleaning = true', () => {
524
+ const cleaningSpy = vi.spyOn(cleaningModule, 'cleaningBehaviour');
525
+ LunaticVariablesStore.makeFromSource(
526
+ {
527
+ components: [],
528
+ variables: [
529
+ {
530
+ name: 'PRENOM',
531
+ values: {
532
+ COLLECTED: 'John',
533
+ },
534
+ variableType: 'COLLECTED',
535
+ },
536
+ {
537
+ name: 'NOM',
538
+ values: {
539
+ COLLECTED: '',
540
+ },
541
+ variableType: 'COLLECTED',
542
+ },
543
+ ],
544
+ cleaning: {
545
+ NOM: {
546
+ PRENOM: 'false',
547
+ },
548
+ },
549
+ },
550
+ {
551
+ COLLECTED: {
552
+ PRENOM: {
553
+ COLLECTED: 'Jane',
554
+ },
555
+ },
556
+ },
557
+ { current: () => {} },
558
+ true // disable cleaning
559
+ );
560
+ expect(cleaningSpy).not.toHaveBeenCalled();
561
+ });
483
562
  });
484
563
  });
@@ -54,7 +54,8 @@ export class LunaticVariablesStore {
54
54
  public static makeFromSource(
55
55
  source: LunaticSource,
56
56
  data: LunaticData,
57
- changeHandler: RefObject<LunaticOptions['onVariableChange']>
57
+ changeHandler: RefObject<LunaticOptions['onVariableChange']>,
58
+ disableCleaning?: boolean
58
59
  ) {
59
60
  const store = new LunaticVariablesStore();
60
61
  if (!source.variables) {
@@ -90,7 +91,9 @@ export class LunaticVariablesStore {
90
91
  }
91
92
  }
92
93
  store.on('change', (e) => changeHandler?.current?.(e.detail));
93
- cleaningBehaviour(store, source.cleaning, sourceValues);
94
+ if (!disableCleaning) {
95
+ cleaningBehaviour(store, source.cleaning, sourceValues);
96
+ }
94
97
  resizingBehaviour(store, source.resizing);
95
98
  missingBehaviour(store, source.missingBlock);
96
99
  return store;
@@ -115,4 +115,56 @@ describe('fillComponentValue', () => {
115
115
  });
116
116
  });
117
117
  });
118
+
119
+ describe('RosterForLoop', () => {
120
+ const rosterForLoopComponent = {
121
+ id: 'loop-prenom',
122
+ componentType: 'RosterForLoop',
123
+ bindingDependencies: ['PRENOM'],
124
+ lines: {
125
+ min: {
126
+ value: '1',
127
+ type: 'VTL',
128
+ },
129
+ max: {
130
+ value: '10',
131
+ type: 'VTL',
132
+ },
133
+ },
134
+ page: '1',
135
+ components: [
136
+ {
137
+ componentType: 'Input',
138
+ label: {
139
+ value: '"Prénom"',
140
+ type: 'VTL|MD',
141
+ },
142
+ conditionFilter: {
143
+ value: 'true',
144
+ type: 'VTL',
145
+ },
146
+ maxLength: 30,
147
+ bindingDependencies: ['PRENOM'],
148
+ id: 'prenom',
149
+ response: {
150
+ name: 'PRENOM',
151
+ },
152
+ },
153
+ ],
154
+ } as any as LunaticComponentDefinition<'RosterForLoop'>;
155
+
156
+ it('should correctly extract values from nested responses as arrays', () => {
157
+ const values = {
158
+ PRENOM: ['Alice', 'Bob', 'Charlie'],
159
+ };
160
+
161
+ expectFilledComponent(rosterForLoopComponent, values).toEqual(values);
162
+ });
163
+
164
+ it('should return null for missing responses', () => {
165
+ expectFilledComponent(rosterForLoopComponent).toEqual({
166
+ PRENOM: null,
167
+ });
168
+ });
169
+ });
118
170
  });
@@ -27,8 +27,11 @@ export function getValueProp(
27
27
  if (hasResponse(component)) {
28
28
  return args.variables.get(component.response.name, iteration);
29
29
  }
30
- // For loop, value will be a map of child component values
31
- if (component.componentType === 'Loop') {
30
+ // For Loop and RosterForLoop, value will be a map of child component values
31
+ if (
32
+ component.componentType === 'Loop' ||
33
+ component.componentType === 'RosterForLoop'
34
+ ) {
32
35
  return getChildResponseValues(component.components, args.variables);
33
36
  }
34
37
  return null;
@@ -64,7 +64,8 @@ export function reducerInitializer({
64
64
  const variables = LunaticVariablesStore.makeFromSource(
65
65
  source,
66
66
  data,
67
- onVariableChange
67
+ onVariableChange,
68
+ disableFilters
68
69
  );
69
70
  const pages = checkLoops(createMapPages(source));
70
71