@ea-lab/reactive-json-docs 2.7.0 → 2.10.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 (65) hide show
  1. package/package.json +2 -2
  2. package/public/rjbuild/docs/advanced-concepts/forward-update.yaml +1 -1
  3. package/public/rjbuild/docs/advanced-concepts/plugins/component-development.yaml +1 -1
  4. package/public/rjbuild/docs/advanced-concepts/plugins/index.yaml +1 -1
  5. package/public/rjbuild/docs/core/action/CustomEventListener.yaml +1 -1
  6. package/public/rjbuild/docs/core/element/form/AutocompleteField.yaml +1 -1
  7. package/public/rjbuild/docs/core/element/form/CheckBoxField.md +142 -18
  8. package/public/rjbuild/docs/core/element/form/CheckBoxField.yaml +133 -8
  9. package/public/rjbuild/docs/core/element/form/DateField.md +79 -0
  10. package/public/rjbuild/docs/core/element/form/DateField.yaml +115 -0
  11. package/public/rjbuild/docs/core/element/form/DateTimeField.md +60 -0
  12. package/public/rjbuild/docs/core/element/form/DateTimeField.yaml +97 -0
  13. package/public/rjbuild/docs/core/element/form/Input.yaml +1 -1
  14. package/public/rjbuild/docs/core/element/form/SelectField.md +36 -10
  15. package/public/rjbuild/docs/core/element/form/SelectField.yaml +38 -4
  16. package/public/rjbuild/docs/core/element/form/TextAreaField.yaml +1 -1
  17. package/public/rjbuild/docs/core/element/form/formElementsCommon.md +32 -0
  18. package/public/rjbuild/docs/core/element/html/Html.yaml +47 -0
  19. package/public/rjbuild/docs/core/element/html/Join.yaml +1 -1
  20. package/public/rjbuild/docs/core/element/html/LabelFromValue.md +159 -0
  21. package/public/rjbuild/docs/core/element/html/LabelFromValue.yaml +163 -10
  22. package/public/rjbuild/docs/core/element/special/PageControls.yaml +1 -1
  23. package/public/rjbuild/docs/core/example/native-html-forms.md +19 -2
  24. package/public/rjbuild/docs/core/hook/index.yaml +1 -1
  25. package/public/rjbuild/docs/core/hook/usePagination.yaml +1 -1
  26. package/public/rjbuild/docs/core/hook/useTransformedAttributes.yaml +1 -1
  27. package/public/rjbuild/docs/core/reaction/collectField.md +51 -0
  28. package/public/rjbuild/docs/core/reaction/collectField.yaml +81 -0
  29. package/public/rjbuild/docs/core/reaction/fetchData.md +21 -1
  30. package/public/rjbuild/docs/core/reaction/fetchData.yaml +2 -2
  31. package/public/rjbuild/docs/core/reaction/index.md +5 -0
  32. package/public/rjbuild/docs/core/reaction/index.yaml +5 -0
  33. package/public/rjbuild/docs/core/reaction/joinData.md +51 -0
  34. package/public/rjbuild/docs/core/reaction/joinData.yaml +87 -0
  35. package/public/rjbuild/docs/core/reaction/printPage.md +22 -0
  36. package/public/rjbuild/docs/core/reaction/printPage.yaml +29 -0
  37. package/public/rjbuild/docs/core/reaction/redirectNow.md +20 -2
  38. package/public/rjbuild/docs/core/reaction/redirectNow.yaml +24 -3
  39. package/public/rjbuild/docs/core/reaction/setClipboardData.md +35 -6
  40. package/public/rjbuild/docs/core/reaction/setClipboardData.yaml +18 -6
  41. package/public/rjbuild/docs/core/reaction/stringifyData.md +45 -0
  42. package/public/rjbuild/docs/core/reaction/stringifyData.yaml +58 -0
  43. package/public/rjbuild/docs/core/reaction/submitData.md +1 -1
  44. package/public/rjbuild/docs/core/reaction/submitData.yaml +4 -2
  45. package/public/rjbuild/docs/core/reaction/toggleData.md +72 -0
  46. package/public/rjbuild/docs/core/reaction/toggleData.yaml +77 -0
  47. package/public/rjbuild/docs/docs-components/Mermaid.yaml +1 -1
  48. package/public/rjbuild/docs/docs-components/SyntaxHighlighter.yaml +1 -1
  49. package/public/rjbuild/docs/docs-components/index.yaml +1 -1
  50. package/public/rjbuild/docs/getting-started/reactions.md +5 -0
  51. package/public/rjbuild/docs/getting-started/reactions.yaml +5 -0
  52. package/public/rjbuild/docs/getting-started/rjbuild-structure.md +19 -1
  53. package/public/rjbuild/docs/getting-started/rjbuild-structure.yaml +21 -1
  54. package/public/rjbuild/docs/getting-started/template-contexts-data-binding.yaml +1 -1
  55. package/public/rjbuild/docs/index.yaml +1 -1
  56. package/public/rjbuild/docs/install.yaml +1 -1
  57. package/public/rjbuild/docs/integration/bootstrap/element/form/CheckBoxField.yaml +1 -1
  58. package/public/rjbuild/docs/integration/bootstrap/element/form/DateField.yaml +1 -1
  59. package/public/rjbuild/docs/integration/bootstrap/element/form/NumberField.yaml +1 -1
  60. package/public/rjbuild/docs/integration/bootstrap/element/form/SelectField.yaml +1 -1
  61. package/public/rjbuild/docs/integration/bootstrap/element/form/TextAreaField.yaml +1 -1
  62. package/public/rjbuild/docs/integration/bootstrap/element/form/TextField.yaml +1 -1
  63. package/public/rjbuild/docs/integration/bootstrap/element/form/index.yaml +1 -1
  64. package/public/rjbuild/docs/integration/bootstrap/example/accordion.yaml +1 -1
  65. package/public/rjbuild/docs/integration/bootstrap/example/dynamic-content.yaml +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ea-lab/reactive-json-docs",
