@mieweb/forms-renderer 0.1.6 → 0.1.8

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 CHANGED
@@ -1,6 +1,6 @@
1
- # @mieweb/forms-renderer `v0.1.5`
1
+ # 📋 @mieweb/forms-renderer
2
2
 
3
- Read-only questionnaire renderer for displaying and filling out forms. Produces FHIR QuestionnaireResponse output.
3
+ Read-only questionnaire renderer with dual distribution: React component or Standalone Web Component.
4
4
 
5
5
  ## 📦 Installation
6
6
 
@@ -8,356 +8,197 @@ Read-only questionnaire renderer for displaying and filling out forms. Produces
8
8
  npm install @mieweb/forms-renderer
9
9
  ```
10
10
 
11
- ### Peer Dependencies (Required)
11
+ ## 🚀 Examples
12
12
 
13
- You must install React 18+ in your project:
13
+ See the complete working examples in this package:
14
+ - [`example-react.jsx`](./example-react.jsx) - ⚛️ React component usage
15
+ - [`example-standalone.html`](./example-standalone.html) - 🌐 Web Component usage
14
16
 
17
+ ## 💻 Usage
18
+
19
+ ### ⚛️ React Component (Recommended for React Projects)
20
+
21
+ Requires React peer dependencies:
15
22
  ```bash
16
23
  npm install react react-dom
17
24
  ```
18
25
 
19
- ### Automatic Dependencies
20
-
21
- The following is installed automatically:
22
-
23
- - `@mieweb/forms-engine` - Core form state and field components
24
-
25
- ## 🚀 Quick Start
26
-
27
- ### Basic Usage
28
-
26
+ From [`example-react.jsx`](./example-react.jsx):
29
27
  ```jsx
30
28
  import { QuestionnaireRenderer } from '@mieweb/forms-renderer';
31
- import { createRoot } from 'react-dom/client';
32
- import './index.css';
33
29
 
34
30
  function App() {
35
31
  const [fields] = React.useState([
36
- { id: 'sec-1', fieldType: 'section', title: 'Personal Info', fields: [] },
37
- { id: 'q-name', fieldType: 'input', question: 'What is your name?', answer: '' },
38
- { id: 'q-gender', fieldType: 'radio', question: 'Gender', options: [{ value: 'Male' }, { value: 'Female' }], selected: null },
32
+ {
33
+ id: 'sec-1',
34
+ fieldType: 'section',
35
+ title: 'Personal Information',
36
+ fields: [
37
+ {
38
+ id: 'q-name',
39
+ fieldType: 'input',
40
+ question: 'What is your full name?',
41
+ answer: ''
42
+ },
43
+ {
44
+ id: 'q-gender',
45
+ fieldType: 'radio',
46
+ question: 'Biological sex',
47
+ options: [
48
+ { id: 'gender-male', value: 'Male' },
49
+ { id: 'gender-female', value: 'Female' }
50
+ ],
51
+ selected: null
52
+ }
53
+ ]
54
+ }
39
55
  ]);
40
- const [submitted, setSubmitted] = React.useState(null);
41
-
42
- return (
43
- <div className="w-full h-dvh bg-slate-100">
44
- <div className="absolute inset-0 overflow-auto p-4 max-w-4xl mx-auto w-full">
45
- <QuestionnaireRenderer
46
- questionnaireId="demo-1"
47
- fields={fields}
48
- onSubmit={(qr) => setSubmitted(qr)}
49
- />
50
- {submitted && (
51
- <pre className="mt-4 bg-neutral-100 p-4">{JSON.stringify(submitted, null, 2)}</pre>
52
- )}
53
- </div>
54
- </div>
55
- );
56
- }
57
-
58
- createRoot(document.getElementById('root')).render(<App />);
59
- ```
60
-
61
- ### Loading from API
62
-
63
- ```jsx
64
- function App() {
65
- const [fields, setFields] = React.useState([]);
66
- const [loading, setLoading] = React.useState(true);
67
-
68
- React.useEffect(() => {
69
- fetch('/api/questionnaire/123')
70
- .then(res => res.json())
71
- .then(data => {
72
- setFields(data.fields);
73
- setLoading(false);
74
- });
75
- }, []);
76
-
77
- if (loading) return <div>Loading...</div>;
78
56
 
79
57
  return (
80
- <div className="absolute inset-0 overflow-auto p-4">
81
- <QuestionnaireRenderer
82
- questionnaireId="patient-intake-v1"
83
- subjectId="patient-12345"
84
- fields={fields}
85
- onSubmit={async (fhirResponse) => {
86
- await fetch('/api/responses', {
87
- method: 'POST',
88
- headers: { 'Content-Type': 'application/json' },
89
- body: JSON.stringify(fhirResponse)
90
- });
91
- alert('Submitted!');
92
- }}
93
- />
94
- </div>
58
+ <QuestionnaireRenderer
59
+ questionnaireId="demo-questionnaire"
60
+ fields={fields}
61
+ onChange={(updatedFields) => console.log('Changed:', updatedFields)}
62
+ onSubmit={(fhirResponse) => console.log('Submitted:', fhirResponse)}
63
+ />
95
64
  );
96
65
  }
