@scm-manager/ui-core 4.0.0-REACT18-20250701-125025 → 4.0.0-REACT19

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 (56) hide show
  1. package/{src/base/buttons/a11y.test.ts → .storybook/i18n.ts} +27 -9
  2. package/.storybook/main.ts +54 -0
  3. package/.storybook/preview-head.html +6 -1
  4. package/.storybook/preview.tsx +125 -0
  5. package/.turbo/turbo-test.log +164 -0
  6. package/.turbo/turbo-typecheck.log +3 -2
  7. package/package.json +37 -42
  8. package/src/base/buttons/Button.stories.tsx +179 -70
  9. package/src/base/buttons/Button.tsx +9 -9
  10. package/src/base/forms/AddListEntryForm.tsx +8 -8
  11. package/src/base/forms/ConfigurationForm.tsx +14 -5
  12. package/src/base/forms/Form.stories.tsx +599 -289
  13. package/src/base/forms/Form.tsx +8 -8
  14. package/src/base/forms/FormPathContext.tsx +3 -3
  15. package/src/base/forms/ScmFormContext.tsx +7 -4
  16. package/src/base/forms/ScmFormListContext.tsx +2 -1
  17. package/src/base/forms/base/Field.tsx +1 -1
  18. package/src/base/forms/base/label/Label.tsx +1 -1
  19. package/src/base/forms/chip-input/ChipInputField.stories.tsx +109 -28
  20. package/src/base/forms/chip-input/ChipInputField.tsx +20 -8
  21. package/src/base/forms/chip-input/ControlledChipInputField.tsx +3 -1
  22. package/src/base/forms/combobox/Combobox.stories.tsx +216 -89
  23. package/src/base/forms/combobox/Combobox.tsx +4 -2
  24. package/src/base/forms/combobox/ComboboxField.tsx +2 -1
  25. package/src/base/forms/headless-chip-input/ChipInput.tsx +9 -9
  26. package/src/base/forms/helpers.ts +12 -9
  27. package/src/base/forms/input/ControlledSecretConfirmationField.tsx +4 -2
  28. package/src/base/forms/radio-button/RadioButton.stories.tsx +317 -124
  29. package/src/base/forms/radio-button/RadioButton.tsx +8 -4
  30. package/src/base/forms/radio-button/RadioButtonContext.tsx +2 -1
  31. package/src/base/forms/table/ControlledColumn.tsx +1 -1
  32. package/src/base/forms/table/ControlledTable.tsx +12 -4
  33. package/src/base/helpers/useDocumentTitle.test.ts +15 -7
  34. package/src/base/layout/card/Card.stories.tsx +171 -72
  35. package/src/base/layout/card/Card.tsx +4 -4
  36. package/src/base/layout/card/CardDetail.tsx +2 -3
  37. package/src/base/layout/card-list/CardList.stories.tsx +283 -169
  38. package/src/base/layout/collapsible/Collapsible.stories.tsx +54 -16
  39. package/src/base/layout/index.ts +2 -5
  40. package/src/base/layout/tabs/Tabs.stories.tsx +58 -16
  41. package/src/base/layout/templates/data-page/DataPage.stories.tsx +289 -156
  42. package/src/base/layout/templates/data-page/DataPageHeader.tsx +1 -1
  43. package/src/base/overlays/dialog/Dialog.stories.tsx +94 -34
  44. package/src/base/overlays/menu/Menu.stories.tsx +116 -48
  45. package/src/base/overlays/menu/Menu.tsx +1 -0
  46. package/src/base/overlays/popover/Popover.stories.tsx +50 -37
  47. package/src/base/shortcuts/iterator/keyboardIterator.test.tsx +16 -7
  48. package/src/base/shortcuts/iterator/keyboardIterator.tsx +13 -5
  49. package/src/base/status/StatusIcon.stories.tsx +76 -27
  50. package/src/base/status/index.ts +1 -1
  51. package/src/base/text/SplitAndReplace.stories.tsx +128 -50
  52. package/src/base/text/index.ts +1 -1
  53. package/.storybook/RemoveThemesPlugin.js +0 -49
  54. package/.storybook/main.js +0 -86
  55. package/.storybook/preview.js +0 -87
  56. package/src/base/buttons/image-snapshot.test.ts +0 -26
