@boostdev/design-system-components 0.1.18 → 1.0.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/AGENTS.md +65 -27
- package/README.md +64 -29
- package/dist/client.cjs +412 -241
- package/dist/client.css +1378 -1186
- package/dist/client.d.cts +43 -16
- package/dist/client.d.ts +43 -16
- package/dist/client.js +414 -245
- package/dist/index.cjs +412 -241
- package/dist/index.css +1378 -1186
- package/dist/index.d.cts +43 -16
- package/dist/index.d.ts +43 -16
- package/dist/index.js +414 -245
- package/package.json +9 -4
- package/src/components/interaction/Button/Button.mdx +2 -5
- package/src/components/interaction/Button/Button.module.css +74 -44
- package/src/components/interaction/Button/Button.stories.tsx +4 -4
- package/src/components/interaction/Button/Button.tsx +5 -5
- package/src/components/interaction/Button/index.ts +1 -0
- package/src/components/interaction/Command/Command.module.css +53 -38
- package/src/components/interaction/Command/Command.spec.tsx +24 -0
- package/src/components/interaction/Command/Command.tsx +5 -0
- package/src/components/interaction/Dialog/Dialog.mdx +2 -2
- package/src/components/interaction/Dialog/Dialog.module.css +20 -10
- package/src/components/interaction/Dialog/Dialog.spec.tsx +19 -0
- package/src/components/interaction/Dialog/Dialog.tsx +22 -0
- package/src/components/interaction/Drawer/Drawer.module.css +11 -11
- package/src/components/interaction/Drawer/Drawer.spec.tsx +30 -0
- package/src/components/interaction/Drawer/Drawer.tsx +7 -3
- package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +24 -19
- package/src/components/interaction/DropdownMenu/DropdownMenu.stories.tsx +4 -4
- package/src/components/interaction/Popover/Popover.module.css +17 -12
- package/src/components/interaction/Popover/Popover.spec.tsx +25 -4
- package/src/components/interaction/Popover/Popover.stories.tsx +1 -1
- package/src/components/interaction/Popover/Popover.tsx +5 -2
- package/src/components/interaction/Rating/Rating.module.css +3 -3
- package/src/components/interaction/Toast/Toast.module.css +19 -19
- package/src/components/interaction/Toast/Toast.tsx +1 -1
- package/src/components/interaction/form/Checkbox/Checkbox.mdx +2 -2
- package/src/components/interaction/form/Checkbox/Checkbox.module.css +22 -18
- package/src/components/interaction/form/Checkbox/Checkbox.tsx +2 -2
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.module.css +29 -0
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.spec.tsx +87 -0
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.stories.tsx +62 -0
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +46 -0
- package/src/components/interaction/form/CheckboxGroup/index.ts +2 -0
- package/src/components/interaction/form/Combobox/Combobox.module.css +44 -34
- package/src/components/interaction/form/Combobox/Combobox.tsx +0 -1
- package/src/components/interaction/form/FileInput/FileInput.module.css +24 -16
- package/src/components/interaction/form/FileInput/FileInput.tsx +5 -2
- package/src/components/interaction/form/FormInput/FormInput.module.css +21 -16
- package/src/components/interaction/form/FormInput/FormInput.tsx +3 -0
- package/src/components/interaction/form/NumberInput/NumberInput.module.css +25 -19
- package/src/components/interaction/form/NumberInput/NumberInput.tsx +5 -2
- package/src/components/interaction/form/Radio/Radio.mdx +3 -3
- package/src/components/interaction/form/Radio/Radio.module.css +28 -24
- package/src/components/interaction/form/Radio/Radio.stories.tsx +1 -1
- package/src/components/interaction/form/Radio/Radio.tsx +2 -2
- package/src/components/interaction/form/RadioGroup/RadioGroup.module.css +29 -0
- package/src/components/interaction/form/RadioGroup/RadioGroup.spec.tsx +75 -0
- package/src/components/interaction/form/RadioGroup/RadioGroup.stories.tsx +64 -0
- package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +46 -0
- package/src/components/interaction/form/RadioGroup/index.ts +2 -0
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.mdx +5 -5
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +26 -26
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +4 -0
- package/src/components/interaction/form/Select/Select.module.css +24 -19
- package/src/components/interaction/form/Select/Select.tsx +3 -0
- package/src/components/interaction/form/Slider/Slider.mdx +1 -1
- package/src/components/interaction/form/Slider/Slider.module.css +24 -24
- package/src/components/interaction/form/Slider/Slider.tsx +1 -0
- package/src/components/interaction/form/Switch/Switch.mdx +4 -4
- package/src/components/interaction/form/Switch/Switch.module.css +22 -16
- package/src/components/interaction/form/Switch/Switch.tsx +1 -1
- package/src/components/interaction/form/Textarea/Textarea.module.css +23 -18
- package/src/components/interaction/form/Textarea/Textarea.tsx +3 -0
- package/src/components/interaction/form/atoms/InputContainer.module.css +2 -2
- package/src/components/interaction/form/atoms/Label.module.css +2 -2
- package/src/components/interaction/form/atoms/Message.module.css +2 -2
- package/src/components/layout/ButtonGroup/ButtonGroup.module.css +2 -2
- package/src/components/layout/ButtonGroup/ButtonGroup.stories.tsx +2 -2
- package/src/components/layout/ButtonGroup/ButtonGroup.tsx +8 -3
- package/src/components/layout/ButtonGroup/index.ts +1 -0
- package/src/components/layout/Card/Card.module.css +30 -16
- package/src/components/layout/Card/Card.tsx +1 -1
- package/src/components/layout/IconWrapper/IconWrapper.mdx +2 -2
- package/src/components/layout/IconWrapper/IconWrapper.module.css +5 -5
- package/src/components/layout/IconWrapper/IconWrapper.tsx +4 -3
- package/src/components/layout/IconWrapper/index.ts +1 -0
- package/src/components/layout/SectionHeader/SectionHeader.module.css +23 -23
- package/src/components/layout/SectionHeader/SectionHeader.spec.tsx +2 -2
- package/src/components/layout/SectionHeader/SectionHeader.tsx +2 -2
- package/src/components/ui/Accordion/Accordion.module.css +23 -18
- package/src/components/ui/Alert/Alert.module.css +31 -27
- package/src/components/ui/Avatar/Avatar.module.css +6 -6
- package/src/components/ui/Avatar/Avatar.tsx +1 -1
- package/src/components/ui/Badge/Badge.mdx +2 -2
- package/src/components/ui/Badge/Badge.module.css +15 -15
- package/src/components/ui/Breadcrumb/Breadcrumb.module.css +11 -11
- package/src/components/ui/Calendar/Calendar.module.css +39 -29
- package/src/components/ui/Calendar/Calendar.tsx +6 -2
- package/src/components/ui/Carousel/Carousel.module.css +15 -10
- package/src/components/ui/Carousel/Carousel.stories.tsx +1 -1
- package/src/components/ui/Collapsible/Collapsible.mdx +6 -6
- package/src/components/ui/Collapsible/Collapsible.module.css +18 -18
- package/src/components/ui/DescriptionList/DescriptionList.module.css +8 -8
- package/src/components/ui/Link/Link.module.css +14 -14
- package/src/components/ui/Loading/Loading.module.css +8 -2
- package/src/components/ui/NotificationBanner/NotificationBanner.module.css +27 -23
- package/src/components/ui/Pagination/Pagination.module.css +22 -17
- package/src/components/ui/Progress/Progress.mdx +1 -1
- package/src/components/ui/Progress/Progress.module.css +10 -10
- package/src/components/ui/ProgressCircle/ProgressCircle.module.css +8 -8
- package/src/components/ui/Separator/Separator.module.css +1 -1
- package/src/components/ui/Separator/Separator.tsx +1 -1
- package/src/components/ui/Skeleton/Skeleton.module.css +5 -5
- package/src/components/ui/SkipLink/SkipLink.module.css +17 -12
- package/src/components/ui/Table/Table.module.css +35 -30
- package/src/components/ui/Table/Table.tsx +2 -1
- package/src/components/ui/Tabs/Tabs.module.css +17 -17
- package/src/components/ui/Tabs/Tabs.tsx +0 -1
- package/src/components/ui/Tooltip/Tooltip.module.css +12 -12
- package/src/components/ui/Tooltip/Tooltip.spec.tsx +3 -3
- package/src/components/ui/Tooltip/Tooltip.tsx +10 -2
- package/src/components/ui/Typography/Typography.module.css +18 -18
- package/src/css/bdc.css +61 -0
- package/src/css/index.css +5 -0
- package/src/index.ts +7 -0
- package/src/stories/DesignSystem/Borders.mdx +158 -0
- package/src/stories/DesignSystem/Colors.mdx +196 -0
- package/src/stories/DesignSystem/Elevation.mdx +127 -0
- package/src/stories/DesignSystem/Grid.mdx +140 -0
- package/src/stories/DesignSystem/Motion.mdx +96 -0
- package/src/stories/DesignSystem/Overview.mdx +99 -0
- package/src/stories/DesignSystem/Spacing.mdx +74 -0
- package/src/stories/DesignSystem/Typography.mdx +110 -0
- package/src/stories/Introduction.css +2 -2
- package/src/stories/Introduction.mdx +7 -7
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { InputHTMLAttributes, useId } from 'react';
|
|
1
|
+
import { InputHTMLAttributes, ReactNode, useId } from 'react';
|
|
2
2
|
import css from './Checkbox.module.css';
|
|
3
3
|
import { Message } from '../atoms/Message';
|
|
4
4
|
import { Label } from '../atoms/Label';
|
|
@@ -6,7 +6,7 @@ import { cn } from '@boostdev/design-system-foundation';
|
|
|
6
6
|
import { InputContainer } from '../atoms/InputContainer';
|
|
7
7
|
|
|
8
8
|
interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
|
|
9
|
-
label:
|
|
9
|
+
label: ReactNode;
|
|
10
10
|
name: string;
|
|
11
11
|
error?: string;
|
|
12
12
|
hint?: string;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
@layer component {
|
|
2
|
+
.group {
|
|
3
|
+
border: none;
|
|
4
|
+
padding: 0;
|
|
5
|
+
margin: 0;
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
gap: var(--bds-space_xs);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.legend {
|
|
12
|
+
font-family: var(--bds-font_family--body);
|
|
13
|
+
font-size: var(--bds-font_size--body--s);
|
|
14
|
+
font-weight: var(--bds-font_weight--medium);
|
|
15
|
+
color: var(--bds-on-bg);
|
|
16
|
+
padding: 0;
|
|
17
|
+
margin-block-end: var(--bds-space_xs);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.required {
|
|
21
|
+
color: var(--bds-error);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.items {
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
gap: var(--bds-space_xs);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { Checkbox } from '../Checkbox';
|
|
3
|
+
import { CheckboxGroup } from './CheckboxGroup';
|
|
4
|
+
|
|
5
|
+
describe('CheckboxGroup', () => {
|
|
6
|
+
it('renders a fieldset with a legend', () => {
|
|
7
|
+
render(
|
|
8
|
+
<CheckboxGroup legend="Notifications">
|
|
9
|
+
<Checkbox label="Email" name="email" />
|
|
10
|
+
</CheckboxGroup>
|
|
11
|
+
);
|
|
12
|
+
expect(screen.getByRole('group', { name: 'Notifications' })).toBeInTheDocument();
|
|
13
|
+
expect(screen.getByText('Notifications')).toBeInTheDocument();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('renders child checkboxes', () => {
|
|
17
|
+
render(
|
|
18
|
+
<CheckboxGroup legend="Options">
|
|
19
|
+
<Checkbox label="Email" name="email" />
|
|
20
|
+
<Checkbox label="SMS" name="sms" />
|
|
21
|
+
</CheckboxGroup>
|
|
22
|
+
);
|
|
23
|
+
expect(screen.getByRole('checkbox', { name: 'Email' })).toBeInTheDocument();
|
|
24
|
+
expect(screen.getByRole('checkbox', { name: 'SMS' })).toBeInTheDocument();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('shows an error message', () => {
|
|
28
|
+
render(
|
|
29
|
+
<CheckboxGroup legend="Required" error="Select at least one option">
|
|
30
|
+
<Checkbox label="Option" name="opt" />
|
|
31
|
+
</CheckboxGroup>
|
|
32
|
+
);
|
|
33
|
+
expect(screen.getByText('Select at least one option')).toBeInTheDocument();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('shows a hint message', () => {
|
|
37
|
+
render(
|
|
38
|
+
<CheckboxGroup legend="Preferences" hint="You can select multiple">
|
|
39
|
+
<Checkbox label="Option" name="opt" />
|
|
40
|
+
</CheckboxGroup>
|
|
41
|
+
);
|
|
42
|
+
expect(screen.getByText('You can select multiple')).toBeInTheDocument();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('links error message via aria-describedby', () => {
|
|
46
|
+
render(
|
|
47
|
+
<CheckboxGroup legend="Required" error="Required field">
|
|
48
|
+
<Checkbox label="Option" name="opt" />
|
|
49
|
+
</CheckboxGroup>
|
|
50
|
+
);
|
|
51
|
+
const fieldset = screen.getByRole('group');
|
|
52
|
+
const errorMsg = screen.getByText('Required field');
|
|
53
|
+
expect(fieldset).toHaveAttribute('aria-describedby');
|
|
54
|
+
expect(fieldset.getAttribute('aria-describedby')).toContain(errorMsg.id);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('links hint message via aria-describedby', () => {
|
|
58
|
+
render(
|
|
59
|
+
<CheckboxGroup legend="Options" hint="Pick any">
|
|
60
|
+
<Checkbox label="Option" name="opt" />
|
|
61
|
+
</CheckboxGroup>
|
|
62
|
+
);
|
|
63
|
+
const fieldset = screen.getByRole('group');
|
|
64
|
+
const hintMsg = screen.getByText('Pick any');
|
|
65
|
+
expect(fieldset).toHaveAttribute('aria-describedby');
|
|
66
|
+
expect(fieldset.getAttribute('aria-describedby')).toContain(hintMsg.id);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('marks fieldset as required when required prop is set', () => {
|
|
70
|
+
render(
|
|
71
|
+
<CheckboxGroup legend="Required" required>
|
|
72
|
+
<Checkbox label="Option" name="opt" />
|
|
73
|
+
</CheckboxGroup>
|
|
74
|
+
);
|
|
75
|
+
expect(screen.getByRole('group')).toHaveAttribute('aria-required', 'true');
|
|
76
|
+
expect(screen.getByText('*')).toBeInTheDocument();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('disables the fieldset when disabled prop is set', () => {
|
|
80
|
+
render(
|
|
81
|
+
<CheckboxGroup legend="Disabled" disabled>
|
|
82
|
+
<Checkbox label="Option" name="opt" />
|
|
83
|
+
</CheckboxGroup>
|
|
84
|
+
);
|
|
85
|
+
expect(screen.getByRole('group')).toBeDisabled();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Checkbox } from '../Checkbox';
|
|
3
|
+
import { CheckboxGroup } from './CheckboxGroup';
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: 'Form/CheckboxGroup',
|
|
7
|
+
component: CheckboxGroup,
|
|
8
|
+
} satisfies Meta<typeof CheckboxGroup>;
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof meta>;
|
|
12
|
+
|
|
13
|
+
export const Default: Story = {
|
|
14
|
+
args: { legend: 'Notifications' },
|
|
15
|
+
render: args => (
|
|
16
|
+
<CheckboxGroup {...args}>
|
|
17
|
+
<Checkbox label="Email" name="notify_email" />
|
|
18
|
+
<Checkbox label="SMS" name="notify_sms" />
|
|
19
|
+
<Checkbox label="Push" name="notify_push" />
|
|
20
|
+
</CheckboxGroup>
|
|
21
|
+
),
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const WithError: Story = {
|
|
25
|
+
args: { legend: 'Required options', error: 'Select at least one option' },
|
|
26
|
+
render: args => (
|
|
27
|
+
<CheckboxGroup {...args}>
|
|
28
|
+
<Checkbox label="Email" name="notify_email" />
|
|
29
|
+
<Checkbox label="SMS" name="notify_sms" />
|
|
30
|
+
</CheckboxGroup>
|
|
31
|
+
),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const WithHint: Story = {
|
|
35
|
+
args: { legend: 'Preferences', hint: 'You can select multiple options' },
|
|
36
|
+
render: args => (
|
|
37
|
+
<CheckboxGroup {...args}>
|
|
38
|
+
<Checkbox label="Dark mode" name="pref_dark" />
|
|
39
|
+
<Checkbox label="Compact view" name="pref_compact" />
|
|
40
|
+
</CheckboxGroup>
|
|
41
|
+
),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const Required: Story = {
|
|
45
|
+
args: { legend: 'Agreement', required: true },
|
|
46
|
+
render: args => (
|
|
47
|
+
<CheckboxGroup {...args}>
|
|
48
|
+
<Checkbox label="I agree to the terms" name="terms" />
|
|
49
|
+
<Checkbox label="I agree to the privacy policy" name="privacy" />
|
|
50
|
+
</CheckboxGroup>
|
|
51
|
+
),
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const Disabled: Story = {
|
|
55
|
+
args: { legend: 'Unavailable options', disabled: true },
|
|
56
|
+
render: args => (
|
|
57
|
+
<CheckboxGroup {...args}>
|
|
58
|
+
<Checkbox label="Email" name="notify_email" defaultChecked />
|
|
59
|
+
<Checkbox label="SMS" name="notify_sms" />
|
|
60
|
+
</CheckboxGroup>
|
|
61
|
+
),
|
|
62
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ReactNode, useId } from 'react';
|
|
2
|
+
import css from './CheckboxGroup.module.css';
|
|
3
|
+
import { Message } from '../atoms/Message';
|
|
4
|
+
import { cn } from '@boostdev/design-system-foundation';
|
|
5
|
+
|
|
6
|
+
export interface CheckboxGroupProps {
|
|
7
|
+
legend: string;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
error?: string;
|
|
10
|
+
hint?: string;
|
|
11
|
+
required?: boolean;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function CheckboxGroup({
|
|
17
|
+
legend,
|
|
18
|
+
children,
|
|
19
|
+
error,
|
|
20
|
+
hint,
|
|
21
|
+
required,
|
|
22
|
+
disabled,
|
|
23
|
+
className,
|
|
24
|
+
}: Readonly<CheckboxGroupProps>) {
|
|
25
|
+
const id = useId();
|
|
26
|
+
const hintId = id + 'hint';
|
|
27
|
+
const errorId = id + 'error';
|
|
28
|
+
const describedBy = [error && errorId, hint && hintId].filter(Boolean).join(' ') || undefined;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<fieldset
|
|
32
|
+
className={cn(css.group, className)}
|
|
33
|
+
disabled={disabled}
|
|
34
|
+
aria-required={required || undefined}
|
|
35
|
+
aria-describedby={describedBy}
|
|
36
|
+
>
|
|
37
|
+
<legend className={css.legend}>
|
|
38
|
+
{legend}
|
|
39
|
+
{required && <span className={css.required} aria-hidden="true"> *</span>}
|
|
40
|
+
</legend>
|
|
41
|
+
<div className={css.items}>{children}</div>
|
|
42
|
+
<Message inputId={id} type="error" message={error} />
|
|
43
|
+
<Message inputId={id} type="hint" message={hint} />
|
|
44
|
+
</fieldset>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -13,22 +13,31 @@
|
|
|
13
13
|
.input {
|
|
14
14
|
appearance: none;
|
|
15
15
|
width: 100%;
|
|
16
|
-
font-family: var(--font_family--body);
|
|
17
|
-
font-size: var(--font_size--body);
|
|
18
|
-
padding: var(--space_s);
|
|
19
|
-
padding-inline-end: var(--space_xxl);
|
|
20
|
-
border:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
font-family: var(--bds-font_family--body);
|
|
17
|
+
font-size: var(--bds-font_size--body);
|
|
18
|
+
padding: var(--bds-space_s);
|
|
19
|
+
padding-inline-end: var(--bds-space_xxl);
|
|
20
|
+
border: var(--combobox_border, none);
|
|
21
|
+
--bdc_color: currentcolor;
|
|
22
|
+
--bdc_radius: var(--bds-border_radius--xs);
|
|
23
|
+
|
|
24
|
+
outline: var(--bdc_width) solid var(--bdc_color);
|
|
25
|
+
outline-offset: var(--bdc_offset);
|
|
26
|
+
border-radius: var(--combobox_radius, var(--bdc_radius));
|
|
27
|
+
box-shadow: var(--combobox_shadow, var(--bdc_shadow));
|
|
28
|
+
background-color: var(--combobox_color_bg, var(--bds-bg));
|
|
29
|
+
color: var(--combobox_color, var(--bds-on-bg));
|
|
30
|
+
transition: --bdc_color var(--bds-animation_transition-duration) var(--bds-animation_easing),
|
|
31
|
+
background-color var(--bds-animation_transition-duration) var(--bds-animation_easing);
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
.input:focus {
|
|
28
|
-
--
|
|
35
|
+
--bdc_color: var(--bdc_color--focus);
|
|
36
|
+
}
|
|
29
37
|
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
.input:focus-visible {
|
|
39
|
+
outline: var(--bds-outline_default);
|
|
40
|
+
outline-offset: var(--bds-outline_offset);
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
.input:disabled {
|
|
@@ -37,20 +46,16 @@
|
|
|
37
46
|
}
|
|
38
47
|
|
|
39
48
|
.inputError {
|
|
40
|
-
--
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
.inputError:focus {
|
|
44
|
-
--combobox_focus-ring: 0 0 0 2px rgb(from var(--color_error) r g b / 20%);
|
|
49
|
+
--bdc_color: var(--bdc_color--error);
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
.chevron {
|
|
48
53
|
position: absolute;
|
|
49
|
-
inset-inline-end: var(--space_s);
|
|
54
|
+
inset-inline-end: var(--bds-space_s);
|
|
50
55
|
display: flex;
|
|
51
56
|
align-items: center;
|
|
52
57
|
pointer-events: none;
|
|
53
|
-
color: var(--combobox_color, var(--
|
|
58
|
+
color: var(--combobox_color, var(--bds-on-bg));
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
.chevron svg {
|
|
@@ -60,39 +65,44 @@
|
|
|
60
65
|
|
|
61
66
|
.listbox {
|
|
62
67
|
position: absolute;
|
|
63
|
-
top: calc(100% + var(--space_xxs));
|
|
68
|
+
top: calc(100% + var(--bds-space_xxs));
|
|
64
69
|
left: 0;
|
|
65
70
|
right: 0;
|
|
66
|
-
z-index: var(--z-index_dropdown);
|
|
71
|
+
z-index: var(--bds-z-index_dropdown);
|
|
67
72
|
max-height: 15rem;
|
|
68
73
|
overflow-y: auto;
|
|
69
74
|
list-style: none;
|
|
70
75
|
margin: 0;
|
|
71
|
-
padding: var(--space_xxs) 0;
|
|
72
|
-
border
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
padding: var(--bds-space_xxs) 0;
|
|
77
|
+
border: var(--combobox_listbox-border, none);
|
|
78
|
+
--bdc_color: currentcolor;
|
|
79
|
+
--bdc_radius: var(--bds-border_radius--xs);
|
|
80
|
+
|
|
81
|
+
outline: var(--bdc_width) solid var(--bdc_color);
|
|
82
|
+
outline-offset: var(--bdc_offset);
|
|
83
|
+
border-radius: var(--combobox_listbox-radius, var(--bdc_radius));
|
|
84
|
+
background-color: var(--combobox_color, var(--bds-bg));
|
|
85
|
+
color: var(--combobox_on-color, var(--bds-on-bg));
|
|
86
|
+
box-shadow: var(--combobox_listbox-shadow, var(--bds-shadow_m));
|
|
77
87
|
}
|
|
78
88
|
|
|
79
89
|
.option {
|
|
80
90
|
display: flex;
|
|
81
91
|
align-items: center;
|
|
82
|
-
padding: var(--space_xs) var(--space_m);
|
|
83
|
-
font-size: var(--font_size--body);
|
|
84
|
-
color: var(--combobox_color, var(--
|
|
92
|
+
padding: var(--bds-space_xs) var(--bds-space_m);
|
|
93
|
+
font-size: var(--bds-font_size--body);
|
|
94
|
+
color: var(--combobox_color, var(--bds-on-bg));
|
|
85
95
|
cursor: pointer;
|
|
86
|
-
transition: var(--animation_transition);
|
|
96
|
+
transition: var(--bds-animation_transition);
|
|
87
97
|
}
|
|
88
98
|
|
|
89
99
|
.option.--highlighted {
|
|
90
|
-
background-color: var(--
|
|
91
|
-
color: var(--
|
|
100
|
+
background-color: var(--bds-bg--subtle);
|
|
101
|
+
color: var(--bds-interactive);
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
.option.--selected {
|
|
95
|
-
font-weight: var(--font_weight--semibold);
|
|
105
|
+
font-weight: var(--bds-font_weight--semibold);
|
|
96
106
|
}
|
|
97
107
|
|
|
98
108
|
.option.--disabled {
|
|
@@ -4,24 +4,32 @@
|
|
|
4
4
|
flex-direction: column;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
.fieldLabel {
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
font-family: var(--bds-font_family--body);
|
|
11
|
+
color: var(--bds-on-bg);
|
|
12
|
+
margin-block-end: var(--bds-space_xs);
|
|
13
|
+
}
|
|
14
|
+
|
|
7
15
|
.dropZone {
|
|
8
16
|
display: flex;
|
|
9
17
|
flex-direction: column;
|
|
10
18
|
align-items: center;
|
|
11
19
|
justify-content: center;
|
|
12
|
-
gap: var(--space_xs);
|
|
13
|
-
padding: var(--space_l) var(--space_m);
|
|
20
|
+
gap: var(--bds-space_xs);
|
|
21
|
+
padding: var(--bds-space_l) var(--bds-space_m);
|
|
14
22
|
border: 2px dashed currentcolor;
|
|
15
|
-
border-radius: var(--border_radius--s);
|
|
16
|
-
color: var(--fileInput_color, var(--
|
|
23
|
+
border-radius: var(--bds-border_radius--s);
|
|
24
|
+
color: var(--fileInput_color, var(--bds-on-bg));
|
|
17
25
|
cursor: pointer;
|
|
18
26
|
text-align: center;
|
|
19
|
-
transition: var(--animation_transition);
|
|
27
|
+
transition: var(--bds-animation_transition);
|
|
20
28
|
}
|
|
21
29
|
|
|
22
30
|
.dropZone:focus-within {
|
|
23
|
-
outline: var(--outline_default);
|
|
24
|
-
outline-offset: var(--outline_offset);
|
|
31
|
+
outline: var(--bds-outline_default);
|
|
32
|
+
outline-offset: var(--bds-outline_offset);
|
|
25
33
|
border-color: var(--fileInput_border-color, currentcolor);
|
|
26
34
|
}
|
|
27
35
|
|
|
@@ -31,7 +39,7 @@
|
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
.hasError {
|
|
34
|
-
border-color: var(--
|
|
42
|
+
border-color: var(--bds-error);
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
.isDisabled {
|
|
@@ -46,18 +54,18 @@
|
|
|
46
54
|
}
|
|
47
55
|
|
|
48
56
|
.prompt {
|
|
49
|
-
font-size: var(--font_size--body);
|
|
50
|
-
color: var(--
|
|
57
|
+
font-size: var(--bds-font_size--body);
|
|
58
|
+
color: var(--bds-on-bg);
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
.prompt strong {
|
|
54
|
-
color: var(--
|
|
55
|
-
font-weight: var(--font_weight--semibold);
|
|
62
|
+
color: var(--bds-interactive);
|
|
63
|
+
font-weight: var(--bds-font_weight--semibold);
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
.acceptHint {
|
|
59
|
-
font-size: var(--font_size--body--s);
|
|
60
|
-
color: var(--
|
|
67
|
+
font-size: var(--bds-font_size--body--s);
|
|
68
|
+
color: var(--bds-on-bg);
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
.hiddenInput {
|
|
@@ -72,8 +80,8 @@
|
|
|
72
80
|
|
|
73
81
|
@media (hover: hover) and (pointer: fine) {
|
|
74
82
|
.dropZone:not(.isDisabled):hover {
|
|
75
|
-
border-color: var(--
|
|
76
|
-
background-color: rgb(from var(--
|
|
83
|
+
border-color: var(--bds-interactive);
|
|
84
|
+
background-color: rgb(from var(--bds-interactive) r g b / 4%);
|
|
77
85
|
}
|
|
78
86
|
}
|
|
79
87
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useId, useRef, useState, DragEvent, ChangeEvent } from 'react';
|
|
2
2
|
import css from './FileInput.module.css';
|
|
3
|
-
import { Label } from '../atoms/Label';
|
|
4
3
|
import { Message } from '../atoms/Message';
|
|
5
4
|
import { InputContainer } from '../atoms/InputContainer';
|
|
6
5
|
import { cn } from '@boostdev/design-system-foundation';
|
|
@@ -11,6 +10,7 @@ interface FileInputProps {
|
|
|
11
10
|
accept?: string;
|
|
12
11
|
multiple?: boolean;
|
|
13
12
|
disabled?: boolean;
|
|
13
|
+
required?: boolean;
|
|
14
14
|
error?: string;
|
|
15
15
|
hint?: string;
|
|
16
16
|
onChange?: (files: FileList | null) => void;
|
|
@@ -23,6 +23,7 @@ export function FileInput({
|
|
|
23
23
|
accept,
|
|
24
24
|
multiple = false,
|
|
25
25
|
disabled = false,
|
|
26
|
+
required,
|
|
26
27
|
error,
|
|
27
28
|
hint,
|
|
28
29
|
onChange,
|
|
@@ -73,7 +74,6 @@ export function FileInput({
|
|
|
73
74
|
|
|
74
75
|
return (
|
|
75
76
|
<InputContainer className={cn(css.formGroup, className)}>
|
|
76
|
-
<Label id={uid} label={label} />
|
|
77
77
|
<label
|
|
78
78
|
htmlFor={uid}
|
|
79
79
|
className={cn(css.dropZone, isDragging && css.isDragging, error && css.hasError, disabled && css.isDisabled)}
|
|
@@ -81,6 +81,7 @@ export function FileInput({
|
|
|
81
81
|
onDragOver={handleDragOver}
|
|
82
82
|
onDragLeave={handleDragLeave}
|
|
83
83
|
>
|
|
84
|
+
<span className={css.fieldLabel}>{label}</span>
|
|
84
85
|
<svg aria-hidden="true" className={css.icon} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
|
|
85
86
|
<path strokeLinecap="round" strokeLinejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" />
|
|
86
87
|
</svg>
|
|
@@ -101,6 +102,8 @@ export function FileInput({
|
|
|
101
102
|
disabled={disabled}
|
|
102
103
|
aria-invalid={!!error}
|
|
103
104
|
aria-describedby={describedBy}
|
|
105
|
+
aria-required={required || undefined}
|
|
106
|
+
required={required}
|
|
104
107
|
className={css.hiddenInput}
|
|
105
108
|
onChange={handleChange}
|
|
106
109
|
/>
|
|
@@ -7,28 +7,33 @@
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
.input {
|
|
10
|
-
font-family: var(--font_family--body);
|
|
11
|
-
font-size: var(--font_size--body);
|
|
12
|
-
padding: var(--space_s);
|
|
13
|
-
border:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
font-family: var(--bds-font_family--body);
|
|
11
|
+
font-size: var(--bds-font_size--body);
|
|
12
|
+
padding: var(--bds-space_s);
|
|
13
|
+
border: var(--input_border, none);
|
|
14
|
+
--bdc_color: currentcolor;
|
|
15
|
+
--bdc_radius: var(--bds-border_radius--xs);
|
|
16
|
+
|
|
17
|
+
outline: var(--bdc_width) solid var(--bdc_color);
|
|
18
|
+
outline-offset: var(--bdc_offset);
|
|
19
|
+
border-radius: var(--input_radius, var(--bdc_radius));
|
|
20
|
+
box-shadow: var(--input_shadow, var(--bdc_shadow));
|
|
21
|
+
color: var(--input_color, var(--bds-on-bg));
|
|
22
|
+
background-color: var(--input_color_bg, var(--bds-bg));
|
|
23
|
+
transition: --bdc_color var(--bds-animation_transition-duration) var(--bds-animation_easing),
|
|
24
|
+
background-color var(--bds-animation_transition-duration) var(--bds-animation_easing);
|
|
18
25
|
}
|
|
19
26
|
|
|
20
27
|
.input:focus {
|
|
21
|
-
--
|
|
22
|
-
|
|
23
|
-
outline: none;
|
|
24
|
-
box-shadow: var(--input_focus-ring, 0 0 0 2px rgb(from var(--color_interactive) r g b / 20%));
|
|
28
|
+
--bdc_color: var(--bdc_color--focus);
|
|
25
29
|
}
|
|
26
30
|
|
|
27
|
-
.
|
|
28
|
-
|
|
31
|
+
.input:focus-visible {
|
|
32
|
+
outline: var(--bds-outline_default);
|
|
33
|
+
outline-offset: var(--bds-outline_offset);
|
|
29
34
|
}
|
|
30
35
|
|
|
31
|
-
.inputError
|
|
32
|
-
--
|
|
36
|
+
.inputError {
|
|
37
|
+
--bdc_color: var(--bdc_color--error);
|
|
33
38
|
}
|
|
34
39
|
}
|
|
@@ -22,6 +22,7 @@ export function FormInput({
|
|
|
22
22
|
error,
|
|
23
23
|
hint,
|
|
24
24
|
className,
|
|
25
|
+
required,
|
|
25
26
|
...props
|
|
26
27
|
}: FormInputProps) {
|
|
27
28
|
const id = name + useId();
|
|
@@ -36,8 +37,10 @@ export function FormInput({
|
|
|
36
37
|
aria-invalid={!!error}
|
|
37
38
|
aria-describedby={describedBy}
|
|
38
39
|
aria-label={ariaLabel}
|
|
40
|
+
aria-required={required || undefined}
|
|
39
41
|
id={id}
|
|
40
42
|
name={name}
|
|
43
|
+
required={required}
|
|
41
44
|
className={cn(css.input, error && css.inputError)}
|
|
42
45
|
{...props}
|
|
43
46
|
/>
|
|
@@ -7,27 +7,37 @@
|
|
|
7
7
|
.inputRow {
|
|
8
8
|
display: flex;
|
|
9
9
|
align-items: stretch;
|
|
10
|
-
border:
|
|
11
|
-
|
|
10
|
+
border: var(--numberInput_border, none);
|
|
11
|
+
--bdc_color: currentcolor;
|
|
12
|
+
--bdc_radius: var(--bds-border_radius--xs);
|
|
13
|
+
|
|
14
|
+
outline: var(--bdc_width) solid var(--bdc_color);
|
|
15
|
+
outline-offset: var(--bdc_offset);
|
|
16
|
+
border-radius: var(--numberInput_radius, var(--bdc_radius));
|
|
17
|
+
box-shadow: var(--numberInput_shadow, var(--bdc_shadow));
|
|
12
18
|
overflow: hidden;
|
|
13
|
-
transition: var(--animation_transition);
|
|
19
|
+
transition: --bdc_color var(--bds-animation_transition-duration) var(--bds-animation_easing);
|
|
14
20
|
}
|
|
15
21
|
|
|
16
22
|
.inputRow:focus-within {
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
--bdc_color: var(--bdc_color--focus);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.inputRow:focus-within:has(:focus-visible) {
|
|
27
|
+
outline: var(--bds-outline_default);
|
|
28
|
+
outline-offset: var(--bds-outline_offset);
|
|
19
29
|
}
|
|
20
30
|
|
|
21
31
|
.input {
|
|
22
32
|
flex: 1;
|
|
23
33
|
border: none;
|
|
24
34
|
outline: none;
|
|
25
|
-
font-family: var(--font_family--body);
|
|
26
|
-
font-size: var(--font_size--body);
|
|
35
|
+
font-family: var(--bds-font_family--body);
|
|
36
|
+
font-size: var(--bds-font_size--body);
|
|
27
37
|
text-align: center;
|
|
28
|
-
padding: var(--space_s);
|
|
38
|
+
padding: var(--bds-space_s);
|
|
29
39
|
background: transparent;
|
|
30
|
-
color: var(--numberInput_color, var(--
|
|
40
|
+
color: var(--numberInput_color, var(--bds-on-bg));
|
|
31
41
|
min-width: 0;
|
|
32
42
|
appearance: textfield;
|
|
33
43
|
}
|
|
@@ -38,11 +48,7 @@
|
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
.inputError {
|
|
41
|
-
--
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.inputError:focus-within {
|
|
45
|
-
--numberInput_focus-ring: 0 0 0 2px rgb(from var(--color_error) r g b / 20%);
|
|
51
|
+
--bdc_color: var(--bdc_color--error);
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
.stepper {
|
|
@@ -50,11 +56,11 @@
|
|
|
50
56
|
display: flex;
|
|
51
57
|
align-items: center;
|
|
52
58
|
justify-content: center;
|
|
53
|
-
padding: var(--space_xs);
|
|
54
|
-
background-color: var(--
|
|
55
|
-
color: var(--numberInput_color, var(--
|
|
59
|
+
padding: var(--bds-space_xs);
|
|
60
|
+
background-color: var(--bds-bg--subtle);
|
|
61
|
+
color: var(--numberInput_color, var(--bds-on-bg));
|
|
56
62
|
cursor: pointer;
|
|
57
|
-
transition: var(--animation_transition);
|
|
63
|
+
transition: var(--bds-animation_transition);
|
|
58
64
|
flex-shrink: 0;
|
|
59
65
|
}
|
|
60
66
|
|
|
@@ -70,7 +76,7 @@
|
|
|
70
76
|
|
|
71
77
|
@media (hover: hover) and (pointer: fine) {
|
|
72
78
|
.stepper:not(:disabled):hover {
|
|
73
|
-
background-color: var(--
|
|
79
|
+
background-color: var(--bds-bg--subtle);
|
|
74
80
|
opacity: 0.7;
|
|
75
81
|
}
|
|
76
82
|
}
|