@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.
- package/{src/base/buttons/a11y.test.ts → .storybook/i18n.ts} +27 -9
- package/.storybook/main.ts +54 -0
- package/.storybook/preview-head.html +6 -1
- package/.storybook/preview.tsx +125 -0
- package/.turbo/turbo-test.log +164 -0
- package/.turbo/turbo-typecheck.log +3 -2
- package/package.json +37 -42
- package/src/base/buttons/Button.stories.tsx +179 -70
- package/src/base/buttons/Button.tsx +9 -9
- package/src/base/forms/AddListEntryForm.tsx +8 -8
- package/src/base/forms/ConfigurationForm.tsx +14 -5
- package/src/base/forms/Form.stories.tsx +599 -289
- package/src/base/forms/Form.tsx +8 -8
- package/src/base/forms/FormPathContext.tsx +3 -3
- package/src/base/forms/ScmFormContext.tsx +7 -4
- package/src/base/forms/ScmFormListContext.tsx +2 -1
- package/src/base/forms/base/Field.tsx +1 -1
- package/src/base/forms/base/label/Label.tsx +1 -1
- package/src/base/forms/chip-input/ChipInputField.stories.tsx +109 -28
- package/src/base/forms/chip-input/ChipInputField.tsx +20 -8
- package/src/base/forms/chip-input/ControlledChipInputField.tsx +3 -1
- package/src/base/forms/combobox/Combobox.stories.tsx +216 -89
- package/src/base/forms/combobox/Combobox.tsx +4 -2
- package/src/base/forms/combobox/ComboboxField.tsx +2 -1
- package/src/base/forms/headless-chip-input/ChipInput.tsx +9 -9
- package/src/base/forms/helpers.ts +12 -9
- package/src/base/forms/input/ControlledSecretConfirmationField.tsx +4 -2
- package/src/base/forms/radio-button/RadioButton.stories.tsx +317 -124
- package/src/base/forms/radio-button/RadioButton.tsx +8 -4
- package/src/base/forms/radio-button/RadioButtonContext.tsx +2 -1
- package/src/base/forms/table/ControlledColumn.tsx +1 -1
- package/src/base/forms/table/ControlledTable.tsx +12 -4
- package/src/base/helpers/useDocumentTitle.test.ts +15 -7
- package/src/base/layout/card/Card.stories.tsx +171 -72
- package/src/base/layout/card/Card.tsx +4 -4
- package/src/base/layout/card/CardDetail.tsx +2 -3
- package/src/base/layout/card-list/CardList.stories.tsx +283 -169
- package/src/base/layout/collapsible/Collapsible.stories.tsx +54 -16
- package/src/base/layout/index.ts +2 -5
- package/src/base/layout/tabs/Tabs.stories.tsx +58 -16
- package/src/base/layout/templates/data-page/DataPage.stories.tsx +289 -156
- package/src/base/layout/templates/data-page/DataPageHeader.tsx +1 -1
- package/src/base/overlays/dialog/Dialog.stories.tsx +94 -34
- package/src/base/overlays/menu/Menu.stories.tsx +116 -48
- package/src/base/overlays/menu/Menu.tsx +1 -0
- package/src/base/overlays/popover/Popover.stories.tsx +50 -37
- package/src/base/shortcuts/iterator/keyboardIterator.test.tsx +16 -7
- package/src/base/shortcuts/iterator/keyboardIterator.tsx +13 -5
- package/src/base/status/StatusIcon.stories.tsx +76 -27
- package/src/base/status/index.ts +1 -1
- package/src/base/text/SplitAndReplace.stories.tsx +128 -50
- package/src/base/text/index.ts +1 -1
- package/.storybook/RemoveThemesPlugin.js +0 -49
- package/.storybook/main.js +0 -86
- package/.storybook/preview.js +0 -87
- 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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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"
|
|
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
|
-
|
|
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
|
-
|
|
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"
|
|
335
|
+
<RadioButton value="BULBASAUR" label="Bulbasaur" />
|
|
112
336
|
</ControlledRadioGroupField>
|
|
113
337
|
</FormRow>
|
|
114
338
|
</Form>
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
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=
|
|
376
|
+
<ScmFormListContextProvider name="trainers">
|
|
183
377
|
<ControlledList withDelete>
|
|
184
|
-
{(
|
|
378
|
+
{() => (
|
|
185
379
|
<>
|
|
186
380
|
<FormRow>
|
|
187
|
-
<ControlledInputField 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
|
-
<
|
|
192
|
-
<
|
|
193
|
-
<
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
<
|
|
198
|
-
<
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
<
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
|
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
|
-
} &
|
|
79
|
+
} & RadioGroupItemProps;
|
|
77
80
|
|
|
78
81
|
/**
|
|
79
82
|
* @beta
|
|
80
83
|
* @since 2.48.0
|
|
81
84
|
*/
|
|
82
|
-
const RadioButton = React.forwardRef<
|
|
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}>{
|
|
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(
|
|
63
|
-
|
|
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
|
-
|
|
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
|
-
{
|
|
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}>
|