From 09e80607bda9217733fbd96ba0f7b658bc71d15b Mon Sep 17 00:00:00 2001 From: Jonathan Date: Thu, 16 May 2024 12:38:28 +0200 Subject: [PATCH 1/3] feat: add fillers behaviour Fix #949 --- e2e/fillers.spec.ts | 25 ++++ lunatic-schema.json | 33 +++++ src/components/FillerLoader/FillerLoader.tsx | 11 ++ src/components/library.ts | 2 + .../shared/HOC/slottableComponent.tsx | 2 + src/components/type.ts | 3 + .../behaviour/fillers/fillers.stories.js | 31 +++++ src/stories/behaviour/fillers/source.json | 77 +++++++++++ src/stories/utils/orchestrator.jsx | 4 + src/use-lunatic/hooks/useFillers.ts | 123 ++++++++++++++++++ src/use-lunatic/type.ts | 5 + src/use-lunatic/use-lunatic.ts | 44 +++++-- 12 files changed, 347 insertions(+), 13 deletions(-) create mode 100644 e2e/fillers.spec.ts create mode 100644 src/components/FillerLoader/FillerLoader.tsx create mode 100644 src/stories/behaviour/fillers/fillers.stories.js create mode 100644 src/stories/behaviour/fillers/source.json create mode 100644 src/use-lunatic/hooks/useFillers.ts diff --git a/e2e/fillers.spec.ts b/e2e/fillers.spec.ts new file mode 100644 index 000000000..11a850794 --- /dev/null +++ b/e2e/fillers.spec.ts @@ -0,0 +1,25 @@ +import { expect, test } from '@playwright/test'; +import { + expectPageToHaveText, + gotoNextPage, + gotoPreviousPage, + goToStory, +} from './utils'; + +test.describe('Fillers', () => { + test(`can fill data`, async ({ page }) => { + await goToStory(page, 'behaviour-fillers--default#t100'); + + // First filling + await page.getByLabel('Code postal').fill('34000'); + await gotoNextPage(page); + await expectPageToHaveText(page, 'Chargement'); + await expect(page.getByLabel('Ville')).toHaveValue('Montpellier'); + + // Second filling + await gotoPreviousPage(page); + await page.getByLabel('Code postal').fill('31000'); + await gotoNextPage(page); + await expect(page.getByLabel('Ville')).toHaveValue('Toulouse'); + }); +}); diff --git a/lunatic-schema.json b/lunatic-schema.json index 20bcb0d5f..7ffba24ab 100644 --- a/lunatic-schema.json +++ b/lunatic-schema.json @@ -172,6 +172,12 @@ }, "maxPage": { "type": "string" + }, + "fillers": { + "type": "array", + "items": { + "$ref": "#/$defs/FillerDefinition" + } } }, "required": ["components", "variables"], @@ -1461,6 +1467,33 @@ } }, "required": ["name", "fields", "queryParser", "version"] + }, + "FillerDefinition": { + "type": "object", + "properties": { + "endpoint": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"] + }, + "responses": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"] + } + } + }, + "required": ["responses", "endpoint"] } } } diff --git a/src/components/FillerLoader/FillerLoader.tsx b/src/components/FillerLoader/FillerLoader.tsx new file mode 100644 index 000000000..c87b2acc7 --- /dev/null +++ b/src/components/FillerLoader/FillerLoader.tsx @@ -0,0 +1,11 @@ +import { slottableComponent } from '../shared/HOC/slottableComponent'; + +/** + * Displays a loader while fetching data to fill the form + */ +export const FillerLoader = slottableComponent( + 'FillerLoader', + function FillerLoader() { + return

Chargement des données...

