@inseefr/lunatic 3.7.4 → 3.7.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.
Files changed (42) hide show
  1. package/components/Loop/Loop.d.ts +1 -0
  2. package/components/Loop/Loop.js +2 -2
  3. package/components/Loop/Loop.js.map +1 -1
  4. package/components/RosterForLoop/RosterForLoop.js +3 -1
  5. package/components/RosterForLoop/RosterForLoop.js.map +1 -1
  6. package/components/RosterForLoop/RosterForLoop.spec.js +12 -0
  7. package/components/RosterForLoop/RosterForLoop.spec.js.map +1 -1
  8. package/components/shared/Missing/Missing.js +3 -3
  9. package/components/shared/Missing/Missing.js.map +1 -1
  10. package/esm/components/Loop/Loop.d.ts +1 -0
  11. package/esm/components/Loop/Loop.js +2 -2
  12. package/esm/components/Loop/Loop.js.map +1 -1
  13. package/esm/components/RosterForLoop/RosterForLoop.js +3 -1
  14. package/esm/components/RosterForLoop/RosterForLoop.js.map +1 -1
  15. package/esm/components/RosterForLoop/RosterForLoop.spec.js +12 -0
  16. package/esm/components/RosterForLoop/RosterForLoop.spec.js.map +1 -1
  17. package/esm/components/shared/Missing/Missing.js +3 -3
  18. package/esm/components/shared/Missing/Missing.js.map +1 -1
  19. package/esm/use-lunatic/lunatic-context.d.ts +25 -23
  20. package/esm/use-lunatic/lunatic-context.js +11 -4
  21. package/esm/use-lunatic/lunatic-context.js.map +1 -1
  22. package/esm/use-lunatic/type.d.ts +56 -11
  23. package/esm/use-lunatic/use-lunatic.js +5 -1
  24. package/esm/use-lunatic/use-lunatic.js.map +1 -1
  25. package/package.json +1 -1
  26. package/src/components/Loop/Loop.tsx +6 -1
  27. package/src/components/RosterForLoop/RosterForLoop.spec.tsx +28 -0
  28. package/src/components/RosterForLoop/RosterForLoop.tsx +3 -0
  29. package/src/components/RosterForLoop/__snapshots__/RosterForLoop.spec.tsx.snap +114 -0
  30. package/src/components/shared/Missing/Missing.tsx +3 -3
  31. package/src/stories/utils/Orchestrator.tsx +8 -1
  32. package/src/use-lunatic/lunatic-context.tsx +26 -14
  33. package/src/use-lunatic/type.ts +59 -12
  34. package/src/use-lunatic/use-lunatic.test.ts +4 -1
  35. package/src/use-lunatic/use-lunatic.ts +5 -1
  36. package/tsconfig.build.tsbuildinfo +1 -1
  37. package/use-lunatic/lunatic-context.d.ts +25 -23
  38. package/use-lunatic/lunatic-context.js +16 -9
  39. package/use-lunatic/lunatic-context.js.map +1 -1
  40. package/use-lunatic/type.d.ts +56 -11
  41. package/use-lunatic/use-lunatic.js +5 -1
  42. package/use-lunatic/use-lunatic.js.map +1 -1
@@ -2,6 +2,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
2
  import { render, screen } from '@testing-library/react';
3
3
  import { RosterForLoop } from './RosterForLoop';
4
4
  import type { LunaticComponentProps } from '../type';
5
+ import { LunaticContext } from '../../use-lunatic/lunatic-context';
5
6
 
