@boostdev/design-system-components 2.14.1 → 2.16.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 +20 -15
- package/dist/client.cjs +271 -205
- package/dist/client.css +808 -648
- package/dist/client.d.cts +19 -4
- package/dist/client.d.ts +19 -4
- package/dist/client.js +206 -139
- package/dist/index.cjs +271 -205
- package/dist/index.css +808 -648
- package/dist/index.d.cts +19 -4
- package/dist/index.d.ts +19 -4
- package/dist/index.js +206 -139
- package/dist/web-components/chunk-2DHW2WJZ.js +154 -0
- package/dist/web-components/{chunk-LDIPCTRN.js → chunk-2MJTO2IW.js} +48 -2
- package/dist/web-components/{chunk-3WAV6MDN.js → chunk-3FSGP35V.js} +1 -0
- package/dist/web-components/{chunk-KA3VPKMY.js → chunk-4MB2ICSK.js} +1 -0
- package/dist/web-components/{chunk-IDSM74LM.js → chunk-6XL5P76D.js} +3 -3
- package/dist/web-components/{chunk-P24JYC5V.js → chunk-7VXRMCAL.js} +10 -4
- package/dist/web-components/{chunk-CUKQ2L5B.js → chunk-AGI6YQRC.js} +9 -16
- package/dist/web-components/chunk-BDRV452N.js +51 -0
- package/dist/web-components/{chunk-3AY2YKCM.js → chunk-CKCPV347.js} +1 -0
- package/dist/web-components/{chunk-HBLJ4SLY.js → chunk-DD3OUXNG.js} +5 -5
- package/dist/web-components/{chunk-RVF7W653.js → chunk-DGOCPKHI.js} +30 -10
- package/dist/web-components/{chunk-2OLVQUBU.js → chunk-EYCW7TKB.js} +8 -15
- package/dist/web-components/{chunk-VANAKB6D.js → chunk-GPJWLAKU.js} +33 -16
- package/dist/web-components/{chunk-VED2DFJ6.js → chunk-GQNQBEYR.js} +8 -15
- package/dist/web-components/{chunk-WFVKU4GX.js → chunk-IKAJLROF.js} +49 -4
- package/dist/web-components/{chunk-RSZ3BWBK.js → chunk-KSK4RZZQ.js} +30 -7
- package/dist/web-components/{chunk-FEDABOR5.js → chunk-LHFUXZ52.js} +7 -15
- package/dist/web-components/{chunk-M3ECULRH.js → chunk-RBF7WPLI.js} +10 -8
- package/dist/web-components/{chunk-6G22I2CK.js → chunk-RZH5IVB5.js} +31 -1
- package/dist/web-components/chunk-SRKQ6GST.js +40 -0
- package/dist/web-components/{chunk-5HUAFCU6.js → chunk-TJV7UIXC.js} +7 -0
- package/dist/web-components/{chunk-J3HVUIUH.js → chunk-XI2U6U26.js} +8 -15
- package/dist/web-components/{chunk-JH6LQKLK.js → chunk-XJU7KITO.js} +8 -15
- package/dist/web-components/{chunk-XIZWAWMJ.js → chunk-XTPUKUN5.js} +16 -6
- package/dist/web-components/{chunk-JV5QT2OU.js → chunk-YLDQOESI.js} +27 -13
- package/dist/web-components/globals.js +1 -1
- package/dist/web-components/index.d.ts +1 -0
- package/dist/web-components/index.js +34 -29
- package/dist/web-components/interaction/bds-button.js +1 -1
- package/dist/web-components/interaction/bds-dropdown-menu-group.d.ts +41 -0
- package/dist/web-components/interaction/bds-dropdown-menu-group.js +7 -0
- package/dist/web-components/interaction/bds-dropdown-menu-item.d.ts +14 -1
- package/dist/web-components/interaction/bds-dropdown-menu-item.js +1 -1
- package/dist/web-components/interaction/bds-dropdown-menu.d.ts +2 -1
- package/dist/web-components/interaction/bds-dropdown-menu.js +1 -1
- package/dist/web-components/interaction/bds-tab.d.ts +5 -0
- package/dist/web-components/interaction/bds-tab.js +1 -1
- package/dist/web-components/interaction/bds-tooltip.js +1 -1
- package/dist/web-components/interaction/form/bds-checkbox-group.d.ts +6 -0
- package/dist/web-components/interaction/form/bds-checkbox-group.js +2 -1
- package/dist/web-components/interaction/form/bds-checkbox.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.d.ts +5 -0
- package/dist/web-components/interaction/form/bds-radio-group.js +2 -1
- package/dist/web-components/interaction/form/bds-radio.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-switch.js +1 -1
- package/dist/web-components/interaction/form/bds-textarea.js +2 -1
- package/dist/web-components/ui/bds-alert.d.ts +8 -1
- package/dist/web-components/ui/bds-alert.js +1 -1
- package/dist/web-components/ui/bds-avatar.d.ts +11 -0
- package/dist/web-components/ui/bds-avatar.js +1 -1
- package/dist/web-components/ui/bds-notification-banner.d.ts +14 -1
- package/dist/web-components/ui/bds-notification-banner.js +1 -1
- package/dist/web-components/ui/bds-section-header.d.ts +6 -0
- package/dist/web-components/ui/bds-section-header.js +1 -1
- package/dist/web-components/ui/bds-separator.js +1 -1
- package/dist/web-components/ui/bds-table.js +1 -1
- package/package.json +6 -2
- package/src/components/interaction/Button/Button.mdx +7 -0
- package/src/components/interaction/Button/Button.module.css +17 -9
- package/src/components/interaction/Button/Button.stories.tsx +28 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.mdx +35 -4
- package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +58 -10
- package/src/components/interaction/DropdownMenu/DropdownMenu.spec.tsx +114 -1
- package/src/components/interaction/DropdownMenu/DropdownMenu.stories.tsx +25 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +69 -17
- package/src/components/interaction/form/Checkbox/Checkbox.module.css +1 -0
- package/src/components/interaction/form/Checkbox/Checkbox.spec.tsx +7 -0
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.mdx +1 -0
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.module.css +7 -0
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.spec.tsx +22 -0
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +1 -1
- package/src/components/interaction/form/Combobox/Combobox.module.css +1 -0
- package/src/components/interaction/form/Combobox/Combobox.spec.tsx +7 -0
- package/src/components/interaction/form/FormInput/FormInput.mdx +9 -0
- package/src/components/interaction/form/FormInput/FormInput.module.css +1 -0
- package/src/components/interaction/form/FormInput/FormInput.spec.tsx +7 -0
- package/src/components/interaction/form/NumberInput/NumberInput.module.css +1 -0
- package/src/components/interaction/form/NumberInput/NumberInput.spec.tsx +7 -0
- package/src/components/interaction/form/Radio/Radio.module.css +1 -0
- package/src/components/interaction/form/Radio/Radio.spec.tsx +7 -0
- package/src/components/interaction/form/RadioGroup/RadioGroup.mdx +1 -0
- package/src/components/interaction/form/RadioGroup/RadioGroup.module.css +7 -0
- package/src/components/interaction/form/RadioGroup/RadioGroup.spec.tsx +22 -0
- package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +1 -1
- package/src/components/interaction/form/Select/Select.module.css +1 -0
- package/src/components/interaction/form/Select/Select.spec.tsx +7 -0
- package/src/components/interaction/form/Switch/Switch.module.css +1 -0
- package/src/components/interaction/form/Switch/Switch.spec.tsx +7 -0
- package/src/components/interaction/form/Textarea/Textarea.module.css +1 -0
- package/src/components/interaction/form/Textarea/Textarea.spec.tsx +7 -0
- package/src/components/interaction/form/atoms/Message.module.css +14 -3
- package/src/components/interaction/form/atoms/Message.spec.tsx +22 -0
- package/src/components/interaction/form/atoms/Message.tsx +5 -0
- package/src/components/layout/SectionHeader/SectionHeader.mdx +18 -5
- package/src/components/layout/SectionHeader/SectionHeader.module.css +35 -14
- package/src/components/layout/SectionHeader/SectionHeader.spec.tsx +44 -0
- package/src/components/layout/SectionHeader/SectionHeader.stories.tsx +5 -0
- package/src/components/layout/SectionHeader/SectionHeader.tsx +3 -1
- package/src/components/ui/Alert/Alert.mdx +11 -0
- package/src/components/ui/Alert/Alert.module.css +30 -1
- package/src/components/ui/Alert/Alert.spec.tsx +21 -0
- package/src/components/ui/Alert/Alert.stories.tsx +20 -0
- package/src/components/ui/Alert/Alert.tsx +8 -1
- package/src/components/ui/Avatar/Avatar.mdx +22 -2
- package/src/components/ui/Avatar/Avatar.module.css +29 -2
- package/src/components/ui/Avatar/Avatar.spec.tsx +26 -0
- package/src/components/ui/Avatar/Avatar.stories.tsx +23 -0
- package/src/components/ui/Avatar/Avatar.tsx +4 -2
- package/src/components/ui/Avatar/avatarTone.spec.ts +41 -0
- package/src/components/ui/Avatar/avatarTone.ts +22 -0
- package/src/components/ui/Collapsible/Collapsible.module.css +1 -2
- package/src/components/ui/NotificationBanner/NotificationBanner.mdx +14 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.module.css +31 -1
- package/src/components/ui/NotificationBanner/NotificationBanner.spec.tsx +29 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.stories.tsx +25 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.tsx +15 -1
- package/src/components/ui/Separator/Separator.mdx +1 -0
- package/src/components/ui/Separator/Separator.module.css +6 -0
- package/src/components/ui/Separator/Separator.spec.tsx +19 -0
- package/src/components/ui/Separator/Separator.stories.tsx +15 -0
- package/src/components/ui/Table/Table.mdx +4 -1
- package/src/components/ui/Table/Table.module.css +7 -7
- package/src/components/ui/Table/Table.spec.tsx +11 -0
- package/src/components/ui/Tabs/Tabs.mdx +11 -5
- package/src/components/ui/Tabs/Tabs.module.css +23 -5
- package/src/components/ui/Tabs/Tabs.spec.tsx +39 -0
- package/src/components/ui/Tabs/Tabs.tsx +2 -1
- package/src/components/ui/Tabs/getNodeText.spec.tsx +23 -0
- package/src/components/ui/Tabs/getNodeText.ts +13 -0
- package/src/components/ui/Tooltip/Tooltip.mdx +6 -3
- package/src/components/ui/Tooltip/Tooltip.module.css +6 -2
- package/src/components/ui/Tooltip/Tooltip.spec.tsx +10 -0
- package/src/components/ui/Tooltip/Tooltip.stories.tsx +13 -0
- package/src/css/bdc.css +5 -2
- package/src/css/bdc.spec.ts +65 -0
- package/src/stories/DesignSystem/Borders.mdx +17 -9
- package/src/web-components/globals.ts +10 -4
- package/src/web-components/index.ts +1 -0
- package/src/web-components/interaction/BdsAccordion.mdx +1 -1
- package/src/web-components/interaction/BdsAccordion.stories.tsx +1 -1
- package/src/web-components/interaction/BdsButton.mdx +9 -1
- package/src/web-components/interaction/BdsButton.stories.tsx +42 -1
- package/src/web-components/interaction/BdsCollapsible.mdx +1 -1
- package/src/web-components/interaction/BdsCollapsible.stories.tsx +1 -1
- package/src/web-components/interaction/BdsDialog.mdx +1 -1
- package/src/web-components/interaction/BdsDialog.stories.tsx +1 -1
- package/src/web-components/interaction/BdsDrawer.mdx +1 -1
- package/src/web-components/interaction/BdsDrawer.stories.tsx +1 -1
- package/src/web-components/interaction/BdsDropdownMenu.mdx +62 -6
- package/src/web-components/interaction/BdsDropdownMenu.stories.tsx +32 -7
- package/src/web-components/interaction/BdsPopover.mdx +1 -1
- package/src/web-components/interaction/BdsPopover.stories.tsx +1 -1
- package/src/web-components/interaction/BdsSkipLink.mdx +1 -1
- package/src/web-components/interaction/BdsSkipLink.stories.tsx +1 -1
- package/src/web-components/interaction/BdsTabs.mdx +10 -1
- package/src/web-components/interaction/BdsTabs.stories.tsx +1 -1
- package/src/web-components/interaction/BdsToastProvider.mdx +1 -1
- package/src/web-components/interaction/BdsToastProvider.stories.tsx +1 -1
- package/src/web-components/interaction/BdsTooltip.mdx +13 -5
- package/src/web-components/interaction/BdsTooltip.stories.tsx +12 -12
- package/src/web-components/interaction/bds-button.ts +16 -6
- package/src/web-components/interaction/bds-dropdown-menu-group.ts +72 -0
- package/src/web-components/interaction/bds-dropdown-menu-item.ts +65 -17
- package/src/web-components/interaction/bds-dropdown-menu.spec.ts +135 -0
- package/src/web-components/interaction/bds-dropdown-menu.ts +5 -4
- package/src/web-components/interaction/bds-tab.spec.ts +34 -2
- package/src/web-components/interaction/bds-tab.ts +31 -6
- package/src/web-components/interaction/bds-tooltip.spec.ts +16 -0
- package/src/web-components/interaction/bds-tooltip.ts +10 -8
- package/src/web-components/interaction/form/BdsCheckbox.mdx +5 -1
- package/src/web-components/interaction/form/BdsCheckbox.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsCheckboxGroup.mdx +5 -1
- package/src/web-components/interaction/form/BdsCheckboxGroup.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsCombobox.mdx +5 -1
- package/src/web-components/interaction/form/BdsCombobox.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsFieldGroup.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsFileInput.mdx +1 -1
- package/src/web-components/interaction/form/BdsFileInput.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsFormInput.mdx +8 -1
- package/src/web-components/interaction/form/BdsFormInput.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsNumberInput.mdx +5 -1
- package/src/web-components/interaction/form/BdsNumberInput.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsRadio.mdx +5 -1
- package/src/web-components/interaction/form/BdsRadio.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsRadioGroup.mdx +5 -1
- package/src/web-components/interaction/form/BdsRadioGroup.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsSegmentedControl.mdx +1 -1
- package/src/web-components/interaction/form/BdsSegmentedControl.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsSelect.mdx +5 -1
- package/src/web-components/interaction/form/BdsSelect.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsSlider.mdx +5 -1
- package/src/web-components/interaction/form/BdsSlider.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsSwitch.mdx +5 -1
- package/src/web-components/interaction/form/BdsSwitch.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsTextarea.mdx +5 -1
- package/src/web-components/interaction/form/BdsTextarea.stories.tsx +1 -1
- package/src/web-components/interaction/form/bds-checkbox-group.spec.ts +32 -3
- package/src/web-components/interaction/form/bds-checkbox-group.ts +33 -17
- package/src/web-components/interaction/form/bds-checkbox.ts +1 -0
- package/src/web-components/interaction/form/bds-combobox.ts +6 -16
- package/src/web-components/interaction/form/bds-form-input.ts +6 -16
- package/src/web-components/interaction/form/bds-number-input.ts +6 -16
- package/src/web-components/interaction/form/bds-radio-group.spec.ts +32 -3
- package/src/web-components/interaction/form/bds-radio-group.ts +26 -14
- package/src/web-components/interaction/form/bds-radio.ts +1 -0
- package/src/web-components/interaction/form/bds-select.ts +6 -16
- package/src/web-components/interaction/form/bds-slider.ts +5 -16
- package/src/web-components/interaction/form/bds-switch.ts +1 -0
- package/src/web-components/interaction/form/bds-textarea.ts +6 -16
- package/src/web-components/interaction/form/field-message.spec.ts +62 -0
- package/src/web-components/interaction/form/field-message.ts +55 -0
- package/src/web-components/layout/BdsGrid.mdx +1 -1
- package/src/web-components/layout/BdsGrid.stories.tsx +1 -1
- package/src/web-components/layout/BdsGridItem.mdx +1 -1
- package/src/web-components/layout/BdsGridItem.stories.tsx +1 -1
- package/src/web-components/ui/BdsAlert.mdx +19 -1
- package/src/web-components/ui/BdsAlert.stories.tsx +29 -3
- package/src/web-components/ui/BdsAvatar.mdx +20 -3
- package/src/web-components/ui/BdsAvatar.stories.tsx +25 -2
- package/src/web-components/ui/BdsBadge.mdx +1 -1
- package/src/web-components/ui/BdsBadge.stories.tsx +1 -1
- package/src/web-components/ui/BdsBreadcrumb.mdx +1 -1
- package/src/web-components/ui/BdsBreadcrumb.stories.tsx +1 -1
- package/src/web-components/ui/BdsButtonContainer.mdx +1 -1
- package/src/web-components/ui/BdsButtonContainer.stories.tsx +1 -1
- package/src/web-components/ui/BdsCalendar.mdx +1 -1
- package/src/web-components/ui/BdsCalendar.stories.tsx +1 -1
- package/src/web-components/ui/BdsCalendarRange.mdx +1 -1
- package/src/web-components/ui/BdsCalendarRange.stories.tsx +1 -1
- package/src/web-components/ui/BdsCard.mdx +1 -1
- package/src/web-components/ui/BdsCard.stories.tsx +1 -1
- package/src/web-components/ui/BdsCarousel.mdx +1 -1
- package/src/web-components/ui/BdsCarousel.stories.tsx +1 -1
- package/src/web-components/ui/BdsDescriptionList.mdx +1 -1
- package/src/web-components/ui/BdsDescriptionList.stories.tsx +1 -1
- package/src/web-components/ui/BdsIconWrapper.mdx +1 -1
- package/src/web-components/ui/BdsIconWrapper.stories.tsx +1 -1
- package/src/web-components/ui/BdsLink.mdx +1 -1
- package/src/web-components/ui/BdsLink.stories.tsx +1 -1
- package/src/web-components/ui/BdsLoading.mdx +1 -1
- package/src/web-components/ui/BdsLoading.stories.tsx +1 -1
- package/src/web-components/ui/BdsNotificationBanner.mdx +10 -1
- package/src/web-components/ui/BdsNotificationBanner.stories.tsx +29 -2
- package/src/web-components/ui/BdsPagination.mdx +1 -1
- package/src/web-components/ui/BdsPagination.stories.tsx +1 -1
- package/src/web-components/ui/BdsProgress.mdx +1 -1
- package/src/web-components/ui/BdsProgress.stories.tsx +1 -1
- package/src/web-components/ui/BdsProgressCircle.mdx +1 -1
- package/src/web-components/ui/BdsProgressCircle.stories.tsx +1 -1
- package/src/web-components/ui/BdsRating.mdx +1 -1
- package/src/web-components/ui/BdsRating.stories.tsx +1 -1
- package/src/web-components/ui/BdsSectionHeader.mdx +18 -6
- package/src/web-components/ui/BdsSectionHeader.stories.tsx +10 -2
- package/src/web-components/ui/BdsSeparator.mdx +2 -1
- package/src/web-components/ui/BdsSeparator.stories.tsx +13 -1
- package/src/web-components/ui/BdsSkeleton.mdx +1 -1
- package/src/web-components/ui/BdsSkeleton.stories.tsx +1 -1
- package/src/web-components/ui/BdsTable.mdx +5 -1
- package/src/web-components/ui/BdsTable.stories.tsx +1 -1
- package/src/web-components/ui/BdsTypography.mdx +1 -1
- package/src/web-components/ui/BdsTypography.stories.tsx +1 -1
- package/src/web-components/ui/bds-alert.spec.ts +27 -0
- package/src/web-components/ui/bds-alert.ts +34 -1
- package/src/web-components/ui/bds-avatar.spec.ts +47 -0
- package/src/web-components/ui/bds-avatar.ts +36 -3
- package/src/web-components/ui/bds-notification-banner.spec.ts +45 -0
- package/src/web-components/ui/bds-notification-banner.ts +53 -1
- package/src/web-components/ui/bds-section-header.spec.ts +36 -3
- package/src/web-components/ui/bds-section-header.ts +32 -10
- package/src/web-components/ui/bds-separator.spec.ts +8 -0
- package/src/web-components/ui/bds-separator.ts +7 -0
- package/src/web-components/ui/bds-table.spec.ts +11 -0
- package/src/web-components/ui/bds-table.ts +5 -5
- package/dist/web-components/chunk-QZAIRJ75.js +0 -111
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
HTMLAttributes,
|
|
3
3
|
KeyboardEvent,
|
|
4
4
|
ReactElement,
|
|
5
|
+
Fragment,
|
|
5
6
|
cloneElement,
|
|
6
7
|
isValidElement,
|
|
7
8
|
useEffect,
|
|
@@ -20,8 +21,29 @@ export interface DropdownMenuItem {
|
|
|
20
21
|
disabled?: boolean;
|
|
21
22
|
icon?: ReactElement;
|
|
22
23
|
separator?: boolean;
|
|
24
|
+
/** Renders the item in the error colour, for actions that delete or discard. */
|
|
25
|
+
destructive?: boolean;
|
|
26
|
+
/** Keyboard shortcut as display text. Decorative (`aria-hidden`); the consumer wires the actual key. */
|
|
27
|
+
shortcut?: string;
|
|
28
|
+
/** Consecutive items with the same group render inside a labelled `role="group"`. */
|
|
29
|
+
group?: string;
|
|
23
30
|
}
|
|
24
31
|
|
|
32
|
+
interface IndexedItem {
|
|
33
|
+
item: DropdownMenuItem;
|
|
34
|
+
index: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Splits items into runs of consecutive items sharing the same `group`. */
|
|
38
|
+
const toSegments = (items: DropdownMenuItem[]): IndexedItem[][] =>
|
|
39
|
+
items.reduce<IndexedItem[][]>((segments, item, index) => {
|
|
40
|
+
const last = segments[segments.length - 1];
|
|
41
|
+
const startsNewSegment = !last || item.group === undefined || last[0].item.group !== item.group;
|
|
42
|
+
return startsNewSegment
|
|
43
|
+
? [...segments, [{ item, index }]]
|
|
44
|
+
: [...segments.slice(0, -1), [...last, { item, index }]];
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
25
47
|
export interface DropdownMenuProps extends WithClassName, HTMLAttributes<HTMLDivElement> {
|
|
26
48
|
trigger: ReactElement;
|
|
27
49
|
items: DropdownMenuItem[];
|
|
@@ -125,23 +147,53 @@ export function DropdownMenu({
|
|
|
125
147
|
role="menu"
|
|
126
148
|
className={cn(css.menu, css[`--placement_${placement}`])}
|
|
127
149
|
>
|
|
128
|
-
{items.map(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
>
|
|
140
|
-
{item.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
150
|
+
{toSegments(items).map(segment => {
|
|
151
|
+
const { item: first } = segment[0];
|
|
152
|
+
const groupLabel = first.group;
|
|
153
|
+
const renderSeparator = () => (
|
|
154
|
+
<li role="presentation" className={css.separatorRow}>
|
|
155
|
+
<hr className={css.separator} role="separator" />
|
|
156
|
+
</li>
|
|
157
|
+
);
|
|
158
|
+
/* The first item's separator sits before the group, so the group holds only its label and items. */
|
|
159
|
+
const leadingSeparator = groupLabel !== undefined && first.separator;
|
|
160
|
+
const rows = segment.map(({ item, index }, position) => (
|
|
161
|
+
<Fragment key={item.id}>
|
|
162
|
+
{item.separator && !(leadingSeparator && position === 0) && renderSeparator()}
|
|
163
|
+
<li role="presentation">
|
|
164
|
+
<button
|
|
165
|
+
ref={el => { itemRefs.current[index] = el; }}
|
|
166
|
+
type="button"
|
|
167
|
+
role="menuitem"
|
|
168
|
+
disabled={item.disabled}
|
|
169
|
+
className={cn(css.item, item.destructive && css['--destructive'])}
|
|
170
|
+
onClick={() => { item.onClick?.(); close(true); }}
|
|
171
|
+
onKeyDown={e => handleItemKeyDown(e, index)}
|
|
172
|
+
>
|
|
173
|
+
{item.icon && <span className={css.icon} aria-hidden="true">{item.icon}</span>}
|
|
174
|
+
<span className={css.label}>{item.label}</span>
|
|
175
|
+
{item.shortcut && <span className={css.shortcut} aria-hidden="true">{item.shortcut}</span>}
|
|
176
|
+
</button>
|
|
177
|
+
</li>
|
|
178
|
+
</Fragment>
|
|
179
|
+
));
|
|
180
|
+
|
|
181
|
+
return groupLabel === undefined ? (
|
|
182
|
+
rows
|
|
183
|
+
) : (
|
|
184
|
+
<Fragment key={`group-${first.id}`}>
|
|
185
|
+
{leadingSeparator && renderSeparator()}
|
|
186
|
+
<li role="presentation">
|
|
187
|
+
<ul role="group" aria-labelledby={`${menuId}-group-${first.id}`} className={css.group}>
|
|
188
|
+
<li role="presentation" id={`${menuId}-group-${first.id}`} className={css.groupLabel}>
|
|
189
|
+
{groupLabel}
|
|
190
|
+
</li>
|
|
191
|
+
{rows}
|
|
192
|
+
</ul>
|
|
193
|
+
</li>
|
|
194
|
+
</Fragment>
|
|
195
|
+
);
|
|
196
|
+
})}
|
|
145
197
|
</ul>
|
|
146
198
|
)}
|
|
147
199
|
</div>
|
|
@@ -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 userEvent from '@testing-library/user-event';
|
|
3
5
|
import { Checkbox } from './Checkbox';
|
|
@@ -42,4 +44,9 @@ describe('Checkbox', () => {
|
|
|
42
44
|
const checkbox = screen.getByLabelText('Accept terms');
|
|
43
45
|
expect(checkbox.id).not.toBe('consumer-supplied-id');
|
|
44
46
|
});
|
|
47
|
+
|
|
48
|
+
it('widens the outline in the error state', () => {
|
|
49
|
+
const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/Checkbox/Checkbox.module.css'), 'utf8');
|
|
50
|
+
expect(css).toMatch(/\.checkboxError \{[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
|
|
51
|
+
});
|
|
45
52
|
});
|
|
@@ -57,3 +57,4 @@ All public override tokens. Set on the CheckboxGroup or any ancestor; they casca
|
|
|
57
57
|
- `disabled` on the group sets the HTML `disabled` attribute on the `<fieldset>`, which disables all child inputs
|
|
58
58
|
- When `required` is true, the legend shows a visible `*` (hidden from assistive tech via `aria-hidden`) plus a visually-hidden "(required)" span, so the fieldset's accessible name resolves to `"<legend> (required)"` (e.g. `"Contact preferences (required)"`). `aria-required` is intentionally **not** set on the `<fieldset>` — it is not supported ARIA on a `role=group` element and would be ignored by assistive tech
|
|
59
59
|
- Error and hint messages are linked via `aria-describedby`
|
|
60
|
+
- With `error`, the child controls show the error ring (`--bdc-outline_width--error`, `--bdc_color--error`) and the message gets a warning icon. Message hooks (`--formMessage_*`) are listed on FormInput
|
|
@@ -42,4 +42,11 @@
|
|
|
42
42
|
flex-direction: column;
|
|
43
43
|
gap: var(--checkboxGroup_items-gap, var(--bds-space_xs));
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
/* Error ring on the controls inside; state rules on the control itself
|
|
47
|
+
(checked, focus) still win because they have higher specificity. */
|
|
48
|
+
.groupError input {
|
|
49
|
+
--bdc_color: var(--bdc_color--error);
|
|
50
|
+
--bdc-outline_width: var(--bdc-outline_width--error, 2px);
|
|
51
|
+
}
|
|
45
52
|
}
|
|
@@ -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 { Checkbox } from '../Checkbox';
|
|
3
5
|
import { CheckboxGroup } from './CheckboxGroup';
|
|
@@ -114,4 +116,24 @@ describe('CheckboxGroup', () => {
|
|
|
114
116
|
);
|
|
115
117
|
expect(screen.getByRole('group')).toBeDisabled();
|
|
116
118
|
});
|
|
119
|
+
|
|
120
|
+
it('marks the group with the error class so the controls inside show the error ring', () => {
|
|
121
|
+
render(
|
|
122
|
+
<CheckboxGroup legend="Pick" error="Required">
|
|
123
|
+
<Checkbox label="One" name="one" value="1" />
|
|
124
|
+
</CheckboxGroup>
|
|
125
|
+
);
|
|
126
|
+
expect(screen.getByRole('group', { name: 'Pick' }).className).toContain('groupError');
|
|
127
|
+
const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/CheckboxGroup/CheckboxGroup.module.css'), 'utf8');
|
|
128
|
+
expect(css).toMatch(/\.groupError input \{[^}]*--bdc_color: var\(--bdc_color--error\)[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('does not mark the group without an error', () => {
|
|
132
|
+
render(
|
|
133
|
+
<CheckboxGroup legend="Pick">
|
|
134
|
+
<Checkbox label="One" name="one" value="1" />
|
|
135
|
+
</CheckboxGroup>
|
|
136
|
+
);
|
|
137
|
+
expect(screen.getByRole('group', { name: 'Pick' }).className).not.toContain('groupError');
|
|
138
|
+
});
|
|
117
139
|
});
|
|
@@ -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 userEvent from '@testing-library/user-event';
|
|
3
5
|
import { Combobox } from './Combobox';
|
|
@@ -125,4 +127,9 @@ describe('Combobox', () => {
|
|
|
125
127
|
expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
|
|
126
128
|
expect(combobox).not.toHaveAttribute('aria-activedescendant');
|
|
127
129
|
});
|
|
130
|
+
|
|
131
|
+
it('widens the outline in the error state', () => {
|
|
132
|
+
const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/Combobox/Combobox.module.css'), 'utf8');
|
|
133
|
+
expect(css).toMatch(/\.inputError \{[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
|
|
134
|
+
});
|
|
128
135
|
});
|
|
@@ -43,9 +43,18 @@ Text input field with label, optional hint, and error state. The label and input
|
|
|
43
43
|
<tr><td>`--formInput_border`</td><td>`none`</td><td>Border</td></tr>
|
|
44
44
|
<tr><td>`--formInput_radius`</td><td>`var(--bds-border_radius--full)`</td><td>Corner radius</td></tr>
|
|
45
45
|
<tr><td>`--formInput_shadow`</td><td>`var(--bdc-outline_shadow)`</td><td>Box shadow</td></tr>
|
|
46
|
+
<tr><td>`--formMessage_font-size`</td><td>`var(--bds-font_size--body--s)`</td><td>Font size of the error and hint message (shared by all form controls)</td></tr>
|
|
47
|
+
<tr><td>`--formMessage_gap`</td><td>`var(--bds-space_xxs)`</td><td>Gap between the error icon and the error text</td></tr>
|
|
48
|
+
<tr><td>`--formMessage_icon-size`</td><td>`1.15em`</td><td>Size of the error icon</td></tr>
|
|
49
|
+
<tr><td>`--formMessage_color--error`</td><td>`var(--bds-color_error_on-bg)`</td><td>Error message colour</td></tr>
|
|
50
|
+
<tr><td>`--formMessage_font-weight--error`</td><td>`var(--bds-font_weight--semibold)`</td><td>Error message weight</td></tr>
|
|
51
|
+
<tr><td>`--formMessage_color--hint`</td><td>`inherit`</td><td>Hint colour</td></tr>
|
|
52
|
+
<tr><td>`--formMessage_font-style--hint`</td><td>`italic`</td><td>Hint font style</td></tr>
|
|
46
53
|
</tbody>
|
|
47
54
|
</table>
|
|
48
55
|
|
|
56
|
+
In the error state the control draws a `--bdc-outline_width--error` (2px) ring in `--bdc_color--error`, and the message gets a warning icon.
|
|
57
|
+
|
|
49
58
|
`--input_*` (e.g. `--input_border`, `--input_radius`, `--input_shadow`, `--input_color`, `--input_color_bg`) are deprecated aliases for the `--formInput_*` tokens above — they still work but `--formInput_*` is the documented, forward-compatible name.
|
|
50
59
|
|
|
51
60
|
|
|
@@ -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 userEvent from '@testing-library/user-event';
|
|
3
5
|
import { FormInput } from './FormInput';
|
|
@@ -60,4 +62,9 @@ describe('FormInput', () => {
|
|
|
60
62
|
render(<FormInput label="Email" name="email" ariaLabel="Deprecated label" aria-label="Native label" />);
|
|
61
63
|
expect(screen.getByRole('textbox')).toHaveAccessibleName('Deprecated label');
|
|
62
64
|
});
|
|
65
|
+
|
|
66
|
+
it('widens the outline in the error state', () => {
|
|
67
|
+
const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/FormInput/FormInput.module.css'), 'utf8');
|
|
68
|
+
expect(css).toMatch(/\.inputError \{[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
|
|
69
|
+
});
|
|
63
70
|
});
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
1
3
|
import { useState } from 'react';
|
|
2
4
|
import { render, screen } from '@testing-library/react';
|
|
3
5
|
import userEvent from '@testing-library/user-event';
|
|
@@ -97,4 +99,9 @@ describe('NumberInput', () => {
|
|
|
97
99
|
expect(input).toHaveValue(null);
|
|
98
100
|
expect(onChange).not.toHaveBeenCalledWith(0);
|
|
99
101
|
});
|
|
102
|
+
|
|
103
|
+
it('widens the outline in the error state', () => {
|
|
104
|
+
const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/NumberInput/NumberInput.module.css'), 'utf8');
|
|
105
|
+
expect(css).toMatch(/\.inputError \{[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
|
|
106
|
+
});
|
|
100
107
|
});
|
|
@@ -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 userEvent from '@testing-library/user-event';
|
|
3
5
|
import { Radio } from './Radio';
|
|
@@ -50,4 +52,9 @@ describe('Radio', () => {
|
|
|
50
52
|
const radio = screen.getByLabelText('Option');
|
|
51
53
|
expect(radio).toHaveAttribute('id', 'custom-radio-id');
|
|
52
54
|
});
|
|
55
|
+
|
|
56
|
+
it('widens the outline in the error state', () => {
|
|
57
|
+
const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/Radio/Radio.module.css'), 'utf8');
|
|
58
|
+
expect(css).toMatch(/\.radioError \{[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
|
|
59
|
+
});
|
|
53
60
|
});
|
|
@@ -57,4 +57,5 @@ All public override tokens. Set on the RadioGroup or any ancestor; they cascade.
|
|
|
57
57
|
- `disabled` on the group sets the HTML `disabled` attribute on the `<fieldset>`, which disables all child inputs
|
|
58
58
|
- When `required` is true, the legend shows a visible `*` (hidden from assistive tech via `aria-hidden`) plus a visually-hidden "(required)" span, so the fieldset's accessible name resolves to `"<legend> (required)"` (e.g. `"Preferred contact (required)"`). `aria-required` is intentionally **not** set on the `<fieldset>` — it is not supported ARIA on a `role=group` element and would be ignored by assistive tech
|
|
59
59
|
- Error and hint messages are linked via `aria-describedby`
|
|
60
|
+
- With `error`, the child controls show the error ring (`--bdc-outline_width--error`, `--bdc_color--error`) and the message gets a warning icon. Message hooks (`--formMessage_*`) are listed on FormInput
|
|
60
61
|
- Arrow keys move between options within the group; `Tab` moves focus out of the group
|
|
@@ -42,4 +42,11 @@
|
|
|
42
42
|
flex-direction: column;
|
|
43
43
|
gap: var(--radioGroup_items-gap, var(--bds-space_xs));
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
/* Error ring on the controls inside; state rules on the control itself
|
|
47
|
+
(checked, focus) still win because they have higher specificity. */
|
|
48
|
+
.groupError input {
|
|
49
|
+
--bdc_color: var(--bdc_color--error);
|
|
50
|
+
--bdc-outline_width: var(--bdc-outline_width--error, 2px);
|
|
51
|
+
}
|
|
45
52
|
}
|
|
@@ -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 { Radio } from '../Radio';
|
|
3
5
|
import { RadioGroup } from './RadioGroup';
|
|
@@ -102,4 +104,24 @@ describe('RadioGroup', () => {
|
|
|
102
104
|
);
|
|
103
105
|
expect(screen.getByRole('group')).toBeDisabled();
|
|
104
106
|
});
|
|
107
|
+
|
|
108
|
+
it('marks the group with the error class so the controls inside show the error ring', () => {
|
|
109
|
+
render(
|
|
110
|
+
<RadioGroup legend="Pick" error="Required">
|
|
111
|
+
<Radio label="One" name="one" value="1" />
|
|
112
|
+
</RadioGroup>
|
|
113
|
+
);
|
|
114
|
+
expect(screen.getByRole('group', { name: 'Pick' }).className).toContain('groupError');
|
|
115
|
+
const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/RadioGroup/RadioGroup.module.css'), 'utf8');
|
|
116
|
+
expect(css).toMatch(/\.groupError input \{[^}]*--bdc_color: var\(--bdc_color--error\)[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('does not mark the group without an error', () => {
|
|
120
|
+
render(
|
|
121
|
+
<RadioGroup legend="Pick">
|
|
122
|
+
<Radio label="One" name="one" value="1" />
|
|
123
|
+
</RadioGroup>
|
|
124
|
+
);
|
|
125
|
+
expect(screen.getByRole('group', { name: 'Pick' }).className).not.toContain('groupError');
|
|
126
|
+
});
|
|
105
127
|
});
|
|
@@ -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 userEvent from '@testing-library/user-event';
|
|
3
5
|
import { Select } from './Select';
|
|
@@ -93,4 +95,9 @@ describe('Select', () => {
|
|
|
93
95
|
expect(select).toHaveAttribute('aria-describedby', 'custom-iderror');
|
|
94
96
|
expect(screen.getByText('Required')).toHaveAttribute('id', 'custom-iderror');
|
|
95
97
|
});
|
|
98
|
+
|
|
99
|
+
it('widens the outline in the error state', () => {
|
|
100
|
+
const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/Select/Select.module.css'), 'utf8');
|
|
101
|
+
expect(css).toMatch(/\.selectError \{[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
|
|
102
|
+
});
|
|
96
103
|
});
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
/* Error state — declared before checked/focus so higher-specificity states can override */
|
|
75
75
|
.switch.switchError + .track {
|
|
76
76
|
--bdc_color: var(--switch_border-color--error, var(--bds-color_error));
|
|
77
|
+
--bdc-outline_width: var(--bdc-outline_width--error, 2px);
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
/* Checked state via CSS sibling combinator */
|
|
@@ -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 userEvent from '@testing-library/user-event';
|
|
3
5
|
import { Switch } from './Switch';
|
|
@@ -88,4 +90,9 @@ describe('Switch', () => {
|
|
|
88
90
|
render(<Switch label="Toggle" name="toggle" role="checkbox" />);
|
|
89
91
|
expect(screen.getByRole('switch')).toBeInTheDocument();
|
|
90
92
|
});
|
|
93
|
+
|
|
94
|
+
it('widens the outline in the error state', () => {
|
|
95
|
+
const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/Switch/Switch.module.css'), 'utf8');
|
|
96
|
+
expect(css).toMatch(/\.switchError \+ \.track \{[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
|
|
97
|
+
});
|
|
91
98
|
});
|
|
@@ -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 userEvent from '@testing-library/user-event';
|
|
3
5
|
import { Textarea } from './Textarea';
|
|
@@ -63,4 +65,9 @@ describe('Textarea', () => {
|
|
|
63
65
|
?.split(' ')
|
|
64
66
|
.forEach((describedById) => expect(describedById.startsWith('custom-id')).toBe(true));
|
|
65
67
|
});
|
|
68
|
+
|
|
69
|
+
it('widens the outline in the error state', () => {
|
|
70
|
+
const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/Textarea/Textarea.module.css'), 'utf8');
|
|
71
|
+
expect(css).toMatch(/\.textareaError \{[^}]*--bdc-outline_width: var\(--bdc-outline_width--error, 2px\)/);
|
|
72
|
+
});
|
|
66
73
|
});
|
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
@layer boostdev.component {
|
|
2
2
|
.error,
|
|
3
3
|
.hint {
|
|
4
|
-
font-size: var(--bds-font_size--body--s);
|
|
4
|
+
font-size: var(--formMessage_font-size, var(--bds-font_size--body--s));
|
|
5
5
|
margin-inline-start: var(--labelPosition, 0);
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
.error {
|
|
9
|
-
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: flex-start;
|
|
11
|
+
gap: var(--formMessage_gap, var(--bds-space_xxs));
|
|
12
|
+
color: var(--formMessage_color--error, var(--bds-color_error_on-bg));
|
|
13
|
+
font-weight: var(--formMessage_font-weight--error, var(--bds-font_weight--semibold));
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
.hint {
|
|
13
|
-
|
|
17
|
+
color: var(--formMessage_color--hint, inherit);
|
|
18
|
+
font-style: var(--formMessage_font-style--hint, italic);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.icon {
|
|
22
|
+
inline-size: var(--formMessage_icon-size, 1.15em);
|
|
23
|
+
block-size: var(--formMessage_icon-size, 1.15em);
|
|
24
|
+
flex-shrink: 0;
|
|
14
25
|
}
|
|
15
26
|
}
|
|
@@ -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 { Message } from './Message';
|
|
3
5
|
|
|
@@ -26,4 +28,24 @@ describe('Message', () => {
|
|
|
26
28
|
render(<Message inputId="field-1" type="error" message="Required" />);
|
|
27
29
|
expect(screen.getByRole('alert')).toHaveAttribute('id', 'field-1error');
|
|
28
30
|
});
|
|
31
|
+
|
|
32
|
+
it('renders an aria-hidden icon before the error text', () => {
|
|
33
|
+
render(<Message inputId="field-1" type="error" message="Required" />);
|
|
34
|
+
const icon = screen.getByRole('alert').querySelector('svg');
|
|
35
|
+
expect(icon).toHaveAttribute('aria-hidden', 'true');
|
|
36
|
+
expect(screen.getByRole('alert')).toHaveTextContent('Required');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('renders no icon for a hint', () => {
|
|
40
|
+
render(<Message inputId="field-1" type="hint" message="Hint" />);
|
|
41
|
+
expect(screen.getByText('Hint').querySelector('svg')).toBeNull();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('exposes the message hooks in the stylesheet', () => {
|
|
45
|
+
const css = readFileSync(resolve(process.cwd(), 'src/components/interaction/form/atoms/Message.module.css'), 'utf8');
|
|
46
|
+
['--formMessage_gap', '--formMessage_icon-size', '--formMessage_color--error', '--formMessage_font-weight--error',
|
|
47
|
+
'--formMessage_color--hint', '--formMessage_font-style--hint', '--formMessage_font-size'].forEach((hook) => {
|
|
48
|
+
expect(css).toContain(hook);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
29
51
|
});
|
|
@@ -13,6 +13,11 @@ export const Message = ({ message, type, inputId, className }: MessageProps) =>
|
|
|
13
13
|
|
|
14
14
|
return (
|
|
15
15
|
<p id={inputId + type} role={type === 'error' ? 'alert' : undefined} className={cn(css[type], className)}>
|
|
16
|
+
{type === 'error' && (
|
|
17
|
+
<svg className={css.icon} aria-hidden="true" viewBox="0 0 256 256" fill="currentColor">
|
|
18
|
+
<path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm-8,56a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm8,104a12,12,0,1,1,12-12A12,12,0,0,1,128,184Z" />
|
|
19
|
+
</svg>
|
|
20
|
+
)}
|
|
16
21
|
{message}
|
|
17
22
|
</p>
|
|
18
23
|
);
|
|
@@ -31,6 +31,17 @@ Heading block with optional subtitle for introducing a content section.
|
|
|
31
31
|
### Centered
|
|
32
32
|
<Canvas of={Stories.Centered} />
|
|
33
33
|
|
|
34
|
+
### Gradient
|
|
35
|
+
Set `appearance="gradient"` for a gradient-filled title. The gradient spans the text, not the row. Where `background-clip: text` is unsupported the title keeps its normal colour; in forced-colors mode it uses `CanvasText`.
|
|
36
|
+
|
|
37
|
+
<Canvas of={Stories.Gradient} />
|
|
38
|
+
|
|
39
|
+
<Canvas of={Stories.GradientExtraSmall} />
|
|
40
|
+
|
|
41
|
+
<Canvas of={Stories.GradientSmall} />
|
|
42
|
+
|
|
43
|
+
<Canvas of={Stories.GradientLarge} />
|
|
44
|
+
|
|
34
45
|
## Props
|
|
35
46
|
|
|
36
47
|
<ArgTypes of={Stories} />
|
|
@@ -43,12 +54,14 @@ Heading block with optional subtitle for introducing a content section.
|
|
|
43
54
|
<tr><th>Variable</th><th>Default</th><th>Description</th></tr>
|
|
44
55
|
</thead>
|
|
45
56
|
<tbody>
|
|
46
|
-
<tr><td>`--sectionHeader_title-color`</td><td>`var(--bds-color_on-bg)`</td><td>Title text colour
|
|
47
|
-
<tr><td>`--sectionHeader_title-
|
|
57
|
+
<tr><td>`--sectionHeader_title-color`</td><td>`var(--bds-color_on-bg)`</td><td>Title text colour; no effect with `appearance="gradient"`, where the colour comes from `--sectionHeader_title-gradient`</td></tr>
|
|
58
|
+
<tr><td>`--sectionHeader_title-font-family`</td><td>`var(--bds-font_family--heading)`</td><td>Title font family</td></tr>
|
|
59
|
+
<tr><td>`--sectionHeader_title-gradient`</td><td>`linear-gradient(90deg, var(--bds-color_primary--strong), var(--bds-color_accent-3--strong))`</td><td>Title gradient, only with `appearance="gradient"`</td></tr>
|
|
60
|
+
<tr><td>`--sectionHeader_title-size`</td><td>`xs` body, `small` heading-3, `medium` heading-2, `large` heading-1</td><td>Title font-size (overrides the variant default)</td></tr>
|
|
48
61
|
<tr><td>`--sectionHeader_title-line-height`</td><td>varies by `size` prop</td><td>Title line-height</td></tr>
|
|
49
|
-
<tr><td>`--sectionHeader_title-weight`</td><td>`var(--bds-font_weight--
|
|
50
|
-
<tr><td>`--sectionHeader_subtitle-color`</td><td>`var(--bds-
|
|
51
|
-
<tr><td>`--sectionHeader_subtitle-size`</td><td
|
|
62
|
+
<tr><td>`--sectionHeader_title-weight`</td><td>`var(--bds-font_weight--heading)`</td><td>Title font-weight</td></tr>
|
|
63
|
+
<tr><td>`--sectionHeader_subtitle-color`</td><td>`var(--bds-color_on-bg--muted)`</td><td>Subtitle text colour</td></tr>
|
|
64
|
+
<tr><td>`--sectionHeader_subtitle-size`</td><td>`xs` body--s, `small`/`medium` body, `large` heading-3</td><td>Subtitle font-size</td></tr>
|
|
52
65
|
<tr><td>`--sectionHeader_subtitle-line-height`</td><td>`1.4` (`xs`/`small`), `1.3` (`medium`/`large`)</td><td>Subtitle line-height</td></tr>
|
|
53
66
|
<tr><td>`--sectionHeader_gap`</td><td>`var(--bds-space_m)`</td><td>Gap between title and subtitle</td></tr>
|
|
54
67
|
</tbody>
|