@boostdev/design-system-components 2.16.0 → 2.18.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 +5 -2
- package/dist/client.cjs +57 -57
- package/dist/client.css +641 -631
- package/dist/client.js +57 -57
- package/dist/index.cjs +57 -57
- package/dist/index.css +641 -631
- package/dist/index.js +57 -57
- package/dist/web-components/{chunk-XI2U6U26.js → chunk-2FNCV46J.js} +4 -5
- package/dist/web-components/{chunk-SYNLFKD3.js → chunk-3LQKFSMR.js} +6 -0
- package/dist/web-components/{chunk-RZH5IVB5.js → chunk-5ATTKUCP.js} +2 -2
- package/dist/web-components/{chunk-XJU7KITO.js → chunk-5QA7JOC3.js} +4 -5
- package/dist/web-components/{chunk-EYCW7TKB.js → chunk-7GEAT2TY.js} +4 -5
- package/dist/web-components/chunk-FYTISJN3.js +18 -0
- package/dist/web-components/{chunk-LHFUXZ52.js → chunk-ISV56UWR.js} +4 -8
- package/dist/web-components/{chunk-YLDQOESI.js → chunk-K3VK6VEL.js} +6 -5
- package/dist/web-components/{chunk-GQNQBEYR.js → chunk-QZMFNJSN.js} +4 -5
- package/dist/web-components/{chunk-BPYKZVDK.js → chunk-SJLMW2VR.js} +2 -2
- package/dist/web-components/{chunk-GPJWLAKU.js → chunk-VGPJ5L6N.js} +6 -5
- package/dist/web-components/{chunk-AGI6YQRC.js → chunk-WOFGYU6E.js} +4 -5
- package/dist/web-components/{chunk-2MJTO2IW.js → chunk-WWWJCSXN.js} +2 -2
- package/dist/web-components/index.js +13 -12
- package/dist/web-components/interaction/form/bds-checkbox-group.js +1 -1
- package/dist/web-components/interaction/form/bds-combobox.js +2 -1
- package/dist/web-components/interaction/form/bds-form-input.js +2 -1
- package/dist/web-components/interaction/form/bds-number-input.js +2 -1
- package/dist/web-components/interaction/form/bds-radio-group.js +1 -1
- package/dist/web-components/interaction/form/bds-select.js +2 -1
- package/dist/web-components/interaction/form/bds-slider.js +2 -1
- package/dist/web-components/interaction/form/bds-textarea.js +2 -1
- package/dist/web-components/ui/bds-alert.js +1 -1
- package/dist/web-components/ui/bds-badge.js +1 -1
- package/dist/web-components/ui/bds-notification-banner.js +1 -1
- package/dist/web-components/ui/bds-rating.js +1 -1
- package/package.json +1 -1
- package/src/components/interaction/Rating/Rating.mdx +4 -1
- package/src/components/interaction/Rating/Rating.module.css +9 -0
- package/src/components/interaction/Rating/Rating.spec.tsx +9 -0
- package/src/components/interaction/form/Checkbox/Checkbox.tsx +1 -1
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.mdx +1 -1
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.spec.tsx +5 -0
- package/src/components/interaction/form/Combobox/Combobox.mdx +2 -0
- package/src/components/interaction/form/FormInput/FormInput.mdx +4 -0
- package/src/components/interaction/form/NumberInput/NumberInput.mdx +2 -0
- package/src/components/interaction/form/Radio/Radio.tsx +1 -1
- package/src/components/interaction/form/RadioGroup/RadioGroup.mdx +1 -1
- package/src/components/interaction/form/RadioGroup/RadioGroup.spec.tsx +5 -0
- package/src/components/interaction/form/Select/Select.mdx +2 -0
- package/src/components/interaction/form/Slider/Slider.mdx +2 -0
- package/src/components/interaction/form/Switch/Switch.tsx +1 -1
- package/src/components/interaction/form/Textarea/Textarea.mdx +2 -0
- package/src/components/interaction/form/atoms/Label.module.css +10 -0
- package/src/components/interaction/form/atoms/Label.spec.tsx +48 -0
- package/src/components/interaction/form/atoms/Label.stories.tsx +3 -0
- package/src/components/interaction/form/atoms/Label.tsx +4 -2
- package/src/components/ui/Alert/Alert.mdx +1 -1
- package/src/components/ui/Alert/Alert.module.css +2 -2
- package/src/components/ui/Alert/Alert.spec.tsx +6 -0
- package/src/components/ui/Badge/Badge.mdx +1 -1
- package/src/components/ui/Badge/Badge.module.css +2 -2
- package/src/components/ui/Badge/Badge.spec.tsx +8 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.mdx +13 -1
- package/src/components/ui/NotificationBanner/NotificationBanner.module.css +2 -2
- package/src/components/ui/NotificationBanner/NotificationBanner.spec.tsx +6 -0
- package/src/test/token-contract.spec.ts +1 -0
- package/src/web-components/interaction/form/BdsCheckboxGroup.mdx +14 -0
- package/src/web-components/interaction/form/BdsCombobox.mdx +4 -0
- package/src/web-components/interaction/form/BdsFormInput.mdx +4 -0
- package/src/web-components/interaction/form/BdsNumberInput.mdx +4 -0
- package/src/web-components/interaction/form/BdsRadioGroup.mdx +11 -1
- package/src/web-components/interaction/form/BdsSelect.mdx +4 -0
- package/src/web-components/interaction/form/BdsSlider.mdx +4 -0
- package/src/web-components/interaction/form/BdsTextarea.mdx +4 -0
- package/src/web-components/interaction/form/bds-checkbox-group.spec.ts +11 -1
- package/src/web-components/interaction/form/bds-checkbox-group.ts +6 -5
- package/src/web-components/interaction/form/bds-combobox.ts +2 -5
- package/src/web-components/interaction/form/bds-form-input.ts +2 -5
- package/src/web-components/interaction/form/bds-number-input.ts +2 -5
- package/src/web-components/interaction/form/bds-radio-group.spec.ts +11 -1
- package/src/web-components/interaction/form/bds-radio-group.ts +6 -5
- package/src/web-components/interaction/form/bds-select.ts +2 -5
- package/src/web-components/interaction/form/bds-slider.ts +2 -8
- package/src/web-components/interaction/form/bds-textarea.ts +2 -5
- package/src/web-components/interaction/form/field-label.spec.ts +42 -0
- package/src/web-components/interaction/form/field-label.ts +20 -0
- package/src/web-components/test/helpers.ts +20 -0
- package/src/web-components/ui/BdsAlert.mdx +1 -0
- package/src/web-components/ui/BdsBadge.mdx +1 -0
- package/src/web-components/ui/BdsNotificationBanner.mdx +1 -1
- package/src/web-components/ui/BdsRating.mdx +4 -1
- package/src/web-components/ui/bds-alert.spec.ts +7 -0
- package/src/web-components/ui/bds-alert.ts +2 -2
- package/src/web-components/ui/bds-badge.spec.ts +6 -0
- package/src/web-components/ui/bds-badge.ts +2 -2
- package/src/web-components/ui/bds-notification-banner.spec.ts +7 -0
- package/src/web-components/ui/bds-notification-banner.ts +2 -2
- package/src/web-components/ui/bds-rating.spec.ts +7 -0
- package/src/web-components/ui/bds-rating.ts +6 -0
|
@@ -6,4 +6,14 @@
|
|
|
6
6
|
color: var(--bds-color_on-bg);
|
|
7
7
|
cursor: pointer;
|
|
8
8
|
}
|
|
9
|
+
|
|
10
|
+
/* The label above a field. Same hooks and defaults as the web component
|
|
11
|
+
field labels (fieldLabelStyles in web-components/interaction/form/field-label.ts),
|
|
12
|
+
so a React form and a bds-* form look the same. */
|
|
13
|
+
.--field {
|
|
14
|
+
font-family: var(--formLabel_font-family, var(--bds-font_family--body));
|
|
15
|
+
font-size: var(--formLabel_font-size, var(--bds-font_size--body--s));
|
|
16
|
+
font-weight: var(--formLabel_font-weight, var(--bds-font_weight--semibold));
|
|
17
|
+
color: var(--formLabel_color, var(--bds-color_on-bg));
|
|
18
|
+
}
|
|
9
19
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { Label } from './Label';
|
|
3
|
+
import { FormInput } from '../FormInput';
|
|
4
|
+
import { Select } from '../Select';
|
|
5
|
+
import { Checkbox } from '../Checkbox';
|
|
6
|
+
import { Radio } from '../Radio';
|
|
7
|
+
import { Switch } from '../Switch';
|
|
8
|
+
|
|
9
|
+
describe('Label', () => {
|
|
10
|
+
it('labels the control with the given id', () => {
|
|
11
|
+
render(<Label id="email" label="Email" />);
|
|
12
|
+
expect(screen.getByText('Email').getAttribute('for')).toBe('email');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('uses the field look by default', () => {
|
|
16
|
+
render(<Label id="email" label="Email" />);
|
|
17
|
+
expect(screen.getByText('Email').className).toContain('--field');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('drops the field look for an option label', () => {
|
|
21
|
+
render(<Label id="terms" label="Accept terms" variant="option" />);
|
|
22
|
+
expect(screen.getByText('Accept terms').className).not.toContain('--field');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('gives field labels the field look', () => {
|
|
26
|
+
render(
|
|
27
|
+
<>
|
|
28
|
+
<FormInput label="Email" name="email" />
|
|
29
|
+
<Select label="Country" name="country" options={[{ value: 'nl', label: 'Netherlands' }]} />
|
|
30
|
+
</>
|
|
31
|
+
);
|
|
32
|
+
expect(screen.getByText('Email').className).toContain('--field');
|
|
33
|
+
expect(screen.getByText('Country').className).toContain('--field');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('keeps checkbox, radio and switch labels on the option look', () => {
|
|
37
|
+
render(
|
|
38
|
+
<>
|
|
39
|
+
<Checkbox label="Accept terms" name="terms" />
|
|
40
|
+
<Radio label="Pick up" name="delivery" value="pickup" />
|
|
41
|
+
<Switch label="Notifications" name="notifications" />
|
|
42
|
+
</>
|
|
43
|
+
);
|
|
44
|
+
['Accept terms', 'Pick up', 'Notifications'].forEach(text =>
|
|
45
|
+
expect(screen.getByText(text).className).not.toContain('--field')
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -22,12 +22,15 @@ export const Required: Story = {
|
|
|
22
22
|
},
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
export const Option: Story = { args: { id: 'input-option', label: 'Accept the terms', variant: 'option' } };
|
|
26
|
+
|
|
25
27
|
export const AllVariants: Story = {
|
|
26
28
|
render: () => (
|
|
27
29
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
|
|
28
30
|
<Label id="a" label="Plain label" />
|
|
29
31
|
<Label id="b" label="Label for a text input" />
|
|
30
32
|
<Label id="c" label={<><strong>Bold</strong> label with markup</>} />
|
|
33
|
+
<Label id="d" label="Option label beside a checkbox" variant="option" />
|
|
31
34
|
</div>
|
|
32
35
|
),
|
|
33
36
|
};
|
|
@@ -6,11 +6,13 @@ import type { WithClassName } from '../../../../types';
|
|
|
6
6
|
interface LabelProps extends WithClassName {
|
|
7
7
|
id: string;
|
|
8
8
|
label: ReactNode;
|
|
9
|
+
/** `field` is the label above a control, `option` the text beside a checkbox, radio or switch. */
|
|
10
|
+
variant?: 'field' | 'option';
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
export const Label = ({ label, id, className }: LabelProps) => {
|
|
13
|
+
export const Label = ({ label, id, className, variant = 'field' }: LabelProps) => {
|
|
12
14
|
return (
|
|
13
|
-
<label htmlFor={id} className={cn(css.label, className)}>
|
|
15
|
+
<label htmlFor={id} className={cn(css.label, variant === 'field' && css['--field'], className)}>
|
|
14
16
|
{label}
|
|
15
17
|
</label>
|
|
16
18
|
);
|
|
@@ -116,7 +116,7 @@ Each variant's actual default resolution:
|
|
|
116
116
|
<tr><td>`info`</td><td>`--bds-color_blue--subtle`</td><td>`--bds-color_on-blue--subtle`</td><td>`--bds-color_blue`</td></tr>
|
|
117
117
|
<tr><td>`success`</td><td>`--bds-color_success--subtle`</td><td>`--bds-color_on-success--subtle`</td><td>`--bds-color_success`</td></tr>
|
|
118
118
|
<tr><td>`warning`</td><td>`--bds-color_orange--subtle`</td><td>`--bds-color_on-orange--subtle`</td><td>`--bds-color_warning`</td></tr>
|
|
119
|
-
<tr><td>`error`</td><td
|
|
119
|
+
<tr><td>`error`</td><td>`--bds-color_error--subtle`</td><td>`--bds-color_on-error--subtle`</td><td>`--bds-color_error`</td></tr>
|
|
120
120
|
</tbody>
|
|
121
121
|
</table>
|
|
122
122
|
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
.--variant_error {
|
|
43
|
-
--alert_bg--variant: var(--alert_bg--error,
|
|
44
|
-
--alert_text--variant: var(--alert_text--error, var(--bds-color_on-
|
|
43
|
+
--alert_bg--variant: var(--alert_bg--error, var(--bds-color_error--subtle));
|
|
44
|
+
--alert_text--variant: var(--alert_text--error, var(--bds-color_on-error--subtle));
|
|
45
45
|
--alert_border--variant: var(--alert_border--error, var(--bds-color_error));
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -101,4 +101,10 @@ describe('Alert', () => {
|
|
|
101
101
|
expect(cssSource).toContain(`--alert_accent--${v}`);
|
|
102
102
|
});
|
|
103
103
|
});
|
|
104
|
+
|
|
105
|
+
it('paints the error variant with the error subtle pair, like the other variants', () => {
|
|
106
|
+
expect(cssSource).toMatch(/\.--variant_error \{[^}]*--alert_bg--variant:\s*var\(--alert_bg--error,\s*var\(--bds-color_error--subtle\)\)/);
|
|
107
|
+
expect(cssSource).toMatch(/\.--variant_error \{[^}]*--alert_text--variant:\s*var\(--alert_text--error,\s*var\(--bds-color_on-error--subtle\)\)/);
|
|
108
|
+
expect(cssSource).not.toContain('rgb(from var(--bds-color_error)');
|
|
109
|
+
});
|
|
104
110
|
});
|
|
@@ -75,7 +75,7 @@ The override surface is **layered**. Setting a public token on the badge (or any
|
|
|
75
75
|
</tbody>
|
|
76
76
|
</table>
|
|
77
77
|
|
|
78
|
-
Defaults resolve to foundation tokens per variant: `success`/`error`/`warning` map directly to `--bds-color_{variant}` / `--bds-color_on-{variant}`. `primary` maps to `--bds-color_interactive` / `--bds-color_on-interactive`, and `secondary` maps to `--bds-
|
|
78
|
+
Defaults resolve to foundation tokens per variant: `success`/`error`/`warning` map directly to `--bds-color_{variant}` / `--bds-color_on-{variant}`. `primary` maps to `--bds-color_interactive` / `--bds-color_on-interactive`, and `secondary` maps to the neutral `--bds-color_grey` / `--bds-color_on-grey`, so it has a solid surface like the other variants. Neither follows the generic `{variant}` naming.
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
## Accessibility
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
.--variant_secondary {
|
|
25
|
-
--badge_bg--variant: var(--badge_bg--secondary, var(--bds-
|
|
26
|
-
--badge_text--variant: var(--badge_text--secondary, var(--bds-color_on-
|
|
25
|
+
--badge_bg--variant: var(--badge_bg--secondary, var(--bds-color_grey));
|
|
26
|
+
--badge_text--variant: var(--badge_text--secondary, var(--bds-color_on-grey));
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
.--variant_success {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
1
3
|
import { render, screen } from '@testing-library/react';
|
|
2
4
|
import { Badge } from './Badge';
|
|
3
5
|
|
|
@@ -16,4 +18,10 @@ describe('Badge', () => {
|
|
|
16
18
|
render(<Badge className="custom">Test</Badge>);
|
|
17
19
|
expect(screen.getByText('Test')).toHaveClass('custom');
|
|
18
20
|
});
|
|
21
|
+
|
|
22
|
+
it('gives the secondary variant a solid neutral surface through its own hooks', () => {
|
|
23
|
+
const css = readFileSync(resolve(process.cwd(), 'src/components/ui/Badge/Badge.module.css'), 'utf8');
|
|
24
|
+
expect(css).toMatch(/\.--variant_secondary \{[^}]*--badge_bg--variant:\s*var\(--badge_bg--secondary,\s*var\(--bds-color_grey\)\)/);
|
|
25
|
+
expect(css).toMatch(/\.--variant_secondary \{[^}]*--badge_text--variant:\s*var\(--badge_text--secondary,\s*var\(--bds-color_on-grey\)\)/);
|
|
26
|
+
});
|
|
19
27
|
});
|
|
@@ -90,7 +90,19 @@ The override surface is **layered**. Setting a public token on the banner (or an
|
|
|
90
90
|
</tbody>
|
|
91
91
|
</table>
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
Each variant's actual default resolution:
|
|
94
|
+
|
|
95
|
+
<table>
|
|
96
|
+
<thead>
|
|
97
|
+
<tr><th>Variant</th><th>Background default</th><th>Text default</th><th>Border default</th></tr>
|
|
98
|
+
</thead>
|
|
99
|
+
<tbody>
|
|
100
|
+
<tr><td>`info`</td><td>`--bds-color_blue--subtle`</td><td>`--bds-color_on-blue--subtle`</td><td>`--bds-color_blue`</td></tr>
|
|
101
|
+
<tr><td>`success`</td><td>`--bds-color_success--subtle`</td><td>`--bds-color_on-success--subtle`</td><td>`--bds-color_success`</td></tr>
|
|
102
|
+
<tr><td>`warning`</td><td>`--bds-color_orange--subtle`</td><td>`--bds-color_on-orange--subtle`</td><td>`--bds-color_warning`</td></tr>
|
|
103
|
+
<tr><td>`error`</td><td>`--bds-color_error--subtle`</td><td>`--bds-color_on-error--subtle`</td><td>`--bds-color_error`</td></tr>
|
|
104
|
+
</tbody>
|
|
105
|
+
</table>
|
|
94
106
|
|
|
95
107
|
|
|
96
108
|
## Accessibility
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
.--variant_error {
|
|
37
|
-
--banner_bg--variant: var(--banner_bg--error,
|
|
38
|
-
--banner_text--variant: var(--banner_text--error, var(--bds-color_on-
|
|
37
|
+
--banner_bg--variant: var(--banner_bg--error, var(--bds-color_error--subtle));
|
|
38
|
+
--banner_text--variant: var(--banner_text--error, var(--bds-color_on-error--subtle));
|
|
39
39
|
--banner_border--variant: var(--banner_border--error, var(--bds-color_error));
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -88,4 +88,10 @@ describe('NotificationBanner', () => {
|
|
|
88
88
|
expect(cssSource).toContain(`--banner_accent--${v}`);
|
|
89
89
|
});
|
|
90
90
|
});
|
|
91
|
+
|
|
92
|
+
it('paints the error variant with the error subtle pair, like the other variants', () => {
|
|
93
|
+
expect(cssSource).toMatch(/\.--variant_error \{[^}]*--banner_bg--variant:\s*var\(--banner_bg--error,\s*var\(--bds-color_error--subtle\)\)/);
|
|
94
|
+
expect(cssSource).toMatch(/\.--variant_error \{[^}]*--banner_text--variant:\s*var\(--banner_text--error,\s*var\(--bds-color_on-error--subtle\)\)/);
|
|
95
|
+
expect(cssSource).not.toContain('rgb(from var(--bds-color_error)');
|
|
96
|
+
});
|
|
91
97
|
});
|
|
@@ -96,6 +96,7 @@ const NON_TEXT_MARKS: { file: string; rule: string }[] = [
|
|
|
96
96
|
{ file: 'components/ui/Accordion/Accordion.module.css', rule: '.item' },
|
|
97
97
|
{ file: 'components/ui/Tabs/Tabs.module.css', rule: '.tabList' },
|
|
98
98
|
{ file: 'components/ui/Table/Table.module.css', rule: '.th' },
|
|
99
|
+
{ file: 'components/interaction/Rating/Rating.module.css', rule: '.star' },
|
|
99
100
|
];
|
|
100
101
|
|
|
101
102
|
describe('non-text marks use the as-surface member of the colour set', () => {
|
|
@@ -53,6 +53,20 @@ Use `<bds-checkbox-group>` when you have multiple related checkboxes that belong
|
|
|
53
53
|
|-------------|-----------------------------------|
|
|
54
54
|
| `(default)` | `<bds-checkbox>` elements |
|
|
55
55
|
|
|
56
|
+
## Overridable CSS custom properties
|
|
57
|
+
|
|
58
|
+
Legend hooks mirror the React `CheckboxGroup` (same names and defaults), so a theme carries over between the two implementations. Set them on the group or any ancestor.
|
|
59
|
+
|
|
60
|
+
| Variable | Default | Description |
|
|
61
|
+
|----------|---------|-------------|
|
|
62
|
+
| `--checkboxGroup_legend-color` | `var(--bds-color_on-bg)` | Legend text colour. |
|
|
63
|
+
| `--checkboxGroup_legend-font-family` | `var(--bds-font_family--body)` | Legend font family. |
|
|
64
|
+
| `--checkboxGroup_legend-font-size` | `var(--bds-font_size--body--s)` | Legend font size, the same as the field labels (`--formLabel_font-size`). |
|
|
65
|
+
| `--checkboxGroup_legend-font-weight` | `var(--bds-font_weight--medium)` | Legend font weight. |
|
|
66
|
+
| `--checkboxGroup_legend-gap` | `var(--bds-space_xs)` | Space below the legend before the items. |
|
|
67
|
+
|
|
68
|
+
Style slotted `<bds-checkbox>` elements via their own tokens.
|
|
69
|
+
|
|
56
70
|
## Usage in plain HTML
|
|
57
71
|
|
|
58
72
|
```html
|
|
@@ -96,6 +96,10 @@ _None._
|
|
|
96
96
|
></bds-combobox>
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
+
## Label
|
|
100
|
+
|
|
101
|
+
The `label` reads the shared label hooks (`--formLabel_*`: font-family, font-size, font-weight, color), listed on `<bds-form-input>`. The React field labels use the same hooks and defaults.
|
|
102
|
+
|
|
99
103
|
## Error state
|
|
100
104
|
|
|
101
105
|
With `error`, the message gets a warning icon and the field ring widens to `--bdc-outline_width--error` (2px). Message hooks (`--formMessage_*`: font-size, gap, icon-size, color--error, font-weight--error, color--hint, font-style--hint) are listed on `<bds-form-input>`.
|
|
@@ -88,6 +88,10 @@ Token names mirror the React `FormInput` component so themes carry over between
|
|
|
88
88
|
| `--formInput_border` | `none` | Border (layered on top of the inset focus/error ring) |
|
|
89
89
|
| `--formInput_radius` | `var(--bds-border_radius--full)` | Corner radius |
|
|
90
90
|
| `--formInput_shadow` | `var(--bdc-outline_shadow)` | Box shadow |
|
|
91
|
+
| `--formLabel_font-family` | `var(--bds-font_family--body)` | Font family of the label above the field (shared by all form controls with a label above the field) |
|
|
92
|
+
| `--formLabel_font-size` | `var(--bds-font_size--body--s)` | Label font size |
|
|
93
|
+
| `--formLabel_font-weight` | `var(--bds-font_weight--semibold)` | Label font weight |
|
|
94
|
+
| `--formLabel_color` | `var(--bds-color_on-bg)` | Label colour |
|
|
91
95
|
| `--formMessage_font-size` | `var(--bds-font_size--body--s)` | Font size of the error and hint message (shared by all form controls) |
|
|
92
96
|
| `--formMessage_gap` | `var(--bds-space_xxs)` | Gap between the error icon and the error text |
|
|
93
97
|
| `--formMessage_icon-size` | `1.15em` | Size of the error icon |
|
|
@@ -73,6 +73,10 @@ Use `<bds-number-input>` for quantity selectors, counters, or any numeric field
|
|
|
73
73
|
<bds-number-input name="qty" label="Quantity" value="1" min="0" max="99" step="1"></bds-number-input>
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
## Label
|
|
77
|
+
|
|
78
|
+
The `label` reads the shared label hooks (`--formLabel_*`: font-family, font-size, font-weight, color), listed on `<bds-form-input>`. The React field labels use the same hooks and defaults.
|
|
79
|
+
|
|
76
80
|
## Error state
|
|
77
81
|
|
|
78
82
|
With `error`, the message gets a warning icon and the field ring widens to `--bdc-outline_width--error` (2px). Message hooks (`--formMessage_*`: font-size, gap, icon-size, color--error, font-weight--error, color--hint, font-style--hint) are listed on `<bds-form-input>`.
|
|
@@ -56,7 +56,17 @@ Use `<bds-radio-group>` whenever you have a set of mutually exclusive options. T
|
|
|
56
56
|
|
|
57
57
|
## Overridable CSS custom properties
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
Legend hooks mirror the React `RadioGroup` (same names and defaults), so a theme carries over between the two implementations. Set them on the group or any ancestor.
|
|
60
|
+
|
|
61
|
+
| Variable | Default | Description |
|
|
62
|
+
|----------|---------|-------------|
|
|
63
|
+
| `--radioGroup_legend-color` | `var(--bds-color_on-bg)` | Legend text colour. |
|
|
64
|
+
| `--radioGroup_legend-font-family` | `var(--bds-font_family--body)` | Legend font family. |
|
|
65
|
+
| `--radioGroup_legend-font-size` | `var(--bds-font_size--body--s)` | Legend font size, the same as the field labels (`--formLabel_font-size`). |
|
|
66
|
+
| `--radioGroup_legend-font-weight` | `var(--bds-font_weight--medium)` | Legend font weight. |
|
|
67
|
+
| `--radioGroup_legend-gap` | `var(--bds-space_xs)` | Space below the legend before the items. |
|
|
68
|
+
|
|
69
|
+
Style slotted `<bds-radio>` elements via their own tokens.
|
|
60
70
|
|
|
61
71
|
## Usage in plain HTML
|
|
62
72
|
|
|
@@ -82,6 +82,10 @@ When `label` is omitted, set `aria-label` on the host element (`<bds-select aria
|
|
|
82
82
|
></bds-select>
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
+
## Label
|
|
86
|
+
|
|
87
|
+
The `label` reads the shared label hooks (`--formLabel_*`: font-family, font-size, font-weight, color), listed on `<bds-form-input>`. The React field labels use the same hooks and defaults.
|
|
88
|
+
|
|
85
89
|
## Error state
|
|
86
90
|
|
|
87
91
|
With `error`, the message gets a warning icon and the field ring widens to `--bdc-outline_width--error` (2px). Message hooks (`--formMessage_*`: font-size, gap, icon-size, color--error, font-weight--error, color--hint, font-style--hint) are listed on `<bds-form-input>`.
|
|
@@ -84,6 +84,10 @@ Use `<bds-slider>` for continuous or stepped numeric ranges — volume controls,
|
|
|
84
84
|
<bds-slider name="volume" label="Volume" value="50" min="0" max="100" step="5" show-value></bds-slider>
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
+
## Label
|
|
88
|
+
|
|
89
|
+
The `label` reads the shared label hooks (`--formLabel_*`: font-family, font-size, font-weight, color), listed on `<bds-form-input>`. The React field labels use the same hooks and defaults.
|
|
90
|
+
|
|
87
91
|
## Error state
|
|
88
92
|
|
|
89
93
|
With `error`, the message gets a warning icon. Message hooks (`--formMessage_*`: font-size, gap, icon-size, color--error, font-weight--error, color--hint, font-style--hint) are listed on `<bds-form-input>`.
|
|
@@ -88,6 +88,10 @@ Use `<bds-textarea>` for multiline text entry — messages, comments, descriptio
|
|
|
88
88
|
<bds-textarea name="feedback" label="Feedback" error="Feedback is required"></bds-textarea>
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
+
## Label
|
|
92
|
+
|
|
93
|
+
The `label` reads the shared label hooks (`--formLabel_*`: font-family, font-size, font-weight, color), listed on `<bds-form-input>`. The React field labels use the same hooks and defaults.
|
|
94
|
+
|
|
91
95
|
## Error state
|
|
92
96
|
|
|
93
97
|
With `error`, the message gets a warning icon and the field ring widens to `--bdc-outline_width--error` (2px). Message hooks (`--formMessage_*`: font-size, gap, icon-size, color--error, font-weight--error, color--hint, font-style--hint) are listed on `<bds-form-input>`.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fixture, cleanup } from '../../test/helpers';
|
|
1
|
+
import { fixture, cleanup, readSource, ruleDeclarations } from '../../test/helpers';
|
|
2
2
|
import { BdsCheckboxGroup } from './bds-checkbox-group';
|
|
3
3
|
|
|
4
4
|
describe('bds-checkbox-group', () => {
|
|
@@ -136,6 +136,16 @@ describe('bds-checkbox-group', () => {
|
|
|
136
136
|
cleanup(el);
|
|
137
137
|
});
|
|
138
138
|
|
|
139
|
+
it('styles the legend through the same hooks and defaults as the React CheckboxGroup', () => {
|
|
140
|
+
const react = ruleDeclarations(readSource('src/components/interaction/form/CheckboxGroup/CheckboxGroup.module.css'), '.legend');
|
|
141
|
+
expect(ruleDeclarations(BdsCheckboxGroup.styles.toString(), '.legend')).toEqual(react);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('sizes the legend like the field labels', () => {
|
|
145
|
+
expect(ruleDeclarations(BdsCheckboxGroup.styles.toString(), '.legend')['font-size'])
|
|
146
|
+
.toBe('var(--checkboxGroup_legend-font-size, var(--bds-font_size--body--s))');
|
|
147
|
+
});
|
|
148
|
+
|
|
139
149
|
it('uses medium font-weight token for the legend (not semibold)', () => {
|
|
140
150
|
const legendRule = BdsCheckboxGroup.styles.toString().match(/\.legend \{[^}]*\}/)?.[0] ?? '';
|
|
141
151
|
expect(legendRule).toContain('--bds-font_weight--medium');
|
|
@@ -63,11 +63,12 @@ export class BdsCheckboxGroup extends LitElement {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
.legend {
|
|
66
|
-
|
|
67
|
-
font-family: var(--bds-font_family--body);
|
|
68
|
-
font-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
padding: 0;
|
|
67
|
+
font-family: var(--checkboxGroup_legend-font-family, var(--bds-font_family--body));
|
|
68
|
+
font-size: var(--checkboxGroup_legend-font-size, var(--bds-font_size--body--s));
|
|
69
|
+
font-weight: var(--checkboxGroup_legend-font-weight, var(--bds-font_weight--medium));
|
|
70
|
+
color: var(--checkboxGroup_legend-color, var(--bds-color_on-bg));
|
|
71
|
+
margin-block-end: var(--checkboxGroup_legend-gap, var(--bds-space_xs));
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
.required {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LitElement, css, html, nothing } from 'lit';
|
|
2
|
+
import { fieldLabelStyles } from './field-label';
|
|
2
3
|
import { fieldMessageStyles, renderFieldMessage } from './field-message';
|
|
3
4
|
|
|
4
5
|
export interface ComboboxOption {
|
|
@@ -59,11 +60,6 @@ export class BdsCombobox extends LitElement {
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
.label {
|
|
62
|
-
display: block;
|
|
63
|
-
font-size: var(--bds-font_size--body--s);
|
|
64
|
-
font-family: var(--bds-font_family--body);
|
|
65
|
-
font-weight: var(--bds-font_weight--semibold);
|
|
66
|
-
color: var(--bds-color_on-bg);
|
|
67
63
|
margin-block-end: var(--bds-space_xxs);
|
|
68
64
|
}
|
|
69
65
|
|
|
@@ -179,6 +175,7 @@ export class BdsCombobox extends LitElement {
|
|
|
179
175
|
color: var(--bds-color_on-bg--subtle);
|
|
180
176
|
}
|
|
181
177
|
|
|
178
|
+
${fieldLabelStyles}
|
|
182
179
|
${fieldMessageStyles}
|
|
183
180
|
`;
|
|
184
181
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { LitElement, css, html } from 'lit';
|
|
2
2
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
3
|
+
import { fieldLabelStyles } from './field-label';
|
|
3
4
|
import { fieldMessageStyles, renderFieldMessage } from './field-message';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -53,11 +54,6 @@ export class BdsFormInput extends LitElement {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
.label {
|
|
56
|
-
display: block;
|
|
57
|
-
font-size: var(--bds-font_size--body--s);
|
|
58
|
-
font-family: var(--bds-font_family--body);
|
|
59
|
-
font-weight: var(--bds-font_weight--semibold);
|
|
60
|
-
color: var(--bds-color_on-bg);
|
|
61
57
|
margin-block-end: var(--bds-space_xxs);
|
|
62
58
|
}
|
|
63
59
|
|
|
@@ -143,6 +139,7 @@ export class BdsFormInput extends LitElement {
|
|
|
143
139
|
display: none;
|
|
144
140
|
}
|
|
145
141
|
|
|
142
|
+
${fieldLabelStyles}
|
|
146
143
|
${fieldMessageStyles}
|
|
147
144
|
`;
|
|
148
145
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LitElement, css, html } from 'lit';
|
|
2
|
+
import { fieldLabelStyles } from './field-label';
|
|
2
3
|
import { fieldMessageStyles, renderFieldMessage } from './field-message';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -46,11 +47,6 @@ export class BdsNumberInput extends LitElement {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
.label {
|
|
49
|
-
display: block;
|
|
50
|
-
font-size: var(--bds-font_size--body--s);
|
|
51
|
-
font-family: var(--bds-font_family--body);
|
|
52
|
-
font-weight: var(--bds-font_weight--semibold);
|
|
53
|
-
color: var(--bds-color_on-bg);
|
|
54
50
|
margin-block-end: var(--bds-space_xxs);
|
|
55
51
|
}
|
|
56
52
|
|
|
@@ -157,6 +153,7 @@ export class BdsNumberInput extends LitElement {
|
|
|
157
153
|
color: var(--numberInput_stepper-color, var(--bds-color_interactive));
|
|
158
154
|
}
|
|
159
155
|
|
|
156
|
+
${fieldLabelStyles}
|
|
160
157
|
${fieldMessageStyles}
|
|
161
158
|
`;
|
|
162
159
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fixture, cleanup } from '../../test/helpers';
|
|
1
|
+
import { fixture, cleanup, readSource, ruleDeclarations } from '../../test/helpers';
|
|
2
2
|
import { BdsRadioGroup } from './bds-radio-group';
|
|
3
3
|
|
|
4
4
|
describe('bds-radio-group', () => {
|
|
@@ -111,6 +111,16 @@ describe('bds-radio-group', () => {
|
|
|
111
111
|
cleanup(el);
|
|
112
112
|
});
|
|
113
113
|
|
|
114
|
+
it('styles the legend through the same hooks and defaults as the React RadioGroup', () => {
|
|
115
|
+
const react = ruleDeclarations(readSource('src/components/interaction/form/RadioGroup/RadioGroup.module.css'), '.legend');
|
|
116
|
+
expect(ruleDeclarations(BdsRadioGroup.styles.toString(), '.legend')).toEqual(react);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('sizes the legend like the field labels', () => {
|
|
120
|
+
expect(ruleDeclarations(BdsRadioGroup.styles.toString(), '.legend')['font-size'])
|
|
121
|
+
.toBe('var(--radioGroup_legend-font-size, var(--bds-font_size--body--s))');
|
|
122
|
+
});
|
|
123
|
+
|
|
114
124
|
it('uses medium font-weight token for the legend (not semibold)', () => {
|
|
115
125
|
const legendRule = BdsRadioGroup.styles.toString().match(/\.legend \{[^}]*\}/)?.[0] ?? '';
|
|
116
126
|
expect(legendRule).toContain('--bds-font_weight--medium');
|
|
@@ -50,11 +50,12 @@ export class BdsRadioGroup extends LitElement {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
.legend {
|
|
53
|
-
|
|
54
|
-
font-family: var(--bds-font_family--body);
|
|
55
|
-
font-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
padding: 0;
|
|
54
|
+
font-family: var(--radioGroup_legend-font-family, var(--bds-font_family--body));
|
|
55
|
+
font-size: var(--radioGroup_legend-font-size, var(--bds-font_size--body--s));
|
|
56
|
+
font-weight: var(--radioGroup_legend-font-weight, var(--bds-font_weight--medium));
|
|
57
|
+
color: var(--radioGroup_legend-color, var(--bds-color_on-bg));
|
|
58
|
+
margin-block-end: var(--radioGroup_legend-gap, var(--bds-space_xs));
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
.required {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LitElement, css, html, nothing } from 'lit';
|
|
2
|
+
import { fieldLabelStyles } from './field-label';
|
|
2
3
|
import { fieldMessageStyles, renderFieldMessage } from './field-message';
|
|
3
4
|
|
|
4
5
|
export interface SelectOption {
|
|
@@ -62,11 +63,6 @@ export class BdsSelect extends LitElement {
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
.label {
|
|
65
|
-
display: block;
|
|
66
|
-
font-size: var(--bds-font_size--body--s);
|
|
67
|
-
font-family: var(--bds-font_family--body);
|
|
68
|
-
font-weight: var(--bds-font_weight--semibold);
|
|
69
|
-
color: var(--bds-color_on-bg);
|
|
70
66
|
margin-block-end: var(--bds-space_xxs);
|
|
71
67
|
}
|
|
72
68
|
|
|
@@ -131,6 +127,7 @@ export class BdsSelect extends LitElement {
|
|
|
131
127
|
block-size: 100%;
|
|
132
128
|
}
|
|
133
129
|
|
|
130
|
+
${fieldLabelStyles}
|
|
134
131
|
${fieldMessageStyles}
|
|
135
132
|
`;
|
|
136
133
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LitElement, css, html } from 'lit';
|
|
2
|
+
import { fieldLabelStyles } from './field-label';
|
|
2
3
|
import { fieldMessageStyles, renderFieldMessage } from './field-message';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -60,14 +61,6 @@ export class BdsSlider extends LitElement {
|
|
|
60
61
|
margin-block-end: var(--bds-space_xxs);
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
.label {
|
|
64
|
-
display: block;
|
|
65
|
-
font-size: var(--bds-font_size--body--s);
|
|
66
|
-
font-family: var(--bds-font_family--body);
|
|
67
|
-
font-weight: var(--bds-font_weight--semibold);
|
|
68
|
-
color: var(--bds-color_on-bg);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
64
|
.value {
|
|
72
65
|
font-size: var(--bds-font_size--body--s);
|
|
73
66
|
font-variant-numeric: tabular-nums;
|
|
@@ -157,6 +150,7 @@ export class BdsSlider extends LitElement {
|
|
|
157
150
|
}
|
|
158
151
|
}
|
|
159
152
|
|
|
153
|
+
${fieldLabelStyles}
|
|
160
154
|
${fieldMessageStyles}
|
|
161
155
|
`;
|
|
162
156
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LitElement, css, html } from 'lit';
|
|
2
|
+
import { fieldLabelStyles } from './field-label';
|
|
2
3
|
import { fieldMessageStyles, renderFieldMessage } from './field-message';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -49,11 +50,6 @@ export class BdsTextarea extends LitElement {
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
.label {
|
|
52
|
-
display: block;
|
|
53
|
-
font-size: var(--bds-font_size--body--s);
|
|
54
|
-
font-family: var(--bds-font_family--body);
|
|
55
|
-
font-weight: var(--bds-font_weight--semibold);
|
|
56
|
-
color: var(--bds-color_on-bg);
|
|
57
53
|
margin-block-end: var(--bds-space_xxs);
|
|
58
54
|
}
|
|
59
55
|
|
|
@@ -108,6 +104,7 @@ export class BdsTextarea extends LitElement {
|
|
|
108
104
|
--bdc-outline_width: var(--bdc-outline_width--error, 2px);
|
|
109
105
|
}
|
|
110
106
|
|
|
107
|
+
${fieldLabelStyles}
|
|
111
108
|
${fieldMessageStyles}
|
|
112
109
|
`;
|
|
113
110
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { readSource, ruleDeclarations } from '../../test/helpers';
|
|
2
|
+
import { fieldLabelStyles } from './field-label';
|
|
3
|
+
import { BdsFormInput } from './bds-form-input';
|
|
4
|
+
import { BdsNumberInput } from './bds-number-input';
|
|
5
|
+
import { BdsCombobox } from './bds-combobox';
|
|
6
|
+
import { BdsSelect } from './bds-select';
|
|
7
|
+
import { BdsTextarea } from './bds-textarea';
|
|
8
|
+
import { BdsSlider } from './bds-slider';
|
|
9
|
+
|
|
10
|
+
const typographyProps = ['font-family', 'font-size', 'font-weight', 'color'];
|
|
11
|
+
|
|
12
|
+
describe('field-label', () => {
|
|
13
|
+
it('reads every label look from a --formLabel_* hook', () => {
|
|
14
|
+
expect(ruleDeclarations(fieldLabelStyles.toString(), '.label')).toEqual({
|
|
15
|
+
display: 'block',
|
|
16
|
+
'font-family': 'var(--formLabel_font-family, var(--bds-font_family--body))',
|
|
17
|
+
'font-size': 'var(--formLabel_font-size, var(--bds-font_size--body--s))',
|
|
18
|
+
'font-weight': 'var(--formLabel_font-weight, var(--bds-font_weight--semibold))',
|
|
19
|
+
color: 'var(--formLabel_color, var(--bds-color_on-bg))',
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('matches the field variant of the React Label atom', () => {
|
|
24
|
+
const react = ruleDeclarations(readSource('src/components/interaction/form/atoms/Label.module.css'), '.--field');
|
|
25
|
+
const wc = ruleDeclarations(fieldLabelStyles.toString(), '.label');
|
|
26
|
+
expect(Object.keys(react).sort()).toEqual([...typographyProps].sort());
|
|
27
|
+
typographyProps.forEach(prop => expect(react[prop]).toBe(wc[prop]));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it.each([
|
|
31
|
+
['bds-form-input', BdsFormInput],
|
|
32
|
+
['bds-number-input', BdsNumberInput],
|
|
33
|
+
['bds-combobox', BdsCombobox],
|
|
34
|
+
['bds-select', BdsSelect],
|
|
35
|
+
['bds-textarea', BdsTextarea],
|
|
36
|
+
['bds-slider', BdsSlider],
|
|
37
|
+
])('%s styles its label with the shared field label', (_tag, ctor) => {
|
|
38
|
+
const css = ctor.styles.toString();
|
|
39
|
+
expect(css).toContain(fieldLabelStyles.toString());
|
|
40
|
+
expect(css.replace(fieldLabelStyles.toString(), '')).not.toMatch(/\.label \{[^}]*font-(size|weight)/);
|
|
41
|
+
});
|
|
42
|
+
});
|