@navikt/ds-react 7.28.1 → 7.29.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.
- package/cjs/accordion/Accordion.d.ts +1 -0
- package/cjs/accordion/Accordion.js.map +1 -1
- package/cjs/form/fieldset/Fieldset.js +3 -2
- package/cjs/form/fieldset/Fieldset.js.map +1 -1
- package/cjs/form/fieldset/useFieldset.d.ts +2 -2
- package/cjs/form/fieldset/useFieldset.js +7 -5
- package/cjs/form/fieldset/useFieldset.js.map +1 -1
- package/cjs/index.d.ts +1 -0
- package/cjs/index.js +4 -2
- package/cjs/index.js.map +1 -1
- package/cjs/layout/box/Box.js +2 -1
- package/cjs/layout/box/Box.js.map +1 -1
- package/cjs/link-card/LinkCard.d.ts +5 -0
- package/cjs/link-card/LinkCard.js +6 -6
- package/cjs/link-card/LinkCard.js.map +1 -1
- package/cjs/process/Process.d.ts +90 -0
- package/cjs/process/Process.js +184 -0
- package/cjs/process/Process.js.map +1 -0
- package/cjs/process/index.d.ts +1 -0
- package/cjs/process/index.js +8 -0
- package/cjs/process/index.js.map +1 -0
- package/cjs/stepper/Stepper.d.ts +1 -0
- package/cjs/stepper/Stepper.js.map +1 -1
- package/cjs/toggle-group/ToggleGroup.js +3 -4
- package/cjs/toggle-group/ToggleGroup.js.map +1 -1
- package/cjs/util/i18n/locales/en.d.ts +3 -0
- package/cjs/util/i18n/locales/en.js +3 -0
- package/cjs/util/i18n/locales/en.js.map +1 -1
- package/cjs/util/i18n/locales/nb.d.ts +4 -0
- package/cjs/util/i18n/locales/nb.js +4 -0
- package/cjs/util/i18n/locales/nb.js.map +1 -1
- package/cjs/util/i18n/locales/nn.d.ts +3 -0
- package/cjs/util/i18n/locales/nn.js +3 -0
- package/cjs/util/i18n/locales/nn.js.map +1 -1
- package/esm/accordion/Accordion.d.ts +1 -0
- package/esm/accordion/Accordion.js.map +1 -1
- package/esm/form/fieldset/Fieldset.js +4 -3
- package/esm/form/fieldset/Fieldset.js.map +1 -1
- package/esm/form/fieldset/useFieldset.d.ts +2 -2
- package/esm/form/fieldset/useFieldset.js +7 -5
- package/esm/form/fieldset/useFieldset.js.map +1 -1
- package/esm/index.d.ts +1 -0
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/layout/box/Box.js +2 -1
- package/esm/layout/box/Box.js.map +1 -1
- package/esm/link-card/LinkCard.d.ts +5 -0
- package/esm/link-card/LinkCard.js +6 -6
- package/esm/link-card/LinkCard.js.map +1 -1
- package/esm/process/Process.d.ts +90 -0
- package/esm/process/Process.js +148 -0
- package/esm/process/Process.js.map +1 -0
- package/esm/process/index.d.ts +1 -0
- package/esm/process/index.js +3 -0
- package/esm/process/index.js.map +1 -0
- package/esm/stepper/Stepper.d.ts +1 -0
- package/esm/stepper/Stepper.js.map +1 -1
- package/esm/toggle-group/ToggleGroup.js +3 -4
- package/esm/toggle-group/ToggleGroup.js.map +1 -1
- package/esm/util/i18n/locales/en.d.ts +3 -0
- package/esm/util/i18n/locales/en.js +3 -0
- package/esm/util/i18n/locales/en.js.map +1 -1
- package/esm/util/i18n/locales/nb.d.ts +4 -0
- package/esm/util/i18n/locales/nb.js +4 -0
- package/esm/util/i18n/locales/nb.js.map +1 -1
- package/esm/util/i18n/locales/nn.d.ts +3 -0
- package/esm/util/i18n/locales/nn.js +3 -0
- package/esm/util/i18n/locales/nn.js.map +1 -1
- package/package.json +14 -4
- package/src/accordion/Accordion.tsx +1 -0
- package/src/form/fieldset/Fieldset.tsx +4 -2
- package/src/form/fieldset/useFieldset.ts +7 -5
- package/src/index.ts +1 -0
- package/src/layout/box/Box.tsx +2 -1
- package/src/link-card/LinkCard.tsx +14 -7
- package/src/process/Process.tsx +358 -0
- package/src/process/index.ts +7 -0
- package/src/stepper/Stepper.tsx +1 -0
- package/src/toggle-group/ToggleGroup.tsx +2 -5
- package/src/util/i18n/locales/en.ts +3 -0
- package/src/util/i18n/locales/nb.ts +3 -0
- package/src/util/i18n/locales/nn.ts +3 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from "react";
|
|
8
|
+
import { useRenameCSS } from "../theme/Theme";
|
|
9
|
+
import { BodyLong, BodyShort, Heading } from "../typography";
|
|
10
|
+
import { useId } from "../util";
|
|
11
|
+
import { createContext } from "../util/create-context";
|
|
12
|
+
import { useMergeRefs } from "../util/hooks";
|
|
13
|
+
import { useI18n } from "../util/i18n/i18n.hooks";
|
|
14
|
+
|
|
15
|
+
interface ProcessProps extends React.HTMLAttributes<HTMLOListElement> {
|
|
16
|
+
/**
|
|
17
|
+
* `<Process.Event />` elements.
|
|
18
|
+
*/
|
|
19
|
+
children: React.ReactElement<typeof ProcessEvent>[];
|
|
20
|
+
/**
|
|
21
|
+
* Hides the "aktiv"-text when the event is active.
|
|
22
|
+
* @default false
|
|
23
|
+
*/
|
|
24
|
+
hideStatusText?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type ProcessContextProps = Pick<ProcessProps, "hideStatusText"> & {
|
|
28
|
+
rootId: string;
|
|
29
|
+
syncAriaControls: () => void;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const [ProcessContextProvider, useProcessContext] =
|
|
33
|
+
createContext<ProcessContextProps>({
|
|
34
|
+
providerName: "ProcessContextProvider",
|
|
35
|
+
hookName: "useProcessContext",
|
|
36
|
+
name: "ProcessContext",
|
|
37
|
+
errorMessage:
|
|
38
|
+
"`<Process.Event />` must be used within a `<Process />` component.",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
interface ProcessComponent
|
|
42
|
+
extends React.ForwardRefExoticComponent<
|
|
43
|
+
ProcessProps & React.RefAttributes<HTMLOListElement>
|
|
44
|
+
> {
|
|
45
|
+
/**
|
|
46
|
+
* @see 🏷️ {@link ProcessEventProps}
|
|
47
|
+
*/
|
|
48
|
+
Event: typeof ProcessEvent;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A component that presents a Process as a vertical line of events.
|
|
53
|
+
* Each event can contain information, actions, links or status indicators.
|
|
54
|
+
*
|
|
55
|
+
* @see [📝 Documentation](https://aksel.nav.no/komponenter/core/process)
|
|
56
|
+
* @see 🏷️ {@link ProcessProps}
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```jsx
|
|
60
|
+
* <>
|
|
61
|
+
* <Heading size="medium" spacing level="2" id="Process-heading">
|
|
62
|
+
* Søknadssteg
|
|
63
|
+
* </Heading>
|
|
64
|
+
* <Process
|
|
65
|
+
* aria-labelledby="Process-heading"
|
|
66
|
+
* activeStep={activeStep}
|
|
67
|
+
* >
|
|
68
|
+
* <Process.Event title="Start søknad" timestamp="21. august 2025" />
|
|
69
|
+
* <Process.Event
|
|
70
|
+
* title="Saksopplysninger"
|
|
71
|
+
* timestamp="22. august 2025"
|
|
72
|
+
* icon={<PaperclipIcon />}
|
|
73
|
+
* >
|
|
74
|
+
* Saksopplysninger er sendt inn
|
|
75
|
+
* </Process.Event>
|
|
76
|
+
* <Process.Event
|
|
77
|
+
* title="Vedlegg"
|
|
78
|
+
* timestamp="25. august 2025"
|
|
79
|
+
* >
|
|
80
|
+
* <h3> Vedlegg er lastet opp </h3>
|
|
81
|
+
* <p>
|
|
82
|
+
* Dokumentasjon av saksopplysninger er lastet opp og tilgjengelig for
|
|
83
|
+
* saksbehandler.
|
|
84
|
+
* </p>
|
|
85
|
+
* </Process.Event>
|
|
86
|
+
* <Process.Event title="Vedtak" timestamp="8. september 2025">
|
|
87
|
+
* Det er gjort endelig vedtak i saken
|
|
88
|
+
* </Process.Event>
|
|
89
|
+
* </Process>
|
|
90
|
+
* </>
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export const Process: ProcessComponent = forwardRef<
|
|
94
|
+
HTMLOListElement,
|
|
95
|
+
ProcessProps
|
|
96
|
+
>(
|
|
97
|
+
(
|
|
98
|
+
{
|
|
99
|
+
children,
|
|
100
|
+
className,
|
|
101
|
+
hideStatusText = false,
|
|
102
|
+
id,
|
|
103
|
+
...restProps
|
|
104
|
+
}: ProcessProps,
|
|
105
|
+
forwardedRef,
|
|
106
|
+
) => {
|
|
107
|
+
const { cn } = useRenameCSS();
|
|
108
|
+
|
|
109
|
+
const rootId = useId(id);
|
|
110
|
+
|
|
111
|
+
const rootRef = useRef<HTMLOListElement>(null);
|
|
112
|
+
const mergedRef = useMergeRefs(forwardedRef, rootRef);
|
|
113
|
+
|
|
114
|
+
const [activeChildId, setActiveChildId] = useState<string | undefined>(
|
|
115
|
+
undefined,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const syncAriaControls = useCallback(() => {
|
|
119
|
+
const activeChildren = rootRef.current?.querySelectorAll(
|
|
120
|
+
'[data-process-event][aria-current="true"]',
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
if (!activeChildren) {
|
|
124
|
+
setActiveChildId(undefined);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (activeChildren.length > 1) {
|
|
129
|
+
if (process.env.NODE_ENV !== "production") {
|
|
130
|
+
console.warn(
|
|
131
|
+
"Aksel: Found multiple `<Process.Event />` elements with `status='active'`. Only one event should be active at a time.",
|
|
132
|
+
rootRef.current,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
setActiveChildId(undefined);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (activeChildren.length === 1) {
|
|
140
|
+
const lastActiveChild = activeChildren[activeChildren.length - 1];
|
|
141
|
+
setActiveChildId(lastActiveChild.id);
|
|
142
|
+
} else {
|
|
143
|
+
setActiveChildId(undefined);
|
|
144
|
+
}
|
|
145
|
+
}, []);
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
// `<ol />` elements with `list-style: none;` tends to be ignored by voiceover on Safari.
|
|
149
|
+
// To resolve this, we add `role="list"` to the `<ol />` element.
|
|
150
|
+
// eslint-disable-next-line jsx-a11y/no-redundant-roles
|
|
151
|
+
<ol
|
|
152
|
+
ref={mergedRef}
|
|
153
|
+
data-color="info"
|
|
154
|
+
// biome-ignore lint/a11y/noRedundantRoles: See comment above
|
|
155
|
+
role="list"
|
|
156
|
+
{...restProps}
|
|
157
|
+
className={cn("navds-process", className)}
|
|
158
|
+
id={rootId}
|
|
159
|
+
aria-controls={activeChildId}
|
|
160
|
+
>
|
|
161
|
+
<ProcessContextProvider
|
|
162
|
+
hideStatusText={hideStatusText}
|
|
163
|
+
rootId={rootId}
|
|
164
|
+
syncAriaControls={syncAriaControls}
|
|
165
|
+
>
|
|
166
|
+
{children}
|
|
167
|
+
</ProcessContextProvider>
|
|
168
|
+
</ol>
|
|
169
|
+
);
|
|
170
|
+
},
|
|
171
|
+
) as ProcessComponent;
|
|
172
|
+
|
|
173
|
+
/* ------------------------------ Process Event ------------------------------ */
|
|
174
|
+
interface ProcessEventProps extends React.HTMLAttributes<HTMLLIElement> {
|
|
175
|
+
/**
|
|
176
|
+
* Rich content to display under the title and timestamp if provided.
|
|
177
|
+
*/
|
|
178
|
+
children?: React.ReactNode;
|
|
179
|
+
/**
|
|
180
|
+
* Hide the content section of the event.
|
|
181
|
+
*/
|
|
182
|
+
hideContent?: boolean;
|
|
183
|
+
/**
|
|
184
|
+
* Step title.
|
|
185
|
+
*/
|
|
186
|
+
title?: string;
|
|
187
|
+
/**
|
|
188
|
+
* Timestamp or date to display for event.
|
|
189
|
+
*/
|
|
190
|
+
timestamp?: string;
|
|
191
|
+
/**
|
|
192
|
+
* Icon or number to display inside the bullet.
|
|
193
|
+
*/
|
|
194
|
+
bullet?: React.ReactNode;
|
|
195
|
+
/**
|
|
196
|
+
* Current event status.
|
|
197
|
+
* @default "uncompleted"
|
|
198
|
+
*/
|
|
199
|
+
status?: "active" | "completed" | "uncompleted";
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export const ProcessEvent = forwardRef<HTMLLIElement, ProcessEventProps>(
|
|
203
|
+
(
|
|
204
|
+
{
|
|
205
|
+
title,
|
|
206
|
+
timestamp,
|
|
207
|
+
children,
|
|
208
|
+
bullet,
|
|
209
|
+
hideContent,
|
|
210
|
+
className,
|
|
211
|
+
id,
|
|
212
|
+
status = "uncompleted",
|
|
213
|
+
...restProps
|
|
214
|
+
}: ProcessEventProps,
|
|
215
|
+
forwardedRef,
|
|
216
|
+
) => {
|
|
217
|
+
const translate = useI18n("Process");
|
|
218
|
+
const { cn } = useRenameCSS();
|
|
219
|
+
const eventId = useId();
|
|
220
|
+
const { syncAriaControls, hideStatusText, rootId } = useProcessContext();
|
|
221
|
+
|
|
222
|
+
// syncAriaControls is already memoized with useCallback
|
|
223
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: We want to run this only when status changes
|
|
224
|
+
useEffect(syncAriaControls, [status]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
225
|
+
|
|
226
|
+
const isActive = status === "active";
|
|
227
|
+
|
|
228
|
+
return (
|
|
229
|
+
<li
|
|
230
|
+
ref={forwardedRef}
|
|
231
|
+
aria-current={isActive}
|
|
232
|
+
id={id ?? eventId}
|
|
233
|
+
{...restProps}
|
|
234
|
+
aria-controls={isActive ? rootId : undefined}
|
|
235
|
+
className={cn("navds-process__event", className)}
|
|
236
|
+
data-dot={bullet === undefined}
|
|
237
|
+
data-process-event=""
|
|
238
|
+
data-status={status}
|
|
239
|
+
>
|
|
240
|
+
<div className={cn("navds-process__item")}>
|
|
241
|
+
<ProcessBullet>{bullet}</ProcessBullet>
|
|
242
|
+
|
|
243
|
+
<div className={cn("navds-process__body")}>
|
|
244
|
+
{title && <ProcessTitle>{title}</ProcessTitle>}
|
|
245
|
+
{isActive && !hideStatusText && (
|
|
246
|
+
<BodyShort
|
|
247
|
+
size="small"
|
|
248
|
+
className={cn("navds-process__active-label")}
|
|
249
|
+
>
|
|
250
|
+
{translate("active")}
|
|
251
|
+
</BodyShort>
|
|
252
|
+
)}
|
|
253
|
+
{timestamp && <ProcessTimestamp>{timestamp}</ProcessTimestamp>}
|
|
254
|
+
{!hideContent && !!children && (
|
|
255
|
+
<ProcessContent>{children}</ProcessContent>
|
|
256
|
+
)}
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
<ProcessLine />
|
|
260
|
+
</li>
|
|
261
|
+
);
|
|
262
|
+
},
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
/* ------------------------------ Process Title ----------------------------- */
|
|
266
|
+
interface ProcessTitleProps {
|
|
267
|
+
/**
|
|
268
|
+
* Title content.
|
|
269
|
+
*/
|
|
270
|
+
children: React.ReactNode;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const ProcessTitle = ({ children }: ProcessTitleProps) => {
|
|
274
|
+
const { cn } = useRenameCSS();
|
|
275
|
+
|
|
276
|
+
return (
|
|
277
|
+
<Heading size="small" as="div" className={cn("navds-process__title")}>
|
|
278
|
+
{children}
|
|
279
|
+
</Heading>
|
|
280
|
+
);
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
/* ---------------------------- Process timestamp --------------------------- */
|
|
284
|
+
interface ProcessTimestampProps {
|
|
285
|
+
/**
|
|
286
|
+
* Timestamp content.
|
|
287
|
+
*/
|
|
288
|
+
children: React.ReactNode;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const ProcessTimestamp = ({ children }: ProcessTimestampProps) => {
|
|
292
|
+
const { cn } = useRenameCSS();
|
|
293
|
+
|
|
294
|
+
return (
|
|
295
|
+
<BodyShort
|
|
296
|
+
spacing
|
|
297
|
+
as="div"
|
|
298
|
+
size="small"
|
|
299
|
+
textColor="subtle"
|
|
300
|
+
className={cn("navds-process__timestamp")}
|
|
301
|
+
>
|
|
302
|
+
{children}
|
|
303
|
+
</BodyShort>
|
|
304
|
+
);
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
/* ----------------------------- Process Content ---------------------------- */
|
|
308
|
+
interface ProcessContentProps {
|
|
309
|
+
/**
|
|
310
|
+
* Content content.
|
|
311
|
+
*/
|
|
312
|
+
children: React.ReactNode;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const ProcessContent = ({ children }: ProcessContentProps) => {
|
|
316
|
+
const { cn } = useRenameCSS();
|
|
317
|
+
|
|
318
|
+
return (
|
|
319
|
+
<BodyLong as="div" className={cn("navds-process__content")}>
|
|
320
|
+
{children}
|
|
321
|
+
</BodyLong>
|
|
322
|
+
);
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
/* ----------------------------- Process Bullet ----------------------------- */
|
|
326
|
+
interface ProcessBulletProps {
|
|
327
|
+
/**
|
|
328
|
+
* Bullet content.
|
|
329
|
+
*/
|
|
330
|
+
children: React.ReactNode;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const ProcessBullet = ({ children }: ProcessBulletProps) => {
|
|
334
|
+
const { cn } = useRenameCSS();
|
|
335
|
+
|
|
336
|
+
return (
|
|
337
|
+
<BodyShort
|
|
338
|
+
as="span"
|
|
339
|
+
weight="semibold"
|
|
340
|
+
className={cn("navds-process__bullet")}
|
|
341
|
+
aria-hidden
|
|
342
|
+
>
|
|
343
|
+
{children}
|
|
344
|
+
</BodyShort>
|
|
345
|
+
);
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
/* ------------------------------ Process Line ------------------------------ */
|
|
349
|
+
const ProcessLine = () => {
|
|
350
|
+
const { cn } = useRenameCSS();
|
|
351
|
+
|
|
352
|
+
return <span className={cn("navds-process__line")} />;
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
/* -------------------------- Process exports ------------------------- */
|
|
356
|
+
Process.Event = ProcessEvent;
|
|
357
|
+
|
|
358
|
+
export type { ProcessEventProps, ProcessProps };
|
package/src/stepper/Stepper.tsx
CHANGED
|
@@ -27,6 +27,7 @@ export interface StepperProps extends React.HTMLAttributes<HTMLOListElement> {
|
|
|
27
27
|
onStepChange?: (step: number) => void;
|
|
28
28
|
/**
|
|
29
29
|
* Makes stepper non-interactive if false.
|
|
30
|
+
* @deprecated Use `interactive` prop on `<Stepper.Step />` instead for individual steps. For completely static steppers, use `Process` component instead.
|
|
30
31
|
* @default true
|
|
31
32
|
*/
|
|
32
33
|
interactive?: boolean;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import cl from "clsx";
|
|
2
1
|
import React, { forwardRef } from "react";
|
|
3
2
|
import { useRenameCSS, useThemeInternal } from "../theme/Theme";
|
|
4
3
|
import { AkselColor } from "../types";
|
|
@@ -48,7 +47,6 @@ export const ToggleGroup = forwardRef<HTMLDivElement, ToggleGroupProps>(
|
|
|
48
47
|
label,
|
|
49
48
|
value,
|
|
50
49
|
defaultValue,
|
|
51
|
-
"aria-describedby": userDescribedby,
|
|
52
50
|
variant,
|
|
53
51
|
fill = false,
|
|
54
52
|
"data-color": color,
|
|
@@ -103,6 +101,7 @@ export const ToggleGroup = forwardRef<HTMLDivElement, ToggleGroupProps>(
|
|
|
103
101
|
>
|
|
104
102
|
{label && (
|
|
105
103
|
<Label
|
|
104
|
+
as="div"
|
|
106
105
|
size={size}
|
|
107
106
|
className={cn("navds-toggle-group__label")}
|
|
108
107
|
id={labelId}
|
|
@@ -111,6 +110,7 @@ export const ToggleGroup = forwardRef<HTMLDivElement, ToggleGroupProps>(
|
|
|
111
110
|
</Label>
|
|
112
111
|
)}
|
|
113
112
|
<div
|
|
113
|
+
aria-labelledby={label ? labelId : undefined}
|
|
114
114
|
{...rest}
|
|
115
115
|
ref={ref}
|
|
116
116
|
className={cn(
|
|
@@ -118,9 +118,6 @@ export const ToggleGroup = forwardRef<HTMLDivElement, ToggleGroupProps>(
|
|
|
118
118
|
`navds-toggle-group--${size}`,
|
|
119
119
|
{ [`navds-toggle-group--${localVariant}`]: localVariant },
|
|
120
120
|
)}
|
|
121
|
-
aria-describedby={
|
|
122
|
-
cl(userDescribedby, !!label && labelId) || undefined
|
|
123
|
-
}
|
|
124
121
|
role="radiogroup"
|
|
125
122
|
>
|
|
126
123
|
{children}
|