@ea-lab/reactive-json-docs 2.8.0 → 2.11.0

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.
Files changed (38) hide show
  1. package/package.json +2 -2
  2. package/public/rjbuild/docs/core/element/form/CheckBoxField.md +142 -18
  3. package/public/rjbuild/docs/core/element/form/CheckBoxField.yaml +132 -7
  4. package/public/rjbuild/docs/core/element/form/SelectField.md +36 -10
  5. package/public/rjbuild/docs/core/element/form/SelectField.yaml +37 -3
  6. package/public/rjbuild/docs/core/element/form/formElementsCommon.md +32 -0
  7. package/public/rjbuild/docs/core/element/html/Html.yaml +47 -0
  8. package/public/rjbuild/docs/core/element/html/LabelFromValue.md +159 -0
  9. package/public/rjbuild/docs/core/element/html/LabelFromValue.yaml +163 -10
  10. package/public/rjbuild/docs/core/element/special/DataSync.md +41 -1
  11. package/public/rjbuild/docs/core/element/special/DataSync.yaml +41 -1
  12. package/public/rjbuild/docs/core/example/native-html-forms.md +19 -2
  13. package/public/rjbuild/docs/core/hook/index.md +3 -0
  14. package/public/rjbuild/docs/core/hook/index.yaml +3 -0
  15. package/public/rjbuild/docs/core/reaction/collectField.md +51 -0
  16. package/public/rjbuild/docs/core/reaction/collectField.yaml +81 -0
  17. package/public/rjbuild/docs/core/reaction/fetchData.md +21 -1
  18. package/public/rjbuild/docs/core/reaction/fetchData.yaml +2 -2
  19. package/public/rjbuild/docs/core/reaction/index.md +5 -0
  20. package/public/rjbuild/docs/core/reaction/index.yaml +5 -0
  21. package/public/rjbuild/docs/core/reaction/joinData.md +51 -0
  22. package/public/rjbuild/docs/core/reaction/joinData.yaml +87 -0
  23. package/public/rjbuild/docs/core/reaction/printPage.md +22 -0
  24. package/public/rjbuild/docs/core/reaction/printPage.yaml +29 -0
  25. package/public/rjbuild/docs/core/reaction/redirectNow.md +20 -2
  26. package/public/rjbuild/docs/core/reaction/redirectNow.yaml +24 -3
  27. package/public/rjbuild/docs/core/reaction/setClipboardData.md +35 -6
  28. package/public/rjbuild/docs/core/reaction/setClipboardData.yaml +18 -6
  29. package/public/rjbuild/docs/core/reaction/stringifyData.md +45 -0
  30. package/public/rjbuild/docs/core/reaction/stringifyData.yaml +58 -0
  31. package/public/rjbuild/docs/core/reaction/submitData.md +1 -1
  32. package/public/rjbuild/docs/core/reaction/submitData.yaml +4 -2
  33. package/public/rjbuild/docs/core/reaction/toggleData.md +72 -0
  34. package/public/rjbuild/docs/core/reaction/toggleData.yaml +77 -0
  35. package/public/rjbuild/docs/getting-started/reactions.md +5 -0
  36. package/public/rjbuild/docs/getting-started/reactions.yaml +5 -0
  37. package/public/rjbuild/docs/getting-started/rjbuild-structure.md +23 -1
  38. package/public/rjbuild/docs/getting-started/rjbuild-structure.yaml +25 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ea-lab/reactive-json-docs",
3
- "version": "2.8.0",
3
+ "version": "2.11.0",
4
4
  "description": "Complete documentation for Reactive-JSON - Components, examples and LLM-parsable guides",
5
5
  "main": "public/rjbuild/docs/index.yaml",