6
7
  describe('RosterForLoop', () => {
7
8
  const mockOnChange = vi.fn();
@@ -38,5 +39,32 @@ describe('RosterForLoop', () => {
38
39
  );
39
40
  expect(container).toMatchSnapshot();
40
41
  expect(screen.getAllByRole('row')).toHaveLength(2);
42
+ expect(screen.getByRole('button', { name: /add/i })).toBeEnabled();
43
+ expect(screen.getByRole('button', { name: /remove/i })).toBeEnabled();
44
+ });
45
+
46
+ it('disable the remove row button', () => {
47
+ const { container } = render(
48
+ <LunaticContext.Provider
49
+ value={{
50
+ componentsOptions: { disableRosterForLoopDeleteRowButton: true },
51
+ }}
52
+ >
53
+ <RosterForLoop
54
+ value={{ name: ['John Doe', 'Jane Doe'] }}
55
+ handleChanges={mockOnChange}
56
+ label="Ceci est un test"
57
+ id="table"
58
+ lines={{ min: 1, max: 3 }}
59
+ iterations={2}
60
+ getComponents={getComponents}
61
+ executeExpression={() => null as any}
62
+ />
63
+ </LunaticContext.Provider>
64
+ );
65
+ expect(container).toMatchSnapshot();
66
+ expect(screen.getAllByRole('row')).toHaveLength(2);
67
+ expect(screen.getByRole('button', { name: /add/i })).toBeEnabled();
68
+ expect(screen.getByRole('button', { name: /remove/i })).toBeDisabled();
41
69
  });
42
70
  });
@@ -10,6 +10,7 @@ import {
10
10
  } from '../shared/ComponentErrors/ComponentErrors';
11
11
  import { CustomLoop } from '../Loop/Loop';
12
12
  import { useLoopUtils } from '../Loop/utils';
13
+ import { useLunaticComponentsOptions } from '../../use-lunatic/lunatic-context';
13
14
 