@@ -14,8 +14,212 @@
14
14
  * along with this program. If not, see https://www.gnu.org/licenses/.
15
15
  */
16
16
 
17
- import React, { useState } from "react";
18
- import { storiesOf } from "@storybook/react";
17
+ // import React, { useState } from "react";
18
+ // import { storiesOf } from "@storybook/react";
19
+ // import RadioButton from "./RadioButton";
20
+ // import RadioGroup from "./RadioGroup";
21
+ // import RadioGroupField from "./RadioGroupField";
22
+ // import Form from "../Form";
23
+ // import ControlledRadioGroupField from "./ControlledRadioGroupField";
24
+ // import ControlledInputField from "../input/ControlledInputField";
25
+ // import FormRow from "../FormRow";
26
+ // import { ScmFormListContextProvider } from "../ScmFormListContext";
27
+ // import ControlledList from "../list/ControlledList";
28
+ // import ControlledTable from "../table/ControlledTable";
29
+ // import ControlledColumn from "../table/ControlledColumn";
30
+ // import AddListEntryForm from "../AddListEntryForm";
31
+ //
32
+ // storiesOf("Radio Group", module)
33
+ // .add("Uncontrolled State", () => {
34
+ // return (
35
+ // <RadioGroup name="starter_pokemon">
36
+ // <RadioButton value="CHARMANDER" label="Charmander" />
37
+ // <RadioButton value="SQUIRTLE" label="Squirtle" />
38
+ // <RadioButton value="BULBASAUR" label="Bulbasaur" helpText="Dont pick this one" />
39
+ // </RadioGroup>
40
+ // );
41
+ // })
42
+ // .add("Controlled State", () => {
43
+ // const [value, setValue] = useState<string | undefined>(undefined);
44
+ // return (
45
+ // <RadioGroup name="starter_pokemon" value={value} onValueChange={setValue}>
46
+ // <RadioButton value="CHARMANDER" label="Charmander" />
47
+ // <RadioButton value="SQUIRTLE" label="Squirtle" />
48
+ // <RadioButton value="BULBASAUR" label="Bulbasaur" helpText="Dont pick this one" />
49
+ // </RadioGroup>
50
+ // );
51
+ // })
52
+ // .add("Radio Group Field", () => {
53
+ // const [value, setValue] = useState<string | undefined>(undefined);
54
+ // return (
55
+ // <RadioGroupField
56
+ // label="Choose your starter"
57
+ // helpText="Choose wisely"
58
+ // name="starter_pokemon"
59
+ // value={value}
60
+ // onValueChange={setValue}
61
+ // >
62
+ // <RadioButton value="CHARMANDER" label="Charmander" />
63
+ // <RadioButton value="SQUIRTLE" label="Squirtle" />
64
+ // <RadioButton value="BULBASAUR" label="Bulbasaur" helpText="Dont pick this one" />
65
+ // </RadioGroupField>
66
+ // );
67
+ // })
68
+ // .add("Controlled Radio Group Field", () => (
69
+ // <Form
70
+ // // eslint-disable-next-line no-console
71
+ // onSubmit={console.log}
72
+ // translationPath={["sample", "form"]}
73
+ // defaultValues={{ name: "", starter_pokemon: null }}
74
+ // >
75
+ // <FormRow>
76
+ // <ControlledInputField name="name" />
77
+ // </FormRow>
78
+ // <FormRow>
79
+ // <ControlledRadioGroupField
80
+ // name="starter_pokemon"
81
+ // label="Starter Pokemon"
82
+ // helpText="Choose wisely"
83
+ // rules={{ required: true }}
84
+ // >
85
+ // <RadioButton value="CHARMANDER" label="Charmander" labelClassName="is-block" />
86
+ // <RadioButton value="SQUIRTLE" label="Squirtle" />
87
+ // <RadioButton value="BULBASAUR" label="Bulbasaur" helpText="Dont pick this one" />
88
+ // </ControlledRadioGroupField>
89
+ // </FormRow>
90
+ // </Form>
91
+ // ))
92
+ // .add("Readonly Controlled Radio Group Field", () => (
93
+ // <Form
94
+ // // eslint-disable-next-line no-console
95
+ // onSubmit={console.log}
96
+ // translationPath={["sample", "form"]}
97
+ // defaultValues={{ name: "Red", starter_pokemon: "SQUIRTLE" }}
98
+ // >
99
+ // <FormRow>
100
+ // <ControlledInputField name="name" />
101
+ // </FormRow>
102
+ // <FormRow>
103
+ // <ControlledRadioGroupField
104
+ // name="starter_pokemon"
105
+ // label="Starter Pokemon"
106
+ // helpText="Choose wisely"
107
+ // readOnly={true}
108
+ // >
109
+ // <RadioButton value="CHARMANDER" label="Charmander" labelClassName="is-block" />
110
+ // <RadioButton value="SQUIRTLE" label="Squirtle" />
111
+ // <RadioButton value="BULBASAUR" label="Bulbasaur" helpText="Dont pick this one" />
112
+ // </ControlledRadioGroupField>
113
+ // </FormRow>
114
+ // </Form>
115
+ // ))
116
+ // .add("With options", () => (
117
+ // <Form
118
+ // // eslint-disable-next-line no-console
119
+ // onSubmit={console.log}
120
+ // translationPath={["sample", "form"]}
121
+ // defaultValues={{ starter_pokemon: "CHARMANDER" }}
122
+ // >
123
+ // <FormRow>
124
+ // <ControlledRadioGroupField
125
+ // name="starter_pokemon"
126
+ // label="Starter Pokemon"
127
+ // helpText="Choose wisely"
128
+ // rules={{ required: true }}
129
+ // options={[
130
+ // { value: "CHARMANDER", label: "Charmander" },
131
+ // { value: "SQUIRTLE", label: "Squirtle" },
132
+ // { value: "BULBASAUR", label: "Bulbasaur", helpText: "Dont pick this one" },
133
+ // ]}
134
+ // />
135
+ // </FormRow>
136
+ // </Form>
137
+ // ))
138
+ // .add("Without label prop", () => (
139
+ // <Form
140
+ // // eslint-disable-next-line no-console
141
+ // onSubmit={console.log}
142
+ // translationPath={["sample", "form"]}
143
+ // defaultValues={{ starter_pokemon: "CHARMANDER" }}
144
+ // >
145
+ // <FormRow>
146
+ // <ControlledRadioGroupField
147
+ // name="starter_pokemon"
148
+ // label="Starter Pokemon"
149
+ // helpText="Choose wisely"
150
+ // rules={{ required: true }}
151
+ // options={[
152
+ // { value: "CHARMANDER" },
153
+ // { value: "SQUIRTLE" },
154
+ // { value: "BULBASAUR", helpText: "Dont pick this one" },
155
+ // ]}
156
+ // />
157
+ // </FormRow>
158
+ // </Form>
159
+ // ))
160
+ // .add("Nested", () => (
161
+ // <Form
162
+ // translationPath={["sample", "form"]}
163
+ // // eslint-disable-next-line no-console
164
+ // onSubmit={console.log}
165
+ // defaultValues={{
166
+ // trainers: [
167
+ // {
168
+ // name: "Red",
169
+ // team: [{ nickname: "Charlie", pokemon: "CHARMANDER" }],
170
+ // },
171
+ // {
172
+ // name: "Blue",
173
+ // team: [{ nickname: "Squirtle", pokemon: "SQUIRTLE" }],
174
+ // },
175
+ // {
176
+ // name: "Green",
177
+ // team: [{ nickname: "Plant", pokemon: "SQUIRTLE" }],
178
+ // },
179
+ // ],
180
+ // }}
181
+ // >
182
+ // <ScmFormListContextProvider name={"trainers"}>
183
+ // <ControlledList withDelete>
184
+ // {({ value: trainers }) => (
185
+ // <>
186
+ // <FormRow>
187
+ // <ControlledInputField name={"name"} />
188
+ // </FormRow>
189
+ // <details className="has-background-dark-25 mb-2 p-2">
190
+ // <summary className="is-clickable">Team</summary>
191
+ // <div>
192
+ // <ScmFormListContextProvider name={"team"}>
193
+ // <ControlledTable withDelete>
194
+ // <ControlledColumn name="nickname" />
195
+ // <ControlledColumn name="pokemon" />
196
+ // </ControlledTable>
197
+ // <AddListEntryForm defaultValues={{ nickname: "", pokemon: null }}>
198
+ // <FormRow>
199
+ // <ControlledInputField name="nickname" />
200
+ // </FormRow>
201
+ // <FormRow>
202
+ // <ControlledRadioGroupField
203
+ // name="pokemon"
204
+ // label="Starter Pokemon"
205
+ // rules={{ required: true }}
206
+ // options={[{ value: "CHARMANDER" }, { value: "SQUIRTLE" }, { value: "BULBASAUR" }]}
207
+ // />
208
+ // </FormRow>
209
+ // </AddListEntryForm>
210
+ // </ScmFormListContextProvider>
211
+ // </div>
212
+ // </details>
213
+ // </>
214
+ // )}
215
+ // </ControlledList>
216
+ // </ScmFormListContextProvider>
217
+ // </Form>
218
+ // ));
219
+
220
+ import React, { FC, useState } from "react";
221
+ import type { Meta, StoryObj } from "@storybook/react";
222
+
19
223
  import RadioButton from "./RadioButton";
