@boostdev/design-system-components 2.7.1 → 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-3AY2YKCM.js +223 -0
- package/dist/web-components/chunk-3WAV6MDN.js +212 -0
- package/dist/web-components/{chunk-CZBIVR4V.js → chunk-47AT5KZD.js} +88 -16
- 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-5R6IFELV.js → chunk-AIBOHNDU.js} +11 -1
- package/dist/web-components/{chunk-ITHLKR7Q.js → chunk-AP4RDUL2.js} +16 -5
- 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-CM3KYZ6Z.js → chunk-JH6LQKLK.js} +78 -6
- 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-57TRNCWQ.js → chunk-WFVKU4GX.js} +22 -11
- 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-F5E3IT7G.js → chunk-XIZWAWMJ.js} +50 -11
- 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 +60 -13
- 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 +102 -17
- 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 +136 -23
- 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 +96 -8
- package/src/web-components/interaction/form/bds-slider.spec.ts +83 -0
- package/src/web-components/interaction/form/bds-slider.ts +181 -33
- package/src/web-components/interaction/form/bds-switch.spec.ts +68 -0
- package/src/web-components/interaction/form/bds-switch.ts +142 -41
- package/src/web-components/interaction/form/bds-textarea.spec.ts +71 -0
- package/src/web-components/interaction/form/bds-textarea.ts +109 -8
- 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 +30 -12
- 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 +113 -36
- 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 +16 -15
- 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 +11 -1
- 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 +19 -6
- 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-5TFWOPLH.js +0 -49
- package/dist/web-components/chunk-7FMR53BW.js +0 -127
- package/dist/web-components/chunk-AGKJTMJI.js +0 -56
- package/dist/web-components/chunk-BQLZ7I4G.js +0 -56
- package/dist/web-components/chunk-IYGF6CD5.js +0 -118
- package/dist/web-components/chunk-QBB6ZX4R.js +0 -107
- package/dist/web-components/chunk-Y3POAKGS.js +0 -133
- 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
|
@@ -25,8 +25,27 @@ The link is visually hidden until focused. Press **Tab** on the preview below to
|
|
|
25
25
|
|
|
26
26
|
## Props
|
|
27
27
|
|
|
28
|
+
> **HTML attributes** — all standard HTML attributes for the root element are forwarded via `{...rest}` (`id`, `className`, `style`, `data-*`, `aria-*`, event handlers, etc.). Component-controlled attributes take precedence.
|
|
29
|
+
|
|
28
30
|
<ArgTypes of={Stories} />
|
|
29
31
|
|
|
32
|
+
## CSS variables
|
|
33
|
+
|
|
34
|
+
<table>
|
|
35
|
+
<thead>
|
|
36
|
+
<tr><th>Variable</th><th>Default</th><th>Description</th></tr>
|
|
37
|
+
</thead>
|
|
38
|
+
<tbody>
|
|
39
|
+
<tr><td>`--skip-link_z-index`</td><td>`var(--bds-z-index_xl)`</td><td>Stacking order. Defaults to the "critical overlay" tier so the focused link paints above sticky headers/navigation.</td></tr>
|
|
40
|
+
<tr><td>`--skip-link_inset`</td><td>`var(--bds-space_s)`</td><td>Distance from the block-start and inline-start edges of the viewport when focused.</td></tr>
|
|
41
|
+
<tr><td>`--skip-link_padding`</td><td>`var(--bds-space_xs) var(--bds-space_m)`</td><td>Inner padding of the link.</td></tr>
|
|
42
|
+
<tr><td>`--skip-link_font-size`</td><td>`var(--bds-font_size--body)`</td><td>Link text size.</td></tr>
|
|
43
|
+
<tr><td>`--skip-link_radius`</td><td>`var(--bds-border_radius--s)`</td><td>Corner radius.</td></tr>
|
|
44
|
+
<tr><td>`--skip-link_border`</td><td>`none`</td><td>Border shorthand, in addition to the focus-colour inset frame.</td></tr>
|
|
45
|
+
<tr><td>`--skip-link_shadow`</td><td>`var(--bdc-outline_shadow)`</td><td>Extra shadow layered after the inset frame border (bdc formula's shadow slot).</td></tr>
|
|
46
|
+
</tbody>
|
|
47
|
+
</table>
|
|
48
|
+
|
|
30
49
|
## Accessibility
|
|
31
50
|
|
|
32
51
|
- Renders as a standard `<a>` — no special ARIA needed
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
@layer boostdev.component {
|
|
2
2
|
.skipLink {
|
|
3
3
|
position: absolute;
|
|
4
|
-
inset-block-start: var(--bds-space_s);
|
|
5
|
-
inset-inline-start: var(--bds-space_s);
|
|
6
|
-
z-index: var(--
|
|
7
|
-
padding: var(--bds-space_xs) var(--bds-space_m);
|
|
4
|
+
inset-block-start: var(--skip-link_inset, var(--bds-space_s));
|
|
5
|
+
inset-inline-start: var(--skip-link_inset, var(--bds-space_s));
|
|
6
|
+
z-index: var(--skip-link_z-index, var(--bds-z-index_xl));
|
|
7
|
+
padding: var(--skip-link_padding, var(--bds-space_xs) var(--bds-space_m));
|
|
8
8
|
background-color: var(--bds-color_bg);
|
|
9
9
|
color: var(--bds-color_on-bg);
|
|
10
|
-
font-size: var(--bds-font_size--body);
|
|
10
|
+
font-size: var(--skip-link_font-size, var(--bds-font_size--body));
|
|
11
11
|
font-weight: var(--bds-font_weight--semibold);
|
|
12
|
-
border-radius: var(--bds-border_radius--s);
|
|
12
|
+
border-radius: var(--skip-link_radius, var(--bds-border_radius--s));
|
|
13
13
|
text-decoration: none;
|
|
14
14
|
|
|
15
15
|
--bdc_color: var(--color_focus, var(--bds-color_interactive));
|
|
16
16
|
|
|
17
17
|
border: var(--skip-link_border, none);
|
|
18
|
-
box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color)
|
|
18
|
+
box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color),
|
|
19
|
+
var(--skip-link_shadow, var(--bdc-outline_shadow));
|
|
19
20
|
transform: translateY(-200%);
|
|
20
21
|
transition: transform var(--bds-animation_transition-duration) var(--bds-animation_easing);
|
|
21
22
|
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
1
3
|
import { render, screen } from '@testing-library/react';
|
|
2
4
|
import { SkipLink } from './SkipLink';
|
|
3
5
|
|
|
6
|
+
const cssSource = readFileSync(
|
|
7
|
+
resolve(process.cwd(), 'src/components/ui/SkipLink/SkipLink.module.css'),
|
|
8
|
+
'utf8',
|
|
9
|
+
);
|
|
10
|
+
|
|
4
11
|
describe('SkipLink', () => {
|
|
5
12
|
it('renders a link with default text', () => {
|
|
6
13
|
render(<SkipLink />);
|
|
@@ -21,4 +28,25 @@ describe('SkipLink', () => {
|
|
|
21
28
|
render(<SkipLink href="#content" />);
|
|
22
29
|
expect(screen.getByRole('link')).toHaveAttribute('href', '#content');
|
|
23
30
|
});
|
|
31
|
+
|
|
32
|
+
it('resolves z-index from a real token with a fallback so the focused link rises above sticky headers', () => {
|
|
33
|
+
// jsdom has no layout engine, so the winning stacking context can't be
|
|
34
|
+
// asserted via computed style; pin the contract in the CSS source
|
|
35
|
+
// instead (same approach as NotificationBanner.spec.tsx). Previously
|
|
36
|
+
// this referenced the undefined --z_overlay token, which resolves to
|
|
37
|
+
// `auto` and defeats the skip link's entire purpose.
|
|
38
|
+
const ruleMatch = cssSource.match(/\.skipLink\s*{[^}]*}/);
|
|
39
|
+
expect(ruleMatch).not.toBeNull();
|
|
40
|
+
const rule = ruleMatch?.[0] ?? '';
|
|
41
|
+
|
|
42
|
+
expect(rule).not.toMatch(/--z_overlay/);
|
|
43
|
+
expect(rule).toMatch(/z-index:\s*var\(--skip-link_z-index,\s*var\(--bds-z-index_xl\)\)/);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('layers a shadow slot after the bdc inset frame border', () => {
|
|
47
|
+
const ruleMatch = cssSource.match(/\.skipLink\s*{[^}]*}/);
|
|
48
|
+
const rule = ruleMatch?.[0] ?? '';
|
|
49
|
+
|
|
50
|
+
expect(rule).toMatch(/var\(--skip-link_shadow,\s*var\(--bdc-outline_shadow\)\)/);
|
|
51
|
+
});
|
|
24
52
|
});
|
|
@@ -28,6 +28,8 @@ Data table with optional sortable columns and caption.
|
|
|
28
28
|
|
|
29
29
|
## Props
|
|
30
30
|
|
|
31
|
+
> **HTML attributes** — all standard HTML attributes for the root element are forwarded via `{...rest}` (`id`, `className`, `style`, `data-*`, `aria-*`, event handlers, etc.). Component-controlled attributes take precedence.
|
|
32
|
+
|
|
31
33
|
<ArgTypes of={Stories} />
|
|
32
34
|
|
|
33
35
|
## CSS variables
|
|
@@ -40,15 +42,27 @@ Data table with optional sortable columns and caption.
|
|
|
40
42
|
<tbody>
|
|
41
43
|
<tr><td>`--table_border`</td><td>`none`</td><td>Container border</td></tr>
|
|
42
44
|
<tr><td>`--table_radius`</td><td>`var(--bdc-outline_radius)`</td><td>Container corner radius</td></tr>
|
|
45
|
+
<tr><td>`--table_shadow`</td><td>`var(--bdc-outline_shadow)`</td><td>Container frame shadow (appended to the inset border box-shadow)</td></tr>
|
|
46
|
+
<tr><td>`--table_cell-padding`</td><td>`var(--bds-space_s) var(--bds-space_m)`</td><td>Padding shared by the caption, header cells and body cells</td></tr>
|
|
47
|
+
<tr><td>`--table_font-size`</td><td>`var(--bds-font_size--body)`</td><td>Body cell font size</td></tr>
|
|
48
|
+
<tr><td>`--table_header-font-size`</td><td>`var(--bds-font_size--body--s)`</td><td>Header cell / sort button font size</td></tr>
|
|
43
49
|
<tr><td>`--table_sortBtn-color--hover`</td><td>`var(--bds-color_on-bg)`</td><td>Sort button text colour on hover</td></tr>
|
|
44
50
|
<tr><td>`--table_row-bg--hover`</td><td>`var(--bds-color_bg--subtle)`</td><td>Table row background on hover</td></tr>
|
|
45
51
|
</tbody>
|
|
46
52
|
</table>
|
|
47
53
|
|
|
48
54
|
|
|
55
|
+
## Notes
|
|
56
|
+
|
|
57
|
+
- `rowKey?: (row: Row, index: number) => string` — derives a stable React key per row. Defaults to the row index when omitted; use this when your row objects may be structurally identical or non-serializable.
|
|
58
|
+
- `TableColumn.sortLabel?: string` — accessible label used for a sortable column's sort button. Provide this when `header` is not a plain string (e.g. an icon or custom `ReactNode`) so the sort button gets a meaningful accessible name instead of falling back to its visible content.
|
|
59
|
+
- `Table` extends `HTMLAttributes<HTMLDivElement>` and spreads unrecognised props onto the scrollable wrapper `<div>` (e.g. `data-testid`, `aria-label`).
|
|
60
|
+
|
|
49
61
|
## Accessibility
|
|
50
62
|
|
|
51
63
|
- Wraps in `<table>` with `<thead>` and `<tbody>`
|
|
52
64
|
- Column headers use `<th scope="col">`
|
|
53
65
|
- Sortable columns set `aria-sort="ascending"` / `"descending"` / `"none"`
|
|
54
66
|
- `caption` renders as `<caption>` — the accessible name of the table
|
|
67
|
+
- The horizontally scrollable wrapper is keyboard focusable (`tabIndex={0}`, overridable via a `tabIndex` prop) and, whenever it has an accessible name — from `caption`, or a consumer-supplied `aria-label`/`aria-labelledby` — exposes `role="region"` labelled accordingly, so keyboard users can scroll it and screen reader users can identify it
|
|
68
|
+
- Sort button accessible names use `col.sortLabel` when provided, otherwise `col.header` when it is a plain string; for non-string headers the button falls back to its visible text content instead of stringifying the `ReactNode`
|
|
@@ -7,20 +7,25 @@
|
|
|
7
7
|
--bdc-outline_radius: var(--bds-border_radius--s);
|
|
8
8
|
|
|
9
9
|
border-radius: var(--table_radius, var(--bdc-outline_radius));
|
|
10
|
-
box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color);
|
|
10
|
+
box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color), var(--table_shadow, var(--bdc-outline_shadow));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.wrapper:focus-visible {
|
|
14
|
+
outline: var(--bds-outline_default);
|
|
15
|
+
outline-offset: calc(var(--bds-outline_offset) * -1);
|
|
11
16
|
}
|
|
12
17
|
|
|
13
18
|
.table {
|
|
14
19
|
inline-size: 100%;
|
|
15
20
|
border-collapse: collapse;
|
|
16
|
-
font-size: var(--bds-font_size--body);
|
|
21
|
+
font-size: var(--table_font-size, var(--bds-font_size--body));
|
|
17
22
|
font-family: var(--bds-font_family--body);
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
.caption {
|
|
21
26
|
caption-side: top;
|
|
22
27
|
text-align: start;
|
|
23
|
-
padding: var(--bds-space_s) var(--bds-space_m);
|
|
28
|
+
padding: var(--table_cell-padding, var(--bds-space_s) var(--bds-space_m));
|
|
24
29
|
font-weight: var(--bds-font_weight--semibold);
|
|
25
30
|
color: var(--bds-color_on-bg);
|
|
26
31
|
}
|
|
@@ -30,10 +35,10 @@
|
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
.th {
|
|
33
|
-
padding: var(--bds-space_s) var(--bds-space_m);
|
|
38
|
+
padding: var(--table_cell-padding, var(--bds-space_s) var(--bds-space_m));
|
|
34
39
|
text-align: start;
|
|
35
40
|
font-weight: var(--bds-font_weight--semibold);
|
|
36
|
-
font-size: var(--bds-font_size--body--s);
|
|
41
|
+
font-size: var(--table_header-font-size, var(--bds-font_size--body--s));
|
|
37
42
|
color: var(--bds-color_on-bg--subtle);
|
|
38
43
|
white-space: nowrap;
|
|
39
44
|
border-block-end: 1px solid var(--bds-color_bg--subtle);
|
|
@@ -48,12 +53,12 @@
|
|
|
48
53
|
display: flex;
|
|
49
54
|
align-items: center;
|
|
50
55
|
gap: var(--bds-space_xxs);
|
|
51
|
-
padding: var(--bds-space_s) var(--bds-space_m);
|
|
56
|
+
padding: var(--table_cell-padding, var(--bds-space_s) var(--bds-space_m));
|
|
52
57
|
inline-size: 100%;
|
|
53
58
|
cursor: pointer;
|
|
54
59
|
color: var(--bds-color_on-bg--subtle);
|
|
55
60
|
font-weight: var(--bds-font_weight--semibold);
|
|
56
|
-
font-size: var(--bds-font_size--body--s);
|
|
61
|
+
font-size: var(--table_header-font-size, var(--bds-font_size--body--s));
|
|
57
62
|
transition: var(--bds-animation_transition);
|
|
58
63
|
box-sizing: border-box;
|
|
59
64
|
}
|
|
@@ -102,7 +107,7 @@
|
|
|
102
107
|
}
|
|
103
108
|
|
|
104
109
|
.td {
|
|
105
|
-
padding: var(--bds-space_s) var(--bds-space_m);
|
|
110
|
+
padding: var(--table_cell-padding, var(--bds-space_s) var(--bds-space_m));
|
|
106
111
|
color: var(--bds-color_on-bg);
|
|
107
112
|
vertical-align: middle;
|
|
108
113
|
}
|
|
@@ -66,4 +66,60 @@ describe('Table', () => {
|
|
|
66
66
|
render(<Table columns={columns} rows={rows} sortKey="role" sortDirection="asc" onSort={() => {}} />);
|
|
67
67
|
expect(screen.getByRole('columnheader', { name: /Role/i })).toHaveAttribute('aria-sort', 'ascending');
|
|
68
68
|
});
|
|
69
|
+
|
|
70
|
+
it('does not stringify a ReactNode header into the sort button aria-label', () => {
|
|
71
|
+
const nodeColumns = [
|
|
72
|
+
...columns,
|
|
73
|
+
{ key: 'extra', header: <span>Extra</span>, sortable: true },
|
|
74
|
+
];
|
|
75
|
+
render(<Table columns={nodeColumns} rows={rows} onSort={() => {}} />);
|
|
76
|
+
const extraButton = screen.getByRole('button', { name: 'Extra' });
|
|
77
|
+
expect(extraButton.hasAttribute('aria-label')).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('uses sortLabel for the sort button aria-label when provided', () => {
|
|
81
|
+
const nodeColumns = [
|
|
82
|
+
...columns,
|
|
83
|
+
{ key: 'extra', header: <span>Extra</span>, sortable: true, sortLabel: 'Extra column' },
|
|
84
|
+
];
|
|
85
|
+
render(<Table columns={nodeColumns} rows={rows} onSort={() => {}} />);
|
|
86
|
+
expect(screen.getByRole('button', { name: 'Sort by Extra column' })).toBeInTheDocument();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('does not warn about duplicate keys when row objects are structurally identical', () => {
|
|
90
|
+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
91
|
+
const duplicateRows = [rows[0], { ...rows[0] }];
|
|
92
|
+
render(<Table columns={columns} rows={duplicateRows} />);
|
|
93
|
+
const keyWarnings = consoleErrorSpy.mock.calls.filter(call => String(call[0]).includes('same key'));
|
|
94
|
+
expect(keyWarnings).toHaveLength(0);
|
|
95
|
+
consoleErrorSpy.mockRestore();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('uses rowKey to derive the row key when provided', () => {
|
|
99
|
+
const rowKey = vi.fn((row: Record<string, unknown>) => String(row.name));
|
|
100
|
+
render(<Table columns={columns} rows={rows} rowKey={rowKey} />);
|
|
101
|
+
expect(rowKey).toHaveBeenCalledWith(rows[0], 0);
|
|
102
|
+
expect(rowKey).toHaveBeenCalledWith(rows[1], 1);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('forwards additional HTML attributes to the scrollable wrapper', () => {
|
|
106
|
+
render(<Table columns={columns} rows={rows} data-testid="my-table" />);
|
|
107
|
+
expect(screen.getByTestId('my-table')).toBeInTheDocument();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('exposes the scroll wrapper as a focusable labelled region when a caption is set', () => {
|
|
111
|
+
render(<Table columns={columns} rows={rows} caption="Team members" />);
|
|
112
|
+
const region = screen.getByRole('region', { name: 'Team members' });
|
|
113
|
+
expect(region).toHaveAttribute('tabindex', '0');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('does not expose an unlabelled region role when there is no caption or aria-label', () => {
|
|
117
|
+
render(<Table columns={columns} rows={rows} />);
|
|
118
|
+
expect(screen.queryByRole('region')).not.toBeInTheDocument();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('respects a consumer-supplied aria-label over the generated caption label', () => {
|
|
122
|
+
render(<Table columns={columns} rows={rows} caption="Team members" aria-label="Custom label" />);
|
|
123
|
+
expect(screen.getByRole('region', { name: 'Custom label' })).toBeInTheDocument();
|
|
124
|
+
});
|
|
69
125
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
1
|
+
import { HTMLAttributes, ReactNode, useId } from 'react';
|
|
2
2
|
import css from './Table.module.css';
|
|
3
3
|
import { cn } from '@boostdev/design-system-foundation';
|
|
4
4
|
import type { WithClassName } from '../../../types';
|
|
@@ -7,16 +7,29 @@ export interface TableColumn<Row> {
|
|
|
7
7
|
key: string;
|
|
8
8
|
header: ReactNode;
|
|
9
9
|
sortable?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Accessible label for the sort button, used instead of stringifying `header`.
|
|
12
|
+
* Required for a reliable accessible name when `header` is not plain text
|
|
13
|
+
* (e.g. an icon or custom ReactNode) — otherwise the button falls back to
|
|
14
|
+
* its visible text content.
|
|
15
|
+
*/
|
|
16
|
+
sortLabel?: string;
|
|
10
17
|
render?: (row: Row) => ReactNode;
|
|
11
18
|
}
|
|
12
19
|
|
|
13
|
-
interface TableProps<Row extends Record<string, unknown>>
|
|
20
|
+
interface TableProps<Row extends Record<string, unknown>>
|
|
21
|
+
extends WithClassName,
|
|
22
|
+
HTMLAttributes<HTMLDivElement> {
|
|
14
23
|
columns: TableColumn<Row>[];
|
|
15
24
|
rows: Row[];
|
|
16
25
|
caption?: string;
|
|
17
26
|
sortKey?: string;
|
|
18
27
|
sortDirection?: 'asc' | 'desc';
|
|
19
28
|
onSort?: (key: string, direction: 'asc' | 'desc') => void;
|
|
29
|
+
/**
|
|
30
|
+
* Derives a stable React key per row. Defaults to the row index when omitted.
|
|
31
|
+
*/
|
|
32
|
+
rowKey?: (row: Row, index: number) => string;
|
|
20
33
|
}
|
|
21
34
|
|
|
22
35
|
export function Table<Row extends Record<string, unknown>>({
|
|
@@ -26,8 +39,15 @@ export function Table<Row extends Record<string, unknown>>({
|
|
|
26
39
|
sortKey,
|
|
27
40
|
sortDirection,
|
|
28
41
|
onSort,
|
|
42
|
+
rowKey,
|
|
29
43
|
className,
|
|
44
|
+
...rest
|
|
30
45
|
}: Readonly<TableProps<Row>>) {
|
|
46
|
+
const generatedId = useId();
|
|
47
|
+
const captionId = caption ? `${generatedId}-caption` : undefined;
|
|
48
|
+
const hasCustomLabel = 'aria-label' in rest || 'aria-labelledby' in rest;
|
|
49
|
+
const hasAccessibleName = Boolean(caption) || hasCustomLabel;
|
|
50
|
+
|
|
31
51
|
const handleSort = (key: string) => {
|
|
32
52
|
if (!onSort) return;
|
|
33
53
|
const nextDirection = sortKey === key && sortDirection === 'asc' ? 'desc' : 'asc';
|
|
@@ -39,10 +59,28 @@ export function Table<Row extends Record<string, unknown>>({
|
|
|
39
59
|
return sortDirection === 'asc' ? 'ascending' : 'descending';
|
|
40
60
|
};
|
|
41
61
|
|
|
62
|
+
const getSortAriaLabel = (col: TableColumn<Row>): string | undefined => {
|
|
63
|
+
const label = col.sortLabel ?? (typeof col.header === 'string' ? col.header : undefined);
|
|
64
|
+
if (!label) return undefined;
|
|
65
|
+
const stateSuffix =
|
|
66
|
+
sortKey === col.key ? `, currently ${sortDirection === 'asc' ? 'ascending' : 'descending'}` : '';
|
|
67
|
+
return `Sort by ${label}${stateSuffix}`;
|
|
68
|
+
};
|
|
69
|
+
|
|
42
70
|
return (
|
|
43
|
-
<div
|
|
71
|
+
<div
|
|
72
|
+
{...rest}
|
|
73
|
+
className={cn(css.wrapper, className)}
|
|
74
|
+
tabIndex={rest.tabIndex ?? 0}
|
|
75
|
+
role={hasAccessibleName ? 'region' : undefined}
|
|
76
|
+
aria-labelledby={hasCustomLabel ? rest['aria-labelledby'] : captionId}
|
|
77
|
+
>
|
|
44
78
|
<table className={css.table}>
|
|
45
|
-
{caption &&
|
|
79
|
+
{caption && (
|
|
80
|
+
<caption id={captionId} className={css.caption}>
|
|
81
|
+
{caption}
|
|
82
|
+
</caption>
|
|
83
|
+
)}
|
|
46
84
|
<thead className={css.thead}>
|
|
47
85
|
<tr>
|
|
48
86
|
{columns.map(col => (
|
|
@@ -57,7 +95,7 @@ export function Table<Row extends Record<string, unknown>>({
|
|
|
57
95
|
type="button"
|
|
58
96
|
className={css.sortButton}
|
|
59
97
|
onClick={() => handleSort(col.key)}
|
|
60
|
-
aria-label={
|
|
98
|
+
aria-label={getSortAriaLabel(col)}
|
|
61
99
|
>
|
|
62
100
|
{col.header}
|
|
63
101
|
<svg
|
|
@@ -84,7 +122,7 @@ export function Table<Row extends Record<string, unknown>>({
|
|
|
84
122
|
</thead>
|
|
85
123
|
<tbody className={css.tbody}>
|
|
86
124
|
{rows.map((row, rowIndex) => (
|
|
87
|
-
<tr key={
|
|
125
|
+
<tr key={rowKey?.(row, rowIndex) ?? rowIndex} className={css.tr}>
|
|
88
126
|
{columns.map(col => (
|
|
89
127
|
<td key={col.key} className={css.td}>
|
|
90
128
|
{col.render ? col.render(row) : String(row[col.key] ?? '')}
|
|
@@ -31,6 +31,8 @@ Organises content into discrete panels accessible via a horizontal tab list.
|
|
|
31
31
|
|
|
32
32
|
## Props
|
|
33
33
|
|
|
34
|
+
> **HTML attributes** — all standard HTML attributes for the root element are forwarded via `{...rest}` (`id`, `className`, `style`, `data-*`, `aria-*`, event handlers, etc.). Component-controlled attributes take precedence.
|
|
35
|
+
|
|
34
36
|
<ArgTypes of={Stories} />
|
|
35
37
|
|
|
36
38
|
## CSS variables
|
|
@@ -64,9 +66,23 @@ The override surface is **layered**. Setting `--tab_color` / `--tab_border-color
|
|
|
64
66
|
|
|
65
67
|
Defaults: hover/active text resolves to `--bds-color_interactive_on-bg`; active border to `--bds-color_interactive`.
|
|
66
68
|
|
|
69
|
+
### Spacing tokens
|
|
70
|
+
|
|
71
|
+
<table>
|
|
72
|
+
<thead>
|
|
73
|
+
<tr><th>Variable</th><th>Default</th><th>Description</th></tr>
|
|
74
|
+
</thead>
|
|
75
|
+
<tbody>
|
|
76
|
+
<tr><td>`--tab_padding`</td><td>`var(--bds-space_s) var(--bds-space_m)`</td><td>Padding of each tab button.</td></tr>
|
|
77
|
+
<tr><td>`--tabs_gap`</td><td>`var(--bds-space_xxs)`</td><td>Gap between tab buttons in the tablist.</td></tr>
|
|
78
|
+
<tr><td>`--tabs_panel-padding`</td><td>`var(--bds-space_m)`</td><td>Block-start padding of the active panel.</td></tr>
|
|
79
|
+
</tbody>
|
|
80
|
+
</table>
|
|
67
81
|
|
|
68
82
|
## Accessibility
|
|
69
83
|
|
|
70
84
|
- Implements ARIA tabs pattern: `role="tablist"`, `role="tab"`, `role="tabpanel"`
|
|
71
85
|
- Arrow keys navigate between tabs; `Enter`/`Space` activate
|
|
72
86
|
- Active tab uses `aria-selected="true"`; panel linked via `aria-labelledby`
|
|
87
|
+
- Tab panels are focusable (`tabIndex={0}`) so keyboard users can move from the active tab into its panel content
|
|
88
|
+
- The initially active tab always resolves to an enabled tab: `defaultTab` is honoured only when it points to a non-disabled tab, otherwise the first enabled tab is used — a disabled first tab (or an invalid `defaultTab`) never leaves the tablist keyboard-unreachable
|
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
.tabList {
|
|
8
8
|
display: flex;
|
|
9
9
|
border-block-end: 1px solid var(--bds-color_bg--subtle);
|
|
10
|
-
gap: var(--bds-space_xxs);
|
|
10
|
+
gap: var(--tabs_gap, var(--bds-space_xxs));
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
.tab {
|
|
14
14
|
all: unset;
|
|
15
15
|
display: inline-flex;
|
|
16
16
|
align-items: center;
|
|
17
|
-
padding: var(--bds-space_s) var(--bds-space_m);
|
|
17
|
+
padding: var(--tab_padding, var(--bds-space_s) var(--bds-space_m));
|
|
18
18
|
font-family: var(--bds-font_family--body);
|
|
19
19
|
font-size: var(--bds-font_size--body);
|
|
20
20
|
font-weight: var(--bds-font_weight--semibold);
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
.panel {
|
|
54
|
-
padding-block-start: var(--bds-space_m);
|
|
54
|
+
padding-block-start: var(--tabs_panel-padding, var(--bds-space_m));
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
.panel:focus-visible {
|
|
@@ -88,4 +88,24 @@ describe('Tabs', () => {
|
|
|
88
88
|
const panelId = firstTab.getAttribute('aria-controls');
|
|
89
89
|
expect(document.getElementById(panelId!)).toBeInTheDocument();
|
|
90
90
|
});
|
|
91
|
+
|
|
92
|
+
it('activates the first enabled tab when the first tab is disabled and no defaultTab is given', () => {
|
|
93
|
+
const tabsWithDisabledFirst = [
|
|
94
|
+
{ id: 'tab1', label: 'First', content: <p>First panel</p>, disabled: true },
|
|
95
|
+
{ id: 'tab2', label: 'Second', content: <p>Second panel</p> },
|
|
96
|
+
];
|
|
97
|
+
render(<Tabs tabs={tabsWithDisabledFirst} />);
|
|
98
|
+
expect(screen.getByRole('tab', { name: 'Second' })).toHaveAttribute('aria-selected', 'true');
|
|
99
|
+
expect(screen.getByRole('tab', { name: 'Second' })).toHaveAttribute('tabindex', '0');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('falls back to the first enabled tab when defaultTab points to a disabled tab', () => {
|
|
103
|
+
render(<Tabs tabs={tabs} defaultTab="tab3" />);
|
|
104
|
+
expect(screen.getByRole('tab', { name: 'First' })).toHaveAttribute('aria-selected', 'true');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('makes the active tab panel focusable', () => {
|
|
108
|
+
render(<Tabs tabs={tabs} />);
|
|
109
|
+
expect(screen.getByText('First panel').closest('[role="tabpanel"]')).toHaveAttribute('tabindex', '0');
|
|
110
|
+
});
|
|
91
111
|
});
|
|
@@ -17,7 +17,10 @@ interface TabsProps extends WithClassName, HTMLAttributes<HTMLDivElement> {
|
|
|
17
17
|
|
|
18
18
|
export function Tabs({ tabs, defaultTab, className, ...rest }: Readonly<TabsProps>) {
|
|
19
19
|
const baseId = useId();
|
|
20
|
-
const [activeTab, setActiveTab] = useState(
|
|
20
|
+
const [activeTab, setActiveTab] = useState(() => {
|
|
21
|
+
const initialTab = tabs.find(tab => tab.id === defaultTab && !tab.disabled) ?? tabs.find(tab => !tab.disabled);
|
|
22
|
+
return initialTab?.id;
|
|
23
|
+
});
|
|
21
24
|
const tabRefs = useRef<(HTMLButtonElement | null)[]>([]);
|
|
22
25
|
|
|
23
26
|
const enabledIndexes = tabs
|
|
@@ -88,6 +91,7 @@ export function Tabs({ tabs, defaultTab, className, ...rest }: Readonly<TabsProp
|
|
|
88
91
|
role="tabpanel"
|
|
89
92
|
aria-labelledby={tabId}
|
|
90
93
|
hidden={!isActive}
|
|
94
|
+
tabIndex={0}
|
|
91
95
|
className={css.panel}
|
|
92
96
|
>
|
|
93
97
|
{tab.content}
|
|
@@ -34,6 +34,8 @@ Short contextual label that appears on hover or focus. Use to supplement icon bu
|
|
|
34
34
|
|
|
35
35
|
## Props
|
|
36
36
|
|
|
37
|
+
> **HTML attributes** — all standard HTML attributes for the root element are forwarded via `{...rest}` (`id`, `className`, `style`, `data-*`, `aria-*`, event handlers, etc.). Component-controlled attributes take precedence.
|
|
38
|
+
|
|
37
39
|
<ArgTypes of={Stories} />
|
|
38
40
|
|
|
39
41
|
## CSS variables
|
|
@@ -46,6 +48,11 @@ Short contextual label that appears on hover or focus. Use to supplement icon bu
|
|
|
46
48
|
<tbody>
|
|
47
49
|
<tr><td>`--tooltip_bg`</td><td>`var(--bds-color_on-bg)`</td><td>Tooltip background colour</td></tr>
|
|
48
50
|
<tr><td>`--tooltip_text`</td><td>`var(--bds-color_bg)`</td><td>Tooltip text colour</td></tr>
|
|
51
|
+
<tr><td>`--tooltip_radius`</td><td>`var(--bds-border_radius--full)`</td><td>Tooltip corner radius</td></tr>
|
|
52
|
+
<tr><td>`--tooltip_padding`</td><td>`var(--bds-space_xxs) var(--bds-space_xs)`</td><td>Tooltip padding</td></tr>
|
|
53
|
+
<tr><td>`--tooltip_font-size`</td><td>`var(--bds-font_size--body--s)`</td><td>Tooltip text size</td></tr>
|
|
54
|
+
<tr><td>`--tooltip_offset`</td><td>`var(--bds-space_xs)`</td><td>Gap between the trigger and the tooltip. Set to `0` to close the gap so the pointer can travel from the trigger onto the tooltip without a dead zone.</td></tr>
|
|
55
|
+
<tr><td>`--tooltip_max-inline-size`</td><td>`20rem`</td><td>Maximum inline size before content wraps</td></tr>
|
|
49
56
|
</tbody>
|
|
50
57
|
</table>
|
|
51
58
|
|
|
@@ -54,4 +61,6 @@ Short contextual label that appears on hover or focus. Use to supplement icon bu
|
|
|
54
61
|
|
|
55
62
|
- Tooltip content is linked via `aria-describedby` on the trigger element
|
|
56
63
|
- Shown on both `:hover` and `:focus` to be keyboard accessible
|
|
64
|
+
- Dismissible with the <kbd>Escape</kbd> key while the tooltip is visible (WCAG 1.4.13)
|
|
65
|
+
- Hoverable — moving the pointer from the trigger onto the tooltip itself keeps it open instead of dismissing it
|
|
57
66
|
- Does not contain interactive content (use `Popover` for that)
|
|
@@ -6,16 +6,27 @@
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
.tooltip {
|
|
9
|
+
/* The `hidden` attribute (toggled by React state) is what makes the
|
|
10
|
+
tooltip's visibility genuinely testable and accessible - it removes
|
|
11
|
+
the element from the accessibility tree natively, matching the
|
|
12
|
+
convention used by Accordion/Tabs panels. Browsers default
|
|
13
|
+
`[hidden]` to `display: none`, which would skip the opacity
|
|
14
|
+
transition below, so it's overridden back to a normal display here;
|
|
15
|
+
showing/hiding stays driven entirely by visibility/opacity via the
|
|
16
|
+
--visible class. */
|
|
17
|
+
display: block;
|
|
9
18
|
position: absolute;
|
|
10
19
|
z-index: var(--bds-z-index_popover);
|
|
11
|
-
padding: var(--bds-space_xxs) var(--bds-space_xs);
|
|
20
|
+
padding: var(--tooltip_padding, var(--bds-space_xxs) var(--bds-space_xs));
|
|
12
21
|
border-radius: var(--tooltip_radius, var(--bds-border_radius--full));
|
|
13
22
|
background-color: var(--tooltip_bg, var(--bds-color_on-bg));
|
|
14
23
|
color: var(--tooltip_text, var(--bds-color_bg));
|
|
15
|
-
font-size: var(--bds-font_size--body--s);
|
|
24
|
+
font-size: var(--tooltip_font-size, var(--bds-font_size--body--s));
|
|
16
25
|
line-height: var(--bds-font_line-height--body);
|
|
17
|
-
|
|
26
|
+
max-inline-size: var(--tooltip_max-inline-size, 20rem);
|
|
27
|
+
white-space: normal;
|
|
18
28
|
pointer-events: none;
|
|
29
|
+
--tooltip_dir: 1;
|
|
19
30
|
|
|
20
31
|
/* Hidden by default */
|
|
21
32
|
visibility: hidden;
|
|
@@ -23,37 +34,46 @@
|
|
|
23
34
|
transition: opacity var(--bds-animation_transition-duration) var(--bds-animation_easing);
|
|
24
35
|
}
|
|
25
36
|
|
|
26
|
-
|
|
27
|
-
.
|
|
28
|
-
|
|
37
|
+
[dir='rtl'] .tooltip,
|
|
38
|
+
.tooltip[dir='rtl'] {
|
|
39
|
+
--tooltip_dir: -1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Shown when the component's isVisible state is true (hover, focus, or
|
|
43
|
+
keyboard-triggered) — driven by React state rather than pure CSS :hover
|
|
44
|
+
so an Escape keypress can dismiss it (WCAG 1.4.13) even while the
|
|
45
|
+
pointer is still over the trigger. pointer-events is enabled only while
|
|
46
|
+
visible so the tooltip content itself is hoverable. */
|
|
47
|
+
.--visible {
|
|
29
48
|
visibility: visible;
|
|
30
49
|
opacity: 1;
|
|
50
|
+
pointer-events: auto;
|
|
31
51
|
}
|
|
32
52
|
|
|
33
53
|
/* Placement: top (default) */
|
|
34
54
|
.--placement_top {
|
|
35
|
-
inset-block-end: calc(100% + var(--bds-space_xs));
|
|
55
|
+
inset-block-end: calc(100% + var(--tooltip_offset, var(--bds-space_xs)));
|
|
36
56
|
inset-inline-start: 50%;
|
|
37
|
-
transform: translateX(-50%);
|
|
57
|
+
transform: translateX(calc(-50% * var(--tooltip_dir, 1)));
|
|
38
58
|
}
|
|
39
59
|
|
|
40
60
|
/* Placement: bottom */
|
|
41
61
|
.--placement_bottom {
|
|
42
|
-
inset-block-start: calc(100% + var(--bds-space_xs));
|
|
62
|
+
inset-block-start: calc(100% + var(--tooltip_offset, var(--bds-space_xs)));
|
|
43
63
|
inset-inline-start: 50%;
|
|
44
|
-
transform: translateX(-50%);
|
|
64
|
+
transform: translateX(calc(-50% * var(--tooltip_dir, 1)));
|
|
45
65
|
}
|
|
46
66
|
|
|
47
67
|
/* Placement: left */
|
|
48
68
|
.--placement_left {
|
|
49
|
-
inset-inline-end: calc(100% + var(--bds-space_xs));
|
|
69
|
+
inset-inline-end: calc(100% + var(--tooltip_offset, var(--bds-space_xs)));
|
|
50
70
|
inset-block-start: 50%;
|
|
51
71
|
transform: translateY(-50%);
|
|
52
72
|
}
|
|
53
73
|
|
|
54
74
|
/* Placement: right */
|
|
55
75
|
.--placement_right {
|
|
56
|
-
inset-inline-start: calc(100% + var(--bds-space_xs));
|
|
76
|
+
inset-inline-start: calc(100% + var(--tooltip_offset, var(--bds-space_xs)));
|
|
57
77
|
inset-block-start: 50%;
|
|
58
78
|
transform: translateY(-50%);
|
|
59
79
|
}
|
|
@@ -43,4 +43,60 @@ describe('Tooltip', () => {
|
|
|
43
43
|
await user.tab();
|
|
44
44
|
expect(screen.getByRole('tooltip')).toBeVisible();
|
|
45
45
|
});
|
|
46
|
+
|
|
47
|
+
it('shows and hides the tooltip on hover', async () => {
|
|
48
|
+
const user = userEvent.setup();
|
|
49
|
+
render(
|
|
50
|
+
<Tooltip content="Hover text">
|
|
51
|
+
<button type="button">Hover target</button>
|
|
52
|
+
</Tooltip>
|
|
53
|
+
);
|
|
54
|
+
const trigger = screen.getByRole('button', { name: 'Hover target' });
|
|
55
|
+
const tooltip = screen.getByRole('tooltip', { hidden: true });
|
|
56
|
+
expect(tooltip).not.toBeVisible();
|
|
57
|
+
|
|
58
|
+
await user.hover(trigger);
|
|
59
|
+
expect(tooltip).toBeVisible();
|
|
60
|
+
|
|
61
|
+
await user.unhover(trigger);
|
|
62
|
+
expect(tooltip).not.toBeVisible();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('dismisses the tooltip when Escape is pressed while visible', async () => {
|
|
66
|
+
const user = userEvent.setup();
|
|
67
|
+
render(
|
|
68
|
+
<Tooltip content="Focused">
|
|
69
|
+
<button type="button">Focus me</button>
|
|
70
|
+
</Tooltip>
|
|
71
|
+
);
|
|
72
|
+
await user.tab();
|
|
73
|
+
const tooltip = screen.getByRole('tooltip');
|
|
74
|
+
expect(tooltip).toBeVisible();
|
|
75
|
+
|
|
76
|
+
await user.keyboard('{Escape}');
|
|
77
|
+
expect(tooltip).not.toBeVisible();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('keeps the tooltip visible when the pointer moves onto the tooltip content, and hides it once the pointer leaves', async () => {
|
|
81
|
+
const user = userEvent.setup();
|
|
82
|
+
render(
|
|
83
|
+
<Tooltip content="Hover text">
|
|
84
|
+
<button type="button">Hover target</button>
|
|
85
|
+
</Tooltip>
|
|
86
|
+
);
|
|
87
|
+
const trigger = screen.getByRole('button', { name: 'Hover target' });
|
|
88
|
+
const tooltip = screen.getByRole('tooltip', { hidden: true });
|
|
89
|
+
|
|
90
|
+
await user.hover(trigger);
|
|
91
|
+
expect(tooltip).toBeVisible();
|
|
92
|
+
|
|
93
|
+
// Moving the pointer onto the (now interactive) tooltip content must not
|
|
94
|
+
// dismiss it - this is only possible once pointer-events is enabled on
|
|
95
|
+
// the visible tooltip.
|
|
96
|
+
await user.hover(tooltip);
|
|
97
|
+
expect(tooltip).toBeVisible();
|
|
98
|
+
|
|
99
|
+
await user.unhover(tooltip);
|
|
100
|
+
expect(tooltip).not.toBeVisible();
|
|
101
|
+
});
|
|
46
102
|
});
|