@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 +304 -0
- package/package.json +3 -2
- package/questionnaire-widget.css +1 -1
- package/questionnaire-widget.global.js +36497 -25
- package/questionnaire-widget.js +36497 -25
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.
|
|
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"
|