; + } +); diff --git a/src/components/library.ts b/src/components/library.ts index 632f5d5a0..0d55158a9 100644 --- a/src/components/library.ts +++ b/src/components/library.ts @@ -24,6 +24,7 @@ import { PairwiseLinks } from './PairwiseLinks/PairwiseLinks'; import { CheckboxOne } from './CheckboxOne/CheckboxOne'; import { Suggester } from './Suggester/Suggester'; import { Summary } from './Summary/Summary'; +import { FillerLoader } from './FillerLoader/FillerLoader'; // List of all the "componentType" export const library = { @@ -51,6 +52,7 @@ export const library = { Suggester: Suggester, Summary: Summary, Accordion: Accordion, + FillerLoader: FillerLoader, } satisfies { [Property in LunaticComponentType]: ComponentType< LunaticComponentProps diff --git a/src/components/shared/HOC/slottableComponent.tsx b/src/components/shared/HOC/slottableComponent.tsx index e09e5a02c..c7cb4feec 100644 --- a/src/components/shared/HOC/slottableComponent.tsx +++ b/src/components/shared/HOC/slottableComponent.tsx @@ -43,6 +43,7 @@ import type { SummaryResponses, SummaryTitle } from '../../Summary/Summary'; import type { LunaticComponentProps } from '../../type'; import type { MarkdownLink } from '../MDLabel/MarkdownLink'; import type { Accordion } from '../../Accordion/Accordion'; +import type { FillerLoader } from '../../FillerLoader/FillerLoader'; /** * Contains the type of every customizable component @@ -109,6 +110,7 @@ export type LunaticSlotComponents = { >; MarkdownLink: typeof MarkdownLink; Accordion: typeof Accordion; + FillerLoader: typeof FillerLoader; }; const empty = {} as Partial | undefined; diff --git a/src/components/type.ts b/src/components/type.ts index fcce36753..89994bf7b 100644 --- a/src/components/type.ts +++ b/src/components/type.ts @@ -304,6 +304,9 @@ export type ComponentPropsByType = { iterations?: VtlExpression; }>; }; + FillerLoader: { + componentType?: 'FillerLoader'; + }; }; export type LunaticComponentType = keyof ComponentPropsByType; diff --git a/src/stories/behaviour/fillers/fillers.stories.js b/src/stories/behaviour/fillers/fillers.stories.js new file mode 100644 index 000000000..e854e2a78 --- /dev/null +++ b/src/stories/behaviour/fillers/fillers.stories.js @@ -0,0 +1,31 @@ +import source from './source.json'; +import Orchestrator from '../../utils/orchestrator.jsx'; + +export default { + title: 'Behaviour/Fillers', + component: Orchestrator, +}; + +export const Default = { + args: { + source: source, + data: {}, + mockFiller: (data) => { + return new Promise((resolve) => { + setTimeout( + () => { + if (data.CODE === '34000') { + return resolve({ CITY: 'Montpellier' }); + } else if (data.CODE === '31000') { + return resolve({ CITY: 'Toulouse' }); + } + return resolve({}); + }, + window.location.hash.startsWith('#t') + ? parseInt(window.location.hash.replace('#t', ''), 10) + : 1500 + ); + }); + }, + }, +}; diff --git a/src/stories/behaviour/fillers/source.json b/src/stories/behaviour/fillers/source.json new file mode 100644 index 000000000..80b4328d4 --- /dev/null +++ b/src/stories/behaviour/fillers/source.json @@ -0,0 +1,77 @@ +{ + "$schema": "../../../../lunatic-schema.json", + "maxPage": "2", + "fillers": [ + { + "endpoint": { + "url": "https://inseefr.github.io/Lunatic/" + }, + "responses": [ + { + "name": "CODE" + } + ] + } + ], + "components": [ + { + "id": "a", + "page": "1", + "componentType": "Input", + "label": { + "type": "TXT", + "value": "Code postal" + }, + "response": { + "name": "CODE" + }, + "declarations": [ + { + "declarationType": "COMMENT", + "position": "DETACHABLE", + "id": "d_a", + "label": { + "value": "Ce code permettra de remplir la ville (34000 ou 31000)", + "type": "TXT" + } + } + ] + }, + { + "id": "a", + "page": "2", + "componentType": "Input", + "label": { + "type": "TXT", + "value": "Ville" + }, + "response": { + "name": "CITY" + } + } + ], + "variables": [ + { + "variableType": "COLLECTED", + "values": { + "COLLECTED": null, + "EDITED": null, + "INPUTTED": null, + "FORCED": null, + "PREVIOUS": null + }, + "name": "CODE" + }, + { + "variableType": "COLLECTED", + "values": { + "COLLECTED": null, + "EDITED": null, + "INPUTTED": null, + "FORCED": null, + "PREVIOUS": null + }, + "name": "CITY" + } + ] +} diff --git a/src/stories/utils/orchestrator.jsx b/src/stories/utils/orchestrator.jsx index c70643746..dc716bc34 100644 --- a/src/stories/utils/orchestrator.jsx +++ b/src/stories/utils/orchestrator.jsx @@ -119,6 +119,7 @@ function OrchestratorForStories({ refusedButton, readOnly = false, disabled = false, + mockFiller = null, ...rest }) { const { maxPage } = source; @@ -154,6 +155,9 @@ function OrchestratorForStories({ withOverview: showOverview, dontKnowButton, refusedButton, + mocks: { + filler: mockFiller, + }, }); const components = getComponents(); diff --git a/src/use-lunatic/hooks/useFillers.ts b/src/use-lunatic/hooks/useFillers.ts new file mode 100644 index 000000000..2a2c0424c --- /dev/null +++ b/src/use-lunatic/hooks/useFillers.ts @@ -0,0 +1,123 @@ +import type { FillerDefinition } from '../../type.source'; +import type { LunaticVariablesStore } from '../commons/variables/lunatic-variables-store'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import type { LunaticChangesHandler } from '../type'; + +type Args = { + variables: LunaticVariablesStore; + fillers: FillerDefinition[]; + handleChanges: LunaticChangesHandler; + fetchMock: + | null + | ((data: Record) => Promise>); +}; + +/** + * Behaviour that fills variables from a server when some variables are collected + * The filling happens when the user move forward in the form + */ +export function useFillers({ + variables, + fillers, + handleChanges, + fetchMock, +}: Args) { + const watchedVariables = useMemo( + () => buildWatchedVariableMap(fillers), + [fillers] + ); + const activeFillers = useRef(new Set()); // List fillers that should be triggerred in the next navigation + const [isFilling, setFilling] = useState(false); + + // Listen for change on variables to detect if a filler need to be triggered on next page change + useEffect(() => { + const listener = ( + e: CustomEvent<{ + name: string; + }> + ) => { + if (watchedVariables.has(e.detail.name)) { + activeFillers.current.add(watchedVariables.get(e.detail.name)!); + } + }; + variables.on('change', listener); + return () => { + variables.off('change', listener); + }; + }, [variables, watchedVariables]); + + // Trigger fillers + const triggerFillers = useCallback(async () => { + if (activeFillers.current.size === 0) { + return; + } + setFilling(true); + Promise.all( + Array.from(activeFillers.current).map((filler) => { + const values = Object.fromEntries( + filler.responses.map((r) => [r.name, variables.get(r.name)]) + ); + return fetchFillerData(filler, values, fetchMock).then((data) => { + handleChanges( + Object.entries(data).map((d) => ({ + name: d[0], + value: d[1], + })) + ); + }); + }) + ) + .catch((e) => { + console.error(e); + alert(e); + }) + .finally(() => { + setFilling(false); + activeFillers.current.clear(); + }); + }, [activeFillers, variables, handleChanges]); + + return { + triggerFillers, + isFilling, + }; +} + +/** + * Creates a map of FillerDefinition indexed by variable name (improves performance) + */ +function buildWatchedVariableMap( + definitions: FillerDefinition[] +): Map { + const map = new Map(); + for (const definition of definitions) { + for (const response of definition.responses) { + map.set(response.name, definition); + } + } + return map; +} + +/** + * Fetch new data from the server (use mock if it exists) + */ +function fetchFillerData( + filler: FillerDefinition, + data: Record, + mock: + | null + | ((data: Record) => Promise>) +): Promise> { + if (mock) { + return mock(data); + } + + return fetch(filler.endpoint.url, { + method: 'POST', + body: JSON.stringify(data), + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + }).then((res) => res.json()); +} diff --git a/src/use-lunatic/type.ts b/src/use-lunatic/type.ts index ef3b18b0e..1f695fd6e 100644 --- a/src/use-lunatic/type.ts +++ b/src/use-lunatic/type.ts @@ -157,6 +157,11 @@ export type LunaticOptions = { // Enable change tracking to keep a track of what variable changed (allow using getChangedData()) trackChanges?: boolean; logger?: LunaticLogger; + mocks?: { + filler: + | null + | ((data: Record) => Promise>); + }; }; // Type representing the return type of "useLunatic()" diff --git a/src/use-lunatic/use-lunatic.ts b/src/use-lunatic/use-lunatic.ts index e64a63d09..906b6465d 100644 --- a/src/use-lunatic/use-lunatic.ts +++ b/src/use-lunatic/use-lunatic.ts @@ -31,6 +31,7 @@ import { mergeDefault } from '../utils/object'; import { useRefSync } from '../hooks/useRefSync'; import { ConsoleLogger } from './logger/ConsoleLogger'; import { useWarnDepChange } from './hooks/useWarnDepChange'; +import { useFillers } from './hooks/useFillers'; const empty = {}; // Keep the same empty object (to avoid problem with useEffect dependencies) const DEFAULT_DATA = empty as LunaticData; @@ -82,6 +83,7 @@ export function useLunatic( trackChanges, preferences, logger, + mocks, } = options; // Help debug with warnings for options expected to be memoized @@ -136,19 +138,6 @@ export function useLunatic( [dispatch] ); - const goNextPage: LunaticState['goNextPage'] = useCallback( - function (payload = {}) { - dispatch(goNextPageAction(payload)); - }, - [dispatch] - ); - - const goToPage: LunaticState['goToPage'] = useCallback( - function (payload) { - dispatch(goToPageAction(payload)); - }, - [dispatch] - ); const handleChanges = useCallback( (responses) => { dispatch(handleChangesAction(responses)); @@ -178,6 +167,28 @@ export function useLunatic( const pageTag = getPageTag(state.pager); const { isFirstPage, isLastPage } = isFirstLastPage(state.pager); + const { triggerFillers, isFilling } = useFillers({ + variables: state.variables, + fillers: source.fillers ?? [], + handleChanges, + fetchMock: mocks?.filler ?? null, + }); + + const goNextPage: LunaticState['goNextPage'] = useCallback( + function (payload = {}) { + dispatch(goNextPageAction(payload)); + triggerFillers(); + }, + [dispatch] + ); + + const goToPage: LunaticState['goToPage'] = useCallback( + function (payload) { + dispatch(goToPageAction(payload)); + }, + [dispatch] + ); + const components = fillComponents(getComponentsFromState(state), { handleChanges, preferences, @@ -190,6 +201,13 @@ export function useLunatic( }); const getComponents: LunaticState['getComponents'] = () => { + if (isFilling) { + return [ + { + componentType: 'FillerLoader', + }, + ]; + } return components; }; From 1b19ac830eaa80a4a35c5c97c70e10861e45891c Mon Sep 17 00:00:00 2001 From: Jonathan Date: Mon, 5 Aug 2024 11:58:32 +0200 Subject: [PATCH 2/3] ref: use get method for fillers --- lunatic-schema.json | 3 + .../behaviour/fillers/fillers.stories.js | 7 +- src/stories/behaviour/fillers/source.json | 151 +++++++++--------- src/type.source.ts | 10 ++ src/use-lunatic/hooks/useFillers.ts | 37 +++-- src/use-lunatic/type.ts | 4 +- src/use-lunatic/use-lunatic.ts | 15 +- 7 files changed, 122 insertions(+), 105 deletions(-) diff --git a/lunatic-schema.json b/lunatic-schema.json index 7ffba24ab..2a20e9a59 100644 --- a/lunatic-schema.json +++ b/lunatic-schema.json @@ -1476,6 +1476,9 @@ "properties": { "url": { "type": "string" + }, + "type": { + "enum": ["VTL", "TXT"] } }, "required": ["url"] diff --git a/src/stories/behaviour/fillers/fillers.stories.js b/src/stories/behaviour/fillers/fillers.stories.js index e854e2a78..57fa603d0 100644 --- a/src/stories/behaviour/fillers/fillers.stories.js +++ b/src/stories/behaviour/fillers/fillers.stories.js @@ -10,13 +10,14 @@ export const Default = { args: { source: source, data: {}, - mockFiller: (data) => { + mockFiller: (url) => { return new Promise((resolve) => { setTimeout( () => { - if (data.CODE === '34000') { + const code = new URL(url).searchParams.get('code'); + if (code === '34000') { return resolve({ CITY: 'Montpellier' }); - } else if (data.CODE === '31000') { + } else if (code === '31000') { return resolve({ CITY: 'Toulouse' }); } return resolve({}); diff --git a/src/stories/behaviour/fillers/source.json b/src/stories/behaviour/fillers/source.json index 80b4328d4..00de6a856 100644 --- a/src/stories/behaviour/fillers/source.json +++ b/src/stories/behaviour/fillers/source.json @@ -1,77 +1,78 @@ { - "$schema": "../../../../lunatic-schema.json", - "maxPage": "2", - "fillers": [ - { - "endpoint": { - "url": "https://inseefr.github.io/Lunatic/" - }, - "responses": [ - { - "name": "CODE" - } - ] - } - ], - "components": [ - { - "id": "a", - "page": "1", - "componentType": "Input", - "label": { - "type": "TXT", - "value": "Code postal" - }, - "response": { - "name": "CODE" - }, - "declarations": [ - { - "declarationType": "COMMENT", - "position": "DETACHABLE", - "id": "d_a", - "label": { - "value": "Ce code permettra de remplir la ville (34000 ou 31000)", - "type": "TXT" - } - } - ] - }, - { - "id": "a", - "page": "2", - "componentType": "Input", - "label": { - "type": "TXT", - "value": "Ville" - }, - "response": { - "name": "CITY" - } - } - ], - "variables": [ - { - "variableType": "COLLECTED", - "values": { - "COLLECTED": null, - "EDITED": null, - "INPUTTED": null, - "FORCED": null, - "PREVIOUS": null - }, - "name": "CODE" - }, - { - "variableType": "COLLECTED", - "values": { - "COLLECTED": null, - "EDITED": null, - "INPUTTED": null, - "FORCED": null, - "PREVIOUS": null - }, - "name": "CITY" - } - ] + "$schema": "../../../../lunatic-schema.json", + "maxPage": "2", + "fillers": [ + { + "endpoint": { + "url": "\"https://inseefr.github.io/Lunatic/?code=\" || CODE", + "type": "VTL" + }, + "responses": [ + { + "name": "CODE" + } + ] + } + ], + "components": [ + { + "id": "a", + "page": "1", + "componentType": "Input", + "label": { + "type": "TXT", + "value": "Code postal" + }, + "response": { + "name": "CODE" + }, + "declarations": [ + { + "declarationType": "COMMENT", + "position": "DETACHABLE", + "id": "d_a", + "label": { + "value": "Ce code permettra de remplir la ville (34000 ou 31000)", + "type": "TXT" + } + } + ] + }, + { + "id": "a", + "page": "2", + "componentType": "Input", + "label": { + "type": "TXT", + "value": "Ville" + }, + "response": { + "name": "CITY" + } + } + ], + "variables": [ + { + "variableType": "COLLECTED", + "values": { + "COLLECTED": null, + "EDITED": null, + "INPUTTED": null, + "FORCED": null, + "PREVIOUS": null + }, + "name": "CODE" + }, + { + "variableType": "COLLECTED", + "values": { + "COLLECTED": null, + "EDITED": null, + "INPUTTED": null, + "FORCED": null, + "PREVIOUS": null + }, + "name": "CITY" + } + ] } diff --git a/src/type.source.ts b/src/type.source.ts index e58d75700..f247011cb 100644 --- a/src/type.source.ts +++ b/src/type.source.ts @@ -337,6 +337,7 @@ export type LunaticSource = { }; }; maxPage?: string; + fillers?: FillerDefinition[]; }; export type VTLExpression = { /** @@ -467,3 +468,12 @@ export type SuggesterDefinition = { type: 'soft'; }; }; +export type FillerDefinition = { + endpoint: { + url: string; + type?: 'VTL' | 'TXT'; + }; + responses: { + name: string; + }[]; +}; diff --git a/src/use-lunatic/hooks/useFillers.ts b/src/use-lunatic/hooks/useFillers.ts index 2a2c0424c..977c72db8 100644 --- a/src/use-lunatic/hooks/useFillers.ts +++ b/src/use-lunatic/hooks/useFillers.ts @@ -1,15 +1,15 @@ import type { FillerDefinition } from '../../type.source'; import type { LunaticVariablesStore } from '../commons/variables/lunatic-variables-store'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import type { LunaticChangesHandler } from '../type'; +import type { LunaticChangesHandler, LunaticReducerState } from '../type'; +import { useRefSync } from '../../hooks/useRefSync'; type Args = { variables: LunaticVariablesStore; fillers: FillerDefinition[]; handleChanges: LunaticChangesHandler; - fetchMock: - | null - | ((data: Record) => Promise>); + fetchMock: null | ((url: string) => Promise>); + executeExpression: LunaticReducerState['executeExpression']; }; /** @@ -21,7 +21,9 @@ export function useFillers({ fillers, handleChanges, fetchMock, + executeExpression, }: Args) { + const executeExpressionRef = useRefSync(executeExpression); const watchedVariables = useMemo( () => buildWatchedVariableMap(fillers), [fillers] @@ -54,10 +56,11 @@ export function useFillers({ setFilling(true); Promise.all( Array.from(activeFillers.current).map((filler) => { - const values = Object.fromEntries( - filler.responses.map((r) => [r.name, variables.get(r.name)]) - ); - return fetchFillerData(filler, values, fetchMock).then((data) => { + return fetchFillerData( + filler, + executeExpressionRef.current, + fetchMock + ).then((data) => { handleChanges( Object.entries(data).map((d) => ({ name: d[0], @@ -103,20 +106,20 @@ function buildWatchedVariableMap( */ function fetchFillerData( filler: FillerDefinition, - data: Record, - mock: - | null - | ((data: Record) => Promise>) + executeExpression: LunaticReducerState['executeExpression'], + mock: null | ((url: string) => Promise>) ): Promise> { + const endpoint = + filler.endpoint.type === 'VTL' + ? executeExpression({ value: filler.endpoint.url, type: 'VTL' }) + : filler.endpoint.url; + if (mock) { - return mock(data); + return mock(endpoint); } - return fetch(filler.endpoint.url, { - method: 'POST', - body: JSON.stringify(data), + return fetch(endpoint, { headers: { - 'Content-Type': 'application/json', Accept: 'application/json', }, }).then((res) => res.json()); diff --git a/src/use-lunatic/type.ts b/src/use-lunatic/type.ts index 1f695fd6e..b0b9baa49 100644 --- a/src/use-lunatic/type.ts +++ b/src/use-lunatic/type.ts @@ -158,9 +158,7 @@ export type LunaticOptions = { trackChanges?: boolean; logger?: LunaticLogger; mocks?: { - filler: - | null - | ((data: Record) => Promise>); + filler: null | ((url: string) => Promise>); }; }; diff --git a/src/use-lunatic/use-lunatic.ts b/src/use-lunatic/use-lunatic.ts index 906b6465d..82684f4cc 100644 --- a/src/use-lunatic/use-lunatic.ts +++ b/src/use-lunatic/use-lunatic.ts @@ -172,6 +172,7 @@ export function useLunatic( fillers: source.fillers ?? [], handleChanges, fetchMock: mocks?.filler ?? null, + executeExpression: state.executeExpression, }); const goNextPage: LunaticState['goNextPage'] = useCallback( @@ -201,13 +202,13 @@ export function useLunatic( }); const getComponents: LunaticState['getComponents'] = () => { - if (isFilling) { - return [ - { - componentType: 'FillerLoader', - }, - ]; - } + if (isFilling) { + return [ + { + componentType: 'FillerLoader', + }, + ]; + } return components; }; From 19aa32a3ce08c90468373b33dda43aebf0a2f1cc Mon Sep 17 00:00:00 2001 From: Jonathan Date: Mon, 5 Aug 2024 12:39:04 +0200 Subject: [PATCH 3/3] fix: id props for loader --- src/components/FillerLoader/FillerLoader.tsx | 2 +- src/components/type.ts | 7 ++++--- src/use-lunatic/use-lunatic.ts | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/FillerLoader/FillerLoader.tsx b/src/components/FillerLoader/FillerLoader.tsx index c87b2acc7..42f536e88 100644 --- a/src/components/FillerLoader/FillerLoader.tsx +++ b/src/components/FillerLoader/FillerLoader.tsx @@ -5,7 +5,7 @@ import { slottableComponent } from '../shared/HOC/slottableComponent'; */ export const FillerLoader = slottableComponent( 'FillerLoader', - function FillerLoader() { + function FillerLoader(props: { id: string }) { return

Chargement des données...

; } ); diff --git a/src/components/type.ts b/src/components/type.ts index 89994bf7b..76059735e 100644 --- a/src/components/type.ts +++ b/src/components/type.ts @@ -304,9 +304,10 @@ export type ComponentPropsByType = { iterations?: VtlExpression; }>; }; - FillerLoader: { - componentType?: 'FillerLoader'; - }; + FillerLoader: { + componentType?: 'FillerLoader'; + id: string; + }; }; export type LunaticComponentType = keyof ComponentPropsByType; diff --git a/src/use-lunatic/use-lunatic.ts b/src/use-lunatic/use-lunatic.ts index 82684f4cc..01c00a43a 100644 --- a/src/use-lunatic/use-lunatic.ts +++ b/src/use-lunatic/use-lunatic.ts @@ -206,6 +206,7 @@ export function useLunatic( return [ { componentType: 'FillerLoader', + id: 'lunatic-filler-loader', }, ]; }