@boostdev/design-system-components 1.2.1 → 1.2.2
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/AGENTS.md +21 -0
- package/README.md +15 -15
- package/dist/client.cjs +141 -120
- package/dist/client.css +468 -468
- package/dist/client.d.cts +71 -96
- package/dist/client.d.ts +71 -96
- package/dist/client.js +141 -120
- package/dist/index.cjs +141 -120
- package/dist/index.css +468 -468
- package/dist/index.d.cts +71 -96
- package/dist/index.d.ts +71 -96
- package/dist/index.js +141 -120
- package/package.json +1 -1
- package/src/components/interaction/Command/Command.tsx +4 -3
- package/src/components/interaction/Dialog/Dialog.tsx +4 -5
- package/src/components/interaction/Drawer/Drawer.spec.tsx +3 -3
- package/src/components/interaction/Drawer/Drawer.tsx +4 -7
- package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +4 -2
- package/src/components/interaction/Popover/Popover.tsx +4 -3
- package/src/components/interaction/Rating/Rating.tsx +4 -2
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +4 -6
- package/src/components/interaction/form/Combobox/Combobox.tsx +4 -2
- package/src/components/interaction/form/FileInput/FileInput.tsx +4 -3
- package/src/components/interaction/form/NumberInput/NumberInput.tsx +4 -3
- package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +4 -6
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +4 -6
- package/src/components/interaction/form/atoms/InputContainer.tsx +2 -2
- package/src/components/layout/ButtonGroup/ButtonGroup.tsx +4 -6
- package/src/components/layout/Card/Card.tsx +4 -10
- package/src/components/layout/IconWrapper/IconWrapper.tsx +4 -5
- package/src/components/layout/SectionHeader/SectionHeader.tsx +5 -4
- package/src/components/ui/Accordion/Accordion.tsx +4 -3
- package/src/components/ui/Alert/Alert.tsx +4 -3
- package/src/components/ui/Avatar/Avatar.tsx +5 -3
- package/src/components/ui/Badge/Badge.tsx +4 -5
- package/src/components/ui/Breadcrumb/Breadcrumb.tsx +4 -3
- package/src/components/ui/Calendar/Calendar.tsx +4 -4
- package/src/components/ui/Carousel/Carousel.tsx +4 -4
- package/src/components/ui/DescriptionList/DescriptionList.tsx +4 -4
- package/src/components/ui/Loading/Loading.tsx +4 -3
- package/src/components/ui/NotificationBanner/NotificationBanner.tsx +4 -2
- package/src/components/ui/Pagination/Pagination.tsx +4 -2
- package/src/components/ui/Progress/Progress.tsx +4 -2
- package/src/components/ui/ProgressCircle/ProgressCircle.tsx +4 -1
- package/src/components/ui/Separator/Separator.tsx +5 -3
- package/src/components/ui/Skeleton/Skeleton.tsx +4 -3
- package/src/components/ui/SkipLink/SkipLink.tsx +4 -5
- package/src/components/ui/Tabs/Tabs.tsx +4 -4
- package/src/components/ui/Tooltip/Tooltip.tsx +4 -2
- package/src/components/ui/Typography/Typography.tsx +4 -5
- package/src/stories/DesignSystem/Grid.mdx +2 -0
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect, useRef, useId, KeyboardEvent, useMemo } from 'react';
|
|
1
|
+
import { HTMLAttributes, useState, useEffect, useRef, useId, KeyboardEvent, useMemo } from 'react';
|
|
2
2
|
import { installInvokerCommandsPolyfill } from '../../../polyfill-invoker-commands';
|
|
3
3
|
|
|
4
4
|
// Install once at module load — no-op if native support is present.
|
|
@@ -17,8 +17,7 @@ export interface CommandItem {
|
|
|
17
17
|
onSelect: () => void;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
interface CommandProps extends WithClassName {
|
|
21
|
-
id?: string;
|
|
20
|
+
interface CommandProps extends WithClassName, HTMLAttributes<HTMLDialogElement> {
|
|
22
21
|
isOpen: boolean;
|
|
23
22
|
onOpen?: () => void;
|
|
24
23
|
onClose: () => void;
|
|
@@ -34,6 +33,7 @@ export function Command({
|
|
|
34
33
|
items,
|
|
35
34
|
placeholder = 'Search commands…',
|
|
36
35
|
className,
|
|
36
|
+
...rest
|
|
37
37
|
}: Readonly<CommandProps>) {
|
|
38
38
|
const [query, setQuery] = useState('');
|
|
39
39
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
@@ -139,6 +139,7 @@ export function Command({
|
|
|
139
139
|
|
|
140
140
|
return (
|
|
141
141
|
<dialog
|
|
142
|
+
{...rest}
|
|
142
143
|
ref={dialogRef}
|
|
143
144
|
id={dialogId}
|
|
144
145
|
className={cn(css.dialog, className)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HTMLAttributes, useEffect, useId, useRef } from 'react';
|
|
2
2
|
import { installInvokerCommandsPolyfill } from '../../../polyfill-invoker-commands';
|
|
3
3
|
|
|
4
4
|
// Install once at module load — no-op if native support is present.
|
|
@@ -9,15 +9,13 @@ import css from './Dialog.module.css';
|
|
|
9
9
|
import { cn } from '@boostdev/design-system-foundation';
|
|
10
10
|
import type { WithClassName } from '../../../types';
|
|
11
11
|
|
|
12
|
-
interface DialogProps extends WithClassName {
|
|
13
|
-
children: ReactNode;
|
|
14
|
-
id?: string;
|
|
12
|
+
interface DialogProps extends WithClassName, HTMLAttributes<HTMLDialogElement> {
|
|
15
13
|
isOpen?: boolean;
|
|
16
14
|
onOpen?: () => void;
|
|
17
15
|
onClose?: () => void;
|
|
18
16
|
}
|
|
19
17
|
|
|
20
|
-
export function Dialog({ children, id: idProp, isOpen = false, className, onOpen, onClose }: DialogProps) {
|
|
18
|
+
export function Dialog({ children, id: idProp, isOpen = false, className, onOpen, onClose, ...rest }: DialogProps) {
|
|
21
19
|
const generatedId = useId();
|
|
22
20
|
const id = idProp ?? generatedId;
|
|
23
21
|
|
|
@@ -92,6 +90,7 @@ export function Dialog({ children, id: idProp, isOpen = false, className, onOpen
|
|
|
92
90
|
|
|
93
91
|
return (
|
|
94
92
|
<dialog
|
|
93
|
+
{...rest}
|
|
95
94
|
ref={dialogRef}
|
|
96
95
|
id={id}
|
|
97
96
|
className={cn(className, css.dialog)}
|
|
@@ -85,13 +85,13 @@ describe('Drawer', () => {
|
|
|
85
85
|
expect(dialog).toHaveAttribute('aria-modal', 'true');
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
it('renders aria-label on the dialog when
|
|
89
|
-
render(<Drawer isOpen title="Settings"
|
|
88
|
+
it('renders aria-label on the dialog when aria-label prop is provided', () => {
|
|
89
|
+
render(<Drawer isOpen title="Settings" aria-label="Settings panel" onClose={() => {}}>Content</Drawer>);
|
|
90
90
|
const dialog = document.querySelector('dialog');
|
|
91
91
|
expect(dialog).toHaveAttribute('aria-label', 'Settings panel');
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
it('does not render aria-label when
|
|
94
|
+
it('does not render aria-label when aria-label prop is not provided', () => {
|
|
95
95
|
render(<Drawer isOpen title="Settings" onClose={() => {}}>Content</Drawer>);
|
|
96
96
|
const dialog = document.querySelector('dialog');
|
|
97
97
|
expect(dialog).not.toHaveAttribute('aria-label');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReactNode, useEffect, useId, useRef } from 'react';
|
|
1
|
+
import { HTMLAttributes, ReactNode, useEffect, useId, useRef } from 'react';
|
|
2
2
|
import { installInvokerCommandsPolyfill } from '../../../polyfill-invoker-commands';
|
|
3
3
|
|
|
4
4
|
// Install once at module load — no-op if native support is present.
|
|
@@ -8,15 +8,12 @@ import css from './Drawer.module.css';
|
|
|
8
8
|
import { cn } from '@boostdev/design-system-foundation';
|
|
9
9
|
import type { WithClassName } from '../../../types';
|
|
10
10
|
|
|
11
|
-
interface DrawerProps extends WithClassName {
|
|
12
|
-
id?: string;
|
|
11
|
+
interface DrawerProps extends WithClassName, Omit<HTMLAttributes<HTMLDialogElement>, 'title'> {
|
|
13
12
|
isOpen: boolean;
|
|
14
13
|
onOpen?: () => void;
|
|
15
14
|
onClose: () => void;
|
|
16
15
|
title: ReactNode;
|
|
17
|
-
children: ReactNode;
|
|
18
16
|
side?: 'left' | 'right';
|
|
19
|
-
ariaLabel?: string;
|
|
20
17
|
}
|
|
21
18
|
|
|
22
19
|
export function Drawer({
|
|
@@ -27,8 +24,8 @@ export function Drawer({
|
|
|
27
24
|
title,
|
|
28
25
|
children,
|
|
29
26
|
side = 'right',
|
|
30
|
-
ariaLabel,
|
|
31
27
|
className,
|
|
28
|
+
...rest
|
|
32
29
|
}: Readonly<DrawerProps>) {
|
|
33
30
|
const generatedId = useId();
|
|
34
31
|
const id = idProp ?? generatedId;
|
|
@@ -92,10 +89,10 @@ export function Drawer({
|
|
|
92
89
|
|
|
93
90
|
return (
|
|
94
91
|
<dialog
|
|
92
|
+
{...rest}
|
|
95
93
|
ref={dialogRef}
|
|
96
94
|
id={id}
|
|
97
95
|
className={cn(css.drawer, css[`--side_${side}`], className)}
|
|
98
|
-
aria-label={ariaLabel}
|
|
99
96
|
aria-modal="true"
|
|
100
97
|
onClick={handleClick}
|
|
101
98
|
onCancel={handleCancel}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
HTMLAttributes,
|
|
2
3
|
KeyboardEvent,
|
|
3
4
|
ReactElement,
|
|
4
5
|
cloneElement,
|
|
@@ -21,7 +22,7 @@ export interface DropdownMenuItem {
|
|
|
21
22
|
separator?: boolean;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
interface DropdownMenuProps extends WithClassName {
|
|
25
|
+
interface DropdownMenuProps extends WithClassName, HTMLAttributes<HTMLDivElement> {
|
|
25
26
|
trigger: ReactElement;
|
|
26
27
|
items: DropdownMenuItem[];
|
|
27
28
|
placement?: 'bottom-start' | 'bottom-end';
|
|
@@ -32,6 +33,7 @@ export function DropdownMenu({
|
|
|
32
33
|
items,
|
|
33
34
|
placement = 'bottom-start',
|
|
34
35
|
className,
|
|
36
|
+
...rest
|
|
35
37
|
}: Readonly<DropdownMenuProps>) {
|
|
36
38
|
const [isOpen, setIsOpen] = useState(false);
|
|
37
39
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
@@ -105,7 +107,7 @@ export function DropdownMenu({
|
|
|
105
107
|
: trigger;
|
|
106
108
|
|
|
107
109
|
return (
|
|
108
|
-
<div ref={containerRef} className={cn(css.wrapper, className)}>
|
|
110
|
+
<div {...rest} ref={containerRef} className={cn(css.wrapper, className)}>
|
|
109
111
|
{triggerEl}
|
|
110
112
|
{isOpen && (
|
|
111
113
|
<ul
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
HTMLAttributes,
|
|
2
3
|
ReactElement,
|
|
3
4
|
ReactNode,
|
|
4
5
|
cloneElement,
|
|
@@ -25,11 +26,10 @@ if (
|
|
|
25
26
|
import('@oddbird/css-anchor-positioning').catch(() => {});
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
interface PopoverProps extends WithClassName {
|
|
29
|
+
interface PopoverProps extends WithClassName, Omit<HTMLAttributes<HTMLSpanElement>, 'content'> {
|
|
29
30
|
children: ReactElement;
|
|
30
31
|
content: ReactNode;
|
|
31
32
|
placement?: 'top' | 'bottom' | 'left' | 'right';
|
|
32
|
-
'aria-label'?: string;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export function Popover({
|
|
@@ -38,6 +38,7 @@ export function Popover({
|
|
|
38
38
|
placement = 'bottom',
|
|
39
39
|
className,
|
|
40
40
|
'aria-label': ariaLabel,
|
|
41
|
+
...rest
|
|
41
42
|
}: Readonly<PopoverProps>) {
|
|
42
43
|
const [isOpen, setIsOpen] = useState(false);
|
|
43
44
|
const containerRef = useRef<HTMLSpanElement>(null);
|
|
@@ -102,7 +103,7 @@ export function Popover({
|
|
|
102
103
|
: children;
|
|
103
104
|
|
|
104
105
|
return (
|
|
105
|
-
<span ref={containerRef} className={cn(css.wrapper, className)}>
|
|
106
|
+
<span {...rest} ref={containerRef} className={cn(css.wrapper, className)}>
|
|
106
107
|
{trigger}
|
|
107
108
|
<div
|
|
108
109
|
ref={panelRef}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
+
import { HTMLAttributes } from 'react';
|
|
1
2
|
import css from './Rating.module.css';
|
|
2
3
|
import { cn } from '@boostdev/design-system-foundation';
|
|
3
4
|
import type { WithClassName } from '../../../types';
|
|
4
5
|
|
|
5
|
-
interface RatingProps extends WithClassName {
|
|
6
|
+
interface RatingProps extends WithClassName, HTMLAttributes<HTMLDivElement> {
|
|
6
7
|
value: number;
|
|
7
8
|
max?: number;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
export function Rating({ value, max = 5, className }: RatingProps) {
|
|
11
|
+
export function Rating({ value, max = 5, className, ...rest }: RatingProps) {
|
|
11
12
|
return (
|
|
12
13
|
<div
|
|
14
|
+
{...rest}
|
|
13
15
|
className={cn(css.rating, className)}
|
|
14
16
|
role="img"
|
|
15
17
|
aria-label={`${value} out of ${max} stars`}
|
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FieldsetHTMLAttributes, useId } from 'react';
|
|
2
2
|
import css from './CheckboxGroup.module.css';
|
|
3
3
|
import { Message } from '../atoms/Message';
|
|
4
4
|
import { cn } from '@boostdev/design-system-foundation';
|
|
5
5
|
import type { WithClassName } from '../../../../types';
|
|
6
6
|
|
|
7
|
-
export interface CheckboxGroupProps extends WithClassName {
|
|
7
|
+
export interface CheckboxGroupProps extends WithClassName, FieldsetHTMLAttributes<HTMLFieldSetElement> {
|
|
8
8
|
legend: string;
|
|
9
|
-
children: ReactNode;
|
|
10
9
|
error?: string;
|
|
11
10
|
hint?: string;
|
|
12
11
|
required?: boolean;
|
|
13
|
-
disabled?: boolean;
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
export function CheckboxGroup({
|
|
@@ -19,8 +17,8 @@ export function CheckboxGroup({
|
|
|
19
17
|
error,
|
|
20
18
|
hint,
|
|
21
19
|
required,
|
|
22
|
-
disabled,
|
|
23
20
|
className,
|
|
21
|
+
...rest
|
|
24
22
|
}: Readonly<CheckboxGroupProps>) {
|
|
25
23
|
const id = useId();
|
|
26
24
|
const hintId = id + 'hint';
|
|
@@ -29,8 +27,8 @@ export function CheckboxGroup({
|
|
|
29
27
|
|
|
30
28
|
return (
|
|
31
29
|
<fieldset
|
|
30
|
+
{...rest}
|
|
32
31
|
className={cn(css.group, className)}
|
|
33
|
-
disabled={disabled}
|
|
34
32
|
aria-required={required || undefined}
|
|
35
33
|
aria-describedby={describedBy}
|
|
36
34
|
>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ChangeEvent,
|
|
3
|
+
HTMLAttributes,
|
|
3
4
|
KeyboardEvent,
|
|
4
5
|
ReactNode,
|
|
5
6
|
useEffect,
|
|
@@ -21,7 +22,7 @@ export interface ComboboxOption {
|
|
|
21
22
|
disabled?: boolean;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
interface ComboboxProps extends WithClassName {
|
|
25
|
+
interface ComboboxProps extends WithClassName, Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
|
25
26
|
label: ReactNode;
|
|
26
27
|
name: string;
|
|
27
28
|
options: ComboboxOption[];
|
|
@@ -44,6 +45,7 @@ export function Combobox({
|
|
|
44
45
|
error,
|
|
45
46
|
hint,
|
|
46
47
|
className,
|
|
48
|
+
...rest
|
|
47
49
|
}: Readonly<ComboboxProps>) {
|
|
48
50
|
const id = name + useId();
|
|
49
51
|
const listboxId = id + 'listbox';
|
|
@@ -122,7 +124,7 @@ export function Combobox({
|
|
|
122
124
|
};
|
|
123
125
|
|
|
124
126
|
return (
|
|
125
|
-
<InputContainer className={cn(css.formGroup, className)}>
|
|
127
|
+
<InputContainer {...rest} className={cn(css.formGroup, className)}>
|
|
126
128
|
<Label id={id} label={label} />
|
|
127
129
|
<div ref={containerRef} className={css.inputWrapper}>
|
|
128
130
|
<input
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { useId, useRef, useState, DragEvent, ChangeEvent } from 'react';
|
|
1
|
+
import { HTMLAttributes, useId, useRef, useState, DragEvent, ChangeEvent } from 'react';
|
|
2
2
|
import css from './FileInput.module.css';
|
|
3
3
|
import { Message } from '../atoms/Message';
|
|
4
4
|
import { InputContainer } from '../atoms/InputContainer';
|
|
5
5
|
import { cn } from '@boostdev/design-system-foundation';
|
|
6
6
|
import type { WithClassName } from '../../../../types';
|
|
7
7
|
|
|
8
|
-
interface FileInputProps extends WithClassName {
|
|
8
|
+
interface FileInputProps extends WithClassName, Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
|
9
9
|
label: string;
|
|
10
10
|
name: string;
|
|
11
11
|
accept?: string;
|
|
@@ -28,6 +28,7 @@ export function FileInput({
|
|
|
28
28
|
hint,
|
|
29
29
|
onChange,
|
|
30
30
|
className,
|
|
31
|
+
...rest
|
|
31
32
|
}: Readonly<FileInputProps>) {
|
|
32
33
|
const uid = name + useId();
|
|
33
34
|
const hintId = uid + 'hint';
|
|
@@ -73,7 +74,7 @@ export function FileInput({
|
|
|
73
74
|
const handleDragLeave = () => setIsDragging(false);
|
|
74
75
|
|
|
75
76
|
return (
|
|
76
|
-
<InputContainer className={cn(css.formGroup, className)}>
|
|
77
|
+
<InputContainer {...rest} className={cn(css.formGroup, className)}>
|
|
77
78
|
<label
|
|
78
79
|
htmlFor={uid}
|
|
79
80
|
className={cn(css.dropZone, isDragging && css.isDragging, error && css.hasError, disabled && css.isDisabled)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useId, useRef, useState} from 'react';
|
|
1
|
+
import {HTMLAttributes, useId, useRef, useState} from 'react';
|
|
2
2
|
import css from './NumberInput.module.css';
|
|
3
3
|
import {Label} from '../atoms/Label';
|
|
4
4
|
import {Message} from '../atoms/Message';
|
|
@@ -6,7 +6,7 @@ import {InputContainer} from '../atoms/InputContainer';
|
|
|
6
6
|
import {cn} from '@boostdev/design-system-foundation';
|
|
7
7
|
import type { WithClassName } from '../../../../types';
|
|
8
8
|
|
|
9
|
-
interface NumberInputProps extends WithClassName {
|
|
9
|
+
interface NumberInputProps extends WithClassName, Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
|
10
10
|
label: string;
|
|
11
11
|
name: string;
|
|
12
12
|
value?: number;
|
|
@@ -33,6 +33,7 @@ export function NumberInput({
|
|
|
33
33
|
hint,
|
|
34
34
|
onChange,
|
|
35
35
|
className,
|
|
36
|
+
...rest
|
|
36
37
|
}: Readonly<NumberInputProps>) {
|
|
37
38
|
const uid = name + useId();
|
|
38
39
|
const hintId = uid + 'hint';
|
|
@@ -62,7 +63,7 @@ export function NumberInput({
|
|
|
62
63
|
};
|
|
63
64
|
|
|
64
65
|
return (
|
|
65
|
-
<InputContainer className={cn(css.formGroup, className)}>
|
|
66
|
+
<InputContainer {...rest} className={cn(css.formGroup, className)}>
|
|
66
67
|
<Label id={uid} label={label} />
|
|
67
68
|
<div className={css.inputRow}>
|
|
68
69
|
<button
|
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FieldsetHTMLAttributes, useId } from 'react';
|
|
2
2
|
import css from './RadioGroup.module.css';
|
|
3
3
|
import { Message } from '../atoms/Message';
|
|
4
4
|
import { cn } from '@boostdev/design-system-foundation';
|
|
5
5
|
import type { WithClassName } from '../../../../types';
|
|
6
6
|
|
|
7
|
-
export interface RadioGroupProps extends WithClassName {
|
|
7
|
+
export interface RadioGroupProps extends WithClassName, FieldsetHTMLAttributes<HTMLFieldSetElement> {
|
|
8
8
|
legend: string;
|
|
9
|
-
children: ReactNode;
|
|
10
9
|
error?: string;
|
|
11
10
|
hint?: string;
|
|
12
11
|
required?: boolean;
|
|
13
|
-
disabled?: boolean;
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
export function RadioGroup({
|
|
@@ -19,8 +17,8 @@ export function RadioGroup({
|
|
|
19
17
|
error,
|
|
20
18
|
hint,
|
|
21
19
|
required,
|
|
22
|
-
disabled,
|
|
23
20
|
className,
|
|
21
|
+
...rest
|
|
24
22
|
}: Readonly<RadioGroupProps>) {
|
|
25
23
|
const id = useId();
|
|
26
24
|
const hintId = id + 'hint';
|
|
@@ -29,8 +27,8 @@ export function RadioGroup({
|
|
|
29
27
|
|
|
30
28
|
return (
|
|
31
29
|
<fieldset
|
|
30
|
+
{...rest}
|
|
32
31
|
className={cn(css.group, className)}
|
|
33
|
-
disabled={disabled}
|
|
34
32
|
aria-required={required || undefined}
|
|
35
33
|
aria-describedby={describedBy}
|
|
36
34
|
>
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { Children,
|
|
1
|
+
import { Children, HTMLAttributes, ReactElement, cloneElement, isValidElement } from 'react';
|
|
2
2
|
import css from './SegmentedControl.module.css';
|
|
3
3
|
import { cn } from '@boostdev/design-system-foundation';
|
|
4
4
|
import type { WithClassName } from '../../../../types';
|
|
5
5
|
|
|
6
|
-
export interface SegmentedControlProps extends WithClassName {
|
|
7
|
-
children: ReactNode;
|
|
6
|
+
export interface SegmentedControlProps extends WithClassName, HTMLAttributes<HTMLDivElement> {
|
|
8
7
|
/**
|
|
9
8
|
* Zero-based index of the active item.
|
|
10
9
|
* When omitted, auto-detected from the first child that has
|
|
@@ -15,7 +14,6 @@ export interface SegmentedControlProps extends WithClassName {
|
|
|
15
14
|
size?: 'small' | 'medium' | 'large';
|
|
16
15
|
/** "outline" (default) — sliding inset border; "filled" — sliding filled thumb */
|
|
17
16
|
variant?: 'outline' | 'filled';
|
|
18
|
-
'aria-label'?: string;
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
export function SegmentedControl({
|
|
@@ -25,7 +23,7 @@ export function SegmentedControl({
|
|
|
25
23
|
size = 'medium',
|
|
26
24
|
variant = 'outline',
|
|
27
25
|
className,
|
|
28
|
-
|
|
26
|
+
...rest
|
|
29
27
|
}: Readonly<SegmentedControlProps>) {
|
|
30
28
|
const validChildren = Children.toArray(children).filter(isValidElement) as ReactElement<Record<string, unknown>>[];
|
|
31
29
|
|
|
@@ -41,8 +39,8 @@ export function SegmentedControl({
|
|
|
41
39
|
|
|
42
40
|
return (
|
|
43
41
|
<div
|
|
42
|
+
{...rest}
|
|
44
43
|
role="group"
|
|
45
|
-
aria-label={ariaLabel}
|
|
46
44
|
className={cn(css.control, css[`--size_${size}`], css[`--variant_${variant}`], className)}
|
|
47
45
|
style={{
|
|
48
46
|
'--control_count': validChildren.length,
|
|
@@ -5,6 +5,6 @@ import type { WithClassName } from '../../../../types';
|
|
|
5
5
|
|
|
6
6
|
type ContainerProps = WithClassName & HTMLAttributes<HTMLDivElement>;
|
|
7
7
|
|
|
8
|
-
export const InputContainer = ({ children, className }: ContainerProps) => {
|
|
9
|
-
return <div className={cn(css.container, className)}>{children}</div>;
|
|
8
|
+
export const InputContainer = ({ children, className, ...rest }: ContainerProps) => {
|
|
9
|
+
return <div {...rest} className={cn(css.container, className)}>{children}</div>;
|
|
10
10
|
};
|
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HTMLAttributes } from 'react';
|
|
2
2
|
import css from './ButtonGroup.module.css';
|
|
3
3
|
import { cn } from '@boostdev/design-system-foundation';
|
|
4
4
|
import type { WithClassName } from '../../../types';
|
|
5
5
|
|
|
6
|
-
export interface ButtonGroupProps extends WithClassName {
|
|
7
|
-
children: ReactNode;
|
|
6
|
+
export interface ButtonGroupProps extends WithClassName, HTMLAttributes<HTMLDivElement> {
|
|
8
7
|
variant?: 'flow' | 'card' | 'modal' | 'content';
|
|
9
|
-
'aria-label'?: string;
|
|
10
8
|
}
|
|
11
9
|
|
|
12
|
-
export function ButtonGroup({ children, className, variant,
|
|
10
|
+
export function ButtonGroup({ children, className, variant, ...rest }: ButtonGroupProps) {
|
|
13
11
|
return (
|
|
14
12
|
<div
|
|
13
|
+
{...rest}
|
|
15
14
|
role="group"
|
|
16
|
-
aria-label={ariaLabel}
|
|
17
15
|
className={cn(css.buttonGroup, className, variant && css[`--variant_${variant}`])}
|
|
18
16
|
>
|
|
19
17
|
<div className={css.container}>{children}</div>
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HTMLAttributes } from 'react';
|
|
2
2
|
import css from './Card.module.css';
|
|
3
3
|
import { cn } from '@boostdev/design-system-foundation';
|
|
4
4
|
import type { WithClassName } from '../../../types';
|
|
5
5
|
|
|
6
|
-
interface CardProps extends WithClassName {
|
|
7
|
-
children: ReactNode;
|
|
6
|
+
interface CardProps extends WithClassName, HTMLAttributes<HTMLElement> {
|
|
8
7
|
variant?: 'default' | 'elevated' | 'outlined';
|
|
9
8
|
padding?: 'none' | 'small' | 'medium' | 'large';
|
|
10
9
|
textAlign?: 'start' | 'center' | 'end';
|
|
11
|
-
style?: CSSProperties;
|
|
12
|
-
onClick?: () => void;
|
|
13
|
-
'aria-label'?: string;
|
|
14
10
|
}
|
|
15
11
|
|
|
16
12
|
export function Card({
|
|
@@ -19,9 +15,8 @@ export function Card({
|
|
|
19
15
|
variant = 'default',
|
|
20
16
|
padding = 'medium',
|
|
21
17
|
textAlign = 'start',
|
|
22
|
-
style,
|
|
23
18
|
onClick,
|
|
24
|
-
|
|
19
|
+
...rest
|
|
25
20
|
}: CardProps) {
|
|
26
21
|
const classNames = cn(
|
|
27
22
|
css.card,
|
|
@@ -36,10 +31,9 @@ export function Card({
|
|
|
36
31
|
|
|
37
32
|
return (
|
|
38
33
|
<Component
|
|
34
|
+
{...rest}
|
|
39
35
|
className={classNames}
|
|
40
36
|
onClick={onClick}
|
|
41
|
-
style={style}
|
|
42
|
-
aria-label={ariaLabel}
|
|
43
37
|
{...(onClick && { type: 'button' as const })}
|
|
44
38
|
>
|
|
45
39
|
{children}
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
1
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
2
|
import css from './IconWrapper.module.css';
|
|
3
3
|
import { cn } from '@boostdev/design-system-foundation';
|
|
4
4
|
import type { WithClassName } from '../../../types';
|
|
5
5
|
|
|
6
|
-
export interface IconWrapperProps extends WithClassName {
|
|
6
|
+
export interface IconWrapperProps extends WithClassName, HTMLAttributes<HTMLDivElement> {
|
|
7
7
|
children: ReactNode;
|
|
8
|
-
'aria-hidden'?: boolean | 'true' | 'false';
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
export function IconWrapper({ children, className,
|
|
10
|
+
export function IconWrapper({ children, className, ...rest }: IconWrapperProps) {
|
|
12
11
|
return (
|
|
13
|
-
<div className={cn(className, css.wrapper)}
|
|
12
|
+
<div {...rest} className={cn(className, css.wrapper)}>{children}</div>
|
|
14
13
|
);
|
|
15
14
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { JSX } from 'react';
|
|
1
|
+
import type { HTMLAttributes, JSX } from 'react';
|
|
2
2
|
import css from './SectionHeader.module.css';
|
|
3
3
|
import { cn } from '@boostdev/design-system-foundation';
|
|
4
4
|
import type { WithClassName } from '../../../types';
|
|
5
5
|
|
|
6
6
|
type IntrinsicElement = keyof JSX.IntrinsicElements;
|
|
7
7
|
|
|
8
|
-
type SectionHeaderProps = WithClassName & {
|
|
8
|
+
type SectionHeaderProps = WithClassName & Omit<HTMLAttributes<HTMLDivElement>, 'title'> & {
|
|
9
9
|
title: string;
|
|
10
10
|
subtitle?: string;
|
|
11
11
|
alignment?: 'start' | 'center' | 'end';
|
|
@@ -19,11 +19,12 @@ export function SectionHeader({
|
|
|
19
19
|
className,
|
|
20
20
|
alignment = 'start',
|
|
21
21
|
size = 'medium',
|
|
22
|
-
titleAs = 'h2'
|
|
22
|
+
titleAs = 'h2',
|
|
23
|
+
...rest
|
|
23
24
|
}: Readonly<SectionHeaderProps>) {
|
|
24
25
|
const Title = titleAs;
|
|
25
26
|
return (
|
|
26
|
-
<div className={cn(css.sectionHeader, css[`--${alignment}`], css[`--${size}`], className)}>
|
|
27
|
+
<div {...rest} className={cn(css.sectionHeader, css[`--${alignment}`], css[`--${size}`], className)}>
|
|
27
28
|
<Title className={css.title}>{title}</Title>
|
|
28
29
|
{subtitle && <p className={css.subtitle}>{subtitle}</p>}
|
|
29
30
|
</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReactNode, useId, useState } from 'react';
|
|
1
|
+
import { HTMLAttributes, ReactNode, useId, useState } from 'react';
|
|
2
2
|
import css from './Accordion.module.css';
|
|
3
3
|
import { cn } from '@boostdev/design-system-foundation';
|
|
4
4
|
import type { WithClassName } from '../../../types';
|
|
@@ -10,7 +10,7 @@ export interface AccordionItem {
|
|
|
10
10
|
disabled?: boolean;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
interface AccordionProps extends WithClassName {
|
|
13
|
+
interface AccordionProps extends WithClassName, HTMLAttributes<HTMLDivElement> {
|
|
14
14
|
items: AccordionItem[];
|
|
15
15
|
allowMultiple?: boolean;
|
|
16
16
|
defaultOpen?: string[];
|
|
@@ -21,6 +21,7 @@ export function Accordion({
|
|
|
21
21
|
allowMultiple = false,
|
|
22
22
|
defaultOpen = [],
|
|
23
23
|
className,
|
|
24
|
+
...rest
|
|
24
25
|
}: Readonly<AccordionProps>) {
|
|
25
26
|
const baseId = useId();
|
|
26
27
|
const [openIds, setOpenIds] = useState<string[]>(defaultOpen);
|
|
@@ -34,7 +35,7 @@ export function Accordion({
|
|
|
34
35
|
};
|
|
35
36
|
|
|
36
37
|
return (
|
|
37
|
-
<div className={cn(css.accordion, className)}>
|
|
38
|
+
<div {...rest} className={cn(css.accordion, className)}>
|
|
38
39
|
{items.map(item => {
|
|
39
40
|
const isOpen = openIds.includes(item.id);
|
|
40
41
|
const triggerId = `${baseId}-trigger-${item.id}`;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
1
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
2
|
import css from './Alert.module.css';
|
|
3
3
|
import { cn } from '@boostdev/design-system-foundation';
|
|
4
4
|
import type { WithClassName } from '../../../types';
|
|
5
5
|
|
|
6
|
-
interface AlertProps extends WithClassName {
|
|
6
|
+
interface AlertProps extends WithClassName, Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
7
7
|
variant?: 'info' | 'success' | 'warning' | 'error';
|
|
8
8
|
icon?: ReactNode;
|
|
9
9
|
title?: string;
|
|
10
|
-
children: ReactNode;
|
|
11
10
|
onDismiss?: () => void;
|
|
12
11
|
}
|
|
13
12
|
|
|
@@ -18,11 +17,13 @@ export function Alert({
|
|
|
18
17
|
children,
|
|
19
18
|
onDismiss,
|
|
20
19
|
className,
|
|
20
|
+
...rest
|
|
21
21
|
}: Readonly<AlertProps>) {
|
|
22
22
|
const isUrgent = variant === 'error' || variant === 'warning';
|
|
23
23
|
|
|
24
24
|
return (
|
|
25
25
|
<div
|
|
26
|
+
{...rest}
|
|
26
27
|
role={isUrgent ? 'alert' : 'status'}
|
|
27
28
|
aria-live={isUrgent ? 'assertive' : 'polite'}
|
|
28
29
|
aria-atomic="true"
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { HTMLAttributes } from 'react';
|
|
1
2
|
import css from './Avatar.module.css';
|
|
2
3
|
import { cn } from '@boostdev/design-system-foundation';
|
|
3
4
|
import type { WithClassName } from '../../../types';
|
|
4
5
|
|
|
5
|
-
interface AvatarProps extends WithClassName {
|
|
6
|
+
interface AvatarProps extends WithClassName, HTMLAttributes<HTMLSpanElement> {
|
|
6
7
|
src?: string;
|
|
7
8
|
alt?: string;
|
|
8
9
|
name?: string;
|
|
@@ -18,12 +19,12 @@ function getInitials(name: string): string {
|
|
|
18
19
|
.join('');
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export function Avatar({ src, alt, name, size = 'medium', className }: Readonly<AvatarProps>) {
|
|
22
|
+
export function Avatar({ src, alt, name, size = 'medium', className, ...rest }: Readonly<AvatarProps>) {
|
|
22
23
|
const sizeClass = css[`--size_${size}`];
|
|
23
24
|
|
|
24
25
|
if (src) {
|
|
25
26
|
return (
|
|
26
|
-
<span className={cn(css.avatar, sizeClass, className)}>
|
|
27
|
+
<span {...rest} className={cn(css.avatar, sizeClass, className)}>
|
|
27
28
|
<img src={src} alt={alt ?? name ?? 'User avatar'} className={css.image} />
|
|
28
29
|
</span>
|
|
29
30
|
);
|
|
@@ -33,6 +34,7 @@ export function Avatar({ src, alt, name, size = 'medium', className }: Readonly<
|
|
|
33
34
|
|
|
34
35
|
return (
|
|
35
36
|
<span
|
|
37
|
+
{...rest}
|
|
36
38
|
role="img"
|
|
37
39
|
aria-label={name ?? 'User avatar'}
|
|
38
40
|
className={cn(css.avatar, css['--fallback'], sizeClass, className)}
|