{"id":24775680,"url":"https://github.com/inseefr/lunatic","last_synced_at":"2026-02-10T17:05:57.646Z","repository":{"id":34241004,"uuid":"170139394","full_name":"InseeFr/Lunatic","owner":"InseeFr","description":"Library of questionnaire components","archived":false,"fork":false,"pushed_at":"2025-04-03T06:20:22.000Z","size":441823,"stargazers_count":20,"open_issues_count":34,"forks_count":21,"subscribers_count":9,"default_branch":"3.0","last_synced_at":"2025-04-03T06:39:51.970Z","etag":null,"topics":["transverse-component"],"latest_commit_sha":null,"homepage":"https://inseefr.github.io/Lunatic/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/InseeFr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-02-11T14:08:30.000Z","updated_at":"2025-04-02T16:59:25.000Z","dependencies_parsed_at":"2023-10-25T18:24:20.239Z","dependency_job_id":"bf607dbd-dac9-47b0-a85f-e52551f9b15d","html_url":"https://github.com/InseeFr/Lunatic","commit_stats":{"total_commits":2513,"total_committers":38,"mean_commits":66.13157894736842,"dds":0.469558296856347,"last_synced_commit":"578a69ec476c511e972b7e36b1a1e253ec0b6a5a"},"previous_names":[],"tags_count":152,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/InseeFr%2FLunatic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/InseeFr%2FLunatic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/InseeFr%2FLunatic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/InseeFr%2FLunatic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/InseeFr","download_url":"https://codeload.github.com/InseeFr/Lunatic/tar.gz/refs/heads/3.0","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247457799,"owners_count":20941906,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["transverse-component"],"created_at":"2025-01-29T06:55:16.856Z","updated_at":"2026-02-10T17:05:57.633Z","avatar_url":"https://github.com/InseeFr.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"Lunatic\" src=\"public/images/lunatic-logo.png\" style=\"max-width: 100%;\"\u003e\n\u003c/p\u003e\n\nLunatic is a front-end library in the form of a React hook and component libraries for generating a questionnaire from the [Lunatic-Model](https://github.com/InseeFr/Lunatic-Model) data format.\n\n[![Lunatic CI](https://github.com/InseeFr/Lunatic/actions/workflows/ci-main.yaml/badge.svg)](https://github.com/InseeFr/Lunatic/actions/workflows/ci-main.yaml)\n[![npm version](https://badge.fury.io/js/%40inseefr%2Flunatic.svg)](https://badge.fury.io/js/%40inseefr%2Flunatic)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=InseeFr_Lunatic\u0026metric=alert_status)](https://sonarcloud.io/dashboard?id=InseeFr_Lunatic)\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=InseeFr_Lunatic\u0026metric=coverage)](https://sonarcloud.io/dashboard?id=InseeFr_Lunatic)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n\n---\n\n- [Storybook 3.0](https://inseefr.github.io/Lunatic/storybook-3.0), branch `3.0`\n- [Storybook 2.7](https://inseefr.github.io/Lunatic/storybook-2.7), branch `2.7`\n- [Storybook 2.6](https://inseefr.github.io/Lunatic/storybook-2.6), branch `2.6`\n- [Storybook v1](https://inseefr.github.io/Lunatic/storybook-0.3.9), branch `v1-main`\n- [Documentation](https://inseefr.github.io/Lunatic/)\n\n## Table of Contents\n\n- [Lunatic](#lunatic)\n  - [Table of Contents](#table-of-contents)\n  - [Usage](#usage)\n    - [The useLunatic Hook](#the-uselunatic-hook)\n    - [Components](#components)\n  - [Customization](#customization)\n  - [Internal Working](#internal-working)\n    - [General Functioning](#general-functioning)\n    - [Pages and Pager](#pages-and-pager)\n    - [VTL Execution](#vtl-execution)\n  - [Convention and Best Practices](#convention-and-best-practices)\n    - [Stable Branchs](#stable-branchs)\n    - [Commits and feature branches](#commits-and-feature-branches)\n\n## Usage\n\nTo get started, you need to install Lunatic:\n\n```bash\npnpm add @inseefr/lunatic\n```\n\n### The useLunatic Hook\n\nNext, wherever you want to display the form, you'll need to use the `useLunatic` hook.\n\n```js\nimport { useLunatic } from '@inseefr/lunatic';\n\nconst obj = useLunatic(source, data, options);\n```\n\nThis hook takes three parameters:\n\n- The **source**, which is a JSON representation of the [Lunatic-Model](https://github.com/InseeFr/Lunatic-Model).\n- The **data**, which contains the initial questionnaire data (can be an empty object).\n- An options object to configure the behavior.\n  - **features** (default `['VTL', 'MD']`), allows you to define supported functionalities.\n  - **preferences** (default `['COLLECTED']`).\n  - **onChange** (default `() =\u003e {}`), allows you to add logic to apply when an answer is changed (must be memoized as it's used as a dependency in an internal `useCallback`).\n  - **management** (default `false`): Not yet implemented, will allow managing multiple states of the same variable (used by recovery positions).\n  - **initialPage** (default `'1'`), allows you to define the starting page.\n  - **lastReachedPage** (default `undefined`), allows you to define the furthest reached page.\n  - **autoSuggesterLoading** (default `false`).\n  - **suggesters**.\n  - **suggesterFetcher** (default `fetch`), method used to retrieve suggester data.\n  - **activeControls** (default `false`), activates data controls.\n\nAnd it returns an object that allows you to control the questionnaire:\n\n- `getComponents()`, returns the components to display for the current page.\n- `goPreviousPage()`, allows you to go to the previous page.\n- `goNextPage()`, allows you to go to the next page.\n- `goToPage({ page: string })`, allows you to go to an arbitrary page.\n- `getErrors()`, returns the errors.\n- `getModalErrors()`, returns the errors in modals.\n- `getCurrentErrors()`, returns the errors for the current page.\n- `pageTag`, a string containing the page number (e.g., 8.1).\n- `isFirstPage`.\n- `isLastPage`.\n- `pager`, an object containing information related to the page.\n- `waiting`, indicates waiting for information from a suggester.\n- `getData()`, returns the collected data in the questionnaire.\n- `loopVariables`, is an array containing the list of variables used for the current loop.\n\nFor more information on the types of this return, you can refer to the available types in the [source code](https://github.com/InseeFr/Lunatic/blob/3.0/src/use-lunatic/type.ts). You can also find an example of using the hook in the [Storybook section](https://github.com/InseeFr/Lunatic/blob/3.0/src/stories/utils/orchestrator.jsx#L121-L152).\n\n### Components\n\nTo display the questionnaire, start by retrieving the list of components to display using the `getComponents()` method returned by the hook.\n\nLunatic offers a library of pre-designed components to cover the different types of fields available in questionnaires.\n\n```jsx\nimport * as lunatic from '@inseefr/lunatic';\n\nfunction App({ source, data }) {\n\tconst { getComponents, getCurrentErrors, getModalErrors } =\n\t\tlunatic.useLunatic(source, data, {});\n\tconst components = getComponents();\n\tconst currentErrors = getCurrentErrors();\n\tconst modalErrors = getModalErrors();\n\n\treturn (\n\t\t\u003cdiv className=\"container\"\u003e\n\t\t\t\u003cLunaticComponents components={components} /\u003e\n\t\t\t\u003clunatic.Modal errors={modalErrors} goNext={goNextPageAction} /\u003e\n\t\t\u003c/div\u003e\n\t);\n}\n```\n\nAll the components offered by Lunatic are available in the [src/components](https://github.com/InseeFr/Lunatic/tree/3.0/src/components) folder.\n\nTo 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`).\n\n## Customization\n\nBy 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`.\n\n```jsx\nconst custom = {\n\tInput: MyCustomInput,\n\tInputNumber: MyCustomInputNumber,\n};\n\nfunction App({ source, data }) {\n\tconst {} = useLunatic(source, data, { custom });\n\n\t// ...\n}\n```\n\n## Internal Working\n\nThis section covers the internal working of the `useLunatic()` hook. The goal is to help understand how it operates.\n\n### General Functioning\n\nThe hook is based on an [internal state](https://github.com/InseeFr/Lunatic/blob/3.0/src/use-lunatic/type.ts#L79-L187) that is updated through a [reducer](https://reactjs.org/docs/hooks-reference.html#usereducer) system. The [actions](https://github.com/InseeFr/Lunatic/blob/3.0/src/use-lunatic/actions.ts) affecting this state are limited:\n\n- An action `'use-lunatic/on-init'` allows initialization of the state from the data received as a parameter of the hook.\n- 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.\n- The action `use-lunatic/handle-change` is the most important action and is called whenever data is changed in the questionnaire.\n\nAll the [reducers corresponding to these actions are available here](https://github.com/InseeFr/Lunatic/blob/3.0/src/use-lunatic/reducer/reducer.ts). Generally, they are broken down into several methods depending on the part of the state they modify.\n\n### Pages and Pager\n\nAt 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.\n\n### VTL Execution\n\nAnother 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](https://github.com/InseeFr/Lunatic/blob/3.0/src/use-lunatic/use-lunatic.ts#L162).\n\nTo 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.\n\n## Convention and Best Practices\n\n### Stable Branchs\n\n- Stable branches follow the glob pattern `'2.*'` or `'3.*'`, like `2.7` or `3.0`.\n- We can maintain if needs, the old stable branches\n- `3.0` branch is currently the most advanced branch\n\n### Commits and feature branches\n\n- Avoid \"default\" exports as it impairs readability during import.\n- Comments in the code should be in English.\n- Files containing JSX should use the .jsx (or .tsx) extension.\n- Commits follow the specification [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).\n- Pull Requests should be prefixed in the same convention as commits:\n  - `test(XXX?)`: XXX for adding tests.\n  - `feat(XXX?)`: XXX for adding new features.\n  - `fix(XXX?)`: XXX for bug fixes.\n  - `docs(XXX?)`: XXX for adding documentation.\n  - `refactor(XXX?)`: XXX for refactoring that doesn't change functionality.\n  - `build(XXX?)`: XXX for changes related to the build process, compilation scripts, etc.\n  - `style(XXX?)`: XXX for style modifications.\n  - `ci(XXX?)`: XXX for CI modification.\n  - `perf(XXX?)`: XXX for performance improvement.\n  - `revert(XXX?)`: XXX to revert a previous PR.\n  - `chore(XXX?)`: XXX for maintenance tasks or tasks that don't fall into other categories.\n\n- Branches should be prefixed (following the same prefixes as Conventional Commits):\n  - `test/XXX`: for adding tests.\n  - `feat/XXX`: for adding new features.\n  - `fix/XXX`: for bug fixes.\n  - `docs/XXX`: for adding documentation.\n  - `refactor/XXX`: for refactoring that doesn't change functionality.\n  - `build/XXX`: for changes related to the build process, compilation scripts, etc.\n  - `style/XXX`: for changes related to code style.\n  - `ci/XXX`: for changes related to continuous integration (CI).\n  - `perf/XXX`: for performance improvements.\n  - `revert/XXX`: to revert a previous PR.\n  - `chore/XXX`: for maintenance tasks or tasks that don't fall into other categories.\n\n## Contributing\n\n### Testing your changes in an external app\n\nYou have made some changes to the code and you want to test them\nin your app before submitting a pull request?\n\nAssuming `you/my-app` have `@inseefr/lunatic` as a dependency.\n\n```bash\ncd ~/github\ngit clone https://github.com/you/my-app\ncd my-app\npnpm install\n\ncd ~/github\ngit clone https://github.com/InseeFr/Lunatic\ncd Lunatic\npnpm install\npnpm run build\nnpx tsc -w\n\n# Open another terminal\n\ncd ~/github/my-app\npnpm link ../lunatic # assuming both project are in the same folder\npnpm run start # Or whatever my-app is using for starting the project\n```\n\nYou don't have to use `~/github` as reference path. Just make sure `my-app` and `@inseefr/lunatic`\nare in the same directory.\n\n### Releasing\n\nFor releasing a new version on GitHub and NPM you don't need to create a tag.  \nJust update the `package.json` version number and push.\n\nFor publishing a release candidate update your `package.json` with `1.3.4-rc.0` (`.1`, `.2`, ...).  \nIt also work if you do it from a branch that have an open PR on main.\n\n\u003e Make sure your have defined the `NPM_TOKEN` repository secret or NPM publishing will fail.\n\n## Build\n\nWe build this library in ESM and CJS. ESM is the standard when you develop a front-end app.\nBut if you need test which use `@inseefr/lunatic`, you need lunatic library as CJS lib (to allow to run in node environnement).\nSo we have two build: one for ESM and one for CJS.\nYou have nothing to change in your code base, it's simply working.\n\n## Testing with playwright\n\nIf this is the first time you've run tests with your computer, run the command :\n\n- `npx playwright install chromium`\n\nThen you can run directly:\n\n- `npx playwright test --reporter=html`\n\nor with UI (launched in your local browser):\n\n- `npx playwright test --ui-port=8888 --ui-host=localhost`\n\n\u003e Tests are launched in built mode i.e the storybook is built before testing\n\n### Alternative\n\nTo have real time test (refreshed when you're coding), you need to run storybook in a separate command prompt and launch test.\n\n- `pnpm run storybook`\n- `npx playwright test`\n\nThe inconvenient is that “dev” mode can be too slow to display storybook pages and fails test. (timeout exceeded)..\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finseefr%2Flunatic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finseefr%2Flunatic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finseefr%2Flunatic/lists"}