@boostdev/design-system-components 2.8.0 → 2.11.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 +54 -46
- package/README.md +34 -20
- package/dist/client.cjs +1317 -779
- package/dist/client.css +1029 -812
- package/dist/client.d.cts +88 -26
- package/dist/client.d.ts +88 -26
- package/dist/client.js +1278 -732
- package/dist/index.cjs +1317 -779
- package/dist/index.css +1029 -812
- package/dist/index.d.cts +88 -26
- package/dist/index.d.ts +88 -26
- package/dist/index.js +1278 -732
- package/dist/web-components/chunk-2OLVQUBU.js +193 -0
- package/dist/web-components/{chunk-OF6FFGOT.js → chunk-3AY2YKCM.js} +77 -21
- package/dist/web-components/chunk-3WAV6MDN.js +212 -0
- package/dist/web-components/{chunk-UUKSDCYS.js → chunk-47AT5KZD.js} +87 -15
- package/dist/web-components/chunk-4A2YYTYN.js +93 -0
- package/dist/web-components/{chunk-Q4T5BJT6.js → chunk-62NK3ZSC.js} +18 -3
- package/dist/web-components/chunk-66M6ETWD.js +77 -0
- package/dist/web-components/{chunk-NCPQ7AF3.js → chunk-6G22I2CK.js} +18 -3
- package/dist/web-components/{chunk-QDST57JC.js → chunk-7ZOJ7N4E.js} +14 -4
- package/dist/web-components/{chunk-QLT6MTZF.js → chunk-AIBOHNDU.js} +10 -0
- package/dist/web-components/{chunk-X3NQMBAQ.js → chunk-AP4RDUL2.js} +14 -3
- package/dist/web-components/{chunk-QWPACK5L.js → chunk-BPYKZVDK.js} +11 -1
- package/dist/web-components/{chunk-YLVTEY23.js → chunk-BTCMJQKB.js} +53 -21
- package/dist/web-components/chunk-CUKQ2L5B.js +245 -0
- package/dist/web-components/chunk-DIZDCL7Z.js +14 -0
- package/dist/web-components/{chunk-PC3IOQAH.js → chunk-DV5T43ZC.js} +27 -7
- package/dist/web-components/{chunk-2QA62WCQ.js → chunk-EF7IVYTY.js} +10 -0
- package/dist/web-components/{chunk-L3XHOMN6.js → chunk-F3ZDNHZZ.js} +26 -1
- package/dist/web-components/chunk-FEDABOR5.js +248 -0
- package/dist/web-components/{chunk-6I2DBFQ7.js → chunk-HGFCJXOQ.js} +36 -5
- package/dist/web-components/{chunk-SOQQ7RVJ.js → chunk-IPGHUCJT.js} +23 -8
- package/dist/web-components/chunk-ITD2MV5K.js +392 -0
- package/dist/web-components/{chunk-ARMKC4BQ.js → chunk-JH6LQKLK.js} +77 -5
- package/dist/web-components/chunk-JMCXAAFK.js +120 -0
- package/dist/web-components/{chunk-KUI55GFB.js → chunk-JUPCZVVW.js} +50 -18
- package/dist/web-components/chunk-JV5QT2OU.js +146 -0
- package/dist/web-components/{chunk-3GFWLSLS.js → chunk-KA3VPKMY.js} +29 -6
- package/dist/web-components/{chunk-CR5PGUHV.js → chunk-KJGRFDFI.js} +26 -11
- package/dist/web-components/{chunk-PY62FTRW.js → chunk-KZPRFY5P.js} +68 -33
- package/dist/web-components/{chunk-X46KL5BK.js → chunk-LDIPCTRN.js} +18 -1
- package/dist/web-components/chunk-M3ECULRH.js +174 -0
- package/dist/web-components/chunk-NBN2ENWJ.js +90 -0
- package/dist/web-components/chunk-NIN3JO6Q.js +189 -0
- package/dist/web-components/{chunk-2FGATTGT.js → chunk-NTQ4TCUT.js} +76 -55
- package/dist/web-components/chunk-OY74D4X6.js +302 -0
- package/dist/web-components/{chunk-3S42DZ7W.js → chunk-P24JYC5V.js} +7 -0
- package/dist/web-components/{chunk-BKIHQYJR.js → chunk-PXPPKCLT.js} +17 -2
- package/dist/web-components/{chunk-TQ7IQ4GZ.js → chunk-QEF2IRID.js} +71 -29
- package/dist/web-components/chunk-QZAIRJ75.js +111 -0
- package/dist/web-components/{chunk-6I3CFC26.js → chunk-RSZ3BWBK.js} +20 -5
- package/dist/web-components/chunk-RVF7W653.js +101 -0
- package/dist/web-components/chunk-SEO2F5KJ.js +234 -0
- package/dist/web-components/{chunk-VMMVWLXL.js → chunk-SFZ6Q4BP.js} +10 -0
- package/dist/web-components/{chunk-LY62SAVN.js → chunk-SYNLFKD3.js} +12 -2
- package/dist/web-components/chunk-TMWXRHGF.js +192 -0
- package/dist/web-components/{chunk-65JXQOBA.js → chunk-U3YZE6R6.js} +51 -7
- package/dist/web-components/chunk-UO7ROSHD.js +424 -0
- package/dist/web-components/chunk-VANAKB6D.js +152 -0
- package/dist/web-components/{chunk-PXZWYO6Z.js → chunk-VED2DFJ6.js} +108 -29
- package/dist/web-components/chunk-W6H5ID2U.js +161 -0
- package/dist/web-components/{chunk-FSZRP43R.js → chunk-WFVKU4GX.js} +21 -10
- package/dist/web-components/{chunk-OQTRTMAB.js → chunk-WIBAN7KP.js} +10 -0
- package/dist/web-components/chunk-XI7KRAH7.js +54 -0
- package/dist/web-components/{chunk-ORC56MDY.js → chunk-XIZWAWMJ.js} +45 -6
- package/dist/web-components/{chunk-FXURM6D5.js → chunk-YFAXGRPR.js} +10 -0
- package/dist/web-components/globals.js +1 -1
- package/dist/web-components/index.d.ts +77 -707
- package/dist/web-components/index.js +339 -2066
- package/dist/web-components/interaction/bds-accordion-item.js +1 -1
- package/dist/web-components/interaction/bds-accordion.js +1 -1
- package/dist/web-components/interaction/bds-button.d.ts +10 -2
- package/dist/web-components/interaction/bds-button.js +1 -1
- package/dist/web-components/interaction/bds-collapsible.d.ts +7 -2
- package/dist/web-components/interaction/bds-collapsible.js +1 -1
- package/dist/web-components/interaction/bds-dialog.d.ts +11 -3
- package/dist/web-components/interaction/bds-dialog.js +1 -1
- package/dist/web-components/interaction/bds-drawer.d.ts +7 -1
- package/dist/web-components/interaction/bds-drawer.js +1 -1
- package/dist/web-components/interaction/bds-dropdown-menu-item.d.ts +51 -0
- package/dist/web-components/interaction/bds-dropdown-menu-item.js +7 -0
- package/dist/web-components/interaction/bds-dropdown-menu.d.ts +70 -0
- package/dist/web-components/interaction/bds-dropdown-menu.js +7 -0
- package/dist/web-components/interaction/bds-popover.d.ts +14 -3
- package/dist/web-components/interaction/bds-popover.js +1 -1
- package/dist/web-components/interaction/bds-skip-link.js +1 -1
- package/dist/web-components/interaction/bds-tab-panel.js +1 -1
- package/dist/web-components/interaction/bds-tab.js +1 -1
- package/dist/web-components/interaction/bds-tabs.d.ts +1 -0
- package/dist/web-components/interaction/bds-tabs.js +1 -1
- package/dist/web-components/interaction/bds-toast-provider.js +1 -1
- package/dist/web-components/interaction/bds-tooltip.d.ts +19 -3
- package/dist/web-components/interaction/bds-tooltip.js +1 -1
- package/dist/web-components/interaction/form/bds-checkbox-group.d.ts +83 -0
- package/dist/web-components/interaction/form/bds-checkbox-group.js +7 -0
- package/dist/web-components/interaction/form/bds-checkbox.d.ts +6 -0
- package/dist/web-components/interaction/form/bds-checkbox.js +1 -1
- package/dist/web-components/interaction/form/bds-combobox.d.ts +18 -0
- package/dist/web-components/interaction/form/bds-combobox.js +1 -1
- package/dist/web-components/interaction/form/bds-field-group.d.ts +89 -0
- package/dist/web-components/interaction/form/bds-field-group.js +7 -0
- package/dist/web-components/interaction/form/bds-file-input.d.ts +1 -1
- package/dist/web-components/interaction/form/bds-file-input.js +1 -1
- package/dist/web-components/interaction/form/bds-form-input.d.ts +92 -0
- package/dist/web-components/interaction/form/bds-form-input.js +7 -0
- package/dist/web-components/interaction/form/bds-number-input.d.ts +18 -1
- package/dist/web-components/interaction/form/bds-number-input.js +1 -1
- package/dist/web-components/interaction/form/bds-radio-group.d.ts +81 -0
- package/dist/web-components/interaction/form/bds-radio-group.js +7 -0
- package/dist/web-components/interaction/form/bds-radio.d.ts +34 -6
- package/dist/web-components/interaction/form/bds-radio.js +1 -1
- package/dist/web-components/interaction/form/bds-segmented-control.d.ts +4 -4
- package/dist/web-components/interaction/form/bds-segmented-control.js +1 -1
- package/dist/web-components/interaction/form/bds-select.d.ts +22 -1
- package/dist/web-components/interaction/form/bds-select.js +1 -1
- package/dist/web-components/interaction/form/bds-slider.d.ts +42 -10
- package/dist/web-components/interaction/form/bds-slider.js +1 -1
- package/dist/web-components/interaction/form/bds-switch.d.ts +20 -0
- package/dist/web-components/interaction/form/bds-switch.js +1 -1
- package/dist/web-components/interaction/form/bds-textarea.d.ts +23 -3
- package/dist/web-components/interaction/form/bds-textarea.js +1 -1
- package/dist/web-components/layout/bds-grid-item.d.ts +63 -0
- package/dist/web-components/layout/bds-grid-item.js +7 -0
- package/dist/web-components/layout/bds-grid.d.ts +108 -0
- package/dist/web-components/layout/bds-grid.js +7 -0
- package/dist/web-components/ui/bds-alert.d.ts +16 -2
- package/dist/web-components/ui/bds-alert.js +1 -1
- package/dist/web-components/ui/bds-avatar.d.ts +2 -1
- package/dist/web-components/ui/bds-avatar.js +1 -1
- package/dist/web-components/ui/bds-badge.js +1 -1
- package/dist/web-components/ui/bds-breadcrumb.d.ts +3 -1
- package/dist/web-components/ui/bds-breadcrumb.js +1 -1
- package/dist/web-components/ui/bds-button-container.d.ts +52 -0
- package/dist/web-components/ui/bds-button-container.js +7 -0
- package/dist/web-components/ui/bds-button-group.d.ts +21 -0
- package/dist/web-components/ui/bds-button-group.js +8 -0
- package/dist/web-components/ui/bds-calendar.d.ts +121 -0
- package/dist/web-components/ui/bds-calendar.js +7 -0
- package/dist/web-components/ui/bds-card.d.ts +12 -3
- package/dist/web-components/ui/bds-card.js +1 -1
- package/dist/web-components/ui/bds-carousel.d.ts +98 -0
- package/dist/web-components/ui/bds-carousel.js +7 -0
- package/dist/web-components/ui/bds-description-list.js +1 -1
- package/dist/web-components/ui/bds-icon-wrapper.d.ts +0 -9
- package/dist/web-components/ui/bds-icon-wrapper.js +1 -1
- package/dist/web-components/ui/bds-link.js +1 -1
- package/dist/web-components/ui/bds-loading.js +1 -1
- package/dist/web-components/ui/bds-notification-banner.js +1 -1
- package/dist/web-components/ui/bds-pagination.d.ts +47 -0
- package/dist/web-components/ui/bds-pagination.js +7 -0
- package/dist/web-components/ui/bds-progress-circle.d.ts +3 -1
- package/dist/web-components/ui/bds-progress-circle.js +1 -1
- package/dist/web-components/ui/bds-progress.d.ts +3 -1
- package/dist/web-components/ui/bds-progress.js +1 -1
- package/dist/web-components/ui/bds-rating.js +1 -1
- package/dist/web-components/ui/bds-section-header.d.ts +18 -4
- 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-skeleton.js +1 -1
- package/dist/web-components/ui/bds-table.d.ts +52 -0
- package/dist/web-components/ui/bds-table.js +7 -0
- package/dist/web-components/ui/bds-typography.d.ts +1 -0
- package/dist/web-components/ui/bds-typography.js +1 -1
- package/package.json +60 -3
- package/src/components/interaction/Button/Button.module.css +23 -14
- package/src/components/interaction/Button/Button.spec.tsx +20 -0
- package/src/components/interaction/Button/Button.tsx +2 -2
- package/src/components/interaction/Command/Command.mdx +5 -2
- package/src/components/interaction/Command/Command.module.css +23 -7
- package/src/components/interaction/Command/Command.spec.tsx +35 -0
- package/src/components/interaction/Command/Command.tsx +15 -4
- package/src/components/interaction/Command/index.ts +1 -1
- package/src/components/interaction/Dialog/Dialog.mdx +4 -4
- package/src/components/interaction/Dialog/Dialog.module.css +20 -0
- package/src/components/interaction/Dialog/Dialog.spec.tsx +20 -0
- package/src/components/interaction/Dialog/Dialog.tsx +8 -3
- package/src/components/interaction/Dialog/index.ts +1 -0
- package/src/components/interaction/Drawer/Drawer.mdx +5 -2
- package/src/components/interaction/Drawer/Drawer.module.css +34 -3
- package/src/components/interaction/Drawer/Drawer.spec.tsx +67 -1
- package/src/components/interaction/Drawer/Drawer.tsx +30 -12
- package/src/components/interaction/Drawer/index.ts +1 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.mdx +2 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.spec.tsx +34 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +15 -5
- package/src/components/interaction/DropdownMenu/index.ts +1 -0
- package/src/components/interaction/Popover/Popover.mdx +6 -2
- package/src/components/interaction/Popover/Popover.module.css +26 -18
- package/src/components/interaction/Popover/Popover.spec.tsx +38 -0
- package/src/components/interaction/Popover/Popover.tsx +19 -5
- package/src/components/interaction/Popover/index.ts +1 -0
- package/src/components/interaction/Rating/Rating.mdx +1 -1
- package/src/components/interaction/Rating/Rating.module.css +2 -2
- package/src/components/interaction/Rating/Rating.tsx +1 -1
- package/src/components/interaction/Rating/index.ts +1 -0
- package/src/components/interaction/Toast/Toast.mdx +3 -0
- package/src/components/interaction/Toast/Toast.module.css +35 -5
- package/src/components/interaction/Toast/Toast.spec.tsx +69 -1
- package/src/components/interaction/Toast/Toast.tsx +15 -9
- package/src/components/interaction/form/Checkbox/Checkbox.mdx +3 -0
- package/src/components/interaction/form/Checkbox/Checkbox.module.css +13 -6
- package/src/components/interaction/form/Checkbox/Checkbox.spec.tsx +6 -0
- package/src/components/interaction/form/Checkbox/Checkbox.tsx +1 -1
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.mdx +16 -1
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.module.css +24 -8
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.spec.tsx +32 -2
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +14 -2
- package/src/components/interaction/form/Combobox/Combobox.mdx +11 -4
- package/src/components/interaction/form/Combobox/Combobox.module.css +6 -6
- package/src/components/interaction/form/Combobox/Combobox.spec.tsx +47 -0
- package/src/components/interaction/form/Combobox/Combobox.tsx +10 -5
- package/src/components/interaction/form/FieldGroup/FieldGroup.mdx +9 -10
- package/src/components/interaction/form/FieldGroup/FieldGroup.module.css +57 -23
- package/src/components/interaction/form/FieldGroup/FieldGroup.spec.tsx +129 -7
- package/src/components/interaction/form/FieldGroup/FieldGroup.tsx +72 -12
- package/src/components/interaction/form/FileInput/FileInput.mdx +9 -1
- package/src/components/interaction/form/FileInput/FileInput.module.css +8 -8
- package/src/components/interaction/form/FileInput/FileInput.spec.tsx +89 -1
- package/src/components/interaction/form/FileInput/FileInput.tsx +6 -2
- package/src/components/interaction/form/FormInput/FormInput.mdx +9 -5
- package/src/components/interaction/form/FormInput/FormInput.module.css +17 -8
- package/src/components/interaction/form/FormInput/FormInput.spec.tsx +20 -0
- package/src/components/interaction/form/FormInput/FormInput.tsx +7 -2
- package/src/components/interaction/form/NumberInput/NumberInput.mdx +5 -1
- package/src/components/interaction/form/NumberInput/NumberInput.module.css +6 -6
- package/src/components/interaction/form/NumberInput/NumberInput.spec.tsx +51 -0
- package/src/components/interaction/form/NumberInput/NumberInput.tsx +36 -8
- package/src/components/interaction/form/Radio/Radio.mdx +8 -1
- package/src/components/interaction/form/Radio/Radio.module.css +20 -7
- package/src/components/interaction/form/Radio/Radio.spec.tsx +15 -0
- package/src/components/interaction/form/Radio/Radio.tsx +10 -4
- package/src/components/interaction/form/RadioGroup/RadioGroup.mdx +16 -1
- package/src/components/interaction/form/RadioGroup/RadioGroup.module.css +24 -8
- package/src/components/interaction/form/RadioGroup/RadioGroup.spec.tsx +32 -2
- package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +14 -2
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.mdx +9 -3
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +25 -12
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.spec.tsx +86 -5
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +44 -4
- package/src/components/interaction/form/Select/Select.mdx +10 -4
- package/src/components/interaction/form/Select/Select.module.css +4 -4
- package/src/components/interaction/form/Select/Select.spec.tsx +34 -0
- package/src/components/interaction/form/Select/Select.tsx +21 -9
- package/src/components/interaction/form/Slider/Slider.mdx +4 -1
- package/src/components/interaction/form/Slider/Slider.module.css +32 -8
- package/src/components/interaction/form/Slider/Slider.spec.tsx +29 -0
- package/src/components/interaction/form/Slider/Slider.tsx +9 -7
- package/src/components/interaction/form/Switch/Switch.mdx +2 -2
- package/src/components/interaction/form/Switch/Switch.module.css +12 -13
- package/src/components/interaction/form/Switch/Switch.spec.tsx +20 -0
- package/src/components/interaction/form/Switch/Switch.tsx +2 -1
- package/src/components/interaction/form/Textarea/Textarea.mdx +5 -0
- package/src/components/interaction/form/Textarea/Textarea.module.css +5 -5
- package/src/components/interaction/form/Textarea/Textarea.spec.tsx +13 -0
- package/src/components/interaction/form/Textarea/Textarea.tsx +3 -1
- package/src/components/interaction/form/atoms/Message.module.css +1 -1
- package/src/components/interaction/form/atoms/Message.spec.tsx +29 -0
- package/src/components/interaction/form/atoms/Message.tsx +1 -1
- package/src/components/layout/ButtonGroup/ButtonGroup.mdx +11 -0
- package/src/components/layout/ButtonGroup/ButtonGroup.module.css +1 -5
- package/src/components/layout/ButtonGroup/ButtonGroup.stories.tsx +14 -0
- package/src/components/layout/Card/Card.mdx +6 -1
- package/src/components/layout/Card/Card.module.css +26 -5
- package/src/components/layout/Card/Card.spec.tsx +7 -0
- package/src/components/layout/Card/Card.tsx +4 -3
- package/src/components/layout/Grid/Grid.spec.tsx +44 -0
- package/src/components/layout/Grid/Grid.tsx +5 -5
- package/src/components/layout/Grid/GridItem.tsx +6 -6
- package/src/components/layout/Grid/masonry.ts +31 -5
- package/src/components/layout/Grid/useIsomorphicLayoutEffect.ts +11 -0
- package/src/components/layout/IconWrapper/IconWrapper.mdx +8 -2
- package/src/components/layout/IconWrapper/IconWrapper.module.css +9 -6
- package/src/components/layout/SectionHeader/SectionHeader.mdx +4 -1
- package/src/components/layout/SectionHeader/SectionHeader.module.css +10 -9
- package/src/components/ui/Accordion/Accordion.mdx +4 -1
- package/src/components/ui/Accordion/Accordion.module.css +2 -1
- package/src/components/ui/Alert/Alert.mdx +21 -4
- package/src/components/ui/Alert/Alert.module.css +9 -4
- package/src/components/ui/Alert/Alert.spec.tsx +20 -0
- package/src/components/ui/Avatar/Avatar.mdx +8 -3
- package/src/components/ui/Avatar/Avatar.module.css +13 -8
- package/src/components/ui/Badge/Badge.mdx +3 -1
- package/src/components/ui/Breadcrumb/Breadcrumb.mdx +3 -0
- package/src/components/ui/Breadcrumb/Breadcrumb.module.css +4 -0
- package/src/components/ui/Breadcrumb/Breadcrumb.spec.tsx +12 -0
- package/src/components/ui/Breadcrumb/Breadcrumb.tsx +11 -3
- package/src/components/ui/Calendar/Calendar.mdx +6 -0
- package/src/components/ui/Calendar/Calendar.module.css +68 -19
- package/src/components/ui/Calendar/Calendar.spec.tsx +139 -24
- package/src/components/ui/Calendar/Calendar.stories.tsx +14 -0
- package/src/components/ui/Calendar/Calendar.tsx +143 -154
- package/src/components/ui/Calendar/CalendarRange.mdx +39 -0
- package/src/components/ui/Calendar/CalendarRange.spec.tsx +119 -0
- package/src/components/ui/Calendar/CalendarRange.stories.tsx +51 -0
- package/src/components/ui/Calendar/CalendarRange.tsx +213 -0
- package/src/components/ui/Calendar/MonthGrid.tsx +141 -0
- package/src/components/ui/Calendar/calendar-core.spec.ts +176 -0
- package/src/components/ui/Calendar/calendar-core.ts +164 -0
- package/src/components/ui/Calendar/index.ts +1 -0
- package/src/components/ui/Carousel/Carousel.mdx +8 -3
- package/src/components/ui/Carousel/Carousel.module.css +7 -5
- package/src/components/ui/Carousel/Carousel.spec.tsx +67 -0
- package/src/components/ui/Carousel/Carousel.tsx +19 -7
- package/src/components/ui/Collapsible/Collapsible.mdx +17 -6
- package/src/components/ui/Collapsible/Collapsible.module.css +7 -2
- package/src/components/ui/Collapsible/Collapsible.spec.tsx +40 -0
- package/src/components/ui/Collapsible/Collapsible.tsx +19 -2
- package/src/components/ui/DescriptionList/DescriptionList.mdx +2 -0
- package/src/components/ui/Link/Link.mdx +4 -2
- package/src/components/ui/Loading/Loading.mdx +6 -3
- package/src/components/ui/Loading/Loading.module.css +7 -7
- package/src/components/ui/Loading/Loading.spec.tsx +12 -0
- package/src/components/ui/Loading/Loading.tsx +8 -3
- package/src/components/ui/NotificationBanner/NotificationBanner.mdx +4 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.module.css +5 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.spec.tsx +20 -0
- package/src/components/ui/Pagination/Pagination.mdx +29 -7
- package/src/components/ui/Pagination/Pagination.module.css +15 -14
- package/src/components/ui/Progress/Progress.mdx +6 -2
- package/src/components/ui/Progress/Progress.module.css +1 -1
- package/src/components/ui/Progress/Progress.spec.tsx +17 -0
- package/src/components/ui/Progress/Progress.tsx +2 -2
- package/src/components/ui/ProgressCircle/ProgressCircle.mdx +8 -4
- package/src/components/ui/ProgressCircle/ProgressCircle.module.css +2 -2
- package/src/components/ui/ProgressCircle/ProgressCircle.spec.tsx +11 -1
- package/src/components/ui/ProgressCircle/ProgressCircle.tsx +1 -1
- package/src/components/ui/Separator/Separator.mdx +7 -0
- package/src/components/ui/Separator/Separator.module.css +1 -0
- package/src/components/ui/Skeleton/Skeleton.mdx +17 -0
- package/src/components/ui/Skeleton/Skeleton.module.css +12 -5
- package/src/components/ui/SkipLink/SkipLink.mdx +19 -0
- package/src/components/ui/SkipLink/SkipLink.module.css +8 -7
- package/src/components/ui/SkipLink/SkipLink.spec.tsx +28 -0
- package/src/components/ui/Table/Table.mdx +14 -0
- package/src/components/ui/Table/Table.module.css +13 -8
- package/src/components/ui/Table/Table.spec.tsx +56 -0
- package/src/components/ui/Table/Table.tsx +44 -6
- package/src/components/ui/Tabs/Tabs.mdx +16 -0
- package/src/components/ui/Tabs/Tabs.module.css +3 -3
- package/src/components/ui/Tabs/Tabs.spec.tsx +20 -0
- package/src/components/ui/Tabs/Tabs.tsx +5 -1
- package/src/components/ui/Tooltip/Tooltip.mdx +9 -0
- package/src/components/ui/Tooltip/Tooltip.module.css +32 -12
- package/src/components/ui/Tooltip/Tooltip.spec.tsx +56 -0
- package/src/components/ui/Tooltip/Tooltip.tsx +26 -7
- package/src/components/ui/Typography/Typography.mdx +7 -0
- package/src/components/ui/Typography/Typography.module.css +5 -5
- package/src/index.ts +7 -3
- package/src/stories/DesignSystem/Borders.mdx +14 -9
- package/src/stories/DesignSystem/Colors.mdx +43 -41
- package/src/stories/DesignSystem/Overview.mdx +4 -3
- package/src/stories/Introduction.mdx +5 -5
- package/src/web-components/globals.ts +7 -0
- package/src/web-components/index.ts +2 -0
- package/src/web-components/interaction/BdsAccordion.mdx +10 -1
- package/src/web-components/interaction/BdsButton.mdx +15 -4
- package/src/web-components/interaction/BdsButton.stories.tsx +1 -1
- package/src/web-components/interaction/BdsCollapsible.mdx +3 -3
- package/src/web-components/interaction/BdsDialog.mdx +12 -7
- package/src/web-components/interaction/BdsDialog.stories.tsx +2 -2
- package/src/web-components/interaction/BdsDrawer.mdx +9 -6
- package/src/web-components/interaction/BdsDropdownMenu.mdx +20 -1
- package/src/web-components/interaction/BdsPopover.mdx +7 -3
- package/src/web-components/interaction/BdsSkipLink.mdx +14 -1
- package/src/web-components/interaction/BdsTabs.mdx +18 -1
- package/src/web-components/interaction/BdsToastProvider.mdx +21 -3
- package/src/web-components/interaction/BdsTooltip.mdx +4 -4
- package/src/web-components/interaction/bds-accordion-item.ts +10 -0
- package/src/web-components/interaction/bds-accordion.spec.ts +41 -0
- package/src/web-components/interaction/bds-accordion.ts +28 -2
- package/src/web-components/interaction/bds-button.spec.ts +81 -0
- package/src/web-components/interaction/bds-button.ts +55 -8
- package/src/web-components/interaction/bds-collapsible.spec.ts +93 -2
- package/src/web-components/interaction/bds-collapsible.ts +49 -7
- package/src/web-components/interaction/bds-dialog.spec.ts +49 -0
- package/src/web-components/interaction/bds-dialog.ts +68 -11
- package/src/web-components/interaction/bds-drawer.spec.ts +93 -4
- package/src/web-components/interaction/bds-drawer.ts +54 -21
- package/src/web-components/interaction/bds-dropdown-menu-item.ts +12 -1
- package/src/web-components/interaction/bds-dropdown-menu.spec.ts +63 -0
- package/src/web-components/interaction/bds-dropdown-menu.ts +45 -7
- package/src/web-components/interaction/bds-popover.spec.ts +80 -6
- package/src/web-components/interaction/bds-popover.ts +83 -32
- package/src/web-components/interaction/bds-skip-link.spec.ts +18 -0
- package/src/web-components/interaction/bds-skip-link.ts +29 -8
- package/src/web-components/interaction/bds-tab-panel.ts +10 -0
- package/src/web-components/interaction/bds-tab.spec.ts +25 -0
- package/src/web-components/interaction/bds-tab.ts +20 -5
- package/src/web-components/interaction/bds-tabs.spec.ts +92 -5
- package/src/web-components/interaction/bds-tabs.ts +42 -1
- package/src/web-components/interaction/bds-toast-provider.spec.ts +43 -0
- package/src/web-components/interaction/bds-toast-provider.ts +50 -21
- package/src/web-components/interaction/bds-tooltip.spec.ts +71 -0
- package/src/web-components/interaction/bds-tooltip.ts +100 -5
- package/src/web-components/interaction/form/BdsCheckbox.mdx +20 -1
- package/src/web-components/interaction/form/BdsCheckbox.stories.tsx +8 -0
- package/src/web-components/interaction/form/BdsCheckboxGroup.mdx +4 -3
- package/src/web-components/interaction/form/BdsCombobox.mdx +39 -2
- package/src/web-components/interaction/form/BdsCombobox.stories.tsx +31 -0
- package/src/web-components/interaction/form/BdsFieldGroup.mdx +8 -3
- package/src/web-components/interaction/form/BdsFieldGroup.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsFileInput.mdx +26 -5
- package/src/web-components/interaction/form/BdsFormInput.mdx +16 -1
- package/src/web-components/interaction/form/BdsNumberInput.mdx +34 -2
- package/src/web-components/interaction/form/BdsNumberInput.stories.tsx +27 -3
- package/src/web-components/interaction/form/BdsRadio.mdx +39 -12
- package/src/web-components/interaction/form/BdsRadio.stories.tsx +26 -0
- package/src/web-components/interaction/form/BdsRadioGroup.mdx +9 -4
- package/src/web-components/interaction/form/BdsSegmentedControl.mdx +37 -11
- package/src/web-components/interaction/form/BdsSelect.mdx +22 -4
- package/src/web-components/interaction/form/BdsSelect.stories.tsx +33 -0
- package/src/web-components/interaction/form/BdsSlider.mdx +36 -19
- package/src/web-components/interaction/form/BdsSlider.stories.tsx +24 -0
- package/src/web-components/interaction/form/BdsSwitch.mdx +38 -11
- package/src/web-components/interaction/form/BdsSwitch.stories.tsx +16 -0
- package/src/web-components/interaction/form/BdsTextarea.mdx +42 -16
- package/src/web-components/interaction/form/BdsTextarea.stories.tsx +21 -0
- package/src/web-components/interaction/form/bds-checkbox-group.spec.ts +99 -0
- package/src/web-components/interaction/form/bds-checkbox-group.ts +95 -9
- package/src/web-components/interaction/form/bds-checkbox.spec.ts +55 -0
- package/src/web-components/interaction/form/bds-checkbox.ts +30 -5
- package/src/web-components/interaction/form/bds-combobox.spec.ts +79 -0
- package/src/web-components/interaction/form/bds-combobox.ts +101 -16
- package/src/web-components/interaction/form/bds-field-group.spec.ts +158 -0
- package/src/web-components/interaction/form/bds-field-group.ts +167 -23
- package/src/web-components/interaction/form/bds-file-input.spec.ts +73 -5
- package/src/web-components/interaction/form/bds-file-input.ts +71 -34
- package/src/web-components/interaction/form/bds-form-input.spec.ts +22 -0
- package/src/web-components/interaction/form/bds-form-input.ts +27 -16
- package/src/web-components/interaction/form/bds-number-input.spec.ts +49 -0
- package/src/web-components/interaction/form/bds-number-input.ts +118 -29
- package/src/web-components/interaction/form/bds-radio-group.spec.ts +57 -0
- package/src/web-components/interaction/form/bds-radio-group.ts +60 -12
- package/src/web-components/interaction/form/bds-radio.spec.ts +123 -0
- package/src/web-components/interaction/form/bds-radio.ts +135 -22
- package/src/web-components/interaction/form/bds-segmented-control.spec.ts +55 -0
- package/src/web-components/interaction/form/bds-segmented-control.ts +81 -59
- package/src/web-components/interaction/form/bds-select.spec.ts +57 -0
- package/src/web-components/interaction/form/bds-select.ts +95 -7
- package/src/web-components/interaction/form/bds-slider.spec.ts +83 -0
- package/src/web-components/interaction/form/bds-slider.ts +154 -26
- package/src/web-components/interaction/form/bds-switch.spec.ts +68 -0
- package/src/web-components/interaction/form/bds-switch.ts +87 -20
- package/src/web-components/interaction/form/bds-textarea.spec.ts +71 -0
- package/src/web-components/interaction/form/bds-textarea.ts +108 -7
- package/src/web-components/layout/BdsGrid.mdx +6 -2
- package/src/web-components/layout/BdsGrid.stories.tsx +4 -0
- package/src/web-components/layout/bds-grid-item.spec.ts +21 -0
- package/src/web-components/layout/bds-grid-item.ts +17 -3
- package/src/web-components/layout/bds-grid.spec.ts +98 -0
- package/src/web-components/layout/bds-grid.ts +139 -19
- package/src/web-components/ui/BdsAlert.mdx +6 -5
- package/src/web-components/ui/BdsAlert.stories.tsx +6 -6
- package/src/web-components/ui/BdsAvatar.mdx +4 -2
- package/src/web-components/ui/BdsBreadcrumb.mdx +3 -2
- package/src/web-components/ui/BdsButtonContainer.mdx +64 -0
- package/src/web-components/ui/BdsButtonContainer.stories.tsx +84 -0
- package/src/web-components/ui/BdsCalendar.mdx +29 -4
- package/src/web-components/ui/BdsCalendar.stories.tsx +16 -18
- package/src/web-components/ui/BdsCalendarRange.mdx +71 -0
- package/src/web-components/ui/BdsCalendarRange.stories.tsx +31 -0
- package/src/web-components/ui/BdsCard.mdx +7 -3
- package/src/web-components/ui/BdsCard.stories.tsx +17 -1
- package/src/web-components/ui/BdsCarousel.mdx +40 -12
- package/src/web-components/ui/BdsDescriptionList.mdx +6 -1
- package/src/web-components/ui/BdsIconWrapper.mdx +3 -2
- package/src/web-components/ui/BdsLink.mdx +14 -2
- package/src/web-components/ui/BdsNotificationBanner.mdx +18 -0
- package/src/web-components/ui/BdsPagination.mdx +40 -1
- package/src/web-components/ui/BdsProgress.mdx +8 -2
- package/src/web-components/ui/BdsProgressCircle.mdx +5 -2
- package/src/web-components/ui/BdsRating.mdx +7 -0
- package/src/web-components/ui/BdsSectionHeader.mdx +22 -3
- package/src/web-components/ui/BdsSectionHeader.stories.tsx +15 -13
- package/src/web-components/ui/BdsTable.mdx +23 -2
- package/src/web-components/ui/BdsTable.stories.tsx +8 -2
- package/src/web-components/ui/BdsTypography.mdx +17 -1
- package/src/web-components/ui/bds-alert.spec.ts +17 -2
- package/src/web-components/ui/bds-alert.ts +31 -5
- package/src/web-components/ui/bds-avatar.spec.ts +29 -0
- package/src/web-components/ui/bds-avatar.ts +29 -11
- package/src/web-components/ui/bds-badge.spec.ts +5 -0
- package/src/web-components/ui/bds-badge.ts +11 -1
- package/src/web-components/ui/bds-breadcrumb.spec.ts +30 -0
- package/src/web-components/ui/bds-breadcrumb.ts +26 -4
- package/src/web-components/ui/bds-button-container.spec.ts +39 -0
- package/src/web-components/ui/bds-button-container.ts +123 -0
- package/src/web-components/ui/bds-button-group.spec.ts +14 -20
- package/src/web-components/ui/bds-button-group.ts +13 -64
- package/src/web-components/ui/bds-calendar-range.spec.ts +123 -0
- package/src/web-components/ui/bds-calendar-range.ts +307 -0
- package/src/web-components/ui/bds-calendar.spec.ts +176 -0
- package/src/web-components/ui/bds-calendar.ts +186 -344
- package/src/web-components/ui/bds-card.spec.ts +32 -0
- package/src/web-components/ui/bds-card.ts +34 -9
- package/src/web-components/ui/bds-carousel.spec.ts +147 -0
- package/src/web-components/ui/bds-carousel.ts +109 -32
- package/src/web-components/ui/bds-description-list.spec.ts +15 -0
- package/src/web-components/ui/bds-description-list.ts +37 -3
- package/src/web-components/ui/bds-icon-wrapper.spec.ts +12 -0
- package/src/web-components/ui/bds-icon-wrapper.ts +15 -14
- package/src/web-components/ui/bds-link.spec.ts +17 -0
- package/src/web-components/ui/bds-link.ts +26 -11
- package/src/web-components/ui/bds-loading.ts +10 -0
- package/src/web-components/ui/bds-notification-banner.spec.ts +13 -0
- package/src/web-components/ui/bds-notification-banner.ts +18 -1
- package/src/web-components/ui/bds-pagination.spec.ts +44 -0
- package/src/web-components/ui/bds-pagination.ts +39 -19
- package/src/web-components/ui/bds-progress-circle.spec.ts +21 -0
- package/src/web-components/ui/bds-progress-circle.ts +17 -5
- package/src/web-components/ui/bds-progress.spec.ts +21 -0
- package/src/web-components/ui/bds-progress.ts +17 -4
- package/src/web-components/ui/bds-rating.spec.ts +6 -0
- package/src/web-components/ui/bds-rating.ts +12 -2
- package/src/web-components/ui/bds-section-header.spec.ts +39 -8
- package/src/web-components/ui/bds-section-header.ts +47 -13
- package/src/web-components/ui/bds-separator.ts +10 -0
- package/src/web-components/ui/bds-skeleton.ts +10 -0
- package/src/web-components/ui/bds-table.spec.ts +63 -0
- package/src/web-components/ui/bds-table.ts +88 -28
- package/src/web-components/ui/bds-typography.spec.ts +47 -0
- package/src/web-components/ui/bds-typography.ts +28 -10
- package/src/web-components/ui/calendar-styles.ts +94 -0
- package/dist/web-components/chunk-7YURXHTS.js +0 -147
- package/dist/web-components/chunk-A3RLT3AS.js +0 -49
- package/dist/web-components/chunk-AGKJTMJI.js +0 -56
- package/dist/web-components/chunk-BQLZ7I4G.js +0 -56
- package/dist/web-components/chunk-DZOEPVFT.js +0 -107
- package/dist/web-components/chunk-JYYRJS2D.js +0 -118
- package/dist/web-components/chunk-Y4MAXTMA.js +0 -80
- package/dist/web-components/chunk-Y7V7LM6R.js +0 -98
- package/src/web-components/ui/BdsButtonGroup.mdx +0 -69
- package/src/web-components/ui/BdsButtonGroup.stories.tsx +0 -66
|
@@ -6,18 +6,20 @@
|
|
|
6
6
|
flex-direction: column;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
/* --input_* below are deprecated aliases for --formInput_*, kept working for one major. */
|
|
9
10
|
.input {
|
|
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);
|
|
11
|
+
font-family: var(--formInput_font-family, var(--bds-font_family--body));
|
|
12
|
+
font-size: var(--formInput_font-size, var(--bds-font_size--body));
|
|
13
|
+
padding: var(--formInput_padding, var(--bds-space_s));
|
|
14
|
+
border: var(--formInput_border, var(--input_border, none));
|
|
14
15
|
--bdc_color: currentcolor;
|
|
15
16
|
--bdc-outline_radius: var(--bds-border_radius--full);
|
|
16
17
|
|
|
17
|
-
border-radius: var(--input_radius, var(--bdc-outline_radius));
|
|
18
|
-
box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color),
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
border-radius: var(--formInput_radius, var(--input_radius, var(--bdc-outline_radius)));
|
|
19
|
+
box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color),
|
|
20
|
+
var(--formInput_shadow, var(--input_shadow, var(--bdc-outline_shadow)));
|
|
21
|
+
color: var(--formInput_color, var(--input_color, var(--bds-color_on-bg)));
|
|
22
|
+
background-color: var(--formInput_color_bg, var(--input_color_bg, var(--bds-color_bg)));
|
|
21
23
|
transition: --bdc_color var(--bds-animation_transition-duration) var(--bds-animation_easing),
|
|
22
24
|
background-color var(--bds-animation_transition-duration) var(--bds-animation_easing);
|
|
23
25
|
}
|
|
@@ -31,6 +33,13 @@
|
|
|
31
33
|
outline-offset: var(--bds-outline_offset);
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
/* Disabled — also matches when an ancestor <fieldset disabled> (e.g. a
|
|
37
|
+
disabled FieldGroup) propagates the disabled state to this input. */
|
|
38
|
+
.input:disabled {
|
|
39
|
+
cursor: not-allowed;
|
|
40
|
+
opacity: 0.5;
|
|
41
|
+
}
|
|
42
|
+
|
|
34
43
|
.inputError {
|
|
35
44
|
--bdc_color: var(--bdc_color--error);
|
|
36
45
|
}
|
|
@@ -40,4 +40,24 @@ describe('FormInput', () => {
|
|
|
40
40
|
render(<FormInput label="Name" name="name" disabled />);
|
|
41
41
|
expect(screen.getByRole('textbox')).toBeDisabled();
|
|
42
42
|
});
|
|
43
|
+
|
|
44
|
+
it('keeps the input associated with its label when a consumer id prop is passed', () => {
|
|
45
|
+
render(<FormInput label="Email" name="email" id="consumer-id" />);
|
|
46
|
+
expect(screen.getByLabelText('Email')).toBeInTheDocument();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('applies aria-label from the deprecated ariaLabel prop', () => {
|
|
50
|
+
render(<FormInput label="Email" name="email" ariaLabel="Email address" />);
|
|
51
|
+
expect(screen.getByRole('textbox')).toHaveAccessibleName('Email address');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('applies aria-label passed as a native attribute', () => {
|
|
55
|
+
render(<FormInput label="Email" name="email" aria-label="Email address" />);
|
|
56
|
+
expect(screen.getByRole('textbox')).toHaveAccessibleName('Email address');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('prefers the deprecated ariaLabel prop over a native aria-label when both are passed', () => {
|
|
60
|
+
render(<FormInput label="Email" name="email" ariaLabel="Deprecated label" aria-label="Native label" />);
|
|
61
|
+
expect(screen.getByRole('textbox')).toHaveAccessibleName('Deprecated label');
|
|
62
|
+
});
|
|
43
63
|
});
|
|
@@ -10,6 +10,10 @@ interface FormInputProps extends WithClassName, Omit<InputHTMLAttributes<HTMLInp
|
|
|
10
10
|
label: ReactNode;
|
|
11
11
|
name: string;
|
|
12
12
|
type?: Exclude<HTMLInputTypeAttribute, 'checkbox' | 'radio' | 'file'>;
|
|
13
|
+
/**
|
|
14
|
+
* @deprecated Use the native `aria-label` attribute instead. Kept working for backwards
|
|
15
|
+
* compatibility; will be removed in a later major.
|
|
16
|
+
*/
|
|
13
17
|
ariaLabel?: string;
|
|
14
18
|
error?: string;
|
|
15
19
|
hint?: string;
|
|
@@ -23,6 +27,7 @@ export function FormInput({
|
|
|
23
27
|
hint,
|
|
24
28
|
className,
|
|
25
29
|
required,
|
|
30
|
+
'aria-label': nativeAriaLabel,
|
|
26
31
|
...props
|
|
27
32
|
}: FormInputProps) {
|
|
28
33
|
const id = name + useId();
|
|
@@ -34,15 +39,15 @@ export function FormInput({
|
|
|
34
39
|
<InputContainer className={cn(css.formGroup, className)}>
|
|
35
40
|
<Label id={id} label={label} />
|
|
36
41
|
<input
|
|
42
|
+
{...props}
|
|
37
43
|
aria-invalid={!!error}
|
|
38
44
|
aria-describedby={describedBy}
|
|
39
|
-
aria-label={ariaLabel}
|
|
45
|
+
aria-label={ariaLabel ?? nativeAriaLabel}
|
|
40
46
|
aria-required={required || undefined}
|
|
41
47
|
id={id}
|
|
42
48
|
name={name}
|
|
43
49
|
required={required}
|
|
44
50
|
className={cn(css.input, error && css.inputError)}
|
|
45
|
-
{...props}
|
|
46
51
|
/>
|
|
47
52
|
<Message inputId={id} type="error" message={error} />
|
|
48
53
|
<Message inputId={id} type="hint" message={hint} />
|
|
@@ -47,6 +47,9 @@ Numeric text input with increment/decrement stepper buttons. Enforces `min`, `ma
|
|
|
47
47
|
<tr><td>`--numberInput_stepper-bg`</td><td>`var(--bds-color_bg--subtle)`</td><td>Stepper button background</td></tr>
|
|
48
48
|
<tr><td>`--numberInput_stepper-bg--hover`</td><td>`var(--bds-color_bg--subtle)`</td><td>Stepper button background on hover</td></tr>
|
|
49
49
|
<tr><td>`--numberInput_stepper-opacity--hover`</td><td>`0.7`</td><td>Stepper button opacity on hover</td></tr>
|
|
50
|
+
<tr><td>`--numberInput_padding`</td><td>`var(--bds-space_s)`</td><td>Input padding</td></tr>
|
|
51
|
+
<tr><td>`--numberInput_font-size`</td><td>`var(--bds-font_size--body)`</td><td>Input font size</td></tr>
|
|
52
|
+
<tr><td>`--numberInput_stepper-padding`</td><td>`var(--bds-space_xs)`</td><td>Stepper button padding</td></tr>
|
|
50
53
|
</tbody>
|
|
51
54
|
</table>
|
|
52
55
|
|
|
@@ -54,5 +57,6 @@ Numeric text input with increment/decrement stepper buttons. Enforces `min`, `ma
|
|
|
54
57
|
## Accessibility
|
|
55
58
|
|
|
56
59
|
- Renders `<input type="number">` — browser-native spinner support
|
|
57
|
-
- Stepper buttons have `aria-label="
|
|
60
|
+
- Stepper buttons have `aria-label="Increase"` / `"Decrease"` by default; override the wording (e.g. for localization) with the `increaseLabel` / `decreaseLabel` props
|
|
58
61
|
- Label linked via `id`/`htmlFor`
|
|
62
|
+
- Clearing the field or typing an intermediate value (e.g. a leading `-` while entering a negative number) is preserved rather than being coerced to `0`; `onChange` only fires once a full number has been parsed
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
border: none;
|
|
34
34
|
outline: none;
|
|
35
35
|
font-family: var(--bds-font_family--body);
|
|
36
|
-
font-size: var(--bds-font_size--body);
|
|
36
|
+
font-size: var(--numberInput_font-size, var(--bds-font_size--body));
|
|
37
37
|
text-align: center;
|
|
38
|
-
padding: var(--bds-space_s);
|
|
38
|
+
padding: var(--numberInput_padding, var(--bds-space_s));
|
|
39
39
|
background: transparent;
|
|
40
40
|
color: var(--numberInput_color, var(--bds-color_on-bg));
|
|
41
41
|
min-inline-size: 0;
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
display: flex;
|
|
57
57
|
align-items: center;
|
|
58
58
|
justify-content: center;
|
|
59
|
-
padding: var(--bds-space_xs);
|
|
60
|
-
margin:
|
|
59
|
+
padding: var(--numberInput_stepper-padding, var(--bds-space_xs));
|
|
60
|
+
margin: var(--bdc-outline_width);
|
|
61
61
|
background-color: var(--numberInput_stepper-bg, var(--bds-color_bg--subtle));
|
|
62
62
|
color: var(--numberInput_color, var(--bds-color_on-bg));
|
|
63
63
|
cursor: pointer;
|
|
@@ -66,8 +66,8 @@
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
.stepper svg {
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
inline-size: 1rem;
|
|
70
|
+
block-size: 1rem;
|
|
71
71
|
color: var(--numberInput_stepper-color, var(--bds-color_interactive));
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -1,7 +1,23 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
1
2
|
import { render, screen } from '@testing-library/react';
|
|
2
3
|
import userEvent from '@testing-library/user-event';
|
|
3
4
|
import { NumberInput } from './NumberInput';
|
|
4
5
|
|
|
6
|
+
function ControlledHarness({ initial, onChange }: { initial: number; onChange?: (value: number) => void }) {
|
|
7
|
+
const [value, setValue] = useState(initial);
|
|
8
|
+
return (
|
|
9
|
+
<NumberInput
|
|
10
|
+
label="Qty"
|
|
11
|
+
name="qty"
|
|
12
|
+
value={value}
|
|
13
|
+
onChange={next => {
|
|
14
|
+
setValue(next);
|
|
15
|
+
onChange?.(next);
|
|
16
|
+
}}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
5
21
|
describe('NumberInput', () => {
|
|
6
22
|
it('renders a number input with label', () => {
|
|
7
23
|
render(<NumberInput label="Quantity" name="qty" />);
|
|
@@ -46,4 +62,39 @@ describe('NumberInput', () => {
|
|
|
46
62
|
expect(screen.getByRole('button', { name: 'Increase' })).toBeDisabled();
|
|
47
63
|
expect(screen.getByRole('button', { name: 'Decrease' })).toBeDisabled();
|
|
48
64
|
});
|
|
65
|
+
|
|
66
|
+
it('supports custom stepper labels via increaseLabel/decreaseLabel', () => {
|
|
67
|
+
render(<NumberInput label="Qty" name="qty" increaseLabel="Verhogen" decreaseLabel="Verlagen" />);
|
|
68
|
+
expect(screen.getByRole('button', { name: 'Verhogen' })).toBeInTheDocument();
|
|
69
|
+
expect(screen.getByRole('button', { name: 'Verlagen' })).toBeInTheDocument();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('allows clearing a controlled value instead of forcing it back to 0', async () => {
|
|
73
|
+
const onChange = vi.fn();
|
|
74
|
+
render(<ControlledHarness initial={5} onChange={onChange} />);
|
|
75
|
+
const input = screen.getByRole('spinbutton');
|
|
76
|
+
await userEvent.clear(input);
|
|
77
|
+
expect(input).toHaveValue(null);
|
|
78
|
+
expect(onChange).not.toHaveBeenCalledWith(0);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('allows typing a negative number into a controlled field', async () => {
|
|
82
|
+
const onChange = vi.fn();
|
|
83
|
+
render(<ControlledHarness initial={5} onChange={onChange} />);
|
|
84
|
+
const input = screen.getByRole('spinbutton');
|
|
85
|
+
await userEvent.clear(input);
|
|
86
|
+
await userEvent.type(input, '-5');
|
|
87
|
+
expect(input).toHaveValue(-5);
|
|
88
|
+
expect(onChange).toHaveBeenCalledWith(-5);
|
|
89
|
+
expect(onChange).not.toHaveBeenCalledWith(0);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('allows clearing an uncontrolled value instead of forcing it back to 0', async () => {
|
|
93
|
+
const onChange = vi.fn();
|
|
94
|
+
render(<NumberInput label="Qty" name="qty" defaultValue={5} onChange={onChange} />);
|
|
95
|
+
const input = screen.getByRole('spinbutton');
|
|
96
|
+
await userEvent.clear(input);
|
|
97
|
+
expect(input).toHaveValue(null);
|
|
98
|
+
expect(onChange).not.toHaveBeenCalledWith(0);
|
|
99
|
+
});
|
|
49
100
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {HTMLAttributes, useId, useRef, useState} from 'react';
|
|
1
|
+
import {HTMLAttributes, useEffect, useId, useRef, useState} from 'react';
|
|
2
2
|
import css from './NumberInput.module.css';
|
|
3
3
|
import {Label} from '../atoms/Label';
|
|
4
4
|
import {Message} from '../atoms/Message';
|
|
@@ -18,6 +18,10 @@ interface NumberInputProps extends WithClassName, Omit<HTMLAttributes<HTMLDivEle
|
|
|
18
18
|
error?: string;
|
|
19
19
|
hint?: string;
|
|
20
20
|
onChange?: (value: number) => void;
|
|
21
|
+
/** Accessible label for the decrement (−) stepper button. Defaults to `'Decrease'`. */
|
|
22
|
+
decreaseLabel?: string;
|
|
23
|
+
/** Accessible label for the increment (+) stepper button. Defaults to `'Increase'`. */
|
|
24
|
+
increaseLabel?: string;
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
export function NumberInput({
|
|
@@ -33,6 +37,8 @@ export function NumberInput({
|
|
|
33
37
|
hint,
|
|
34
38
|
onChange,
|
|
35
39
|
className,
|
|
40
|
+
decreaseLabel = 'Decrease',
|
|
41
|
+
increaseLabel = 'Increase',
|
|
36
42
|
...rest
|
|
37
43
|
}: Readonly<NumberInputProps>) {
|
|
38
44
|
const uid = name + useId();
|
|
@@ -45,6 +51,18 @@ export function NumberInput({
|
|
|
45
51
|
const [internalValue, setInternalValue] = useState(defaultValue ?? 0);
|
|
46
52
|
const currentValue = isControlled ? value! : internalValue;
|
|
47
53
|
|
|
54
|
+
// Controlled mode renders the input from a local text draft instead of the raw
|
|
55
|
+
// numeric `value` prop directly, so the field can be cleared and intermediate
|
|
56
|
+
// input (an empty string while retyping) doesn't get coerced back to a number
|
|
57
|
+
// on every keystroke. The draft re-syncs whenever an external `value` change
|
|
58
|
+
// doesn't already match what's currently drafted (e.g. a consumer-side reset).
|
|
59
|
+
const [controlledDraft, setControlledDraft] = useState(() => (isControlled ? String(value) : ''));
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (!isControlled) return;
|
|
63
|
+
setControlledDraft(prev => (parseFloat(prev) === value ? prev : String(value)));
|
|
64
|
+
}, [isControlled, value]);
|
|
65
|
+
|
|
48
66
|
const clamp = (v: number): number => {
|
|
49
67
|
const withMin = min !== undefined ? Math.max(min, v) : v;
|
|
50
68
|
return max !== undefined ? Math.min(max, withMin) : withMin;
|
|
@@ -58,6 +76,8 @@ export function NumberInput({
|
|
|
58
76
|
inputRef.current.value = String(next);
|
|
59
77
|
inputRef.current.dispatchEvent(new Event('input', { bubbles: true }));
|
|
60
78
|
}
|
|
79
|
+
} else {
|
|
80
|
+
setControlledDraft(String(next));
|
|
61
81
|
}
|
|
62
82
|
onChange?.(next);
|
|
63
83
|
};
|
|
@@ -69,7 +89,7 @@ export function NumberInput({
|
|
|
69
89
|
<button
|
|
70
90
|
type="button"
|
|
71
91
|
className={css.stepper}
|
|
72
|
-
aria-label=
|
|
92
|
+
aria-label={decreaseLabel}
|
|
73
93
|
aria-controls={uid}
|
|
74
94
|
disabled={disabled || (min !== undefined && currentValue <= min)}
|
|
75
95
|
onClick={() => adjust(-step)}
|
|
@@ -84,7 +104,7 @@ export function NumberInput({
|
|
|
84
104
|
id={uid}
|
|
85
105
|
type="number"
|
|
86
106
|
name={name}
|
|
87
|
-
{...(isControlled ? { value } : { defaultValue })}
|
|
107
|
+
{...(isControlled ? { value: controlledDraft } : { defaultValue })}
|
|
88
108
|
min={min}
|
|
89
109
|
max={max}
|
|
90
110
|
step={step}
|
|
@@ -93,16 +113,24 @@ export function NumberInput({
|
|
|
93
113
|
aria-describedby={describedBy}
|
|
94
114
|
className={cn(css.input, error ? css.inputError : undefined)}
|
|
95
115
|
onChange={e => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
116
|
+
// `validity.badInput` is true while the user is mid-typing text the
|
|
117
|
+
// browser can't parse yet (e.g. a lone '-' or a trailing '.'). Leave
|
|
118
|
+
// the field's own editing buffer untouched instead of forcing it back
|
|
119
|
+
// to the last committed value on every keystroke.
|
|
120
|
+
if (e.target.validity.badInput) return;
|
|
121
|
+
const raw = e.target.value;
|
|
122
|
+
if (isControlled) setControlledDraft(raw);
|
|
123
|
+
if (raw === '') return; // field genuinely cleared - don't coerce to 0
|
|
124
|
+
const parsed = parseFloat(raw);
|
|
125
|
+
if (Number.isNaN(parsed)) return;
|
|
126
|
+
if (!isControlled) setInternalValue(parsed);
|
|
127
|
+
onChange?.(parsed);
|
|
100
128
|
}}
|
|
101
129
|
/>
|
|
102
130
|
<button
|
|
103
131
|
type="button"
|
|
104
132
|
className={css.stepper}
|
|
105
|
-
aria-label=
|
|
133
|
+
aria-label={increaseLabel}
|
|
106
134
|
aria-controls={uid}
|
|
107
135
|
disabled={disabled || (max !== undefined && currentValue >= max)}
|
|
108
136
|
onClick={() => adjust(step)}
|
|
@@ -47,8 +47,14 @@ Single-select option within a group. Renders a styled native `<input type="radio
|
|
|
47
47
|
</thead>
|
|
48
48
|
<tbody>
|
|
49
49
|
<tr><td>`--radio_color-active`</td><td>`var(--bdc-color_active)`</td><td>Selected fill and border colour</td></tr>
|
|
50
|
-
<tr><td>`--
|
|
50
|
+
<tr><td>`--radio_color_bg`</td><td>`var(--bds-color_bg)`</td><td>Inner dot colour (the cut-out centre of the selected fill) and unselected background</td></tr>
|
|
51
51
|
<tr><td>`--radio_description-color`</td><td>`var(--bds-color_on-bg--subtle)`</td><td>Description text colour</td></tr>
|
|
52
|
+
<tr><td>`--radio_size`</td><td>`calc(var(--bds-space_s) * 2)`</td><td>Input diameter</td></tr>
|
|
53
|
+
<tr><td>`--radio_offset`</td><td>`0.2em`</td><td>Optical vertical alignment offset between the input and the label baseline</td></tr>
|
|
54
|
+
<tr><td>`--radio_radius`</td><td>`var(--bds-border_radius--circle)`</td><td>Input border radius</td></tr>
|
|
55
|
+
<tr><td>`--radio_color`</td><td>`var(--bds-color_on-bg)`</td><td>Border colour (unselected)</td></tr>
|
|
56
|
+
<tr><td>`--radio_border`</td><td>`none`</td><td>Additional border shorthand</td></tr>
|
|
57
|
+
<tr><td>`--radio_shadow`</td><td>`var(--bdc-outline_shadow)`</td><td>Additional bdc shadow-tail slot</td></tr>
|
|
52
58
|
</tbody>
|
|
53
59
|
</table>
|
|
54
60
|
|
|
@@ -58,3 +64,4 @@ Single-select option within a group. Renders a styled native `<input type="radio
|
|
|
58
64
|
- Native `<input type="radio">` — group semantics via shared `name`
|
|
59
65
|
- Wrap groups in `<fieldset>` with a `<legend>` for a group label
|
|
60
66
|
- Arrow keys move between radios in the same group
|
|
67
|
+
- `description`, `hint` and `error` text are all associated with the input via `aria-describedby`
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
@layer boostdev.component {
|
|
2
2
|
.radioGroup {
|
|
3
|
-
--
|
|
4
|
-
--
|
|
3
|
+
--radio_size: calc(var(--bds-space_s) * 2);
|
|
4
|
+
--inputSize: var(--radio_size);
|
|
5
5
|
|
|
6
6
|
display: flex;
|
|
7
7
|
flex-direction: column;
|
|
@@ -28,13 +28,13 @@
|
|
|
28
28
|
|
|
29
29
|
.radio {
|
|
30
30
|
flex-shrink: 0;
|
|
31
|
-
margin-block-start: 0.2em;
|
|
32
|
-
inline-size: var(--
|
|
33
|
-
block-size: var(--
|
|
31
|
+
margin-block-start: var(--radio_offset, 0.2em);
|
|
32
|
+
inline-size: var(--radio_size);
|
|
33
|
+
block-size: var(--radio_size);
|
|
34
34
|
color: var(--radio_color, var(--bds-color_on-bg));
|
|
35
35
|
border: var(--radio_border, none);
|
|
36
36
|
--bdc_color: currentcolor;
|
|
37
|
-
--bdc-outline_radius:
|
|
37
|
+
--bdc-outline_radius: var(--bds-border_radius--circle);
|
|
38
38
|
|
|
39
39
|
border-radius: var(--radio_radius, var(--bdc-outline_radius));
|
|
40
40
|
box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color), var(--radio_shadow, var(--bdc-outline_shadow));
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
background-color: var(--radio_color_bg, var(--bds-color_bg));
|
|
43
43
|
cursor: pointer;
|
|
44
44
|
position: relative;
|
|
45
|
-
transition: --bdc_color
|
|
45
|
+
transition: --bdc_color var(--bds-animation_transition-duration) var(--bds-animation_easing),
|
|
46
|
+
background-color var(--bds-animation_transition-duration) var(--bds-animation_easing);
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
.radio:checked {
|
|
@@ -63,6 +64,18 @@
|
|
|
63
64
|
outline-offset: var(--bds-outline_offset);
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
/* Disabled — also matches when an ancestor <fieldset disabled> (e.g. a
|
|
68
|
+
disabled RadioGroup/FieldGroup) propagates the disabled state. */
|
|
69
|
+
.radio:disabled {
|
|
70
|
+
cursor: not-allowed;
|
|
71
|
+
opacity: 0.5;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.radio:disabled ~ .textWrapper {
|
|
75
|
+
opacity: 0.5;
|
|
76
|
+
cursor: not-allowed;
|
|
77
|
+
}
|
|
78
|
+
|
|
66
79
|
.radioError {
|
|
67
80
|
--bdc_color: var(--bdc_color--error);
|
|
68
81
|
}
|
|
@@ -35,4 +35,19 @@ describe('Radio', () => {
|
|
|
35
35
|
render(<Radio label="Disabled" name="disabled" value="x" disabled />);
|
|
36
36
|
expect(screen.getByRole('radio')).toBeDisabled();
|
|
37
37
|
});
|
|
38
|
+
|
|
39
|
+
it('associates the description with the input via aria-describedby', () => {
|
|
40
|
+
render(<Radio label="Option" name="opt" value="x" description="More info about this option" />);
|
|
41
|
+
const radio = screen.getByRole('radio');
|
|
42
|
+
const describedBy = radio.getAttribute('aria-describedby');
|
|
43
|
+
expect(describedBy).toBeTruthy();
|
|
44
|
+
const description = screen.getByText('More info about this option');
|
|
45
|
+
expect(describedBy).toContain(description.id);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('keeps the label associated with the input when a consumer id is passed', () => {
|
|
49
|
+
render(<Radio label="Option" name="opt" value="x" id="custom-radio-id" />);
|
|
50
|
+
const radio = screen.getByLabelText('Option');
|
|
51
|
+
expect(radio).toHaveAttribute('id', 'custom-radio-id');
|
|
52
|
+
});
|
|
38
53
|
});
|
|
@@ -14,11 +14,13 @@ interface RadioProps extends WithClassName, Omit<InputHTMLAttributes<HTMLInputEl
|
|
|
14
14
|
hint?: string;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function Radio({ label, name, description, error, hint, className, ...props }: RadioProps) {
|
|
18
|
-
const id = name + useId();
|
|
17
|
+
export function Radio({ label, name, description, error, hint, className, id: consumerId, ...props }: RadioProps) {
|
|
18
|
+
const id = consumerId ?? name + useId();
|
|
19
|
+
const descriptionId = id + 'description';
|
|
19
20
|
const hintId = id + 'hint';
|
|
20
21
|
const errorId = id + 'error';
|
|
21
|
-
const describedBy =
|
|
22
|
+
const describedBy =
|
|
23
|
+
[description && descriptionId, error && errorId, hint && hintId].filter(Boolean).join(' ') || undefined;
|
|
22
24
|
|
|
23
25
|
return (
|
|
24
26
|
<InputContainer className={cn(css.radioGroup, className)}>
|
|
@@ -34,7 +36,11 @@ export function Radio({ label, name, description, error, hint, className, ...pro
|
|
|
34
36
|
/>
|
|
35
37
|
<div className={css.textWrapper}>
|
|
36
38
|
<Label id={id} label={label} />
|
|
37
|
-
{description &&
|
|
39
|
+
{description && (
|
|
40
|
+
<span id={descriptionId} className={css.description}>
|
|
41
|
+
{description}
|
|
42
|
+
</span>
|
|
43
|
+
)}
|
|
38
44
|
</div>
|
|
39
45
|
</div>
|
|
40
46
|
<Message inputId={id} type="error" message={error} />
|
|
@@ -36,10 +36,25 @@ Groups multiple `Radio` inputs under a shared label. Renders a `<fieldset>` with
|
|
|
36
36
|
|
|
37
37
|
<ArgTypes of={Stories} />
|
|
38
38
|
|
|
39
|
+
## CSS variables
|
|
40
|
+
|
|
41
|
+
All public override tokens. Set on the RadioGroup or any ancestor; they cascade.
|
|
42
|
+
|
|
43
|
+
| Variable | Default | Description |
|
|
44
|
+
|----------|---------|-------------|
|
|
45
|
+
| `--radioGroup_gap` | `var(--bds-space_xs)` | Gap between the legend/items block and, via `--radioGroup_items-gap`, between the child radios. |
|
|
46
|
+
| `--radioGroup_items-gap` | `var(--bds-space_xs)` | Gap between the child radios. |
|
|
47
|
+
| `--radioGroup_legend-color` | `var(--bds-color_on-bg)` | Legend text colour. |
|
|
48
|
+
| `--radioGroup_legend-font-family` | `var(--bds-font_family--body)` | Legend font family. |
|
|
49
|
+
| `--radioGroup_legend-font-size` | `var(--bds-font_size--body--s)` | Legend font size. |
|
|
50
|
+
| `--radioGroup_legend-font-weight` | `var(--bds-font_weight--medium)` | Legend font weight. |
|
|
51
|
+
| `--radioGroup_legend-gap` | `var(--bds-space_xs)` | Space below the legend before the items. |
|
|
52
|
+
| `--radioGroup_required-color` | `var(--bds-color_error)` | Colour of the visible `*` required marker. |
|
|
53
|
+
|
|
39
54
|
## Accessibility
|
|
40
55
|
|
|
41
56
|
- Wraps radio buttons in a `<fieldset>` with a `<legend>` — screen readers announce the group label before each option
|
|
42
57
|
- `disabled` on the group sets the HTML `disabled` attribute on the `<fieldset>`, which disables all child inputs
|
|
43
|
-
- `aria-required` is set on the `<fieldset>`
|
|
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
|
|
44
59
|
- Error and hint messages are linked via `aria-describedby`
|
|
45
60
|
- Arrow keys move between options within the group; `Tab` moves focus out of the group
|
|
@@ -5,25 +5,41 @@
|
|
|
5
5
|
margin: 0;
|
|
6
6
|
display: flex;
|
|
7
7
|
flex-direction: column;
|
|
8
|
-
gap: var(--bds-space_xs);
|
|
8
|
+
gap: var(--radioGroup_gap, var(--bds-space_xs));
|
|
9
9
|
}
|
|
10
10
|
|
|
11
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-color_on-bg);
|
|
12
|
+
font-family: var(--radioGroup_legend-font-family, var(--bds-font_family--body));
|
|
13
|
+
font-size: var(--radioGroup_legend-font-size, var(--bds-font_size--body--s));
|
|
14
|
+
font-weight: var(--radioGroup_legend-font-weight, var(--bds-font_weight--medium));
|
|
15
|
+
color: var(--radioGroup_legend-color, var(--bds-color_on-bg));
|
|
16
16
|
padding: 0;
|
|
17
|
-
margin-block-end: var(--bds-space_xs);
|
|
17
|
+
margin-block-end: var(--radioGroup_legend-gap, var(--bds-space_xs));
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
.required {
|
|
21
|
-
color: var(--bds-color_error);
|
|
21
|
+
color: var(--radioGroup_required-color, var(--bds-color_error));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Visually hidden but still exposed to assistive tech — carries the
|
|
25
|
+
"required" state into the fieldset's accessible name, since the
|
|
26
|
+
visible asterisk above is aria-hidden and `aria-required` is not
|
|
27
|
+
supported ARIA on a fieldset (role=group). */
|
|
28
|
+
.requiredLabel {
|
|
29
|
+
position: absolute;
|
|
30
|
+
inline-size: 1px;
|
|
31
|
+
block-size: 1px;
|
|
32
|
+
padding: 0;
|
|
33
|
+
margin: -1px;
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
clip: rect(0, 0, 0, 0);
|
|
36
|
+
white-space: nowrap;
|
|
37
|
+
border-width: 0;
|
|
22
38
|
}
|
|
23
39
|
|
|
24
40
|
.items {
|
|
25
41
|
display: flex;
|
|
26
42
|
flex-direction: column;
|
|
27
|
-
gap: var(--bds-space_xs);
|
|
43
|
+
gap: var(--radioGroup_items-gap, var(--bds-space_xs));
|
|
28
44
|
}
|
|
29
45
|
}
|
|
@@ -54,16 +54,46 @@ describe('RadioGroup', () => {
|
|
|
54
54
|
expect(fieldset.getAttribute('aria-describedby')).toContain(errorMsg.id);
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
-
it('
|
|
57
|
+
it('does not set the unsupported aria-required attribute on the fieldset', () => {
|
|
58
|
+
render(
|
|
59
|
+
<RadioGroup legend="Required" required>
|
|
60
|
+
<Radio label="Option" name="opt" value="opt" />
|
|
61
|
+
</RadioGroup>
|
|
62
|
+
);
|
|
63
|
+
expect(screen.getByRole('group')).not.toHaveAttribute('aria-required');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('shows a visible required marker when required prop is set', () => {
|
|
58
67
|
render(
|
|
59
68
|
<RadioGroup legend="Required" required>
|
|
60
69
|
<Radio label="Option" name="opt" value="opt" />
|
|
61
70
|
</RadioGroup>
|
|
62
71
|
);
|
|
63
|
-
expect(screen.getByRole('group')).toHaveAttribute('aria-required', 'true');
|
|
64
72
|
expect(screen.getByText('*')).toBeInTheDocument();
|
|
65
73
|
});
|
|
66
74
|
|
|
75
|
+
it('conveys required state to assistive tech via the accessible group name', () => {
|
|
76
|
+
render(
|
|
77
|
+
<RadioGroup legend="Preferred contact" required>
|
|
78
|
+
<Radio label="Option" name="opt" value="opt" />
|
|
79
|
+
</RadioGroup>
|
|
80
|
+
);
|
|
81
|
+
expect(
|
|
82
|
+
screen.getByRole('group', { name: 'Preferred contact (required)' })
|
|
83
|
+
).toBeInTheDocument();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('omits the required marker and accessible required text when required is not set', () => {
|
|
87
|
+
render(
|
|
88
|
+
<RadioGroup legend="Optional contact">
|
|
89
|
+
<Radio label="Option" name="opt" value="opt" />
|
|
90
|
+
</RadioGroup>
|
|
91
|
+
);
|
|
92
|
+
expect(screen.queryByText('*')).not.toBeInTheDocument();
|
|
93
|
+
expect(screen.queryByText('(required)')).not.toBeInTheDocument();
|
|
94
|
+
expect(screen.getByRole('group', { name: 'Optional contact' })).toBeInTheDocument();
|
|
95
|
+
});
|
|
96
|
+
|
|
67
97
|
it('disables the fieldset when disabled prop is set', () => {
|
|
68
98
|
render(
|
|
69
99
|
<RadioGroup legend="Disabled" disabled>
|
|
@@ -29,12 +29,24 @@ export function RadioGroup({
|
|
|
29
29
|
<fieldset
|
|
30
30
|
{...rest}
|
|
31
31
|
className={cn(css.group, className)}
|
|
32
|
-
aria-required={required || undefined}
|
|
33
32
|
aria-describedby={describedBy}
|
|
34
33
|
>
|
|
35
34
|
<legend className={css.legend}>
|
|
36
35
|
{legend}
|
|
37
|
-
{required &&
|
|
36
|
+
{required && (
|
|
37
|
+
<>
|
|
38
|
+
{/* Visible-only marker — aria-hidden because `aria-required` is not
|
|
39
|
+
supported ARIA on a fieldset/role=group, so requiredness must be
|
|
40
|
+
conveyed through the legend's accessible text instead (below). The
|
|
41
|
+
literal `{' '}` text node sits directly in the legend (not nested
|
|
42
|
+
inside a span) so it survives accessible-name computation — a
|
|
43
|
+
leading space nested inside an element gets trimmed away when
|
|
44
|
+
that element's own text alternative is computed. */}
|
|
45
|
+
<span className={css.required} aria-hidden="true"> *</span>
|
|
46
|
+
{' '}
|
|
47
|
+
<span className={css.requiredLabel}>(required)</span>
|
|
48
|
+
</>
|
|
49
|
+
)}
|
|
38
50
|
</legend>
|
|
39
51
|
<div className={css.items}>{children}</div>
|
|
40
52
|
<Message inputId={id} type="error" message={error} />
|
|
@@ -97,18 +97,24 @@ When `selectedIndex` is omitted, the component auto-detects the active item from
|
|
|
97
97
|
| `--control_track-bg` | `var(--bds-color_bg--subtle)` | Track background colour (both variants) |
|
|
98
98
|
| `--control_item-bg` | `transparent` | Inactive item background (outline variant) |
|
|
99
99
|
| `--control_item-bg--active` | `var(--bds-color_bg)` | Active item background (outline variant) |
|
|
100
|
-
| `--control_radius` | `var(--bds-border_radius--
|
|
100
|
+
| `--control_radius` | `var(--bds-border_radius--full)` | Border radius of the outer track |
|
|
101
|
+
| `--control_track-pad` | `var(--bds-space_xxs)` | Inset gap between the track edge and the thumb/indicator |
|
|
101
102
|
| `--control_thumb-bg` | `var(--bds-color_interactive)` | Filled thumb colour (filled variant) |
|
|
102
|
-
| `--control_thumb-radius` | `var(--bds-border_radius--
|
|
103
|
+
| `--control_thumb-radius` | `var(--bds-border_radius--full)` | Border radius of the thumb, indicator, and item focus ring |
|
|
104
|
+
| `--control_thumb-shadow` | `var(--bds-shadow_s)` | Filled thumb box-shadow (filled variant) |
|
|
105
|
+
| `--control_item-padding` | `var(--bds-space_xs) var(--bds-space_m)` | Item padding (medium size) |
|
|
103
106
|
| `--control_label-color` | `var(--bds-color_on-bg--subtle)` | Inactive item text colour |
|
|
104
107
|
| `--control_label-color--active` | `var(--bds-color_on-interactive)` (filled) / `var(--bds-color_interactive)` (outline) | Active item text colour |
|
|
105
108
|
| `--control_label-color--hover` | `var(--bds-color_on-bg)` | Hovered inactive item text colour |
|
|
106
109
|
| `--control_indicator-color` | `var(--bds-color_interactive)` | Outline indicator border colour (outline variant) |
|
|
110
|
+
| `--control_indicator-width` | `2px` | Outline indicator border width (outline variant) |
|
|
107
111
|
|
|
108
112
|
## Accessibility
|
|
109
113
|
|
|
110
114
|
- The container renders as `role="group"`. Provide `aria-label` when the context doesn't supply a visible label.
|
|
111
115
|
- Each child is responsible for its own semantics: buttons use `aria-pressed`, links use `aria-current="page"`.
|
|
116
|
+
- When `selectedIndex` drives the active item and the child hasn't already set `aria-current`/`aria-pressed`/`aria-selected` itself, the component injects `aria-pressed={true}` (buttons) or `aria-current="page"` (links) onto the active child automatically.
|
|
117
|
+
- When neither `selectedIndex` nor an ARIA-marked child is present, no item is treated as active — the sliding thumb/indicator stay hidden instead of defaulting to the first item.
|
|
118
|
+
- `disabled` (on the control, or on an individual child via its own `disabled`/`aria-disabled` prop) is enforced, not just styled: the item receives `aria-disabled="true"`; `<button>` children also get the native `disabled` attribute (removing them from the tab order and blocking activation); non-button children (e.g. links) get `tabIndex={-1}` and their click is prevented.
|
|
112
119
|
- The sliding thumb and indicator are `aria-hidden` — they are purely decorative.
|
|
113
|
-
- The active item is communicated via the child's own ARIA attribute, not via the container.
|
|
114
120
|
- Respects `prefers-reduced-motion` — the sliding animation is disabled when the user has opted out.
|