Lunatic is a front-end library in the form of a React hook and component libraries for generating a questionnaire from the Lunatic-Model data format.
- Storybook 3.0, branch
3.0
- Storybook 2.7, branch
2.7
- Storybook 2.6, branch
2.6
- Storybook v1, branch
v1-main
- Documentation
To get started, you need to install Lunatic:
yarn add @inseefr/lunatic
Next, wherever you want to display the form, you'll need to use the useLunatic
hook.
import { useLunatic } from '@inseefr/lunatic';
const obj = useLunatic(source, data, options);
This hook takes three parameters:
- The source, which is a JSON representation of the Lunatic-Model.
- The data, which contains the initial questionnaire data (can be an empty object).
- An options object to configure the behavior.
- features (default
['VTL', 'MD']
), allows you to define supported functionalities. - preferences (default
['COLLECTED']
). - onChange (default
() => {}
), allows you to add logic to apply when an answer is changed (must be memoized as it's used as a dependency in an internaluseCallback
). - management (default
false
): Not yet implemented, will allow managing multiple states of the same variable (used by recovery positions). - initialPage (default
'1'
), allows you to define the starting page. - lastReachedPage (default
undefined
), allows you to define the furthest reached page. - autoSuggesterLoading (default
false
). - suggesters.
- suggesterFetcher (default
fetch
), method used to retrieve suggester data. - activeControls (default
false
), activates data controls.
- features (default
And it returns an object that allows you to control the questionnaire:
getComponents()
, returns the components to display for the current page.goPreviousPage()
, allows you to go to the previous page.goNextPage()
, allows you to go to the next page.goToPage({ page: string })
, allows you to go to an arbitrary page.getErrors()
, returns the errors.getModalErrors()
, returns the errors in modals.getCurrentErrors()
, returns the errors for the current page.pageTag
, a string containing the page number (e.g., 8.1).isFirstPage
.isLastPage
.pager
, an object containing information related to the page.waiting
, indicates waiting for information from a suggester.getData()
, returns the collected data in the questionnaire.loopVariables
, is an array containing the list of variables used for the current loop.
For more information on the types of this return, you can refer to the available types in the source code. You can also find an example of using the hook in the Storybook section.
To display the questionnaire, start by retrieving the list of components to display using the getComponents()
method returned by the hook.
Lunatic offers a library of pre-designed components to cover the different types of fields available in questionnaires.
import * as lunatic from '@inseefr/lunatic';
function App({ source, data }) {
const { getComponents, getCurrentErrors, getModalErrors } =
lunatic.useLunatic(source, data, {});
const components = getComponents();
const currentErrors = getCurrentErrors();
const modalErrors = getModalErrors();
return (
<div className="container">
<LunaticComponents components={components} />
<lunatic.Modal errors={modalErrors} goNext={goNextPageAction} />
</div>
);
}
All the components offered by Lunatic are available in the src/components folder.
To activate the autofocus, you need to pass a key in the autoFocusKey
property of LunaticComponents
. As soon as this value changes, the first field is focused (a good solution is to pass the pageTag
provided by useLunatic
).
By default, the components offered by Lunatic are quite simple with a minimal style. You can customize the fields with your own CSS, but for more complex cases, you can also replace the basic components using the custom
property that you can pass when calling useLunatic
.
const custom = {
Input: MyCustomInput,
InputNumber: MyCustomInputNumber,
};
function App({ source, data }) {
const {} = useLunatic(source, data, { custom });
// ...
}
This section covers the internal working of the useLunatic()
hook. The goal is to help understand how it operates.
The hook is based on an internal state that is updated through a reducer system. The actions affecting this state are limited:
- An action
'use-lunatic/on-init'
allows initialization of the state from the data received as a parameter of the hook. - The actions
'use-lunatic/go-previous'
,'use-lunatic/go-next'
, and'use-lunatic/go-to-page'
are called during navigation using the methods returned by the hook. - The action
use-lunatic/handle-change
is the most important action and is called whenever data is changed in the questionnaire.
All the reducers corresponding to these actions are available here. Generally, they are broken down into several methods depending on the part of the state they modify.
At initialization, the questionnaire scenario is modeled as an object which is stored in the state (in the pages
property). This object is indexed by page number and contains the list of components to display for each page. Combined with the pager
which contains the state of navigation, this property allows resolving the components to display.
Another important point of Lunatic is the execution of VTL expressions which allow making certain properties dynamic (labels, errors, etc.). This filling is done when the state changes.
To facilitate expression execution, an executeExpression()
method is exposed in the Lunatic state. This method is accompanied by an updateBindings()
method which allows updating internal values. This expression execution system uses a memoization system to not re-execute the same expression multiple times. When the use-lunatic/handle-change
action is executed, the values ("bindings") are updated to memorize the values associated with the different VTL variables. Similarly, the values of calculated variables on which the modified variable depends are forgotten to refresh the value during the next execution.
- Stable branches follow the glob pattern
'2.*'
or'3.*'
, like2.7
or3.0
. - We can maintain if needs, the old stable branches
3.0
branch is currently the most advanced branch
-
Avoid "default" exports as it impairs readability during import.
-
Comments in the code should be in English.
-
Files containing JSX should use the .jsx (or .tsx) extension.
-
Commits follow the specification Conventional Commits.
-
Pull Requests should be prefixed in the same convention as commits:
test(XXX?)
: XXX for adding tests.feat(XXX?)
: XXX for adding new features.fix(XXX?)
: XXX for bug fixes.docs(XXX?)
: XXX for adding documentation.refactor(XXX?)
: XXX for refactoring that doesn't change functionality.build(XXX?)
: XXX for changes related to the build process, compilation scripts, etc.style(XXX?)
: XXX for style modifications.ci(XXX?)
: XXX for CI modification.perf(XXX?)
: XXX for performance improvement.revert(XXX?)
: XXX to revert a previous PR.chore(XXX?)
: XXX for maintenance tasks or tasks that don't fall into other categories.
-
Branches should be prefixed (following the same prefixes as Conventional Commits):
test/XXX
: for adding tests.feat/XXX
: for adding new features.fix/XXX
: for bug fixes.docs/XXX
: for adding documentation.refactor/XXX
: for refactoring that doesn't change functionality.build/XXX
: for changes related to the build process, compilation scripts, etc.style/XXX
: for changes related to code style.ci/XXX
: for changes related to continuous integration (CI).perf/XXX
: for performance improvements.revert/XXX
: to revert a previous PR.chore/XXX
: for maintenance tasks or tasks that don't fall into other categories.
You have made some changes to the code and you want to test them in your app before submitting a pull request?
Assuming you/my-app
have @inseefr/lunatic
as a dependency.
cd ~/github
git clone https://github.com/you/my-app
cd my-app
yarn
cd ~/github
git clone https://github.com/InseeFr/Lunatic
cd Lunatic
yarn
yarn build
yarn link-in-app my-app
npx tsc -w
# Open another terminal
cd ~/github/my-app
rm -rf node_modules/.cache
yarn start # Or whatever my-app is using for starting the project
You don't have to use ~/github
as reference path. Just make sure my-app
and @inseefr/lunatic
are in the same directory.
Note for the maintainer: You might run into issues if you do not list all your singleton dependencies in
src/link-in-app.js -> singletonDependencies
. A singleton dependency is a dependency that can only be present once in an App. Singleton dependencies are usually listed as peerDependencies examplereact
,@emotion/*
.
For releasing a new version on GitHub and NPM you don't need to create a tag.
Just update the package.json
version number and push.
For publishing a release candidate update your package.json
with 1.3.4-rc.0
(.1
, .2
, ...).
It also work if you do it from a branch that have an open PR on main.
Make sure your have defined the
NPM_TOKEN
repository secret or NPM publishing will fail.
We build this library in ESM and CJS. ESM is the standard when you develop a front-end app.
But if you need test which use @inseefr/lunatic
, you need lunatic library as CJS lib (to allow to run in node environnement).
So we have two build: one for ESM and one for CJS.
You have nothing to change in your code base, it's simply working.