20
224
  import RadioGroup from "./RadioGroup";
21
225
  import RadioGroupField from "./RadioGroupField";
@@ -29,45 +233,68 @@ import ControlledTable from "../table/ControlledTable";
29
233
  import ControlledColumn from "../table/ControlledColumn";
30
234
  import AddListEntryForm from "../AddListEntryForm";
31
235
 
32
- storiesOf("Radio Group", module)
33
- .add("Uncontrolled State", () => {
34
- return (
35
- <RadioGroup name="starter_pokemon">
36
- <RadioButton value="CHARMANDER" label="Charmander" />
37
- <RadioButton value="SQUIRTLE" label="Squirtle" />
38
- <RadioButton value="BULBASAUR" label="Bulbasaur" helpText="Dont pick this one" />
39
- </RadioGroup>
40
- );
41
- })
42
- .add("Controlled State", () => {
43
- const [value, setValue] = useState<string | undefined>(undefined);
44
- return (
45
- <RadioGroup name="starter_pokemon" value={value} onValueChange={setValue}>
46
- <RadioButton value="CHARMANDER" label="Charmander" />
47
- <RadioButton value="SQUIRTLE" label="Squirtle" />
48
- <RadioButton value="BULBASAUR" label="Bulbasaur" helpText="Dont pick this one" />
49
- </RadioGroup>
50
- );
51
- })
52
- .add("Radio Group Field", () => {
53
- const [value, setValue] = useState<string | undefined>(undefined);
54
- return (
55
- <RadioGroupField
56
- label="Choose your starter"
57
- helpText="Choose wisely"
58
- name="starter_pokemon"
59
- value={value}
60
- onValueChange={setValue}
61
- >
62
- <RadioButton value="CHARMANDER" label="Charmander" />
63
- <RadioButton value="SQUIRTLE" label="Squirtle" />
64
- <RadioButton value="BULBASAUR" label="Bulbasaur" helpText="Dont pick this one" />
65
- </RadioGroupField>
66
- );
67
- })
68
- .add("Controlled Radio Group Field", () => (
236
+ const ControlledStateExample: FC = () => {
237
+ const [value, setValue] = useState<string | undefined>(undefined);
238
+ return (
239
+ <RadioGroup name="starter_pokemon" value={value} onValueChange={setValue}>
240
+ <RadioButton value="CHARMANDER" label="Charmander" />
241
+ <RadioButton value="SQUIRTLE" label="Squirtle" />
242
+ <RadioButton value="BULBASAUR" label="Bulbasaur" helpText="Dont pick this one" />
243
+ </RadioGroup>
244
+ );
245
+ };
246
+
247
+ const RadioGroupFieldExample: FC = () => {
248
+ const [value, setValue] = useState<string | undefined>(undefined);
249
+ return (
250
+ <RadioGroupField
251
+ label="Choose your starter"
252
+ helpText="Choose wisely"
253
+ name="starter_pokemon"
254
+ value={value}
255
+ onValueChange={setValue}
256
+ >
257
+ <RadioButton value="CHARMANDER" label="Charmander" />
258
+ <RadioButton value="SQUIRTLE" label="Squirtle" />
259
+ <RadioButton value="BULBASAUR" label="Bulbasaur" helpText="Dont pick this one" />
260
+ </RadioGroupField>
261
+ );
262
+ };
263
+
264
+ const meta: Meta<typeof RadioGroup> = {
265
+ title: "Forms/RadioGroup",
266
+ component: RadioGroup,
267
+ decorators: [(Story) => <div style={{ margin: "2rem", maxWidth: "40rem" }}>{Story()}</div>],
268
+ tags: ["autodocs"],
269
+ };
270
+
271
+ export default meta;
272
+
273
+ type Story = StoryObj<typeof meta>;
274
+
275
+ export const UncontrolledState: Story = {
276
+ render: () => (
277
+ <RadioGroup name="starter_pokemon">
278
+ <RadioButton value="CHARMANDER" label="Charmander" />
279
+ <RadioButton value="SQUIRTLE" label="Squirtle" />
280
+ <RadioButton value="BULBASAUR" label="Bulbasaur" helpText="Dont pick this one" />
281
+ </RadioGroup>
282
+ ),
283
+ };
284
+
285
+ export const ControlledState: Story = {
286
+ render: () => <ControlledStateExample />,
287
+ };
288
+
289
+ export const AsAField: Story = {
290
+ name: "As a Field",
291
+ render: () => <RadioGroupFieldExample />,
292
+ };
293
+
294
+ export const InAForm: Story = {
295
+ name: "In a Form (Controlled)",
296
+ render: () => (
69
297
  <Form
70
- // eslint-disable-next-line no-console
71
298
  onSubmit={console.log}
72
299
  translationPath={["sample", "form"]}
73
300
  defaultValues={{ name: "", starter_pokemon: null }}
@@ -82,44 +309,40 @@ storiesOf("Radio Group", module)
82
309
  helpText="Choose wisely"
83
310
  rules={{ required: true }}
84
311
  >
85
- <RadioButton value="CHARMANDER" label="Charmander" labelClassName="is-block" />
312
+ <RadioButton value="CHARMANDER" label="Charmander" />
86
313
  <RadioButton value="SQUIRTLE" label="Squirtle" />
87
314
  <RadioButton value="BULBASAUR" label="Bulbasaur" helpText="Dont pick this one" />
88
315
  </ControlledRadioGroupField>
89
316
  </FormRow>
90
317
  </Form>
91
- ))
92
- .add("Readonly Controlled Radio Group Field", () => (
318
+ ),
319
+ };
320
+
321
+ export const ReadOnly: Story = {
322
+ render: () => (
93
323
  <Form
94
- // eslint-disable-next-line no-console
95
324
  onSubmit={console.log}
96
325
  translationPath={["sample", "form"]}
97
326
  defaultValues={{ name: "Red", starter_pokemon: "SQUIRTLE" }}
98
327
  >
99
328
  <FormRow>
100
- <ControlledInputField name="name" />
329
+ <ControlledInputField name="name" readOnly />
101
330
  </FormRow>
102
331
  <FormRow>
103
- <ControlledRadioGroupField
104
- name="starter_pokemon"
105
- label="Starter Pokemon"
106
- helpText="Choose wisely"
107
- readOnly={true}
108
- >
109
- <RadioButton value="CHARMANDER" label="Charmander" labelClassName="is-block" />
332
+ <ControlledRadioGroupField name="starter_pokemon" label="Starter Pokemon" helpText="Choose wisely" readOnly>
333
+ <RadioButton value="CHARMANDER" label="Charmander" />
110
334
  <RadioButton value="SQUIRTLE" label="Squirtle" />
111
- <RadioButton value="BULBASAUR" label="Bulbasaur" helpText="Dont pick this one" />
335
+ <RadioButton value="BULBASAUR" label="Bulbasaur" />
112
336
  </ControlledRadioGroupField>
113
337
  </FormRow>
114
338
  </Form>
115
- ))
116
- .add("With options", () => (
117
- <Form
118
- // eslint-disable-next-line no-console
119
- onSubmit={console.log}
120
- translationPath={["sample", "form"]}
121
- defaultValues={{ starter_pokemon: "CHARMANDER" }}
122
- >
339
+ ),
340
+ };
341
+
342
+ export const WithOptionsProp: Story = {
343
+ name: "With Options Prop",
344
+ render: () => (
345
+ <Form onSubmit={console.log} translationPath={["sample", "form"]} defaultValues={{ starter_pokemon: "CHARMANDER" }}>
123
346
  <FormRow>
124
347
  <ControlledRadioGroupField
125
348
  name="starter_pokemon"
@@ -134,85 +357,55 @@ storiesOf("Radio Group", module)
134
357
  />
135
358
  </FormRow>
136
359
  </Form>
137
- ))
138
- .add("Without label prop", () => (
139
- <Form
140
- // eslint-disable-next-line no-console
141
- onSubmit={console.log}
142
- translationPath={["sample", "form"]}
143
- defaultValues={{ starter_pokemon: "CHARMANDER" }}
144
- >
145
- <FormRow>
146
- <ControlledRadioGroupField
147
- name="starter_pokemon"
148
- label="Starter Pokemon"
149
- helpText="Choose wisely"
150
- rules={{ required: true }}
151
- options={[
152
- { value: "CHARMANDER" },
153
- { value: "SQUIRTLE" },
154
- { value: "BULBASAUR", helpText: "Dont pick this one" },
155
- ]}
156
- />
157
- </FormRow>
158
- </Form>
159
- ))
160
- .add("Nested", () => (
360
+ ),
361
+ };
362
+
363
+ export const NestedInTable: Story = {
364
+ name: "Nested in a Table",
365
+ render: () => (
161
366
  <Form
162
367
  translationPath={["sample", "form"]}
163
- // eslint-disable-next-line no-console
164
368
  onSubmit={console.log}
165
369
  defaultValues={{
166
370
  trainers: [
167
- {
168
- name: "Red",
169
- team: [{ nickname: "Charlie", pokemon: "CHARMANDER" }],
170
- },
171
- {
172
- name: "Blue",
173
- team: [{ nickname: "Squirtle", pokemon: "SQUIRTLE" }],
174
- },
175
- {
176
- name: "Green",
177
- team: [{ nickname: "Plant", pokemon: "SQUIRTLE" }],
178
- },
371
+ { name: "Red", team: [{ nickname: "Charlie", pokemon: "CHARMANDER" }] },
372
+ { name: "Blue", team: [{ nickname: "Squirtle", pokemon: "SQUIRTLE" }] },
179
373
  ],
180
374
  }}
181
375
  >
182
- <ScmFormListContextProvider name={"trainers"}>
376
+ <ScmFormListContextProvider name="trainers">
183
377
  <ControlledList withDelete>
184
- {({ value: trainers }) => (
378
+ {() => (
185
379
  <>
186
380
  <FormRow>
187
- <ControlledInputField name={"name"} />
381
+ <ControlledInputField name="name" />
188
382
  </FormRow>
189
- <details className="has-background-dark-25 mb-2 p-2">
383
+ <details className="has-background-dark-25 mb-2 p-2" open>
190
384
  <summary className="is-clickable">Team</summary>
191
- <div>
192
- <ScmFormListContextProvider name={"team"}>
193
- <ControlledTable withDelete>
194
- <ControlledColumn name="nickname" />
195
- <ControlledColumn name="pokemon" />
196
- </ControlledTable>
197
- <AddListEntryForm defaultValues={{ nickname: "", pokemon: null }}>
198
- <FormRow>
199
- <ControlledInputField name="nickname" />
200
- </FormRow>
201
- <FormRow>
202
- <ControlledRadioGroupField
203
- name="pokemon"
204
- label="Starter Pokemon"
205
- rules={{ required: true }}
206
- options={[{ value: "CHARMANDER" }, { value: "SQUIRTLE" }, { value: "BULBASAUR" }]}
207
- />
208
- </FormRow>
209
- </AddListEntryForm>
210
- </ScmFormListContextProvider>
211
- </div>
385
+ <ScmFormListContextProvider name="team">
386
+ <ControlledTable withDelete>
387
+ <ControlledColumn name="nickname" />
388
+ <ControlledColumn name="pokemon" />
389
+ </ControlledTable>
390
+ <AddListEntryForm defaultValues={{ nickname: "", pokemon: null }}>
391
+ <FormRow>
392
+ <ControlledInputField name="nickname" />
393
+ </FormRow>
394
+ <FormRow>
395
+ <ControlledRadioGroupField
396
+ name="pokemon"
397
+ label="Pokemon"
398
+ rules={{ required: true }}
399
+ options={[{ value: "CHARMANDER" }, { value: "SQUIRTLE" }, { value: "BULBASAUR" }]}
400
+ />
401
+ </FormRow>
402
+ </AddListEntryForm>
403
+ </ScmFormListContextProvider>
212
404
  </details>
213
405
  </>
214
406
  )}
215
407
  </ControlledList>
216
408
  </ScmFormListContextProvider>
217
409
  </Form>
218
- ));
410
+ ),
411
+ };
@@ -14,7 +14,7 @@
14
14
  * along with this program. If not, see https://www.gnu.org/licenses/.
15
15
  */
16
16
 
17
- import React, { ComponentProps } from "react";
17
+ import React from "react";
18
18
  import classNames from "classnames";
19
19
  import Help from "../base/help/Help";
20
20
  import * as RadioGroup from "@radix-ui/react-radio-group";
@@ -65,6 +65,9 @@ const StyledIndicator = styled(RadioGroup.Indicator)`
65
65
  }
66
66
  `;
67
67
 
68
+ type RadioGroupItemElement = React.ElementRef<typeof RadioGroup.Item>;
69
+ type RadioGroupItemProps = React.ComponentPropsWithoutRef<typeof RadioGroup.Item>;
70
+
68
71
  type Props = {
69
72
  value: string;
70
73
  id?: string;
@@ -73,17 +76,18 @@ type Props = {
73
76
  label?: string;
74
77
  labelClassName?: string;
75
78
  helpText?: string;
76
- } & ComponentProps<typeof RadioGroup.Item>;
79
+ } & RadioGroupItemProps;
77
80
 
78
81
  /**
79
82
  * @beta
80
83
  * @since 2.48.0
81
84
  */
82
- const RadioButton = React.forwardRef<HTMLButtonElement, Props>(
85
+ const RadioButton = React.forwardRef<RadioGroupItemElement, Props>(
83
86
  ({ id, testId, indicatorClassName, label, labelClassName, className, helpText, value, ...props }, ref) => {
84
87
  const context = useRadioButtonContext();
85
88
  const inputId = useAriaId(id);
86
89
  const labelKey = `${context?.prefix}.radio.${value}`;
90
+ const displayLabel = label ?? context?.t(labelKey) ?? value ?? "";
87
91
 
88
92
  return (
89
93
  <div className={classNames("radio is-flex is-align-items-center", labelClassName)}>
@@ -98,7 +102,7 @@ const RadioButton = React.forwardRef<HTMLButtonElement, Props>(
98
102
  >
99
103
  <StyledIndicator className={indicatorClassName} />
100
104
  </StyledRadioButton>
101
- <label htmlFor={inputId}>{label ?? context?.t(labelKey) ?? value}</label>
105
+ <label htmlFor={inputId}>{displayLabel}</label>
102
106
  {helpText ? <Help className="ml-3" text={helpText} /> : null}
103
107
  </div>
104
108
  );
@@ -14,13 +14,14 @@
14
14
  * along with this program. If not, see https://www.gnu.org/licenses/.
15
15
  */
16
16
 
17
- import React, { createContext, FC, useContext } from "react";
17
+ import React, { createContext, FC, ReactNode, useContext } from "react";
18
18
  import { TFunction } from "i18next";
19
19
 
20
20
  type ContextType = {
21
21
  t: TFunction;
22
22
  prefix: string;
23
23
  formId?: string;
24
+ children?: ReactNode;
24
25
  };
25
26
 
26
27
  const RadioButtonContext = createContext<ContextType | null>(null);
@@ -22,7 +22,7 @@ import { useWatch } from "react-hook-form";
22
22
  type Props = {
23
23
  name: string;
24
24
  children?: (value: unknown) => React.ReactNode | React.ReactNode[];
25
- } & HTMLAttributes<HTMLTableCellElement>;
25
+ } & Omit<HTMLAttributes<HTMLTableCellElement>, "children">;
26
26
 
27
27
  /**
28
28
  * @beta
@@ -54,23 +54,31 @@ function ControlledTable<T extends Record<string, unknown>, PATH extends Path<T>
54
54
  const deleteLabel = t(`${prefixedNameWithoutIndices}.delete`) || defaultTranslate("delete.label");
55
55
  const emptyTableLabel = t(`${prefixedNameWithoutIndices}.empty`) || defaultTranslate("empty.label");
56
56
  const actionHeaderLabel = t(`${prefixedNameWithoutIndices}.action.label`) || defaultTranslate("headers.action.label");
57
+ const renderedChildren = typeof children === "function" ? children({} as RenderProps<T, PATH>) : children;
57
58
 
58
59
  return (
59
60
  <table className={classNames("table content is-hoverable", className)}>
60
61
  <thead>
61
62
  <tr>
62
- {React.Children.map(children, (child) => (
63
- <th>{t(`${prefixedNameWithoutIndices}.${(child as ReactElement).props.name}.label`)}</th>
63
+ {React.Children.map(renderedChildren, (child) => (
64
+ // @ts-ignore
65
+ <th>{String(t(`${prefixedNameWithoutIndices}.${(child as ReactElement).props.name}.label`))}</th>
64
66
  ))}
65
67
  {withDelete && !readOnly ? <th className="has-text-right">{actionHeaderLabel}</th> : null}
66
68
  </tr>
67
69
  </thead>
68
70
  <tbody>
69
- {fields.length === 0 ? <tr><td colSpan={1000}><Notification type="info">{emptyTableLabel}</Notification></td></tr> : null}
71
+ {fields.length === 0 ? (
72
+ <tr>
73
+ <td colSpan={1000}>
74
+ <Notification type="info">{emptyTableLabel}</Notification>
75
+ </td>
76
+ </tr>
77
+ ) : null}
70
78
  {fields.map((value, index) => (
71
79
  <ScmFormPathContextProvider key={value.id} path={`${nameWithPrefix}.${index}`}>
72
80
  <tr>
73
- {children}
81
+ {renderedChildren}
74
82
  {withDelete && !readOnly ? (
75
83
  <td className="has-text-right">
76
84
  <Button className="px-4" onClick={() => remove(index)} aria-label={deleteLabel}>