97
66
  ```
98
67
 
99
- The `fields` prop accepts any data source (API, database, local storage) that matches this JSON structure:
68
+ ### 🌐 Standalone Web Component (Framework-Agnostic)
100
69
 
101
- ```js
102
- // Example: Simple questionnaire data
103
- [
104
- {
105
- id: '1',
106
- fieldType: 'input',
107
- question: 'What is your name?',
108
- answer: ''
109
- },
110
- {
111
- id: '2',
112
- fieldType: 'radio',
113
- question: 'Select your role',
114
- options: ['Developer', 'Designer', 'Manager'],
115
- selected: null
116
- }
117
- ]
70
+ ✨ Zero dependencies - works with any framework or vanilla JS.
118
71
 
119
- // Example: Complex medical screening with sections and conditional logic
120
- [
121
- {
122
- fieldType: "section",
123
- title: "Patient Information",
124
- id: "sec-patient-info",
125
- fields: [
126
- { fieldType: "input", question: "First name", answer: "", id: "pi-first-name" },
127
- { fieldType: "input", question: "Last name", answer: "", id: "pi-last-name" },
128
- {
129
- fieldType: "selection",
130
- question: "Biological sex",
131
- options: [
132
- { id: "pi-sex-m", value: "Male" },
133
- { id: "pi-sex-f", value: "Female" }
134
- ],
135
- selected: null,
136
- id: "pi-sex"
137
- }
138
- ]
139
- },
140
- {
141
- fieldType: "section",
142
- title: "Pregnancy & OB",
143
- id: "sec-pregnancy",
144
- enableWhen: {
145
- logic: "AND",
146
- conditions: [
147
- { targetId: "pi-sex", operator: "equals", value: "pi-sex-f" }
72
+ From [`example-standalone.html`](./example-standalone.html):
73
+ ```html
74
+ <script type="module">
75
+ import './package/dist/standalone.js';
76
+
77
+ const renderer = document.querySelector('questionnaire-renderer');
78
+ renderer.fields = [
79
+ {
80
+ id: 'sec-1',
81
+ fieldType: 'section',
82
+ title: 'Patient Information',
83
+ fields: [
84
+ {
85
+ id: 'q-name',
86
+ fieldType: 'input',
87
+ question: 'Full Name',
88
+ answer: ''
89
+ },
90
+ {
91
+ id: 'q-gender',
92
+ fieldType: 'radio',
93
+ question: 'Biological sex',
94
+ options: [
95
+ { id: 'gender-male', value: 'Male' },
96
+ { id: 'gender-female', value: 'Female' }
97
+ ],
98
+ selected: null
99
+ }
148
100
  ]
149
- },
150
- fields: [
151
- {
152
- fieldType: "radio",
153
- question: "Are you currently pregnant?",
154
- options: [
155
- { id: "preg-yes", value: "Yes" },
156
- { id: "preg-no", value: "No" }
157
- ],
158
- selected: null,
159
- id: "preg-status"
160
- }
161
- ]
162
- }
163
- ]
164
- ```
165
-
166
- **Any JSON object matching this structure works** - whether from your backend API, a database query, local storage, or a CMS.
167
-
168
- ---
169
-
170
- ## 📖 Props
171
-
172
- ### `QuestionnaireRenderer`
173
-
174
- | Prop | Type | Default | Description |
175
- |------|------|---------|-------------|
176
- | `fields` | `Array` | **Required** | Questionnaire definition from your data source (API, database, etc.) |
177
- | `onChange` | `Function` | `undefined` | Callback when answers change: `(fields) => void` |
178
- | `onSubmit` | `Function` | `undefined` | Callback on submit: `(fhirResponse) => void` |
179
- | `questionnaireId` | `String` | `'questionnaire-1'` | ID for FHIR Questionnaire reference |
180
- | `subjectId` | `String` | `undefined` | Patient/subject ID for FHIR response |
181
- | `className` | `String` | `''` | Additional CSS classes |
182
- | `fullHeight` | `Boolean` | `false` | Use full viewport height |
183
-
184
- ## ✨ Features
185
-
186
- ### ✅ Read-Only Display
187
-
188
- - Displays questionnaire fields without editing controls
189
- - Users can **fill out** the form but **cannot add/remove/reorder** fields
101
+ }
102
+ ];
103
+
104
+ renderer.onSubmit = (fhirResponse) => {
105
+ console.log('Form submitted:', fhirResponse);
106
+ };
107
+ </script>
190
108
 
191
- ### 📋 Supported Field Types
109
+ <questionnaire-renderer
110
+ questionnaire-id="standalone-demo"
111
+ full-height>
112
+ </questionnaire-renderer>
113
+ ```
192
114
 
193
- - **Text Input** - Single-line text entry
194
- - **Radio Buttons** - Single choice selection
195
- - **Checkboxes** - Multiple choice selection
196
- - **Dropdown** - Select menu
197
- - **Section** - Grouped fields with collapse/expand
115
+ ## ⚙️ Props/Attributes
198
116
 
199
- ### 🔀 Conditional Logic (enableWhen)
117
+ ### ⚛️ React Component
118
+ - `fields` - Questionnaire definition array
119
+ - `onChange` - Callback when answers change
120
+ - `onSubmit` - Callback on form submit
121
+ - `questionnaireId` - FHIR Questionnaire ID
122
+ - `subjectId` - Patient/subject ID
123
+ - `className` - CSS classes
124
+ - `fullHeight` - Full viewport height
200
125
 
201
- Automatically shows/hides fields based on answers:
126
+ ### 🌐 Web Component
127
+ - `questionnaire-id` - FHIR Questionnaire ID (attribute)
128
+ - `full-height` - Full viewport height (attribute)
129
+ - `fields` - Questionnaire definition (property)
130
+ - `onChange` - Change callback (property)
131
+ - `onSubmit` - Submit callback (property)
202
132
 
203
- ```jsx
204
- const fields = [
205
- {
206
- id: '1',
207
- fieldType: 'radio',
208
- question: 'Do you have symptoms?',
209
- options: ['Yes', 'No'],
210
- selected: null
211
- },
212
- {
213
- id: '2',
214
- fieldType: 'input',
215
- question: 'Describe your symptoms',
216
- answer: '',
217
- enableWhen: [
218
- {
219
- question: '1', // ID of field to check
220
- operator: 'equals',
221
- answer: 'Yes'
222
- }
223
- ]
224
- }
225
- ];
226
- ```
133
+ ## 🔧 Field Types
227
134
 
228
- Field `2` only appears when field `1` is answered with "Yes".
135
+ - `input` - 📝 Text input field
136
+ - `radio` - 🔘 Single selection radio buttons
137
+ - `check` - ☑️ Multiple selection checkboxes
138
+ - `selection` - 📋 Dropdown selection
139
+ - `section` - 📂 Container for grouping fields
229
140
 
230
- ### 🏥 FHIR QuestionnaireResponse
141
+ ## 🔀 Conditional Logic (enableWhen)
231
142
 
232
- On submit, generates a standard FHIR R4 QuestionnaireResponse:
143
+ Fields can be shown/hidden based on other field values. Both examples include conditional logic:
233
144
 
234
- ```js
145
+ From [`example-react.jsx`](./example-react.jsx):
146
+ ```javascript
235
147
  {
236
- resourceType: "QuestionnaireResponse",
237
- id: "response-uuid",
238
- questionnaire: "questionnaire-1",
239
- status: "completed",
240
- authored: "2025-10-02T10:30:00Z",
241
- subject: {
242
- reference: "Patient/patient-12345"
148
+ id: 'sec-pregnancy',
149
+ fieldType: 'section',
150
+ title: 'Pregnancy Information',
151
+ enableWhen: {
152
+ logic: 'AND',
153
+ conditions: [
154
+ { targetId: 'q-gender', operator: 'equals', value: 'gender-female' }
155
+ ]
243
156
  },
244
- item: [
157
+ fields: [
245
158
  {
246
- linkId: "1",
247
- text: "What is your name?",
248
- answer: [
249
- {
250
- valueString: "John Doe"
251
- }
252
- ]
159
+ id: 'q-weeks',
160
+ fieldType: 'input',
161
+ question: 'Weeks gestation (if known)',
162
+ answer: '',
163
+ enableWhen: {
164
+ logic: 'AND',
165
+ conditions: [
166
+ { targetId: 'q-pregnant', operator: 'equals', value: 'preg-yes' }
167
+ ]
168
+ }
253
169
  }
254
- // ... more items
255
170
  ]
256
171
  }
257
172
  ```
258
173
 
259
- ## 🎯 Advanced Usage
260
-
261
- ### Pre-filled Responses
262
-
263
- ```jsx
264
- const prefilledFields = [
265
- {
266
- id: '1',
267
- fieldType: 'input',
268
- question: 'Full Name',
269
- answer: 'Jane Doe' // Pre-filled
270
- },
271
- {
272
- id: '2',
273
- fieldType: 'radio',
274
- question: 'Gender',
275
- options: [{ value: 'Male' }, { value: 'Female' }, { value: 'Other' }],
276
- selected: { value: 'Female' } // Pre-filled
277
- }
278
- ];
279
-
280
- <QuestionnaireRenderer
281
- fields={prefilledFields}
282
- questionnaireId="follow-up-visit"
283
- subjectId="patient-67890"
284
- />
285
- ```
286
-
287
- ### Track Form Changes
174
+ ## 🏥 FHIR Output
288
175
 
289
- ```jsx
290
- function App() {
291
- const [fields, setFields] = React.useState(initialFields);
292
-
293
- const handleChange = (updatedFields) => {
294
- setFields(updatedFields);
295
- console.log('User updated:', updatedFields);
296
- };
176
+ The `onSubmit` callback receives a FHIR QuestionnaireResponse:
297
177
 
298
- return (
299
- <QuestionnaireRenderer
300
- fields={fields}
301
- onChange={handleChange}
302
- onSubmit={(fhirResponse) => {
303
- console.log('Submitting:', fhirResponse);
304
- }}
305
- />
306
- );
307
- }
308
- ```
309
-
310
- ## 🔧 Field Structure
311
-
312
- Fields use the same structure as `@mieweb/forms-editor`:
313
-
314
- ```js
178
+ ```javascript
315
179
  {
316
- id: 'unique-id',
317
- fieldType: 'input' | 'radio' | 'check' | 'dropdown' | 'section',
318
- question: 'Your question text',
319
- answer: '', // For input
320
- options: [], // For radio/check/dropdown
321
- selected: null, // For radio
322
- selectedOptions: [], // For check
323
- fields: [], // For section
324
- enableWhen: [] // Conditional logic
325
- }
326
- ```
327
-
328
- ## 📦 Bundle Size
329
-
330
- - **ESM format** with tree-shaking support
331
- - **TypeScript definitions** included
332
- - **Very lightweight** - perfect for embedding in patient portals
333
- - **CSS automatically injected** via `@mieweb/forms-engine` dependency
334
- - Dependencies: `@mieweb/forms-engine` (auto-installed)
335
- - Peer dependencies: React 18+
336
-
337
- ## 🎨 Styling
338
-
339
- **CSS is automatically included** when you import the package! The styles come bundled via the `@mieweb/forms-engine` dependency.
340
-
341
- Override with custom CSS:
342
-
343
- ```css
344
- .qr-renderer-root {
345
- max-width: 800px;
346
- margin: 0 auto;
347
- }
348
-
349
- .qr-submit-btn {
350
- background: #10b981;
351
- color: white;
352
- padding: 0.75rem 2rem;
180
+ resourceType: 'QuestionnaireResponse',
181
+ questionnaire: 'demo-1',
182
+ status: 'completed',
183
+ authored: '2023-01-01T12:00:00Z',
184
+ item: [
185
+ {
186
+ linkId: 'q1',
187
+ text: 'What is your name?',
188
+ answer: [{ valueString: 'John Doe' }]
189
+ }
190
+ ]
353
191
  }
354
192
  ```
355
193
 
356
- ## 🔗 Related Packages
194
+ ## 📊 Bundle Sizes
357
195
 
358
- - **@mieweb/forms-engine** - Core form primitives (auto-installed)
359
- - **@mieweb/forms-editor** - Full questionnaire editor UI
196
+ - **⚛️ React version**: ~24 KB (requires peer deps)
197
+ - **🌐 Standalone version**: ~819 KB (zero dependencies)
360
198
 
361
- ## 📄 License
199
+ ## 📚 Documentation
362
200
 
363
- MIT
201
+ - [📖 Migration Guide](./docs/MIGRATION.md)
202
+ - [🚀 Meteor/Blaze Guide](./docs/METEOR-BLAZE-GUIDE.md)
203
+ - [🌐 Web Component Summary](./docs/WEB-COMPONENT-SUMMARY.md)
204
+ - [🏗️ Architecture](./docs/ARCHITECTURE.md)