@inseefr/lunatic 3.4.10-rc.0 → 3.4.10
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/README.md +6 -4
- package/components/LunaticComponents.d.ts +10 -1
- package/components/LunaticComponents.js +3 -1
- package/components/LunaticComponents.js.map +1 -1
- package/components/shared/HOC/slottableComponent.d.ts +3 -3
- package/components/shared/HOC/slottableComponent.js +2 -2
- package/components/shared/Radio/RadioOption.js +5 -1
- package/components/shared/Radio/RadioOption.js.map +1 -1
- package/components/shared/Radio/RadioOption.spec.js +21 -0
- package/components/shared/Radio/RadioOption.spec.js.map +1 -1
- package/esm/components/LunaticComponents.d.ts +10 -1
- package/esm/components/LunaticComponents.js +3 -1
- package/esm/components/LunaticComponents.js.map +1 -1
- package/esm/components/shared/HOC/slottableComponent.d.ts +3 -3
- package/esm/components/shared/HOC/slottableComponent.js +2 -2
- package/esm/components/shared/Radio/RadioOption.js +5 -1
- package/esm/components/shared/Radio/RadioOption.js.map +1 -1
- package/esm/components/shared/Radio/RadioOption.spec.js +21 -0
- package/esm/components/shared/Radio/RadioOption.spec.js.map +1 -1
- package/esm/use-lunatic/commons/compile-controls.d.ts +1 -1
- package/esm/use-lunatic/commons/compile-controls.js +4 -3
- package/esm/use-lunatic/commons/compile-controls.js.map +1 -1
- package/esm/use-lunatic/commons/variables/lunatic-variables-store.d.ts +16 -0
- package/esm/use-lunatic/commons/variables/lunatic-variables-store.js +6 -6
- package/esm/use-lunatic/commons/variables/lunatic-variables-store.js.map +1 -1
- package/esm/use-lunatic/hooks/use-loop-variables.d.ts +1 -1
- package/esm/use-lunatic/hooks/use-loop-variables.js +1 -1
- package/esm/use-lunatic/hooks/use-page-has-response.d.ts +1 -1
- package/esm/use-lunatic/hooks/use-page-has-response.js +6 -6
- package/esm/use-lunatic/hooks/useOverview.d.ts +1 -1
- package/esm/use-lunatic/hooks/useOverview.js +4 -4
- package/esm/use-lunatic/hooks/useWarnDepChange.d.ts +3 -2
- package/esm/use-lunatic/hooks/useWarnDepChange.js +3 -2
- package/esm/use-lunatic/hooks/useWarnDepChange.js.map +1 -1
- package/esm/use-lunatic/lunatic-context.d.ts +5 -2
- package/esm/use-lunatic/lunatic-context.js +5 -2
- package/esm/use-lunatic/lunatic-context.js.map +1 -1
- package/esm/use-lunatic/props/propOptions.d.ts +2 -0
- package/esm/use-lunatic/props/propOptions.js +4 -0
- package/esm/use-lunatic/props/propOptions.js.map +1 -1
- package/esm/use-lunatic/type.d.ts +152 -2
- package/esm/use-lunatic/use-lunatic.d.ts +13 -36
- package/esm/use-lunatic/use-lunatic.js +13 -2
- package/esm/use-lunatic/use-lunatic.js.map +1 -1
- package/package.json +1 -1
- package/src/components/LunaticComponents.tsx +10 -8
- package/src/components/shared/HOC/slottableComponent.tsx +3 -3
- package/src/components/shared/Radio/RadioOption.spec.tsx +55 -0
- package/src/components/shared/Radio/RadioOption.tsx +5 -0
- package/src/use-lunatic/commons/compile-controls.ts +4 -3
- package/src/use-lunatic/commons/variables/lunatic-variables-store.ts +18 -18
- package/src/use-lunatic/hooks/use-loop-variables.ts +1 -1
- package/src/use-lunatic/hooks/use-page-has-response.ts +6 -6
- package/src/use-lunatic/hooks/useOverview.ts +4 -4
- package/src/use-lunatic/hooks/useWarnDepChange.ts +3 -2
- package/src/use-lunatic/lunatic-context.tsx +5 -2
- package/src/use-lunatic/props/propOptions.ts +5 -0
- package/src/use-lunatic/type.ts +153 -17
- package/src/use-lunatic/use-lunatic.ts +11 -2
- package/tsconfig.build.tsbuildinfo +1 -1
- package/use-lunatic/commons/compile-controls.d.ts +1 -1
- package/use-lunatic/commons/compile-controls.js +4 -3
- package/use-lunatic/commons/compile-controls.js.map +1 -1
- package/use-lunatic/commons/variables/lunatic-variables-store.d.ts +16 -0
- package/use-lunatic/commons/variables/lunatic-variables-store.js +13 -13
- package/use-lunatic/commons/variables/lunatic-variables-store.js.map +1 -1
- package/use-lunatic/hooks/use-loop-variables.d.ts +1 -1
- package/use-lunatic/hooks/use-loop-variables.js +1 -1
- package/use-lunatic/hooks/use-page-has-response.d.ts +1 -1
- package/use-lunatic/hooks/use-page-has-response.js +6 -6
- package/use-lunatic/hooks/useOverview.d.ts +1 -1
- package/use-lunatic/hooks/useOverview.js +4 -4
- package/use-lunatic/hooks/useWarnDepChange.d.ts +3 -2
- package/use-lunatic/hooks/useWarnDepChange.js +3 -2
- package/use-lunatic/hooks/useWarnDepChange.js.map +1 -1
- package/use-lunatic/lunatic-context.d.ts +5 -2
- package/use-lunatic/lunatic-context.js +5 -2
- package/use-lunatic/lunatic-context.js.map +1 -1
- package/use-lunatic/props/propOptions.d.ts +2 -0
- package/use-lunatic/props/propOptions.js +4 -0
- package/use-lunatic/props/propOptions.js.map +1 -1
- package/use-lunatic/type.d.ts +152 -2
- package/use-lunatic/use-lunatic.d.ts +13 -36
- package/use-lunatic/use-lunatic.js +13 -2
- package/use-lunatic/use-lunatic.js.map +1 -1
|
@@ -45,7 +45,7 @@ import type { MarkdownLink } from '../MDLabel/MarkdownLink';
|
|
|
45
45
|
import type { Accordion } from '../../Accordion/Accordion';
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
48
|
+
* Contain the type of every customizable components.
|
|
49
49
|
*/
|
|
50
50
|
export type LunaticSlotComponents = {
|
|
51
51
|
// Components
|
|
@@ -130,9 +130,9 @@ export const SlotsProvider = ({
|
|
|
130
130
|
};
|
|
131
131
|
|
|
132
132
|
/**
|
|
133
|
-
* Create a replaceable version of a component
|
|
133
|
+
* Create a replaceable version of a component.
|
|
134
134
|
*
|
|
135
|
-
* The component can be replaced
|
|
135
|
+
* The component can be replaced through the `slots` props on `LunaticComponents`.
|
|
136
136
|
*/
|
|
137
137
|
export function slottableComponent<T>(
|
|
138
138
|
name: keyof LunaticSlotComponents,
|
|
@@ -25,6 +25,61 @@ describe('RadioOption', () => {
|
|
|
25
25
|
expect(onClickMock).toHaveBeenCalled();
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
+
it('does not allow to uncheck modality if checkboxStyle is not defined', () => {
|
|
29
|
+
const onClickMock = vi.fn();
|
|
30
|
+
render(
|
|
31
|
+
<RadioOption
|
|
32
|
+
id="radio-option"
|
|
33
|
+
label="Test Option"
|
|
34
|
+
onCheck={onClickMock}
|
|
35
|
+
onUncheck={onClickMock}
|
|
36
|
+
checked
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const option = screen.getByRole('radio');
|
|
41
|
+
fireEvent.click(option);
|
|
42
|
+
expect(onClickMock).not.toHaveBeenCalled();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('does not allow to uncheck modality if checkboxStyle is false', () => {
|
|
46
|
+
const onClickMock = vi.fn();
|
|
47
|
+
|
|
48
|
+
render(
|
|
49
|
+
<RadioOption
|
|
50
|
+
id="radio-option"
|
|
51
|
+
label="Test Option"
|
|
52
|
+
onCheck={onClickMock}
|
|
53
|
+
onUncheck={onClickMock}
|
|
54
|
+
checkboxStyle={false}
|
|
55
|
+
checked
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const option = screen.getByRole('radio');
|
|
60
|
+
fireEvent.click(option);
|
|
61
|
+
expect(onClickMock).not.toHaveBeenCalled();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('allows to uncheck modality if checkboxStyle = true and onUncheck', () => {
|
|
65
|
+
const onClickMock = vi.fn();
|
|
66
|
+
|
|
67
|
+
render(
|
|
68
|
+
<RadioOption
|
|
69
|
+
id="radio-option"
|
|
70
|
+
label="Test Option"
|
|
71
|
+
onCheck={onClickMock}
|
|
72
|
+
onUncheck={onClickMock}
|
|
73
|
+
checkboxStyle={true}
|
|
74
|
+
checked
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const option = screen.getByRole('radio');
|
|
79
|
+
fireEvent.click(option);
|
|
80
|
+
expect(onClickMock).toHaveBeenCalled();
|
|
81
|
+
});
|
|
82
|
+
|
|
28
83
|
it('sets the tabIndex to 0 when unchecked', () => {
|
|
29
84
|
const { getByRole } = render(
|
|
30
85
|
<RadioOption id="radio-option" label="Test Option" checked={false} />
|
|
@@ -40,6 +40,7 @@ function LunaticRadioOption({
|
|
|
40
40
|
detailLabel,
|
|
41
41
|
detailValue,
|
|
42
42
|
onCheck,
|
|
43
|
+
onUncheck,
|
|
43
44
|
}: Props) {
|
|
44
45
|
const divEl = useRef<HTMLDivElement>(null);
|
|
45
46
|
const isEnabled = !disabled && !readOnly;
|
|
@@ -48,6 +49,10 @@ function LunaticRadioOption({
|
|
|
48
49
|
|
|
49
50
|
const onClickOption = () => {
|
|
50
51
|
if (!isEnabled || !onCheck || checked) {
|
|
52
|
+
// for checkboxStyle=true (only used by CheckboxOne) , we allow uncheck
|
|
53
|
+
if (checkboxStyle && onUncheck) {
|
|
54
|
+
onUncheck();
|
|
55
|
+
}
|
|
51
56
|
return;
|
|
52
57
|
}
|
|
53
58
|
onCheck();
|
|
@@ -35,7 +35,8 @@ const isLoopComponent = (
|
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
* Check if components of the current page have errors, and return a map of
|
|
38
|
+
* Check if components of the current page have errors, and return a map of
|
|
39
|
+
* errors (indexed by component ID).
|
|
39
40
|
*/
|
|
40
41
|
function checkComponents(
|
|
41
42
|
state: StateForControls,
|
|
@@ -93,7 +94,7 @@ function checkControls(
|
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
/**
|
|
96
|
-
* Figure out the number of iterations of a component
|
|
97
|
+
* Figure out the number of iterations of a component.
|
|
97
98
|
*/
|
|
98
99
|
function computeIterations(
|
|
99
100
|
component: InterpretedComponent | ComponentDefinition,
|
|
@@ -210,7 +211,7 @@ function hasCriticalError(errors?: Record<string, LunaticError[]>): boolean {
|
|
|
210
211
|
}
|
|
211
212
|
|
|
212
213
|
/**
|
|
213
|
-
* Check controls for currently visible components and output errors
|
|
214
|
+
* Check controls for currently visible components and output errors.
|
|
214
215
|
*/
|
|
215
216
|
export function compileControls(state: StateForControls) {
|
|
216
217
|
const components = replaceComponentSequence(getComponentsFromState(state));
|
|
@@ -19,23 +19,23 @@ import {
|
|
|
19
19
|
VTLMissingDependency,
|
|
20
20
|
} from './errors';
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
/** Interpret counter. Used for testing purpose. */
|
|
23
23
|
let interpretCount = 0;
|
|
24
|
-
|
|
24
|
+
/** Special variable that will take the current iteration value. */
|
|
25
25
|
const iterationVariableName = 'GLOBAL_ITERATION_INDEX';
|
|
26
26
|
|
|
27
27
|
type IterationLevel = number[];
|
|
28
28
|
export type EventArgs = {
|
|
29
29
|
change: {
|
|
30
|
-
|
|
30
|
+
/** Name of the changed variable. */
|
|
31
31
|
name: string;
|
|
32
|
-
|
|
32
|
+
/** New value for the variable. */
|
|
33
33
|
value: unknown;
|
|
34
|
-
|
|
34
|
+
/** Iteration changed (for array). */
|
|
35
35
|
iteration?: IterationLevel | undefined;
|
|
36
|
-
|
|
36
|
+
/** What triggered this change. */
|
|
37
37
|
cause?: 'resizing' | 'cleaning';
|
|
38
|
-
|
|
38
|
+
/** Extra sent when setting the variable. */
|
|
39
39
|
[extra: string]: unknown;
|
|
40
40
|
};
|
|
41
41
|
};
|
|
@@ -220,7 +220,7 @@ export class LunaticVariablesStore {
|
|
|
220
220
|
this.eventTarget.removeEventListener(eventName, cb as EventListener);
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
|
|
223
|
+
/** Retrieve the number of interpret() run (used in testing only). */
|
|
224
224
|
get interpretCount() {
|
|
225
225
|
return interpretCount;
|
|
226
226
|
}
|
|
@@ -249,25 +249,25 @@ export class LunaticVariablesStore {
|
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
class LunaticVariable {
|
|
252
|
-
|
|
252
|
+
/** Last time the value was updated (changed). */
|
|
253
253
|
public updatedAt = new Map<undefined | string, number>();
|
|
254
|
-
|
|
254
|
+
/** Last time "calculation" was run (for calculated variable). */
|
|
255
255
|
private calculatedAt = new Map<undefined | string, number>();
|
|
256
|
-
|
|
256
|
+
/** Internal value for the variable. */
|
|
257
257
|
private value: unknown;
|
|
258
|
-
|
|
258
|
+
/** List of dependencies, ex: ['FIRSTNAME', 'LASTNAME']. */
|
|
259
259
|
private dependencies?: string[];
|
|
260
|
-
|
|
260
|
+
/** Expression for calculated variable. */
|
|
261
261
|
public readonly expression?: string;
|
|
262
|
-
|
|
262
|
+
/** Dictionary holding all the available variables. */
|
|
263
263
|
private readonly dictionary?: Map<string, LunaticVariable>;
|
|
264
|
-
|
|
264
|
+
/** Specific iteration depth to get value from dependencies (used for yAxis for instance). */
|
|
265
265
|
private readonly iterationDepth?: number;
|
|
266
|
-
|
|
266
|
+
/** For calculated variable, shape is copied from another variable. */
|
|
267
267
|
private readonly shapeFrom?: string[];
|
|
268
|
-
|
|
268
|
+
/** Keep a record of variable name (optional, used for debug). */
|
|
269
269
|
public readonly name?: string;
|
|
270
|
-
|
|
270
|
+
/** Count the number of calculation. */
|
|
271
271
|
public calculatedCount = 0;
|
|
272
272
|
|
|
273
273
|
constructor(
|
|
@@ -2,7 +2,7 @@ import type { LunaticComponentDefinition, LunaticReducerState } from '../type';
|
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Extract the list of variables used for the current loop
|
|
5
|
+
* Extract the list of variables used for the current loop.
|
|
6
6
|
*/
|
|
7
7
|
export function useLoopVariables(
|
|
8
8
|
pager: LunaticReducerState['pager'],
|
|
@@ -4,7 +4,7 @@ import type { LunaticComponentDefinition, LunaticReducerState } from '../type';
|
|
|
4
4
|
import type { LunaticComponentProps } from '../../components/type';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Check if a page has one response (value is filled for at least one field)
|
|
7
|
+
* Check if a page has one response (value is filled for at least one field).
|
|
8
8
|
*/
|
|
9
9
|
export function usePageHasResponse(
|
|
10
10
|
components: LunaticComponentProps[],
|
|
@@ -67,10 +67,10 @@ export function usePageHasResponse(
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
|
-
* Check if a value is empty
|
|
71
|
-
* - null
|
|
72
|
-
* - for arrays, every item must be empty
|
|
73
|
-
* - for objects, every value must be empty
|
|
70
|
+
* Check if a value is empty.
|
|
71
|
+
* - `null`, `undefined` or `''`.
|
|
72
|
+
* - for arrays, every item must be empty.
|
|
73
|
+
* - for objects, every value must be empty.
|
|
74
74
|
*/
|
|
75
75
|
function isEmpty(value: unknown): boolean {
|
|
76
76
|
// Array is empty if all items are empty
|
|
@@ -86,7 +86,7 @@ function isEmpty(value: unknown): boolean {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
/**
|
|
89
|
-
* For complex component we need to inspect child components, interpret the response value
|
|
89
|
+
* For complex component we need to inspect child components, interpret the response value.
|
|
90
90
|
*/
|
|
91
91
|
function isSubComponentsEmpty(
|
|
92
92
|
components: (LunaticComponentProps | LunaticComponentDefinition)[],
|
|
@@ -18,7 +18,7 @@ export type InterpretedLunaticOverviewItem = {
|
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* Build a filled overview everytime the deps change.
|
|
22
22
|
*/
|
|
23
23
|
export const useOverview = (
|
|
24
24
|
{
|
|
@@ -36,7 +36,7 @@ export const useOverview = (
|
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
* Use
|
|
39
|
+
* Use Lunatic data to interpret the static overview (calculated on init) with the real data.
|
|
40
40
|
*/
|
|
41
41
|
const interpretOverview = (
|
|
42
42
|
overviewItems: LunaticOverviewItem[],
|
|
@@ -71,7 +71,7 @@ const interpretOverview = (
|
|
|
71
71
|
};
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
|
-
* Interpret expression inside an item (label & condition)
|
|
74
|
+
* Interpret expression inside an item (label & condition).
|
|
75
75
|
*/
|
|
76
76
|
const interpretOverviewItem = (
|
|
77
77
|
items: InterpretedLunaticOverviewItem[],
|
|
@@ -127,7 +127,7 @@ const interpretOverviewItem = (
|
|
|
127
127
|
};
|
|
128
128
|
|
|
129
129
|
/**
|
|
130
|
-
* Set the current property in the correct overview item
|
|
130
|
+
* Set the current property in the correct overview item.
|
|
131
131
|
*/
|
|
132
132
|
const applyCurrentPage = (
|
|
133
133
|
items: InterpretedLunaticOverviewItem[],
|
|
@@ -3,8 +3,9 @@ import type { LunaticLogger } from '../logger/type';
|
|
|
3
3
|
import { useRefSync } from '../../hooks/useRefSync';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Log a warning when the variable change
|
|
7
|
-
*
|
|
6
|
+
* Log a warning when the variable change.
|
|
7
|
+
*
|
|
8
|
+
* Ensure that we received a memoized value and help debug.
|
|
8
9
|
*/
|
|
9
10
|
export function useWarnDepChange(
|
|
10
11
|
variable: unknown,
|
|
@@ -17,8 +17,11 @@ const LunaticContext = createContext({
|
|
|
17
17
|
refusedButton: D.RF,
|
|
18
18
|
componentsOptions: { detailAlwaysDisplayed: false },
|
|
19
19
|
});
|
|
20
|
-
/**
|
|
21
|
-
*
|
|
20
|
+
/**
|
|
21
|
+
* Provide `missing`, `missingStrategy`, `shortcut` and `missingShortcut`,
|
|
22
|
+
* `dontKnowButton`, `refusedButton` to `Missing` component to manage
|
|
23
|
+
* non-response buttons and shortcut.
|
|
24
|
+
*/
|
|
22
25
|
export const useLunaticMissing = () => {
|
|
23
26
|
const {
|
|
24
27
|
missing,
|
|
@@ -18,6 +18,7 @@ export type InterpretedOption = {
|
|
|
18
18
|
detailValue?: string | null;
|
|
19
19
|
onDetailChange?: (value: string) => void;
|
|
20
20
|
onCheck?: () => void;
|
|
21
|
+
onUncheck?: () => void;
|
|
21
22
|
};
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -86,6 +87,10 @@ export function getOptionsProp(
|
|
|
86
87
|
{ name: definition.response.name, value: option.value },
|
|
87
88
|
]);
|
|
88
89
|
},
|
|
90
|
+
// for CheckboxOne, we allow uncheck
|
|
91
|
+
onUncheck: () => {
|
|
92
|
+
handleChanges([{ name: definition.response.name, value: null }]);
|
|
93
|
+
},
|
|
89
94
|
detailValue:
|
|
90
95
|
'detail' in option && option.detail
|
|
91
96
|
? variables.get(option.detail.response.name, iteration)
|
package/src/use-lunatic/type.ts
CHANGED
|
@@ -35,6 +35,7 @@ export type LunaticOverviewItem = {
|
|
|
35
35
|
|
|
36
36
|
export type LunaticSuggester = SuggesterDefinition;
|
|
37
37
|
|
|
38
|
+
/** Survey data. */
|
|
38
39
|
export type LunaticData = Partial<
|
|
39
40
|
Record<Exclude<VariableType, 'COLLECTED'>, Record<string, unknown>> & {
|
|
40
41
|
COLLECTED: Record<string, LunaticCollectedValue>;
|
|
@@ -45,6 +46,10 @@ export type LunaticValues = {
|
|
|
45
46
|
[variableName: string]: unknown;
|
|
46
47
|
};
|
|
47
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Errors returned by `useLunatic` hook when an input check is made with their
|
|
51
|
+
* id, criticity, type and the message to display to the user.
|
|
52
|
+
*/
|
|
48
53
|
export type LunaticError = Pick<
|
|
49
54
|
ControlDefinition,
|
|
50
55
|
'id' | 'criticality' | 'typeOfControl'
|
|
@@ -55,8 +60,17 @@ export type LunaticError = Pick<
|
|
|
55
60
|
export type VariableType = 'COLLECTED' | 'EXTERNAL' | 'CALCULATED';
|
|
56
61
|
export type LunaticExpression = VTLExpression | VTLScalarExpression;
|
|
57
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Page numerotation.
|
|
65
|
+
*
|
|
66
|
+
* String representing a location in the survey. It has one of the following
|
|
67
|
+
* format:
|
|
68
|
+
* - [page].[sous-page]#[iteration], when we are in a loop or a roundabount
|
|
69
|
+
* - [page]
|
|
70
|
+
*/
|
|
58
71
|
export type PageTag = `${number}.${number}#${number}` | `${number}`;
|
|
59
72
|
|
|
73
|
+
/** Variables provided to Lunatic through the source and used internally in a store. */
|
|
60
74
|
export type LunaticVariable = Variable;
|
|
61
75
|
export type LunaticCollectedValue = Partial<{
|
|
62
76
|
COLLECTED: unknown;
|
|
@@ -76,16 +90,52 @@ export type LunaticStateVariable = {
|
|
|
76
90
|
};
|
|
77
91
|
}[LunaticVariable['variableType']];
|
|
78
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Contains informations about navigation (last page reached, number of pages, subpages, etc.).
|
|
95
|
+
*
|
|
96
|
+
* This is the object used internally to determine where we are in the navigation.
|
|
97
|
+
*
|
|
98
|
+
* When we are in a loop, the pager will have additional properties.
|
|
99
|
+
*/
|
|
79
100
|
export type LunaticPager = {
|
|
101
|
+
/** Last page reached. */
|
|
80
102
|
lastReachedPage?: PageTag;
|
|
103
|
+
/** Last page of the survey. */
|
|
81
104
|
maxPage: number;
|
|
82
|
-
|
|
105
|
+
/** Current page. */
|
|
83
106
|
page: number;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Current subpage.
|
|
110
|
+
*
|
|
111
|
+
* Only in a loop.
|
|
112
|
+
*/
|
|
84
113
|
subPage?: number;
|
|
85
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Number of pages in a loop.
|
|
116
|
+
*
|
|
117
|
+
* Only in a loop.
|
|
118
|
+
*/
|
|
119
|
+
nbSubPages?: number;
|
|
120
|
+
/**
|
|
121
|
+
* Iteration index (starts at 0).
|
|
122
|
+
*
|
|
123
|
+
* Only in a loop.
|
|
124
|
+
*/
|
|
86
125
|
iteration?: number;
|
|
126
|
+
/**
|
|
127
|
+
* Number of iterations (i.e. number of people).
|
|
128
|
+
*
|
|
129
|
+
* Only in a loop.
|
|
130
|
+
*/
|
|
87
131
|
nbIterations?: number;
|
|
132
|
+
/**
|
|
133
|
+
* Only in a loop.
|
|
134
|
+
*/
|
|
88
135
|
shallowIteration?: number;
|
|
136
|
+
/**
|
|
137
|
+
* Only in a loop.
|
|
138
|
+
*/
|
|
89
139
|
linksIterations?: number[];
|
|
90
140
|
};
|
|
91
141
|
|
|
@@ -107,25 +157,25 @@ export type LunaticReducerState = {
|
|
|
107
157
|
components: LunaticSource['components'];
|
|
108
158
|
isLoop: true;
|
|
109
159
|
iterations: VTLScalarExpression;
|
|
110
|
-
|
|
160
|
+
/** Variables affecting this loop. */
|
|
111
161
|
loopDependencies: string[];
|
|
112
|
-
|
|
162
|
+
/** List of child pages (ex: ['20.1', '20.2'] */
|
|
113
163
|
subPages: string[];
|
|
114
164
|
};
|
|
115
165
|
};
|
|
116
|
-
|
|
166
|
+
/** Run an expression using the value from the state. */
|
|
117
167
|
executeExpression: <T = unknown>(
|
|
118
168
|
expression: VTLExpression,
|
|
119
169
|
args?: {
|
|
120
170
|
iteration?: number | number[];
|
|
121
|
-
|
|
171
|
+
/** @deprecated */
|
|
122
172
|
bindingDependencies?: string[];
|
|
123
173
|
deps?: string[];
|
|
124
174
|
}
|
|
125
175
|
) => T;
|
|
126
176
|
isInLoop: boolean;
|
|
127
177
|
updatedAt: number;
|
|
128
|
-
|
|
178
|
+
/** Update the value collected for the variable. */
|
|
129
179
|
updateBindings: (
|
|
130
180
|
variableName: string,
|
|
131
181
|
value: unknown,
|
|
@@ -136,78 +186,164 @@ export type LunaticReducerState = {
|
|
|
136
186
|
};
|
|
137
187
|
};
|
|
138
188
|
|
|
189
|
+
/** Specific behaviour options defined in the {@link useLunatic} hook. */
|
|
139
190
|
export type LunaticOptions = {
|
|
191
|
+
/** Ignore filters. (default: `false`) */
|
|
140
192
|
disableFilters?: boolean;
|
|
193
|
+
/** Enable VTL and Markdown support. */
|
|
141
194
|
features?: ('MD' | 'VTL')[];
|
|
142
195
|
preferences?: ['COLLECTED'];
|
|
196
|
+
/** Key in which the data is saved. (default: `"COLLECTED"`) */
|
|
143
197
|
savingType?: 'COLLECTED';
|
|
198
|
+
/** 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). */
|
|
144
199
|
onChange?: LunaticChangesHandler;
|
|
145
200
|
onVariableChange?: (event: LunaticVariablesStoreEvents['change']) => void;
|
|
201
|
+
/**
|
|
202
|
+
* Not yet implemented.
|
|
203
|
+
*
|
|
204
|
+
* Enable management mode which allow to handle multiple states of the same variable (used by recovery positions).
|
|
205
|
+
*
|
|
206
|
+
* The administrator can switch between `COLLECTED`, `EDITED`, `INPUTTED` modes. (default: `false`)
|
|
207
|
+
*/
|
|
146
208
|
management?: boolean;
|
|
147
|
-
|
|
209
|
+
/** Enable keyboard shortcuts for checkboxes, radio buttons and missing buttons (default: `false`). */
|
|
148
210
|
shortcut?: boolean;
|
|
211
|
+
/** Starting page at launch. (default: `"1"`) */
|
|
149
212
|
initialPage?: PageTag;
|
|
213
|
+
/** Furthest page the user ever reached. */
|
|
150
214
|
lastReachedPage?: PageTag;
|
|
215
|
+
/** Enable the preemptive loading of suggester data on Lunatic initialization. (default: `false`) */
|
|
151
216
|
autoSuggesterLoading?: boolean;
|
|
217
|
+
/** Function called to fetch nomenclatures used by the suggesters. */
|
|
152
218
|
getReferentiel?: (name: string) => Promise<Array<IndexEntry>>;
|
|
153
|
-
|
|
219
|
+
/** Enable data controls (form validation). (default: `false`) */
|
|
154
220
|
activeControls?: boolean;
|
|
221
|
+
/** Enable overview system. (default: `false`) */
|
|
155
222
|
withOverview?: boolean;
|
|
223
|
+
/** Enable missing system. (default: `false`) */
|
|
156
224
|
missing?: boolean;
|
|
225
|
+
/** Function triggered when a missing button is clicked. */
|
|
157
226
|
missingStrategy?: () => void;
|
|
227
|
+
/** Keyboard shortcut that triggers missing buttons. */
|
|
158
228
|
missingShortcut?: { dontKnow: string; refused: string };
|
|
229
|
+
/** "Don't know" button label. */
|
|
159
230
|
dontKnowButton?: string;
|
|
231
|
+
/** "Refused" button label. */
|
|
160
232
|
refusedButton?: string;
|
|
161
|
-
|
|
233
|
+
/** Enable change tracking to keep a track of what variable changed (allow using getChangedData()). (default: `false`) */
|
|
162
234
|
trackChanges?: boolean;
|
|
163
235
|
logger?: LunaticLogger;
|
|
164
236
|
componentsOptions?: { detailAlwaysDisplayed?: boolean };
|
|
165
237
|
};
|
|
166
238
|
|
|
167
|
-
|
|
239
|
+
/**
|
|
240
|
+
* Return type of {@link useLunatic}.
|
|
241
|
+
*
|
|
242
|
+
* Allow to operate the survey.
|
|
243
|
+
*/
|
|
168
244
|
export type LunaticState = {
|
|
245
|
+
/** Current pager. */
|
|
169
246
|
pager: LunaticPager;
|
|
170
247
|
overview: InterpretedLunaticOverviewItem[];
|
|
248
|
+
/** Current page numerotation. */
|
|
171
249
|
pageTag: PageTag;
|
|
250
|
+
/** Date of the last `handleChange` function call. */
|
|
172
251
|
updatedAt: number;
|
|
252
|
+
/** Necessary component that must wraps `LunaticComponents` to make the library works. */
|
|
173
253
|
Provider: FunctionComponent<PropsWithChildren>;
|
|
254
|
+
/** Whether or not we're in a loop. */
|
|
174
255
|
isInLoop: boolean;
|
|
256
|
+
/** Current loop's variables. */
|
|
175
257
|
loopVariables: string[];
|
|
258
|
+
/** Whether or not we're on the survey first page. */
|
|
176
259
|
isFirstPage: boolean;
|
|
260
|
+
/** Whether or not we're on the survey last page (we reached `maxPage`). */
|
|
177
261
|
isLastPage: boolean;
|
|
178
|
-
|
|
262
|
+
/** Errors in the survey. */
|
|
179
263
|
errors?: { [page: string]: { [id: string]: LunaticError[] } };
|
|
180
|
-
|
|
264
|
+
/** Errors in the current page / iteration. */
|
|
181
265
|
currentErrors?: { [id: string]: LunaticError[] };
|
|
182
|
-
|
|
266
|
+
/** Errors in modal. */
|
|
183
267
|
modalErrors?: Record<string, LunaticError[]>;
|
|
268
|
+
/** Navigate to a specific page. */
|
|
184
269
|
goToPage: (page: {
|
|
185
270
|
page: PageTag | number;
|
|
186
271
|
iteration?: number;
|
|
187
272
|
nbIterations?: number;
|
|
188
273
|
subPage?: number;
|
|
189
274
|
}) => void;
|
|
190
|
-
|
|
275
|
+
/** Navigate to the next page. */
|
|
191
276
|
goNextPage: () => void;
|
|
277
|
+
/** Navigate to the previous page. */
|
|
192
278
|
goPreviousPage: () => void;
|
|
279
|
+
/** Allow to fetch controls. */
|
|
193
280
|
compileControls: () => {
|
|
194
281
|
currentErrors: Record<string, LunaticError[]> | undefined;
|
|
195
282
|
isCritical: boolean;
|
|
196
283
|
};
|
|
284
|
+
/**
|
|
285
|
+
* Components to display in the current page.
|
|
286
|
+
*
|
|
287
|
+
* Return an array with the various components' properties. The orchestrator
|
|
288
|
+
* has to handle how they are displayed, using the `componentType` property to
|
|
289
|
+
* select the appropriate component.
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* // using `LunaticComponents`
|
|
293
|
+
* import { useLunatic, LunaticComponents } from '@inseefr/lunatic';
|
|
294
|
+
*
|
|
295
|
+
* function App({ source, data }) {
|
|
296
|
+
* const { getComponents, Provider } = useLunatic(source, data, {});
|
|
297
|
+
* const components = getComponents();
|
|
298
|
+
*
|
|
299
|
+
* return (
|
|
300
|
+
* <Provider>
|
|
301
|
+
* <LunaticComponents components={components} />
|
|
302
|
+
* </Provider>
|
|
303
|
+
* );
|
|
304
|
+
* }
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* // using custom components
|
|
308
|
+
* import { useLunatic, LunaticComponents } from '@inseefr/lunatic';
|
|
309
|
+
*
|
|
310
|
+
* const customCompoonents = {
|
|
311
|
+
* Input: MyCustomInput,
|
|
312
|
+
* InputNumber: MyCustomInputNumber,
|
|
313
|
+
* };
|
|
314
|
+
*
|
|
315
|
+
* function App({ source, data }) {
|
|
316
|
+
* const { getComponents, Provider } = useLunatic(source, data, {});
|
|
317
|
+
* const components = getComponents();
|
|
318
|
+
*
|
|
319
|
+
* return (
|
|
320
|
+
* <Provider>
|
|
321
|
+
* <LunaticComponents components={components} slots={customComponents} />
|
|
322
|
+
* </Provider>
|
|
323
|
+
* );
|
|
324
|
+
* }
|
|
325
|
+
*
|
|
326
|
+
* @see {@link LunaticComponents}
|
|
327
|
+
*/
|
|
197
328
|
getComponents: () => LunaticComponentProps[];
|
|
329
|
+
/** Get data collected by the survey. */
|
|
198
330
|
getData: (
|
|
199
331
|
withRefreshedCalculated: boolean,
|
|
200
332
|
variableNames?: string[]
|
|
201
333
|
) => LunaticData;
|
|
202
|
-
|
|
334
|
+
/** Get data that have changed since last reset. Returns the same thing as `getData()`. */
|
|
335
|
+
getChangedData: (reset?: boolean) => LunaticData;
|
|
336
|
+
/** Empty the store of changed variables. */
|
|
203
337
|
resetChangedData: () => void;
|
|
338
|
+
/** Return `true` as soon as the current page has at least one answer. */
|
|
204
339
|
hasPageResponse: () => boolean;
|
|
205
|
-
|
|
340
|
+
/** Used for testing purpose only. */
|
|
206
341
|
testing: {
|
|
207
342
|
handleChanges: LunaticChangesHandler;
|
|
208
343
|
};
|
|
209
344
|
};
|
|
210
345
|
|
|
346
|
+
/** Function taking as arguments the various changes the user has made. */
|
|
211
347
|
export type LunaticChangesHandler = (
|
|
212
348
|
args: {
|
|
213
349
|
name: string;
|
|
@@ -66,11 +66,19 @@ const defaultOptions = {
|
|
|
66
66
|
componentsOptions: { detailAlwaysDisplayed: false },
|
|
67
67
|
} satisfies LunaticOptions;
|
|
68
68
|
|
|
69
|
+
/** The first library entrypoint is the `useLunatic` hook. */
|
|
69
70
|
export function useLunatic(
|
|
71
|
+
/**
|
|
72
|
+
* JSON representation of our survey unit in the Lunatic Model.
|
|
73
|
+
*
|
|
74
|
+
* {@link https://github.com/InseeFr/Lunatic-Model}
|
|
75
|
+
*/
|
|
70
76
|
source: LunaticSource,
|
|
77
|
+
/** Initial survey data (i.e. if it has been partially filled). */
|
|
71
78
|
data: LunaticData = DEFAULT_DATA,
|
|
79
|
+
/** Specific behaviour options. */
|
|
72
80
|
argOptions: LunaticOptions = empty
|
|
73
|
-
) {
|
|
81
|
+
): LunaticState {
|
|
74
82
|
const options = mergeDefault(argOptions, defaultOptions);
|
|
75
83
|
const {
|
|
76
84
|
disableFilters,
|
|
@@ -106,7 +114,7 @@ export function useLunatic(
|
|
|
106
114
|
reducerInitializer
|
|
107
115
|
);
|
|
108
116
|
|
|
109
|
-
|
|
117
|
+
/** Required context provider: cleaner than prop drilling through every component */
|
|
110
118
|
const Provider = useMemo(
|
|
111
119
|
() =>
|
|
112
120
|
createLunaticProvider({
|
|
@@ -157,6 +165,7 @@ export function useLunatic(
|
|
|
157
165
|
},
|
|
158
166
|
[dispatch]
|
|
159
167
|
);
|
|
168
|
+
|
|
160
169
|
const handleChanges = useCallback<LunaticChangesHandler>(
|
|
161
170
|
(responses) => {
|
|
162
171
|
dispatch(handleChangesAction(responses));
|