3
- "version": "2.7.0",
3
+ "version": "2.10.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.5.1",
29
+ "@ea-lab/reactive-json": "^2.10.0",
30
30
  "@ea-lab/reactive-json-chartjs": "^1.1.1",
31
31
  "@npmcli/fs": "^4.0.0",
32
32
  "@reduxjs/toolkit": "^2.6.1",
@@ -176,4 +176,4 @@ renderView:
176
176
 
177
177
  templates:
178
178
 
179
- data: {}
179
+ data:
@@ -225,4 +225,4 @@ renderView:
225
225
 
226
226
  templates:
227
227
 
228
- data: {}
228
+ data:
@@ -26,4 +26,4 @@ renderView:
26
26
 
27
27
  templates:
28
28
 
29
- data: {}
29
+ data:
@@ -155,4 +155,4 @@ renderView:
155
155
 
156
156
  templates:
157
157
 
158
- data: {}
158
+ data:
@@ -222,4 +222,4 @@ renderView:
222
222
  - No built-in keyboard navigation for the dropdown.
223
223
 
224
224
  templates: {}
225
- data: {}
225
+ data:
@@ -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
@@ -218,5 +343,5 @@ renderView:
218
343
  - For radio buttons, `multiple` is automatically set to `false` regardless of the `multiple` property
219
344
 
220
345
  templates: {}
221
- data: {}
346
+ data:
222
347
 
