@sergeymosyakov/questionnaire-widget 1.0.13 → 1.0.15

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 ADDED
@@ -0,0 +1,304 @@
1
+ # QuestionnaireRenderer — embeddable FHIR form widget
2
+
3
+ The **right-hand runtime** of the FHIR Questionnaire Builder, packaged as a
4
+ self-contained, embeddable widget. Drop a FHIR R4 `Questionnaire` into any web
5
+ page and get a live, fillable form that runs the SDC logic (enableWhen,
6
+ `calculatedExpression`, constraints, validation) and returns a valid
7
+ `QuestionnaireResponse` — **no builder shell, no iframe, no framework**.
8
+
9
+ - **In-page & multi-instance** — put several forms on one page; each keeps its
10
+ own answers, calculations and validation state with zero cross-talk.
11
+ - **Vanilla ES module** — no React/Vue/Angular required. Works with any stack.
12
+ - **Host-driven UI** — the widget has no menus; you turn features on through
13
+ `config` and drive it through a small public API.
14
+
15
+ ---
16
+
17
+ ## Table of contents
18
+
19
+ - [Install](#install)
20
+ - [Quick start](#quick-start)
21
+ - [Configuration](#configuration)
22
+ - [Public API](#public-api)
23
+ - [Events](#events)
24
+ - [Example: custom “Export FHIR R4” button](#example-custom-export-fhir-r4-button)
25
+ - [Preview modes](#preview-modes)
26
+ - [Multiple isolated widgets](#multiple-isolated-widgets)
27
+ - [Styling](#styling)
28
+ - [Browser support & dependencies](#browser-support--dependencies)
29
+
30
+ ---
31
+
32
+ ## Install
33
+
34
+ ### Fastest: CDN (no install, no auth)
35
+
36
+ Load the widget straight from [jsDelivr](https://www.jsdelivr.com/), pinned to a
37
+ release tag — nothing to download, no npm, no token:
38
+
39
+ ```html
40
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/sergeymosyakov/fhir-questionnaire-builder@widget-v1.0.3/dist/questionnaire-widget.css">
41
+ <script type="module">
42
+ import { QuestionnaireRenderer } from 'https://cdn.jsdelivr.net/gh/sergeymosyakov/fhir-questionnaire-builder@widget-v1.0.3/dist/questionnaire-widget.js';
43
+ // …
44
+ </script>
45
+ ```
46
+
47
+ Or the classic global build (`window.FhirQuestionnaireWidget`):
48
+
49
+ ```html
50
+ <script src="https://cdn.jsdelivr.net/gh/sergeymosyakov/fhir-questionnaire-builder@widget-v1.0.3/dist/questionnaire-widget.global.js"></script>
51
+ ```
52
+
53
+ > Pin to a specific `@widget-v1.0.x` tag for stability; jsDelivr caches immutably.
54
+
55
+ ### Download the release bundle
56
+
57
+ Grab the files from the
58
+ [**GitHub Releases**](https://github.com/sergeymosyakov/fhir-questionnaire-builder/releases)
59
+ page and self-host them. Each release contains:
60
+
61
+ | File | Use it when… |
62
+ |------|--------------|
63
+ | `questionnaire-widget.js` | You use ES modules (`import`). **Recommended.** |
64
+ | `questionnaire-widget.global.js` | You want a classic `<script>` global (`window.FhirQuestionnaireWidget`). |
65
+ | `questionnaire-widget.css` | Always — the widget’s styles. |
66
+
67
+ Verify the download with the `SHA256SUMS.txt` published alongside the assets.
68
+
69
+ ```html
70
+ <link rel="stylesheet" href="questionnaire-widget.css">
71
+ <script type="module">
72
+ import { QuestionnaireRenderer } from './questionnaire-widget.js';
73
+ // …
74
+ </script>
75
+ ```
76
+
77
+ ### npm (optional, for bundler projects)
78
+
79
+ If a maintainer has published to npm, bundler users (React / Vite / webpack) can:
80
+
81
+ ```bash
82
+ npm install @sergeymosyakov/questionnaire-widget
83
+ ```
84
+ ```js
85
+ import { QuestionnaireRenderer } from '@sergeymosyakov/questionnaire-widget';
86
+ import '@sergeymosyakov/questionnaire-widget/css';
87
+ ```
88
+
89
+ > npm publishing uses [Trusted Publishing](https://docs.npmjs.com/trusted-publishers)
90
+ > (OIDC from GitHub Actions — no token/secret involved); the CDN and Release
91
+ > above need no install and no auth and remain the primary channels.
92
+
93
+ Or build it yourself from source:
94
+
95
+ ```bash
96
+ npm install
97
+ npm run build:widget # → dist/questionnaire-widget.{js,global.js,css}
98
+ ```
99
+
100
+ ### NuGet (for .NET web apps)
101
+
102
+ For Blazor, Razor Pages, or MVC apps that want the widget's static JS/CSS
103
+ assets without touching npm:
104
+
105
+ ```bash
106
+ dotnet add package FhirQuestionnaireWidget
107
+ ```
108
+
109
+ Reference it from a Razor/HTML page — the assets are served under
110
+ `_content/FhirQuestionnaireWidget/`:
111
+
112
+ ```html
113
+ <link rel="stylesheet" href="_content/FhirQuestionnaireWidget/questionnaire-widget.css">
114
+ <script type="module">
115
+ import { QuestionnaireRenderer } from '/_content/FhirQuestionnaireWidget/questionnaire-widget.js';
116
+ // …
117
+ </script>
118
+ ```
119
+
120
+ > Same publishing model as npm above — [NuGet Trusted Publishing](https://learn.microsoft.com/nuget/nuget-org/trusted-publishing)
121
+ > (OIDC from GitHub Actions, no token/secret). It's a static-asset-only
122
+ > package — no C# API surface, just the built widget files.
123
+
124
+ ---
125
+
126
+ ## Quick start
127
+
128
+ ```html
129
+ <link rel="stylesheet" href="questionnaire-widget.css">
130
+ <div id="form"></div>
131
+
132
+ <script type="module">
133
+ import { QuestionnaireRenderer } from './questionnaire-widget.js';
134
+
135
+ const questionnaire = await (await fetch('my-questionnaire.json')).json();
136
+
137
+ const widget = new QuestionnaireRenderer(document.getElementById('form'), {
138
+ questionnaire,
139
+ config: { previewMode: 'patient', validation: true },
140
+ });
141
+
142
+ widget.on('response-changed', qr => console.log('answers now:', qr));
143
+ </script>
144
+ ```
145
+
146
+ That renders the questionnaire as a patient-facing form with a live PASS/FAIL
147
+ validation badge.
148
+
149
+ ---
150
+
151
+ ## Configuration
152
+
153
+ `new QuestionnaireRenderer(mountEl, { questionnaire, response?, config? })`
154
+
155
+ | Argument | Type | Description |
156
+ |----------|------|-------------|
157
+ | `mountEl` | `HTMLElement` | The container the form is rendered into (its contents are replaced). |
158
+ | `questionnaire` | `object` | FHIR R4 `Questionnaire` JSON. **Required.** |
159
+ | `response` | `object` | Optional `QuestionnaireResponse` to pre-fill answers. |
160
+ | `config` | `object` | Options below. Everything is **off unless you opt in**. |
161
+
162
+ ### `config` options
163
+
164
+ | Option | Type | Default | What it does |
165
+ |--------|------|---------|--------------|
166
+ | `previewMode` | `'patient' \| 'preview' \| 'json'` | `'patient'` | Form view (see [Preview modes](#preview-modes)). |
167
+ | `search` | `boolean` | `false` | A search box that highlights matching rows (or JSON). |
168
+ | `validation` | `boolean` | `false` | Live **PASS / FAIL** badge + dropdown of items still needing attention; clicking one scrolls to it. |
169
+ | `explain` | `boolean` | `false` | Makes calculated values and FHIRPath/`enableWhen` conditions clickable to open an **Explain** popup showing *why* a value or visibility rule evaluates the way it does. |
170
+ | `tooltips` | `boolean` | `false` | Rich hover tooltips describing each field and its FHIR mapping. |
171
+ | `navButton` | `boolean` | `false` | A “go to builder node” arrow on each row (only meaningful when a builder is present). |
172
+ | `viewPrefs` | `object` | `{}` | Design-view toggles: `{ showLinkId, showPrefix, showBadges, showHiddenItems }`. |
173
+ | `language` | `string` | `''` | Show a translated language if the questionnaire carries translations (`''` = source). |
174
+ | `fhirBaseUrl` | `string` | — | FHIR base server for reference search, server-side `$populate`, and `populate()`/`structureMapPopulate()` below. |
175
+ | `getAuthToken` | `() => string \| Promise<string>` | — | Returns a Bearer token for `fhirBaseUrl` requests (your host's own auth — e.g. you're already logged into the FHIR server in your own app). The widget **never** runs its own OAuth login popup; without this, requests go out with no `Authorization` header (fine for public/CORS-open test servers). Independent per instance. |
176
+ | `corsProxy` | `string` | — | CORS proxy for the FHIR/terminology requests. |
177
+ | `terminology` | `object` | — | This widget's own terminology server for external `answerValueSet` expansion: `{ server, corsProxy, nlmApiBase }`. Independent per instance — two widgets on the same page can point to two different terminology servers. Falls back to the public HL7 server (`tx.fhir.org/r4`) when omitted. |
178
+ | `readOnly` | `boolean` | `false` | Render answers without editable controls. |
179
+ | `onProgress` | `(msg\|null) => void` | — | Called with a message while long operations run, `null` when done. |
180
+
181
+ ---
182
+
183
+ ## Public API
184
+
185
+ ```js
186
+ widget.getResponse(); // → current answers as a FHIR QuestionnaireResponse
187
+ widget.setResponse(qr); // load answers from a QuestionnaireResponse
188
+ widget.setLanguage('es'); // switch active language ('' = source)
189
+ widget.setConfig({ language: 'es' }); // runtime config — only `language` takes effect
190
+ widget.populate('Patient/123'); // SDC $populate against config.fhirBaseUrl
191
+ widget.structureMapPopulate('Patient/123'); // run the Questionnaire's sourceStructureMap against a fetched resource
192
+ widget.on(event, cb); // subscribe (returns the widget)
193
+ widget.off(event, cb); // unsubscribe
194
+ widget.destroy(); // remove the widget and free all listeners
195
+ ```
196
+
197
+ `populate()`/`structureMapPopulate()` merge the resulting answers in place and
198
+ emit `info` on success or `error` on failure (see [Events](#events)) — nothing
199
+ is shown on the page unless you subscribe.
200
+
201
+ `getResponse()` always returns a fresh, valid FHIR R4 `QuestionnaireResponse`
202
+ built from the current answers — this is your integration point for saving,
203
+ submitting, or exporting.
204
+
205
+ > **Note:** the chrome flags (`search`, `validation`, `explain`, `tooltips`,
206
+ > `navButton`) are **construction-time** — set them in the initial `config`.
207
+ > `setConfig()` only applies `language` at runtime; to change chrome, `destroy()`
208
+ > and create a new instance.
209
+
210
+ ---
211
+
212
+ ## Events
213
+
214
+ Subscribe with `widget.on(name, cb)`:
215
+
216
+ | Event | Payload | Fires when |
217
+ |-------|---------|-----------|
218
+ | `ready` | — | The widget has mounted and rendered the first time. |
219
+ | `response-changed` | `QuestionnaireResponse` | Any answer changes. |
220
+ | `language-changed` | `string` (lang) | The active language changes. |
221
+ | `render` | — | The form re-renders. |
222
+ | `info` | `string` | `populate()`/`structureMapPopulate()` succeeded. |
223
+ | `error` | `string` | `populate()`/`structureMapPopulate()` failed (e.g. `fhirBaseUrl` not set) — nothing is shown unless you listen. |
224
+
225
+ ---
226
+
227
+ ## Example: custom “Export FHIR R4” button
228
+
229
+ The widget deliberately ships **no toolbar**. To let a user export their answers,
230
+ add your own button on the host page and call `getResponse()` — the returned
231
+ object is a ready-to-save FHIR R4 `QuestionnaireResponse`:
232
+
233
+ ```html
234
+ <div id="form"></div>
235
+ <button id="export">Export FHIR R4</button>
236
+
237
+ <script type="module">
238
+ import { QuestionnaireRenderer } from './questionnaire-widget.js';
239
+
240
+ const questionnaire = await (await fetch('my-questionnaire.json')).json();
241
+ const widget = new QuestionnaireRenderer(document.getElementById('form'), {
242
+ questionnaire,
243
+ config: { previewMode: 'patient', validation: true },
244
+ });
245
+
246
+ document.getElementById('export').addEventListener('click', () => {
247
+ const qr = widget.getResponse(); // ← ask the widget for answers
248
+ const blob = new Blob([JSON.stringify(qr, null, 2)], { type: 'application/fhir+json' });
249
+ const url = URL.createObjectURL(blob);
250
+ const a = Object.assign(document.createElement('a'), {
251
+ href: url, download: 'questionnaire-response.json',
252
+ });
253
+ a.click();
254
+ URL.revokeObjectURL(url);
255
+ });
256
+ </script>
257
+ ```
258
+
259
+ A working version of this button is in the [live demo](https://fhirbuilder.com/widget-demo.html).
260
+
261
+ ---
262
+
263
+ ## Preview modes
264
+
265
+ `config.previewMode` picks how the form looks:
266
+
267
+ - **`patient`** — the clean, fillable form a respondent sees.
268
+ - **`preview`** — the design view with link IDs, prefixes and status badges.
269
+ - **`json`** — the live `QuestionnaireResponse` / `Questionnaire` JSON.
270
+
271
+ ---
272
+
273
+ ## Multiple isolated widgets
274
+
275
+ Each widget runs on its **own event channel and its own answer store**, so two
276
+ forms on the same page never affect each other:
277
+
278
+ ```js
279
+ const a = new QuestionnaireRenderer(elA, { questionnaire: qA });
280
+ const b = new QuestionnaireRenderer(elB, { questionnaire: qB });
281
+ // answering a never touches b
282
+ ```
283
+
284
+ See the [live demo](https://fhirbuilder.com/widget-demo.html) for a three-widget page (one per
285
+ preview mode) over the same questionnaire.
286
+
287
+ ---
288
+
289
+ ## Styling
290
+
291
+ Link `questionnaire-widget.css` once. It carries the widget’s design tokens and
292
+ all preview/control/modal styles, scoped so they don’t leak into your page’s
293
+ layout (no global `body`/reset rules). Override the CSS custom properties on a
294
+ wrapping element to re-theme (e.g. `--c-primary`, `--c-border`, `--c-surface`).
295
+
296
+ ---
297
+
298
+ ## Browser support & dependencies
299
+
300
+ - Modern evergreen browsers (ES2020 modules).
301
+ - The ESM/global bundles include their runtime dependencies (FHIRPath, DOMPurify,
302
+ marked) — no extra `<script>` tags needed.
303
+ - No network calls unless you set `fhirBaseUrl` (reference search / `$populate`)
304
+ or use terminology expansion.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sergeymosyakov/questionnaire-widget",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "Embeddable FHIR R4 QuestionnaireRenderer widget",
5
5
  "type": "module",
6
6
  "main": "questionnaire-widget.js",
@@ -14,7 +14,8 @@
14
14
  "questionnaire-widget.js",
15
15
  "questionnaire-widget.global.js",
16
16
  "questionnaire-widget.css",
17
- "LICENSE"
17
+ "LICENSE",
18
+ "README.md"
18
19
  ],
19
20
  "repository": "git+https://github.com/sergeymosyakov/fhir-questionnaire-builder.git",
20
21
  "license": "SEE LICENSE IN LICENSE"