(feat) Add form engine cookbook reference form#69
Conversation
Introduces `form-engine-cookbook-core_demo.json`, a reference form that demonstrates one canonical form-engine pattern per page. Batch 1 covers the first five pages: - Introduction (markdown) - Conditional visibility: `hide.hideWhenExpression`, `disabled.disableWhenExpression`, `isDisabled` - Required variants: boolean, string expression, `conditionalAnswered` object with `referenceQuestionId` / `referenceQuestionAnswers` - Validators: `js_expression`, `date` (with `allowFutureDates`), `conditionalAnswered` - Computed fields: `calcBMI(height, weight)`, `calcEDD(lmp)` via `questionOptions.calculate.calculateExpression` The form is `published: false` so it does not appear in the patient chart. Examples use real concept UUIDs from the existing demo metadata so the form also renders in a dev deployment. Further pages (previous- value controls, special renderings, special field types, form-level metadata, composition, question-option grab bag) will follow in subsequent commits on this branch.
Appends three more pages to the form engine cookbook:
- Previous-value and historical: `formOptions.usePreviousValueDisabled`,
`questionOptions.enablePreviousValue`, `historicalExpression` with
`HD.getObject('prevEnc').getValue(...)`.
- Special renderings: `toggle` with `toggleOptions`, `content-switcher`,
`fixed-value`, `select-concept-answers`, `extension-widget` with
`extensionId` / `extensionSlotName`.
- Special field types: `patientIdentifier` (OpenMRS ID via
`identifierType`), `personAttribute` (telephone via `attributeType`),
`testOrder` with `orderSettingUuid` / `orderType` / `selectableOrders`,
`programState` with `programUuid` / `workflowUuid`.
Also adds `formOptions.usePreviousValueDisabled: false` at the form
root so the form exercises the form-level option alongside the question-
level one.
Completes the form engine cookbook with four more pages and a companion library form. Pages added to the cookbook: - Form-level metadata: `availableIntents` + `behaviours` overrides per intent. The cookbook's root also gains `postSubmissionActions`, `translations`, `meta.programs`, `defaultPage`, `allowUnspecifiedAll`, and `referencedForms` so the form itself exercises each feature. - Composition: a section with `reference` pulling the Vitals snippet from the library form. - Subform page: `isSubform: true` with an inline `subform.form`. - Question-option grab bag: `orientation`, `locationTag`, `weeksList`, `shownDateOptions`, `showComment`, `diagnosis.*`, `allowMultiple` + `isSearchable`, `isCheckboxSearchable`, `allowedFileTypes`, `isTransient`. New file: `form-engine-cookbook-library-core_demo.json`. A minimal one-page form containing a Vitals snippet section that the cookbook's composition page references via `section.reference`. Also `published: false`. Both files validate cleanly against the aligned `form.schema.json`.
Two related batches of work on the cookbook form.
Make every page render in the form-builder preview, not just in a real
distro. Several patterns were authored in shapes that depend on chart-
side context the preview doesn't have, which was crashing the whole
form during render:
- The composition page's `reference` section pointed at a sibling form
(`Form Engine Cookbook Library`) that isn't loaded when previewing in
isolation; the engine threw with "Form not found" before any page
rendered. Replaced with a markdown explainer of the pattern plus an
inline equivalent vitals section so the shape is still discoverable.
- The composition page's subform page set `isSubform: true` with a
nested form schema. The default schema transformer only walks
`page.sections[].questions[]`, so subform fields never got `meta`
initialised and the renderer threw on the first one. Replaced with a
markdown explainer; subforms only render properly via the chart-side
pipeline anyway.
- The `programState` field needs program/workflow data fetched from the
backend, which the form-builder doesn't have. Replaced with a markdown
explainer.
- The `weeksList` question option is declared in the engine's schema
types but no renderer reads it; the field rendered as a `select`
with no answers and crashed in `dropdown.component.tsx`. Replaced
with a markdown note that the option is reserved.
- The `shownDateOptions` field used `rendering: "select"` with a CIEL
concept and no inline answers. Added a small inline `answers` array
so the dropdown has something to render in the preview while still
exercising the `showDate` / `shownDateOptions` shape.
- Form-builder validator wanted `showComment` as a string token, not
a boolean. Changed `true` -> `"true"`; the engine accepts both.
Rewrite the 12 explainer markdowns from "engine feature description"
voice to a Scenario -> Shape -> Notes structure aimed at form authors.
The cookbook's audience is mostly implementers and country teams, not
application developers, so the explainers now lead with the clinical or
authoring goal ("you want a field to appear only when another answer is
set"), then show the JSON shape, then call out edge cases. The actual
sample fields and their JSON are unchanged - only the prose around them.
Companion changes for richer markdown rendering ride in
openmrs-esm-form-engine-lib#733 (expanded markdown allowlist + styles
for code, lists, blockquotes, etc.). Without that PR the explainers
still render but lose list bullets and code-block styling.
The cookbook's root-level `translations` field shipped a flat map of
French strings (`{ Yes: "Oui", No: "Non", "Weight (kg)": "Poids (kg)",
"Height (cm)": "Taille (cm)" }`). The form engine reads this via
`useFormJson.ts` and registers it through
`window.i18next.addResourceBundle(language, '@openmrs/esm-form-engine-app',
formJson.translations, ...)` where `language` is whatever locale i18next
is currently in. So a flat map of French strings gets bound to whatever
locale the viewer happens to be using - including English - and
`t("Yes")` returns "Oui" for every reader, in every language, every
time they open the form.
Drop the block. The `translations` feature is still documented in the
form-level metadata explainer for anyone who wants to use it; we just
don't ship a broken-by-shape example.
|
@denniskigen, this is such a useful guide. Do we have such expressions?
|
|
Thanks @VeronicaMuthee, those are good suggestions. On the unique identifier one, the engine doesn't have anything for this. Expressions can see what's on the form plus a bit of patient context (sex, age, current visit, most recent encounter via HD), but they can't reach across to other patients to check whether an identifier is already taken. That check has to come from the backend, via the identifier source and OpenMRS's identifier validator. I'll call this out on the For pre-filling from the last encounter, this is what "Previous-value and historical" is for, but I think I framed it too abstractly to connect. The shape is The next-appointment-from-days-dispensed one is a natural fit for a computed field: "calculate": { "calculateExpression": "addDaysToDate(today(), drugDurationDays)" }where drugDurationDays is the id of the number field holding the dispensed quantity. Computed fields only has BMI and EDD right now, neither of which exercises date math, so this slots in nicely. |
Summary
Add a
Form Engine Cookbookreference form to the demo content package. The form is a working library of patterns supported by the OpenMRS 3 form engine, intended for form authors (implementers, country teams, anyone editing form JSON, in or out of Form Builder).Each page covers one authoring task with the same shape:
Topics covered, page by page:
hide.hideWhenExpression,disabled.disableWhenExpression, staticisDisabledconditionalAnsweredjs_expression,date,conditionalAnswered,default_valuecalculate.calculateExpressionand the helper library (calcBMI,calcEDD, z-score family, etc.)formOptions.usePreviousValueDisabled,enablePreviousValue,historicalExpressionwith theHDhelpertoggle,content-switcher,fixed-value,select-concept-answers,extension-widgetpatientIdentifier,personAttribute,testOrder,programStateavailableIntents,behavioursoverrides,postSubmissionActions, embeddedtranslations,meta.programs,defaultPage,allowUnspecifiedAllreferenceand pageisSubformweeksList(reserved),shownDateOptions,showComment, plus other less-common keysMost fields use real concepts from the reference-app demo metadata so the form renders end-to-end against a dev distro. Patterns that depend on chart-side context the form-builder preview can't supply (referenced sibling forms, subforms, program states) are documented via markdown explainers rather than embedded as live fields, with a note about why.
The form is
published: falseby design — it will not appear in the patient chart unless that flag is flipped for local testing.Companion change
Richer markdown rendering (inline code, fenced code blocks, lists, links, blockquotes) is gated by openmrs/openmrs-esm-form-engine-lib#733, which expands the markdown wrapper's allowlist and adds the styles to match. Without that PR the cookbook still renders but loses list bullets and code-block styling — the explainer text remains readable.