14
15
  /**
15
16
  * Loop displayed as a table
@@ -17,6 +18,7 @@ import { useLoopUtils } from '../Loop/utils';
17
18
  export const RosterForLoop = (
18
19
  props: LunaticComponentProps<'RosterForLoop'>
19
20
  ) => {
21
+ const { disableRosterForLoopDeleteRowButton } = useLunaticComponentsOptions();
20
22
  const { min, max, nbRows, addRow, removeRow } = useLoopUtils(props);
21
23
  const {
22
24
  errors,
@@ -43,6 +45,7 @@ export const RosterForLoop = (
43
45
  addRow={nbRows === max ? undefined : addRow}
44
46
  removeRow={nbRows === min ? undefined : removeRow}
45
47
  canControlRows={!!(min && max && min !== max)}
48
+ disableDeleteRowButton={disableRosterForLoopDeleteRowButton}
46
49
  >
47
50
  <Table id={id}>
48
51
  {header && <TableHeader header={header} />}
@@ -1,5 +1,119 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
+ exports[`RosterForLoop > disable the remove row button 1`] = `
4
+ <div>
5
+ <label
6
+ class="lunatic-label"
7
+ for="table"
8
+ id="label-table"
9
+ >
10
+ Ceci est un test
11
+ </label>
12
+ <table
13
+ class="lunatic-table"
14
+ id="table-table"
15
+ >
16
+ <tbody
17
+ class="lunatic-table-tbody"
18
+ >
19
+ <tr
20
+ class="lunatic-table-tr"
21
+ >
22
+ <td
23
+ class="lunatic-table-td"
24
+ >
25
+ <div
26
+ class="field-container"
27
+ >
28
+ <div
29
+ class="field"
30
+ >
31
+ <div
32
+ class="lunatic-input"
33
+ >
34
+ <div
35
+ class="field-with-count"
36
+ >
37
+ <input
38
+ aria-describedby="characters-count-jrc3ye5q-QOP-lo6tcvvx-0"
39
+ aria-invalid="false"
40
+ aria-labelledby="label-jrc3ye5q-QOP-lo6tcvvx-0"
41
+ autocomplete="off"
42
+ id="jrc3ye5q-QOP-lo6tcvvx-0"
43
+ maxlength="249"
44
+ title="azeaze"
45
+ type="text"
46
+ value="azeaze"
47
+ />
48
+ <span
49
+ class="characters-count"
50
+ id="characters-count-jrc3ye5q-QOP-lo6tcvvx-0"
51
+ >
52
+ 6/249
53
+ </span>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </td>
59
+ </tr>
60
+ <tr
61
+ class="lunatic-table-tr"
62
+ >
63
+ <td
64
+ class="lunatic-table-td"
65
+ >
66
+ <div
67
+ class="field-container"
68
+ >
69
+ <div
70
+ class="field"
71
+ >
72
+ <div
73
+ class="lunatic-input"
74
+ >
75
+ <div
76
+ class="field-with-count"
77
+ >
78
+ <input
79
+ aria-describedby="characters-count-jrc3ye5q-QOP-lo6tcvvx-1"
80
+ aria-invalid="false"
81
+ aria-labelledby="label-jrc3ye5q-QOP-lo6tcvvx-1"
82
+ autocomplete="off"
83
+ id="jrc3ye5q-QOP-lo6tcvvx-1"
84
+ maxlength="249"
85
+ title="azeaze"
86
+ type="text"
87
+ value="azeaze"
88
+ />
89
+ <span
90
+ class="characters-count"
91
+ id="characters-count-jrc3ye5q-QOP-lo6tcvvx-1"
92
+ >
93
+ 6/249
94
+ </span>
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ </td>
100
+ </tr>
101
+ </tbody>
102
+ </table>
103
+ <input
104
+ class="button-lunatic"
105
+ type="button"
106
+ value="Add row"
107
+ />
108
+ <input
109
+ class="button-lunatic disabled"
110
+ disabled=""
111
+ type="button"
112
+ value="Remove row"
113
+ />
114
+ </div>
115
+ `;
116
+
3
117
  exports[`RosterForLoop > renders the right number of columns 1`] = `
4
118
  <div>
5
119
  <label
@@ -82,12 +82,12 @@ export const MissingPure = (
82
82
  );
83
83
 
84
84
  useKeyboardKey(
85
- Object.values(missingShortcut),
85
+ missingShortcut ? Object.values(missingShortcut) : [],
86
86
  (e) => {
87
87
  e.preventDefault();
88
- if (e.key.toLowerCase() === missingShortcut.dontKnow.toLowerCase())
88
+ if (e.key.toLowerCase() === missingShortcut?.dontKnow.toLowerCase())
89
89
  onClickDK();
90
- if (e.key.toLowerCase() === missingShortcut.refused.toLowerCase())
90
+ if (e.key.toLowerCase() === missingShortcut?.refused.toLowerCase())
91
91
  onClickRF();
92
92
  },
93
93
  hasKeyboardShortcut
@@ -36,6 +36,8 @@ type Props = {
36
36
  getReferentiel: (name: string) => Promise<IndexEntry[]>;
37
37
  // Display detail input even if the corresponding checkbox is not checked
38
38
  detailAlwaysDisplayed?: boolean;
39
+ // Set the "remove row" button as disabled in roster for loop.
40
+ disableRosterForLoopDeleteRowButton?: boolean;
39
41
  // Enable missing buttons
40
42
  missing?: boolean;
41
43
  // Readonly mode
@@ -78,11 +80,15 @@ function OrchestratorForStories(props: Readonly<Props>) {
78
80
  readOnly,
79
81
  disabled,
80
82
  detailAlwaysDisplayed,
83
+ disableRosterForLoopDeleteRowButton,
81
84
  autoSuggesterLoading,
82
85
  extraTabs = [],
83
86
  } = props;
84
87
 
85
- const componentsOptions = { detailAlwaysDisplayed };
88
+ const componentsOptions = {
89
+ detailAlwaysDisplayed,
90
+ disableRosterForLoopDeleteRowButton,
91
+ };
86
92
 
87
93
  const {
88
94
  getComponents,
@@ -313,6 +319,7 @@ export const OrchestratorMeta: Meta<typeof Orchestrator> = {
313
319
  readOnly: false,
314
320
  disabled: false,
315
321
  detailAlwaysDisplayed: false,
322
+ disableRosterForLoopDeleteRowButton: false,
316
323
  autoSuggesterLoading: false,
317
324
  },
318
325
  };
@@ -6,8 +6,21 @@ import {
6
6
  } from 'react';
7
7
 
8
8
  import D from '../i18n/index';
9
+ import type { LunaticOptions } from './type';
9
10
 
10
- const LunaticContext = createContext({
11
+ type LunaticContextType = {
12
+ missingStrategy?: LunaticOptions['missingStrategy'];
13
+ management?: LunaticOptions['management'];
14
+ missing?: LunaticOptions['missing'];
15
+ shortcut?: LunaticOptions['shortcut'];
16
+ missingShortcut?: LunaticOptions['missingShortcut'];
17
+ dontKnowButton?: LunaticOptions['dontKnowButton'];
18
+ refusedButton?: LunaticOptions['refusedButton'];
19
+ componentsOptions?: LunaticOptions['componentsOptions'];
20
+ };
21
+
22
+ /** Mandatory values used as a context's last-resort fallback. */
23
+ const defaultValues = {
11
24
  missingStrategy: () => {},
12
25
  management: false,
13
26
  missing: false,
@@ -15,8 +28,15 @@ const LunaticContext = createContext({
15
28
  missingShortcut: { dontKnow: '', refused: '' },
16
29
  dontKnowButton: D.DK,
17
30
  refusedButton: D.RF,
18
- componentsOptions: { detailAlwaysDisplayed: false },
19
- });
31
+ componentsOptions: {
32
+ detailAlwaysDisplayed: false,
33
+ disableRosterForLoopDeleteRowButton: false,
34
+ },
35
+ };
36
+
37
+ /** Expose specific Lunatic options to handle some (e.g. missing, management, components options) */
38
+ export const LunaticContext = createContext<LunaticContextType>(defaultValues);
39
+
20
40
  /**
21
41
  * Provide `missing`, `missingStrategy`, `shortcut` and `missingShortcut`,
22
42
  * `dontKnowButton`, `refusedButton` to `Missing` component to manage
@@ -41,9 +61,10 @@ export const useLunaticMissing = () => {
41
61
  };
42
62
  };
43
63
 
64
+ /** Expose the specified options to override default component behaviour. */
44
65
  export const useLunaticComponentsOptions = () => {
45
66
  const { componentsOptions } = useContext(LunaticContext);
46
- return componentsOptions;
67
+ return componentsOptions ?? {};
47
68
  };