@@ -0,0 +1,79 @@
1
+ # DateField
2
+
3
+ The `DateField` component is a thin convenience wrapper around [`Input`](Input.md) that renders an HTML `date` input (`<input type="date">`). It inherits every `Input` option (label, wrapper, attributes, actions, data binding) and adds an optional `storageFormat` for binding to a fixed-format storage string.
4
+
5
+ ## Basic Syntax
6
+
7
+ ```yaml
8
+ - type: DateField
9
+ dataLocation: ~.startDate
10
+ label: "Start date:"
11
+ ```
12
+
13
+ By default the raw value of the HTML `date` input (`YYYY-MM-DD`) is stored as-is — identical to any other `Input` wrapper.
14
+
15
+ ## Properties
16
+
17
+ - `dataLocation` (string, optional): Path to bind the field value in the data context. Without it, the value is stored in the template context under the component's `datafield`.
18
+ - `defaultFieldValue` (string, optional): Default value when no data is present.
19
+ - `label` (string or View props, optional): Field label text (supports template evaluation and View rendering). A wrapper `<div>` is added automatically when a label is present.
20
+ - `storageFormat` (string, optional): Binds the field to a fixed-format **storage string** instead of the raw input value. Accepts `"date"` or `"datetime"` (see [Storage format](#storage-format)). When omitted, the raw `YYYY-MM-DD` value is stored unchanged (original behavior).
21
+ - `attributes` (object, optional): Attributes applied to the wrapper `<div>` (or to the input itself when there is no wrapper).
22
+ - `inputAttributes` (object, optional): Attributes applied directly to the `<input>` element (e.g. `class`, `id`, `min`, `max`).
23
+ - `labelAttributes` (object, optional): Attributes applied to the label (`htmlFor` is managed automatically).
24
+ - `forceWrapper` (boolean, optional): Forces the presence (`true`) or absence (`false`) of the wrapper `<div>`. If omitted, the wrapper appears only when a label is present.
25
+ - `actions` (array, optional): Actions and conditional reactions to execute based on field state.
26
+
27
+ ## Storage format
28
+
29
+ An HTML `date` input can only read and write `YYYY-MM-DD`. Backends often store dates in a fixed, stricter format — for example Drupal datetime fields validate against `Y-m-d` (date-only field) or `Y-m-d\TH:i:s` (datetime field). Feeding such a value straight to a `date` input leaves it blank (the input rejects a value carrying a time), and writing back `YYYY-MM-DD` is rejected by a datetime field.
30
+
31
+ Set `storageFormat` to bridge the two. It applies **two conversions**:
32
+
33
+ - **On read** — only the date part of the stored value is shown, so a datetime value like `2016-11-29T23:00:00` displays as `2016-11-29` instead of leaving the input empty.
34
+ - **On write** — the value is re-serialized to the target storage format:
35
+ - `storageFormat: date` → `YYYY-MM-DD`
36
+ - `storageFormat: datetime` → `YYYY-MM-DDT00:00:00` (the time is anchored at midnight, since a `date` input carries no time)
37
+
38
+ An empty input stores an empty string (clears the field). When `storageFormat` is omitted, no conversion happens and the raw input value is stored — so existing usages are unaffected.
39
+
40
+ > For editing a value that also carries a **time** the user should see and change, use [`DateTimeField`](DateTimeField.md) instead.
41
+
42
+ ## Examples
43
+
44
+ ### Default behavior (raw `YYYY-MM-DD`)
45
+
46
+ ```yaml
47
+ renderView:
48
+ - type: DateField
49
+ dataLocation: ~.startDate
50
+ label: "Start date:"
51
+ - type: div
52
+ content:
53
+ - type: strong
54
+ content: "Stored value: "
55
+ - ~.startDate
56
+
57
+ data:
58
+ startDate: "2027-03-15"
59
+ ```
60
+
61
+ ### `storageFormat: datetime`
62
+
63
+ The `date` input drives a datetime storage string (time anchored at midnight):
64
+
65
+ ```yaml
66
+ renderView:
67
+ - type: DateField
68
+ dataLocation: ~.closeDate
69
+ label: "Close date:"
70
+ storageFormat: datetime
71
+ - type: div
72
+ content:
73
+ - type: strong
74
+ content: "Stored value: "
75
+ - ~.closeDate
76
+
77
+ data:
78
+ closeDate: "2016-11-29T23:00:00"
79
+ ```