6
6
  "files": [
@@ -26,7 +26,7 @@
26
26
  "private": false,
27
27
  "devDependencies": {
28
28
  "@craco/craco": "^7.1.0",
29
- "@ea-lab/reactive-json": "^2.8.0",
29
+ "@ea-lab/reactive-json": "^2.11.1",
30
30
  "@ea-lab/reactive-json-chartjs": "^1.1.1",
31
31
  "@npmcli/fs": "^4.0.0",
32
32
  "@reduxjs/toolkit": "^2.6.1",
@@ -24,14 +24,15 @@ The `CheckBoxField` component provides checkbox and radio button groups with aut
24
24
  - `label` (string or View props, optional): Field label text (supports template evaluation and View component rendering).
25
25
  - `options` (array, optional): Static array of option objects, each with:
26
26
  - `label` (string, optional): Display text for the option (supports template evaluation).
27
- - `value` (any, required): Value stored when this option is selected.
27
+ - `value` (any, required): Value stored when this option is selected (supports template evaluation, like `label`).
28
28
  - `attributes` (object, optional): Attributes applied to the option's container div.
29
- - `dynamicOptions` (string, optional): Template path to dynamic options array (e.g., `~.availableItems`). Takes precedence over `options` if both are provided.
29
+ - `dynamicOptions` (string, optional): Template path to dynamic options array (e.g., `~.availableItems`). Takes precedence over `options` if both are provided. The items can be `{label, value}` objects, scalars (each scalar is used as both label and value), or objects of any shape mapped with `optionMapping`.
30
+ - `optionMapping` (object, optional): Builds each option from an item of the options list. It is evaluated once per item, **with the item as the template context**: `~.title` reads the item's `title`, `~` is the whole item, `~~.` still reads the global data. The mapped keys (`label`, `value`, `attributes`) override the item's keys. See [Data-driven checkbox lists](#data-driven-checkbox-lists).
30
31
  - `controlType` (string, optional): Type of control - `"checkbox"` (default) or `"radio"`.
31
- - `multiple` (boolean, optional): Whether multiple selections are allowed. Defaults to `true` if more than one option exists, `false` otherwise. For radio buttons, this is automatically `false`.
32
+ - `multiple` (boolean, optional): Whether multiple selections are allowed. Defaults to `true` when the bound data is an array or when more than one option exists, `false` otherwise. For radio buttons, this is automatically `false`.
32
33
  - `attributes` (object, optional): Attributes applied to the wrapper div (or container div if no wrapper).
33
34
  - `inputAttributes` (object, optional): Attributes applied directly to each input element.
34
- - `labelAttributes` (object, optional): Attributes applied to the main label (htmlFor is automatically managed).
35
+ - `labelAttributes` (object, optional): Attributes applied to the field label and to each option label (`htmlFor` is automatically managed). An `id` is only applied to the field label.
35
36
  - `forceWrapper` (boolean, optional): Forces the presence (true) or absence (false) of the wrapper div. If omitted, wrapper is automatic only if label is present.
36
37
  - `actions` (array, optional): Actions to execute based on field state.
37
38
 
@@ -43,6 +44,8 @@ The component automatically synchronizes its value with the global data context.
43
44
  - **Single checkbox**: Stores a boolean (`true`) or the option's value when checked, `false` when unchecked.
44
45
  - **Multiple checkboxes**: Stores an array of selected option values.
45
46
 
47
+ The mode is chosen as follows: radio when `controlType: "radio"`; otherwise `multiple` when set; otherwise **multiple when the bound data is already an array** or when there is more than one option. Initializing the data as an array (`[]`) is therefore enough to get a list of values, even with a single option.
48
+
46
49
  When `dataLocation` is used, the value is stored at the specified path. Without `dataLocation`, the value is stored in the template context using the component's `datafield`.
47
50
 
48
51
  ## Selection Modes
@@ -69,7 +72,7 @@ Radio buttons allow only one selection at a time. Set `controlType: "radio"`:
69
72
 
70
73
  ### Single Checkbox
71
74
 
72
- A single checkbox stores a boolean or the option's value:
75
+ A single checkbox (one option, data not initialized as an array) stores a boolean or the option's value:
73
76
 
74
77
  ```yaml
75
78
  - type: CheckBoxField
@@ -83,6 +86,8 @@ A single checkbox stores a boolean or the option's value:
83
86
 
84
87
  **Data stored**: `true` when checked, `false` when unchecked (or the option's value if different from `true`)
85
88
 
89
+ Several single checkboxes bound to the same data location overwrite each other's value, so they check and uncheck together. When they have different values, the component logs a warning: initialize the data as an array, or set `multiple: true`.
90
+
86
91
  ### Multiple Checkboxes
87
92
 
88
93
  Multiple checkboxes allow selecting several options simultaneously:
@@ -118,17 +123,15 @@ You can override the default behavior using the `forceWrapper` property. Setting
118
123
 
119
124
  **With label (automatic wrapper):**
120
125
  ```html
121
- <div>
122
- <label>Select options:</label>
126
+ <div role="group" aria-labelledby="checkbox-abc123-label">
127
+ <label id="checkbox-abc123-label">Select options:</label>
123
128
  <div>
124
- <div>
125
- <input type="checkbox" id="checkbox-abc123-0" value="opt1" />
126
- <label htmlFor="checkbox-abc123-0">Option 1</label>
127
- </div>
128
- <div>
129
- <input type="checkbox" id="checkbox-abc123-1" value="opt2" />
130
- <label htmlFor="checkbox-abc123-1">Option 2</label>
131
- </div>
129
+ <input type="checkbox" id="checkbox-abc123-0" value="opt1" />
130
+ <label for="checkbox-abc123-0">Option 1</label>
131
+ </div>
132
+ <div>
133
+ <input type="checkbox" id="checkbox-abc123-1" value="opt2" />
134
+ <label for="checkbox-abc123-1">Option 2</label>
132
135
  </div>
133
136
  </div>
134
137
  ```
@@ -138,7 +141,7 @@ You can override the default behavior using the `forceWrapper` property. Setting
138
141
  <div>
139
142
  <div>
140
143
  <input type="checkbox" id="checkbox-xyz789-0" value="opt1" />
141
- <label htmlFor="checkbox-xyz789-0">Option 1</label>
144
+ <label for="checkbox-xyz789-0">Option 1</label>
142
145
  </div>
143
146
  </div>
144
147
  ```
@@ -186,6 +189,102 @@ Each option can have its own attributes applied to its container div:
186
189
 
187
190
  The component automatically generates unique IDs for each checkbox/radio button. If you provide an `id` in `inputAttributes`, it will be used as the base ID, and each option will have an index appended (e.g., `"my-id-0"`, `"my-id-1"`).
188
191
 
192
+ ## Accessibility
193
+
194
+ - Each option label is associated with its input (`for` / `id`).
195
+ - When a field `label` names several options, the element containing the options gets `role="group"` and `aria-labelledby` pointing to the field label (id from `labelAttributes.id`, or `<base id>-label`). Assistive technologies announce the field label when entering the group.
196
+ - When a field `label` comes with a single option without its own label, the field label is directly associated with the input.
197
+ - The radios of a field share a `name` (the base ID), so they form a native radio group: one tab stop, arrow keys navigation. Set `inputAttributes.name` to group radios rendered by several fields (one per `Switch` item, for example).
198
+ - `attributes` can override `role` and `aria-labelledby`.
199
+
200
+ ## Data-driven checkbox lists
201
+
202
+ To let users check items of a list held in the data (sessions, tags, recipients...), prefer, in this order:
203
+
204
+ ### 1. One field fed by the list (recommended)
205
+
206
+ Use `dynamicOptions` on the list and `optionMapping` to read the keys of the items. The items can have any shape: here, a list of `{label}` objects typed by an editor.
207
+
208
+ ```yaml
209
+ renderView:
210
+ - type: CheckBoxField
211
+ dataLocation: ~~.lead.sessions
212
+ label: "Sessions"
213
+ dynamicOptions: ~~.sessions
214
+ optionMapping:
215
+ label: ~.label
216
+ value: ~.label
217
+
218
+ data:
219
+ sessions:
220
+ - label: "20 octobre"
221
+ - label: "17 novembre"
222
+ - label: "15 décembre"
223
+ lead:
224
+ sessions: []
225
+ ```
226
+
227
+ Checking "17 novembre" then "20 octobre" stores `lead.sessions: ["17 novembre", "20 octobre"]`. The options are a labelled group for assistive technologies. With a list of scalars (`sessions: ["20 octobre", "17 novembre"]`), `optionMapping` is not needed: each scalar is both the label and the value.
228
+
229
+ ### 2. One field per Switch item
230
+
231
+ When each item needs its own markup around the checkbox, render one single-option field per item. The option `value` is evaluated in the item context, and the data initialized as an array keeps the fields independent:
232
+
233
+ ```yaml
234
+ renderView:
235
+ - type: Switch
236
+ content: ~~.sessions
237
+ singleOption:
238
+ load: sessionItem
239
+
240
+ templates:
241
+ sessionItem:
242
+ - type: CheckBoxField
243
+ dataLocation: ~~.lead.sessions
244
+ options:
245
+ - label: ~.label
246
+ value: ~.label
247
+
248
+ data:
249
+ sessions:
250
+ - label: "20 octobre"
251
+ - label: "17 novembre"
252
+ lead:
253
+ sessions: []
254
+ ```
255
+
256
+ Keep `lead.sessions: []` in the data (or set `multiple: true` on the field): without an array, each field is a single checkbox that overwrites the shared value, and a warning is logged. For a single choice, use `controlType: radio` with a shared `inputAttributes.name`; the data then holds the selected value.
257
+
258
+ ### 3. Raw `input` elements (last resort)
259
+
260
+ A raw `type: input` checkbox is a controlled input: its `checked` attribute must be bound, otherwise it is forced to `false` and never shows as checked (a warning is logged when it has event reactions). Bind a boolean with `checked: ~.isSelected`, or the membership in a list with an attribute transform:
261
+
262
+ ```yaml
263
+ - type: input
264
+ attributes:
265
+ type: checkbox
266
+ value: ~.label
267
+ attributeTransforms:
268
+ - what: setAttributeValue
269
+ name: checked
270
+ mode: replace
271
+ value: true
272
+ when: ~~.lead.sessions
273
+ contains: ~.label
274
+ ```
275
+
276
+ To store the list of checked values, add a [toggleData](../../reaction/toggleData.md) reaction: it adds the value when absent, and removes it when present.
277
+
278
+ ```yaml
279
+ actions:
280
+ - what: toggleData
281
+ on: change
282
+ path: ~~.lead.sessions
283
+ value: ~.label
284
+ ```
285
+
286
+ Prefer patterns 1 and 2, which also handle the labels and the accessibility of the group.
287
+
189
288
  ## Empty Options Handling
190
289
 
191
290
  If `options` is empty or not an array, the component returns `null` and renders nothing. This is useful for conditional rendering scenarios.
@@ -297,6 +396,31 @@ data:
297
396
  selectedItems: []
298
397
  ```
299
398
 
399
+ ### Dynamic Options with a Mapping
400
+
401
+ ```yaml
402
+ renderView:
403
+ - type: CheckBoxField
404
+ dataLocation: ~.recipients
405
+ label: "Recipients:"
406
+ dynamicOptions: ~.users
407
+ optionMapping:
408
+ label: ~.displayName
409
+ value: ~.uid
410
+ attributes:
411
+ class: ~.role
412
+
413
+ data:
414
+ users:
415
+ - uid: 12
416
+ displayName: "Ada Lovelace"
417
+ role: "admin"
418
+ - uid: 27
419
+ displayName: "Alan Turing"
420
+ role: "editor"
421
+ recipients: []
422
+ ```
423
+
300
424
  ### With Actions
301
425
 
302
426
  ```yaml
@@ -347,11 +471,11 @@ data:
347
471
 
348
472
  - **Flexible selection modes**: Supports radio buttons, single checkbox, and multiple checkboxes
349
473
  - **Automatic data synchronization**: Values are automatically stored in the correct format
350
- - **Dynamic options**: Options can be provided via template evaluation using `dynamicOptions`
474
+ - **Dynamic options**: Options can be provided via template evaluation using `dynamicOptions`, from a list of any shape with `optionMapping`
351
475
  - **Per-option customization**: Each option can have its own attributes
352
476
  - **Label flexibility**: Supports both simple strings and View component rendering
353
477
  - **Wrapper control**: Flexible wrapper system for styling control
354
- - **Accessibility**: Automatic label association with htmlFor attributes
478
+ - **Accessibility**: Automatic label association, labelled group of options, native radio groups
355
479
  - **Empty state handling**: Gracefully handles empty options
356
480
 
357
481
  ## Limitations
@@ -46,14 +46,20 @@ renderView:
46
46
  content: |
47
47
  Static array of option objects, each with:
48
48
  - `label` (string, optional): Display text for the option (supports template evaluation)
49
- - `value` (any, required): Value stored when this option is selected
49
+ - `value` (any, required): Value stored when this option is selected (supports template evaluation, like `label`)
50
50
  - `attributes` (object, optional): Attributes applied to the option's container div
51
51
  - term:
52
52
  code: dynamicOptions
53
53
  after: "(string, optional)"
54
54
  details:
55
55
  type: Markdown
56
- content: "Template path to dynamic options array (e.g., `~.availableItems`). Takes precedence over `options` if both are provided."
56
+ content: "Template path to dynamic options array (e.g., `~.availableItems`). Takes precedence over `options` if both are provided. The items can be `{label, value}` objects, scalars (each scalar is used as both label and value), or objects of any shape mapped with `optionMapping`."
57
+ - term:
58
+ code: optionMapping
59
+ after: "(object, optional)"
60
+ details:
61
+ type: Markdown
62
+ content: "Builds each option from an item of the options list. It is evaluated once per item, **with the item as the template context**: `~.title` reads the item's `title`, `~` is the whole item, `~~.` still reads the global data. The mapped keys (`label`, `value`, `attributes`) override the item's keys. See *Data-driven checkbox lists* below."
57
63
  - term:
58
64
  code: controlType
59
65
  after: "(string, optional)"
@@ -61,7 +67,7 @@ renderView:
61
67
  - term:
62
68
  code: multiple
63
69
  after: "(boolean, optional)"
64
- details: "Whether multiple selections are allowed. Defaults to `true` if more than one option exists, `false` otherwise. For radio buttons, this is automatically `false`."
70
+ details: "Whether multiple selections are allowed. Defaults to `true` when the bound data is an array or when more than one option exists, `false` otherwise. For radio buttons, this is automatically `false`."
65
71
  - term:
66
72
  code: attributes
67
73
  after: "(object, optional)"
@@ -73,7 +79,7 @@ renderView:
73
79
  - term:
74
80
  code: labelAttributes
75
81
  after: "(object, optional)"
76
- details: "Attributes applied to the main label (htmlFor is automatically managed)."
82
+ details: "Attributes applied to the field label and to each option label (`htmlFor` is automatically managed). An `id` is only applied to the field label."
77
83
  - term:
78
84
  code: forceWrapper
79
85
  after: "(boolean, optional)"
@@ -93,6 +99,8 @@ renderView:
93
99
  - **Single checkbox**: Stores a boolean (`true`) or the option's value when checked, `false` when unchecked
94
100
  - **Multiple checkboxes**: Stores an array of selected option values
95
101
 
102
+ The mode is chosen as follows: radio when `controlType: "radio"`; otherwise `multiple` when set; otherwise **multiple when the bound data is already an array** or when there is more than one option. Initializing the data as an array (`[]`) is therefore enough to get a list of values, even with a single option.
103
+
96
104
  When `dataLocation` is used, the value is stored at the specified path. Without `dataLocation`, the value is stored in the template context using the component's `datafield`.
97
105
 
98
106
  ## Selection Modes
@@ -105,10 +113,12 @@ renderView:
105
113
 
106
114
  ### Single Checkbox
107
115
 
108
- A single checkbox stores a boolean or the option's value:
116
+ A single checkbox (one option, data not initialized as an array) stores a boolean or the option's value:
109
117
 
110
118
  **Data stored**: `true` when checked, `false` when unchecked (or the option's value if different from `true`)
111
119
 
120
+ Several single checkboxes bound to the same data location overwrite each other's value, so they check and uncheck together. When they have different values, the component logs a warning: initialize the data as an array, or set `multiple: true`.
121
+
112
122
  ### Multiple Checkboxes
113
123
 
114
124
  Multiple checkboxes allow selecting several options simultaneously:
@@ -195,6 +205,121 @@ renderView:
195
205
  value: "item4"
196
206
  selectedItems: []
197
207
 
208
+ - type: Markdown
209
+ content: |
210
+ ## Accessibility
211
+
212
+ - Each option label is associated with its input (`for` / `id`).
213
+ - When a field `label` names several options, the element containing the options gets `role="group"` and `aria-labelledby` pointing to the field label (id from `labelAttributes.id`, or `<base id>-label`). Assistive technologies announce the field label when entering the group.
214
+ - When a field `label` comes with a single option without its own label, the field label is directly associated with the input.
215
+ - The radios of a field share a `name` (the base ID), so they form a native radio group: one tab stop, arrow keys navigation. Set `inputAttributes.name` to group radios rendered by several fields (one per `Switch` item, for example).
216
+ - `attributes` can override `role` and `aria-labelledby`.
217
+
218
+ ## Data-driven checkbox lists
219
+
220
+ To let users check items of a list held in the data (sessions, tags, recipients...), prefer, in this order:
221
+
222
+ ### 1. One field fed by the list (recommended)
223
+
224
+ Use `dynamicOptions` on the list and `optionMapping` to read the keys of the items. The items can have any shape: here, a list of `{label}` objects typed by an editor. With a list of scalars, `optionMapping` is not needed: each scalar is both the label and the value.
225
+
226
+ - type: RjBuildDescriber
227
+ title: "One field fed by the list"
228
+ description: "dynamicOptions + optionMapping: the items only have a label, which is also the stored value"
229
+ toDescribe:
230
+ renderView:
231
+ - type: CheckBoxField
232
+ dataLocation: ~~.lead.sessions
233
+ label: "Sessions"
234
+ dynamicOptions: ~~.sessions
235
+ optionMapping:
236
+ label: ~.label
237
+ value: ~.label
238
+ - type: div
239
+ content:
240
+ - type: strong
241
+ content: "Selected: "
242
+ - type: Join
243
+ content: ~~.lead.sessions
244
+ separator: ", "
245
+ data:
246
+ sessions:
247
+ - label: "20 octobre"
248
+ - label: "17 novembre"
249
+ - label: "15 décembre"
250
+ lead:
251
+ sessions: []
252
+
253
+ - type: Markdown
254
+ content: |
255
+ ### 2. One field per Switch item
256
+
257
+ When each item needs its own markup around the checkbox, render one single-option field per item. The option `value` is evaluated in the item context, and the data initialized as an array keeps the fields independent.
258
+
259
+ Keep `lead.sessions: []` in the data (or set `multiple: true` on the field): without an array, each field is a single checkbox that overwrites the shared value, and a warning is logged. For a single choice, use `controlType: radio` with a shared `inputAttributes.name`; the data then holds the selected value.
260
+
261
+ - type: RjBuildDescriber
262
+ title: "One field per Switch item"
263
+ description: "Each item renders its own single-option CheckBoxField, all bound to the same array"
264
+ toDescribe:
265
+ renderView:
266
+ - type: Switch
267
+ content: ~~.sessions
268
+ singleOption:
269
+ load: sessionItem
270
+ - type: div
271
+ content:
272
+ - type: strong
273
+ content: "Selected: "
274
+ - type: Join
275
+ content: ~~.lead.sessions
276
+ separator: ", "
277
+ templates:
278
+ sessionItem:
279
+ - type: CheckBoxField
280
+ dataLocation: ~~.lead.sessions
281
+ options:
282
+ - label: ~.label
283
+ value: ~.label
284
+ data:
285
+ sessions:
286
+ - label: "20 octobre"
287
+ - label: "17 novembre"
288
+ - label: "15 décembre"
289
+ lead:
290
+ sessions: []
291
+
292
+ - type: Markdown
293
+ content: |
294
+ ### 3. Raw `input` elements (last resort)
295
+
296
+ A raw `type: input` checkbox is a controlled input: its `checked` attribute must be bound, otherwise it is forced to `false` and never shows as checked (a warning is logged when it has event reactions). Bind a boolean with `checked: ~.isSelected`, or the membership in a list with an attribute transform:
297
+
298
+ ```yaml
299
+ - type: input
300
+ attributes:
301
+ type: checkbox
302
+ value: ~.label
303
+ attributeTransforms:
304
+ - what: setAttributeValue
305
+ name: checked
306
+ mode: replace
307
+ value: true
308
+ when: ~~.lead.sessions
309
+ contains: ~.label
310
+ ```
311
+
312
+ To store the list of checked values, add a [toggleData](../../reaction/toggleData) reaction: it adds the value when absent, and removes it when present.
313
+
314
+ ```yaml
315
+ actions:
316
+ - what: toggleData
317
+ on: change
318
+ path: ~~.lead.sessions
319
+ value: ~.label
320
+ ```
321
+
322
+ Prefer patterns 1 and 2, which also handle the labels and the accessibility of the group.
198
323
 
199
324
  - type: Markdown
200
325
  content: |
@@ -202,11 +327,11 @@ renderView:
202
327
 
203
328
  - **Flexible selection modes**: Supports radio buttons, single checkbox, and multiple checkboxes
204
329
  - **Automatic data synchronization**: Values are automatically stored in the correct format
205
- - **Dynamic options**: Options can be provided via template evaluation using `dynamicOptions`
330
+ - **Dynamic options**: Options can be provided via template evaluation using `dynamicOptions`, from a list of any shape with `optionMapping`
206
331
  - **Per-option customization**: Each option can have its own attributes
207
332
  - **Label flexibility**: Supports both simple strings and View component rendering
208
333
  - **Wrapper control**: Flexible wrapper system for styling control
209
- - **Accessibility**: Automatic label association with htmlFor attributes
334
+ - **Accessibility**: Automatic label association, labelled group of options, native radio groups
210
335
  - **Empty state handling**: Gracefully handles empty options
211
336
 
212
337
  ## Limitations
@@ -22,8 +22,9 @@ The `SelectField` component provides a native HTML select dropdown with automati
22
22
  - `dataLocation` (string, optional): Path to bind the field value in the data context.
23
23
  - `defaultFieldValue` (any, optional): Default value when no data is present.
24
24
  - `label` (string or View props, optional): Field label text (supports template evaluation and View component rendering).
25
- - `options` (array, optional): Static array of option objects with `label` and `value` properties.
26
- - `dynamicOptions` (string, optional): Template path to dynamic options array (e.g., `~.availableCategories`). Takes precedence over `options` if both are provided.
25
+ - `options` (array, optional): Static array of option objects with `label` and `value` properties (both support template evaluation).
26
+ - `dynamicOptions` (string, optional): Template path to dynamic options array (e.g., `~.availableCategories`). Takes precedence over `options` if both are provided. The items can be `{label, value}` objects, scalars (each scalar is used as both label and value), or objects of any shape mapped with `optionMapping`.
27
+ - `optionMapping` (object, optional): Builds each option from an item of the options list. It is evaluated once per item, **with the item as the template context**: `~.title` reads the item's `title`, `~` is the whole item, `~~.` still reads the global data. The mapped keys (`label`, `value`) override the item's keys.
27
28
  - `allowEmptyStringAsValue` (boolean, optional): Preserve empty strings as `""` instead of converting to `undefined` (default: `false`).
28
29
  - `attributes` (object, optional): Attributes applied to the container div (or merged with inputAttributes if no wrapper).
29
30
  - `inputAttributes` (object, optional): Attributes applied directly to the select element.
@@ -109,20 +110,22 @@ The `label` property supports both simple strings and View component rendering,
109
110
  dynamicOptions: ~.categories
110
111
  ```
111
112
 
112
- ## Option Labels
113
+ ## Option Labels and Values
113
114
 
114
- Option labels support template evaluation, allowing dynamic content:
115
+ The `label` and the `value` of each option support template evaluation: a string that is a whole template reference (`~.key`, `~~.key`, `~>key`...) is replaced by the referenced data. Text around a reference is not interpolated (`"Item: ~.itemName"` is displayed as is).
115
116
 
116
117
  ```yaml
117
118
  - type: SelectField
118
- dataLocation: ~.item
119
+ dataLocation: ~.owner
119
120
  options:
120
- - label: "Item: ~.itemName"
121
- value: "item1"
122
- - label: ["Count: ", ~.count]
123
- value: "item2"
121
+ - label: "Nobody"
122
+ value: ""
123
+ - label: ~~.currentUser.name
124
+ value: ~~.currentUser.id
124
125
  ```
125
126
 
127
+ Inside a template (a `Switch` item, for example), `~.` reads the item data, so each item can offer its own option.
128
+
126
129
  ## Empty Options Handling
127
130
 
128
131
  If `options` is empty or not an array, the component displays a single option: "No options available". This is useful for conditional rendering scenarios where options may be loaded dynamically.
@@ -211,6 +214,29 @@ data:
211
214
  selectedCategory: ""
212
215
  ```
213
216
 
217
+ ### Dynamic Options with a Mapping
218
+
219
+ Use `optionMapping` when the items of the list do not have `label` / `value` keys:
220
+
221
+ ```yaml
222
+ renderView:
223
+ - type: SelectField
224
+ dataLocation: ~.stage
225
+ label: "Stage:"
226
+ dynamicOptions: ~.stages
227
+ optionMapping:
228
+ label: ~.title
229
+ value: ~.id
230
+
231
+ data:
232
+ stages:
233
+ - id: "new"
234
+ title: "New"
235
+ - id: "won"
236
+ title: "Won"
237
+ stage: ""
238
+ ```
239
+
214
240
  ### Custom Attributes
215
241
 
216
242
  ```yaml
@@ -369,7 +395,7 @@ data:
369
395
  - **Automatic synchronization**: Unlike raw HTML elements that require manual setData actions
370
396
  - **Flexible wrapper**: Avoids unnecessary HTML when not needed
371
397
  - **Intelligent value handling**: Automatic conversion of boolean and null strings
372
- - **Dynamic options**: Options can be provided via template evaluation using `dynamicOptions`
398
+ - **Dynamic options**: Options can be provided via template evaluation using `dynamicOptions`, from a list of any shape with `optionMapping`
373
399
  - **Label flexibility**: Supports both simple strings and View component rendering
374
400
  - **Empty state handling**: Gracefully handles empty options
375
401
 
@@ -43,13 +43,19 @@ renderView:
43
43
  after: "(array, optional)"
44
44
  details:
45
45
  type: Markdown
46
- content: "Static array of option objects with `label` and `value` properties."
46
+ content: "Static array of option objects with `label` and `value` properties (both support template evaluation)."
47
47
  - term:
48
48
  code: dynamicOptions
49
49
  after: "(string, optional)"
50
50
  details:
51
51
  type: Markdown
52
- content: "Template path to dynamic options array (e.g., `~.availableCategories`). Takes precedence over `options` if both are provided."
52
+ content: "Template path to dynamic options array (e.g., `~.availableCategories`). Takes precedence over `options` if both are provided. The items can be `{label, value}` objects, scalars (each scalar is used as both label and value), or objects of any shape mapped with `optionMapping`."
53
+ - term:
54
+ code: optionMapping
55
+ after: "(object, optional)"
56
+ details:
57
+ type: Markdown
58
+ content: "Builds each option from an item of the options list. It is evaluated once per item, **with the item as the template context**: `~.title` reads the item's `title`, `~` is the whole item, `~~.` still reads the global data. The mapped keys (`label`, `value`) override the item's keys."
53
59
  - term:
54
60
  code: allowEmptyStringAsValue
55
61
  after: "(boolean, optional)"
@@ -222,6 +228,34 @@ renderView:
222
228
  value: "science"
223
229
  selectedCategory: ""
224
230
 
231
+ - type: RjBuildDescriber
232
+ title: "Dynamic Options with a Mapping"
233
+ description: "optionMapping reads the keys of items that have no label / value keys"
234
+ toDescribe:
235
+ renderView:
236
+ - type: SelectField
237
+ dataLocation: ~.stage
238
+ label: "Stage:"
239
+ dynamicOptions: ~.stages
240
+ optionMapping:
241
+ label: ~.title
242
+ value: ~.id
243
+ - type: div
244
+ attributes:
245
+ class: "mt-5 p-2.5 rounded border border-gray-300 dark:border-gray-600"
246
+ content:
247
+ - type: strong
248
+ content: "Selected stage: "
249
+ - ~.stage
250
+ data:
251
+ stages:
252
+ - id: "new"
253
+ title: "New"
254
+ - id: "qualified"
255
+ title: "Qualified"
256
+ - id: "won"
257
+ title: "Won"
258
+ stage: "new"
225
259
 
226
260
  - type: Markdown
227
261
  content: |
@@ -234,7 +268,7 @@ renderView:
234
268
  - **Automatic synchronization**: Unlike raw HTML elements that require manual setData actions
235
269
  - **Flexible wrapper**: Avoids unnecessary HTML when not needed
236
270
  - **Intelligent value handling**: Automatic conversion of boolean and null strings
237
- - **Dynamic options**: Options can be provided via template evaluation using `dynamicOptions`
271
+ - **Dynamic options**: Options can be provided via template evaluation using `dynamicOptions`, from a list of any shape with `optionMapping`
238
272
  - **Label flexibility**: Supports both simple strings and View component rendering
239
273
  - **Empty state handling**: Gracefully handles empty options
240
274
 
@@ -95,6 +95,38 @@ const onChange = (e) => {
95
95
 
96
96
  This ensures consistent behavior across TextField, NumberField, SelectField, CheckBoxField, TextAreaField, and DateField components.
97
97
 
98
+ ## evaluateFieldOptions Function
99
+
100
+ This utility returns the evaluated options of an option-based component (`SelectField`, `CheckBoxField`, `LabelFromValue`). Use it in a custom component that reads `options` / `dynamicOptions` / `optionMapping`, so that it supports the same option sources as the core components.
101
+
102
+ ### Parameters
103
+
104
+ - `props` (object): The component props. The function reads `dynamicOptions`, `options` and `optionMapping`.
105
+ - `globalDataContext` (object): The global data context object
106
+ - `templateContext` (object): The template context object
107
+
108
+ ### Return Value
109
+
110
+ An array of option objects with evaluated `label` and `value` (empty when the source is not an array):
111
+
112
+ - The source is `props.dynamicOptions` (a template reference, takes precedence) or the static `props.options` array.
113
+ - With `props.optionMapping`, the mapping is evaluated for each item, the item being the template context (`~`). The mapped keys override the item keys.
114
+ - Without `props.optionMapping`, the `label` and `value` of each item are evaluated in the component's template context. A scalar item is used as both label and value.
115
+
116
+ ### Usage Example
117
+
118
+ ```javascript
119
+ const options = evaluateFieldOptions({ props, globalDataContext, templateContext });
120
+
121
+ return (
122
+ <ul>
123
+ {options.map((option, index) => (
124
+ <li key={index} data-value={option.value}>{option.label}</li>
125
+ ))}
126
+ </ul>
127
+ );
128
+ ```
129
+
98
130
  ## Limitations
99
131
 
100
132
  - Template data modification may not be the ideal approach (noted in code comments)