48
69
 
49
70
  /** Provide `management` to display data states [COLLECTED,EDITED,FORCED] */
@@ -60,16 +81,7 @@ export function createLunaticProvider({
60
81
  dontKnowButton,
61
82
  refusedButton,
62
83
  componentsOptions,
63
- }: {
64
- management: boolean;
65
- missing: boolean;
66
- missingStrategy: () => void;
67
- shortcut: boolean;
68
- missingShortcut: { dontKnow: string; refused: string };
69
- dontKnowButton: string;
70
- refusedButton: string;
71
- componentsOptions: { detailAlwaysDisplayed: boolean };
72
- }): FunctionComponent<PropsWithChildren> {
84
+ }: LunaticContextType): FunctionComponent<PropsWithChildren> {
73
85
  const value = {
74
86
  management,
75
87
  missing,
@@ -185,16 +185,25 @@ export type LunaticReducerState = {
185
185
 
186
186
  /** Specific behaviour options defined in the {@link useLunatic} hook. */
187
187
  export type LunaticOptions = {
188
- /** Ignore filters. (default: `false`) */
188
+ /**
189
+ * Ignore filters.
190
+ * @default false
191
+ */
189
192
  disableFilters?: boolean;
190
193
 
191
- /** Disable filters description. (default: `true`) */
194
+ /**
195
+ * Disable filters description.
196
+ * @default true
197
+ */
192
198
  disableFiltersDescription?: boolean;
193
199
 
194
200
  /** Enable VTL and Markdown support. */
195
201
  features?: ('MD' | 'VTL')[];
196
202
  preferences?: ['COLLECTED'];
197
- /** Key in which the data is saved. (default: `"COLLECTED"`) */
203
+ /**
204
+ * Key in which the data is saved.
205
+ * @default "COLLECTED"
206
+ */
198
207
  savingType?: 'COLLECTED';
199
208
  /** Function called when a variable is changed by a user input (must be memoized as it is used in dependency of a `useCallback` by the library). */
200
209
  onChange?: LunaticChangesHandler;
@@ -204,24 +213,43 @@ export type LunaticOptions = {
204
213
  *
205
214
  * Enable management mode which allow to handle multiple states of the same variable (used by recovery positions).
206
215
  *
207
- * The administrator can switch between `COLLECTED`, `EDITED`, `INPUTTED` modes. (default: `false`)
216
+ * The administrator can switch between `COLLECTED`, `EDITED`, `INPUTTED` modes.
217
+ * @default false
208
218
  */
209
219
  management?: boolean;
210
- /** Enable keyboard shortcuts for checkboxes, radio buttons and missing buttons (default: `false`). */
220
+ /**
221
+ * Enable keyboard shortcuts for checkboxes, radio buttons and missing buttons.
222
+ * @default false
223
+ */
211
224
  shortcut?: boolean;
212
- /** Starting page at launch. (default: `"1"`) */
225
+ /**
226
+ * Starting page at launch.
227
+ * @default '1'
228
+ */
213
229
  initialPage?: PageTag;
214
230
  /** Furthest page the user ever reached. */
215
231
  lastReachedPage?: PageTag;
216
- /** Enable the preemptive loading of suggester data on Lunatic initialization. (default: `false`) */
232
+ /**
233
+ * Enable the preemptive loading of suggester data on Lunatic initialization.
234
+ * @default false
235
+ */
217
236
  autoSuggesterLoading?: boolean;
218
237
  /** Function called to fetch nomenclatures used by the suggesters. */
219
238
  getReferentiel?: (name: string) => Promise<Array<IndexEntry>>;
220
- /** Enable data controls (form validation). (default: `false`) */
239
+ /**
240
+ * Enable data controls (form validation).
241
+ * @default false
242
+ */
221
243
  activeControls?: boolean;
222
- /** Enable overview system. (default: `false`) */
244
+ /**
245
+ * Enable overview system.
246
+ * @default false
247
+ */
223
248
  withOverview?: boolean;
224
- /** Enable missing system. (default: `false`) */
249
+ /**
250
+ * Enable missing system.
251
+ * @default false
252
+ */
225
253
  missing?: boolean;
226
254
  /** Function triggered when a missing button is clicked. */
227
255
  missingStrategy?: () => void;
@@ -231,10 +259,29 @@ export type LunaticOptions = {
231
259
  dontKnowButton?: string;
232
260
  /** "Refused" button label. */
233
261
  refusedButton?: string;
234
- /** Enable change tracking to keep a track of what variable changed (allow using getChangedData()). (default: `false`) */
262
+ /**
263
+ * Enable change tracking to keep a track of what variable changed
264
+ * (allow using getChangedData()).
265
+ * @default false
266
+ */
235
267
  trackChanges?: boolean;
236
268
  logger?: LunaticLogger;
237
- componentsOptions?: { detailAlwaysDisplayed?: boolean };
269
+ /** Options that can be set to override default component behaviour. */
270
+ componentsOptions?: {
271
+ /**
272
+ * Set the details ("autre précisez") as always displayed (even if the
273
+ * related "other" modality is not currently selected).
274
+ * @since 3.4.9
275
+ * @default false
276
+ */
277
+ detailAlwaysDisplayed?: boolean;
278
+ /**
279
+ * Set the "remove row" button as disabled in roster for loop.
280
+ * @since 3.7.5
281
+ * @default false
282
+ */
283
+ disableRosterForLoopDeleteRowButton?: boolean;
284
+ };
238
285
  /** Commit variable change automatically for resizing / cleaning (used for testing) **/
239
286
  autoCommit?: boolean;
240
287
  };
@@ -106,7 +106,10 @@ describe('use-lunatic()', () => {
106
106
  missingShortcut: { dontKnow: '1', refused: '2' },
107
107
  dontKnowButton: 'DK',
108
108
  refusedButton: 'RF',
109
- componentsOptions: { detailAlwaysDisplayed: false },
109
+ componentsOptions: {
110
+ detailAlwaysDisplayed: false,
111
+ disableRosterForLoopDeleteRowButton: false,
112
+ },
110
113
  });
111
114
  });
112
115
 
@@ -67,7 +67,10 @@ const defaultOptions = {
67
67
  refusedButton: DEFAULT_REFUSED,
68
68
  trackChanges: false,
69
69
  logger: ConsoleLogger,
70
- componentsOptions: { detailAlwaysDisplayed: false },
70
+ componentsOptions: {
71
+ detailAlwaysDisplayed: false,
72
+ disableRosterForLoopDeleteRowButton: false,
73
+ },
71
74
  } satisfies LunaticOptions;
72
75
 
73
76
  /** The first library entrypoint is the `useLunatic` hook. */
@@ -143,6 +146,7 @@ export function useLunatic(
143
146
  dontKnowButton,
144
147
  refusedButton,
145
148
  componentsOptions.detailAlwaysDisplayed,
149
+ componentsOptions.disableRosterForLoopDeleteRowButton,
146
150
  ]
147
151
  );
148
152