@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
|
@@ -17,6 +17,16 @@ import { LitElement, css, html } from 'lit';
|
|
|
17
17
|
*/
|
|
18
18
|
export class BdsTab extends LitElement {
|
|
19
19
|
static styles = css`
|
|
20
|
+
/* Shadow DOM does not inherit the global box-sizing reset, so set it
|
|
21
|
+
here; otherwise elements with an explicit size + padding size as
|
|
22
|
+
content-box and layouts drift from the React (light-DOM) versions. */
|
|
23
|
+
:host,
|
|
24
|
+
*,
|
|
25
|
+
*::before,
|
|
26
|
+
*::after {
|
|
27
|
+
box-sizing: border-box;
|
|
28
|
+
}
|
|
29
|
+
|
|
20
30
|
:host {
|
|
21
31
|
display: inline-flex;
|
|
22
32
|
}
|
|
@@ -29,8 +39,13 @@ export class BdsTab extends LitElement {
|
|
|
29
39
|
font-family: var(--bds-font_family--body);
|
|
30
40
|
font-size: var(--bds-font_size--body);
|
|
31
41
|
font-weight: var(--bds-font_weight--semibold);
|
|
32
|
-
|
|
33
|
-
|
|
42
|
+
|
|
43
|
+
/* Layered cascade: public overrides (--tab_color, --tab_border-color) win over
|
|
44
|
+
per-state defaults. Active/hover write to the inner --tab_*--state slot —
|
|
45
|
+
mirrors the React Tabs.module.css pattern so a consumer's --tab_color
|
|
46
|
+
override is never shadowed by the selected/hover rules below. */
|
|
47
|
+
color: var(--tab_color, var(--tab_color--state, var(--bds-color_on-bg)));
|
|
48
|
+
border-block-end: 2px solid var(--tab_border-color, var(--tab_border-color--state, transparent));
|
|
34
49
|
margin-block-end: -1px;
|
|
35
50
|
cursor: pointer;
|
|
36
51
|
transition: var(--bds-animation_transition);
|
|
@@ -38,8 +53,8 @@ export class BdsTab extends LitElement {
|
|
|
38
53
|
}
|
|
39
54
|
|
|
40
55
|
:host([selected]) .tab {
|
|
41
|
-
--tab_color: var(--bds-color_interactive_on-bg);
|
|
42
|
-
--tab_border-color: var(--bds-color_interactive);
|
|
56
|
+
--tab_color--state: var(--tab_color--active, var(--bds-color_interactive_on-bg));
|
|
57
|
+
--tab_border-color--state: var(--tab_border-color--active, var(--bds-color_interactive));
|
|
43
58
|
}
|
|
44
59
|
|
|
45
60
|
.tab:disabled {
|
|
@@ -55,7 +70,7 @@ export class BdsTab extends LitElement {
|
|
|
55
70
|
|
|
56
71
|
@media (hover: hover) and (pointer: fine) {
|
|
57
72
|
.tab:not(:disabled, [aria-selected='true']):hover {
|
|
58
|
-
--tab_color: var(--bds-color_interactive_on-bg);
|
|
73
|
+
--tab_color--state: var(--tab_color--hover, var(--bds-color_interactive_on-bg));
|
|
59
74
|
}
|
|
60
75
|
}
|
|
61
76
|
`;
|
|
@@ -36,6 +36,23 @@ describe('bds-tabs', () => {
|
|
|
36
36
|
cleanup(el);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
+
it('does not select a disabled first tab — activates the first enabled tab instead', async () => {
|
|
40
|
+
const el = await fixture(`
|
|
41
|
+
<bds-tabs>
|
|
42
|
+
<bds-tab slot="tab" disabled>Tab 1</bds-tab>
|
|
43
|
+
<bds-tab slot="tab">Tab 2</bds-tab>
|
|
44
|
+
<bds-tab-panel slot="panel">Panel 1</bds-tab-panel>
|
|
45
|
+
<bds-tab-panel slot="panel">Panel 2</bds-tab-panel>
|
|
46
|
+
</bds-tabs>
|
|
47
|
+
`);
|
|
48
|
+
// Wait for deferred _applyState
|
|
49
|
+
await Promise.resolve();
|
|
50
|
+
const tabs = [...el.querySelectorAll<BdsTab>('bds-tab')];
|
|
51
|
+
expect(tabs[0].selected).toBe(false);
|
|
52
|
+
expect(tabs[1].selected).toBe(true);
|
|
53
|
+
cleanup(el);
|
|
54
|
+
});
|
|
55
|
+
|
|
39
56
|
it('first panel is visible, others are hidden by default', async () => {
|
|
40
57
|
const el = await fixture(html);
|
|
41
58
|
await Promise.resolve();
|
|
@@ -77,9 +94,10 @@ describe('bds-tabs', () => {
|
|
|
77
94
|
it('ArrowRight moves to next enabled tab', async () => {
|
|
78
95
|
const el = await fixture(html);
|
|
79
96
|
await Promise.resolve();
|
|
80
|
-
el.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }));
|
|
81
|
-
await Promise.resolve();
|
|
82
97
|
const tabs = [...el.querySelectorAll<BdsTab>('bds-tab')];
|
|
98
|
+
// Real usage: the keydown originates from the focused (currently active) tab.
|
|
99
|
+
tabs[0].dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }));
|
|
100
|
+
await Promise.resolve();
|
|
83
101
|
expect(tabs[1].selected).toBe(true);
|
|
84
102
|
cleanup(el);
|
|
85
103
|
});
|
|
@@ -89,7 +107,7 @@ describe('bds-tabs', () => {
|
|
|
89
107
|
await Promise.resolve();
|
|
90
108
|
const tabs = [...el.querySelectorAll<BdsTab>('bds-tab')];
|
|
91
109
|
tabs[2].disabled = true; // ensure disabled is set (Lit property may not reflect from HTML attribute in jsdom)
|
|
92
|
-
|
|
110
|
+
tabs[0].dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft', bubbles: true }));
|
|
93
111
|
// disabled tab is skipped; enabled tabs are 0 and 1 → wraps to last enabled (index 1)
|
|
94
112
|
expect(tabs[1].selected).toBe(true);
|
|
95
113
|
cleanup(el);
|
|
@@ -102,7 +120,7 @@ describe('bds-tabs', () => {
|
|
|
102
120
|
await Promise.all(tabs.map(t => t.updateComplete));
|
|
103
121
|
// Navigate to tab 1 first
|
|
104
122
|
tabs[1].dispatchEvent(new CustomEvent('bds-tab-select', { bubbles: true, composed: true }));
|
|
105
|
-
|
|
123
|
+
tabs[1].dispatchEvent(new KeyboardEvent('keydown', { key: 'Home', bubbles: true }));
|
|
106
124
|
expect(tabs[0].selected).toBe(true);
|
|
107
125
|
cleanup(el);
|
|
108
126
|
});
|
|
@@ -112,9 +130,78 @@ describe('bds-tabs', () => {
|
|
|
112
130
|
await Promise.resolve();
|
|
113
131
|
const tabs = [...el.querySelectorAll<BdsTab>('bds-tab')];
|
|
114
132
|
tabs[2].disabled = true; // ensure disabled is set
|
|
115
|
-
|
|
133
|
+
tabs[0].dispatchEvent(new KeyboardEvent('keydown', { key: 'End', bubbles: true }));
|
|
116
134
|
// Last enabled tab is index 1 (tab 3 is disabled)
|
|
117
135
|
expect(tabs[1].selected).toBe(true);
|
|
118
136
|
cleanup(el);
|
|
119
137
|
});
|
|
138
|
+
|
|
139
|
+
it('does not hijack Arrow/Home/End keys typed outside a <bds-tab> (e.g. host-level dispatch)', async () => {
|
|
140
|
+
const el = await fixture(html);
|
|
141
|
+
await Promise.resolve();
|
|
142
|
+
const tabs = [...el.querySelectorAll<BdsTab>('bds-tab')];
|
|
143
|
+
// Dispatched directly on the <bds-tabs> host — not on a <bds-tab> — must be ignored.
|
|
144
|
+
el.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }));
|
|
145
|
+
await Promise.resolve();
|
|
146
|
+
expect(tabs[0].selected).toBe(true);
|
|
147
|
+
expect(tabs[1].selected).toBe(false);
|
|
148
|
+
cleanup(el);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('does not hijack ArrowLeft pressed inside a tab-panel input (caret movement stays with the input)', async () => {
|
|
152
|
+
const el = await fixture(`
|
|
153
|
+
<bds-tabs>
|
|
154
|
+
<bds-tab slot="tab">Tab 1</bds-tab>
|
|
155
|
+
<bds-tab slot="tab">Tab 2</bds-tab>
|
|
156
|
+
<bds-tab-panel slot="panel"><input type="text" /></bds-tab-panel>
|
|
157
|
+
<bds-tab-panel slot="panel">Panel 2</bds-tab-panel>
|
|
158
|
+
</bds-tabs>
|
|
159
|
+
`);
|
|
160
|
+
await Promise.resolve();
|
|
161
|
+
const tabs = [...el.querySelectorAll<BdsTab>('bds-tab')];
|
|
162
|
+
const input = el.querySelector('input')!;
|
|
163
|
+
input.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft', bubbles: true }));
|
|
164
|
+
await Promise.resolve();
|
|
165
|
+
expect(tabs[0].selected).toBe(true);
|
|
166
|
+
expect(tabs[1].selected).toBe(false);
|
|
167
|
+
cleanup(el);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('wires role/id/aria-controls on tabs and role/id/aria-labelledby on panels', async () => {
|
|
171
|
+
const el = await fixture(html);
|
|
172
|
+
await Promise.resolve();
|
|
173
|
+
const tabs = [...el.querySelectorAll<BdsTab>('bds-tab')];
|
|
174
|
+
const panels = [...el.querySelectorAll<BdsTabPanel>('bds-tab-panel')];
|
|
175
|
+
|
|
176
|
+
tabs.forEach((tab, i) => {
|
|
177
|
+
expect(tab.getAttribute('role')).toBe('tab');
|
|
178
|
+
expect(tab.id).toBeTruthy();
|
|
179
|
+
expect(tab.getAttribute('aria-controls')).toBe(panels[i].id);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
panels.forEach((panel, i) => {
|
|
183
|
+
expect(panel.getAttribute('role')).toBe('tabpanel');
|
|
184
|
+
expect(panel.id).toBeTruthy();
|
|
185
|
+
expect(panel.getAttribute('aria-labelledby')).toBe(tabs[i].id);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
cleanup(el);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('preserves a consumer-supplied id instead of overwriting it', async () => {
|
|
192
|
+
const el = await fixture(`
|
|
193
|
+
<bds-tabs>
|
|
194
|
+
<bds-tab slot="tab" id="custom-tab-id">Tab 1</bds-tab>
|
|
195
|
+
<bds-tab-panel slot="panel" id="custom-panel-id">Panel 1</bds-tab-panel>
|
|
196
|
+
</bds-tabs>
|
|
197
|
+
`);
|
|
198
|
+
await Promise.resolve();
|
|
199
|
+
const tab = el.querySelector<BdsTab>('bds-tab')!;
|
|
200
|
+
const panel = el.querySelector<BdsTabPanel>('bds-tab-panel')!;
|
|
201
|
+
expect(tab.id).toBe('custom-tab-id');
|
|
202
|
+
expect(panel.id).toBe('custom-panel-id');
|
|
203
|
+
expect(tab.getAttribute('aria-controls')).toBe('custom-panel-id');
|
|
204
|
+
expect(panel.getAttribute('aria-labelledby')).toBe('custom-tab-id');
|
|
205
|
+
cleanup(el);
|
|
206
|
+
});
|
|
120
207
|
});
|
|
@@ -26,6 +26,16 @@ import type { BdsTabPanel } from './bds-tab-panel';
|
|
|
26
26
|
*/
|
|
27
27
|
export class BdsTabs extends LitElement {
|
|
28
28
|
static styles = css`
|
|
29
|
+
/* Shadow DOM does not inherit the global box-sizing reset, so set it
|
|
30
|
+
here; otherwise elements with an explicit size + padding size as
|
|
31
|
+
content-box and layouts drift from the React (light-DOM) versions. */
|
|
32
|
+
:host,
|
|
33
|
+
*,
|
|
34
|
+
*::before,
|
|
35
|
+
*::after {
|
|
36
|
+
box-sizing: border-box;
|
|
37
|
+
}
|
|
38
|
+
|
|
29
39
|
:host {
|
|
30
40
|
display: flex;
|
|
31
41
|
flex-direction: column;
|
|
@@ -40,6 +50,7 @@ export class BdsTabs extends LitElement {
|
|
|
40
50
|
|
|
41
51
|
private _activeIndex = 0;
|
|
42
52
|
private _mo: MutationObserver | null = null;
|
|
53
|
+
private readonly _uid = Math.random().toString(36).slice(2, 8);
|
|
43
54
|
|
|
44
55
|
connectedCallback() {
|
|
45
56
|
super.connectedCallback();
|
|
@@ -70,7 +81,10 @@ export class BdsTabs extends LitElement {
|
|
|
70
81
|
private get _enabledIndexes(): number[] {
|
|
71
82
|
return this._tabs
|
|
72
83
|
.map((tab, i) => ({ tab, i }))
|
|
73
|
-
|
|
84
|
+
// Check the attribute too: on first render the reflected `disabled`
|
|
85
|
+
// property may not be set yet when _applyState runs, but the authored
|
|
86
|
+
// `disabled` attribute is already present.
|
|
87
|
+
.filter(({ tab }) => !tab.disabled && !tab.hasAttribute('disabled'))
|
|
74
88
|
.map(({ i }) => i);
|
|
75
89
|
}
|
|
76
90
|
|
|
@@ -83,8 +97,28 @@ export class BdsTabs extends LitElement {
|
|
|
83
97
|
this._activeIndex = Math.max(0, tabs.length - 1);
|
|
84
98
|
}
|
|
85
99
|
|
|
100
|
+
// A disabled tab must never be the active one. If the current active index
|
|
101
|
+
// is not an enabled tab (e.g. the default index-0 tab is <bds-tab disabled>),
|
|
102
|
+
// move selection to the first enabled tab — matching the React Tabs behaviour.
|
|
103
|
+
if (this._enabledIndexes.length && !this._enabledIndexes.includes(this._activeIndex)) {
|
|
104
|
+
this._activeIndex = this._enabledIndexes[0];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Assign role/id/aria-* on the light-DOM host elements (not the shadow-internal
|
|
108
|
+
// button/slot) — <bds-tab> and <bds-tab-panel> share the light-DOM tree with
|
|
109
|
+
// <bds-tabs>, so id-based aria-controls/aria-labelledby references resolve.
|
|
110
|
+
// Shadow-internal ids would not be valid IDREF targets from outside their root.
|
|
86
111
|
tabs.forEach((tab, i) => {
|
|
112
|
+
const panel = panels[i];
|
|
87
113
|
tab.selected = (i === this._activeIndex);
|
|
114
|
+
tab.setAttribute('role', 'tab');
|
|
115
|
+
if (!tab.id) tab.id = `bds-tabs-${this._uid}-tab-${i}`;
|
|
116
|
+
if (panel) {
|
|
117
|
+
if (!panel.id) panel.id = `bds-tabs-${this._uid}-panel-${i}`;
|
|
118
|
+
tab.setAttribute('aria-controls', panel.id);
|
|
119
|
+
panel.setAttribute('role', 'tabpanel');
|
|
120
|
+
panel.setAttribute('aria-labelledby', tab.id);
|
|
121
|
+
}
|
|
88
122
|
});
|
|
89
123
|
|
|
90
124
|
panels.forEach((panel, i) => {
|
|
@@ -106,6 +140,13 @@ export class BdsTabs extends LitElement {
|
|
|
106
140
|
};
|
|
107
141
|
|
|
108
142
|
private _handleKeydown = (e: KeyboardEvent) => {
|
|
143
|
+
// Only handle Arrow/Home/End when the key originates from a <bds-tab> (i.e. a
|
|
144
|
+
// tab button has focus). Without this guard the host-level listener hijacked
|
|
145
|
+
// these keys anywhere in the light DOM, including tab-panel content such as
|
|
146
|
+
// text inputs, sliders, or carousels.
|
|
147
|
+
const origin = e.target as Element | null;
|
|
148
|
+
if (!origin?.closest('bds-tab')) return;
|
|
149
|
+
|
|
109
150
|
const enabled = this._enabledIndexes;
|
|
110
151
|
if (!enabled.length) return;
|
|
111
152
|
|
|
@@ -119,4 +119,47 @@ describe('bds-toast-provider', () => {
|
|
|
119
119
|
expect(el.shadowRoot!.querySelectorAll('.toast').length).toBe(2);
|
|
120
120
|
cleanup(el);
|
|
121
121
|
});
|
|
122
|
+
|
|
123
|
+
it('updated() does not throw when showPopover() reports an already-open popover (regression)', async () => {
|
|
124
|
+
// jsdom does not implement the Popover API, so showPopover/hidePopover are
|
|
125
|
+
// undefined on real elements in tests — mock them to reproduce the real-browser
|
|
126
|
+
// InvalidStateError that fires when showPopover() is called on an already-open
|
|
127
|
+
// popover (e.g. a 2nd/3rd toast joining the queue while the popover is showing).
|
|
128
|
+
const el = await fixture('<bds-toast-provider></bds-toast-provider>') as BdsToastProvider;
|
|
129
|
+
el.showToast('First', 'info');
|
|
130
|
+
await el.updateComplete;
|
|
131
|
+
const container = el.shadowRoot!.querySelector('[popover="manual"]') as HTMLElement & {
|
|
132
|
+
showPopover?: () => void;
|
|
133
|
+
};
|
|
134
|
+
container.showPopover = vi.fn(() => {
|
|
135
|
+
throw new DOMException('Invalid state', 'InvalidStateError');
|
|
136
|
+
});
|
|
137
|
+
expect(() => (el as unknown as { updated: () => void }).updated()).not.toThrow();
|
|
138
|
+
expect(container.showPopover).toHaveBeenCalledTimes(1);
|
|
139
|
+
cleanup(el);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('variant rules write --toast_color--variant, not the public --toast_color hook (cascade parity)', () => {
|
|
143
|
+
const cssText = BdsToastProvider.styles.cssText;
|
|
144
|
+
// Variants must only set the inner slot so a consumer-set --toast_color still
|
|
145
|
+
// wins the cascade (element-level declarations otherwise beat inherited values).
|
|
146
|
+
expect(cssText).toMatch(/\.toast\.--variant_error\s*{[^}]*--toast_color--variant:\s*var\(--toast_color--error, var\(--bds-color_error\)\)/);
|
|
147
|
+
expect(cssText).not.toMatch(/\.toast\.--variant_error\s*{[^}]*--toast_color:\s*var\(--bds-color_error\)/);
|
|
148
|
+
expect(cssText).toMatch(/background-color:\s*var\(--toast_color,\s*var\(--toast_color--variant,\s*var\(--bds-color_bg\)\)\);/);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('close button expands its hit area to >=44px via the ::after pseudo-element (touch target)', () => {
|
|
152
|
+
const cssText = BdsToastProvider.styles.cssText;
|
|
153
|
+
expect(cssText).toMatch(/\.close::after\s*{[^}]*inset:\s*-0\.75rem;/);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('container/toast/close use logical properties, not physical ones', () => {
|
|
157
|
+
const cssText = BdsToastProvider.styles.cssText;
|
|
158
|
+
expect(cssText).toMatch(/inset-block-end:\s*var\(--bds-space_xl,\s*2rem\);/);
|
|
159
|
+
expect(cssText).toMatch(/inset-inline-end:\s*var\(--bds-space_xl,\s*2rem\);/);
|
|
160
|
+
expect(cssText).not.toMatch(/\n\s*bottom:/);
|
|
161
|
+
expect(cssText).not.toMatch(/\n\s*right:/);
|
|
162
|
+
expect(cssText).not.toMatch(/\bwidth:\s*1\.25rem/);
|
|
163
|
+
expect(cssText).not.toMatch(/\bheight:\s*1\.25rem/);
|
|
164
|
+
});
|
|
122
165
|
});
|
|
@@ -38,6 +38,16 @@ interface ToastEntry {
|
|
|
38
38
|
*/
|
|
39
39
|
export class BdsToastProvider extends LitElement {
|
|
40
40
|
static styles = css`
|
|
41
|
+
/* Shadow DOM does not inherit the global box-sizing reset, so set it
|
|
42
|
+
here; otherwise elements with an explicit size + padding size as
|
|
43
|
+
content-box and layouts drift from the React (light-DOM) versions. */
|
|
44
|
+
:host,
|
|
45
|
+
*,
|
|
46
|
+
*::before,
|
|
47
|
+
*::after {
|
|
48
|
+
box-sizing: border-box;
|
|
49
|
+
}
|
|
50
|
+
|
|
41
51
|
:host {
|
|
42
52
|
display: contents;
|
|
43
53
|
}
|
|
@@ -46,8 +56,8 @@ export class BdsToastProvider extends LitElement {
|
|
|
46
56
|
/* Reset UA popover defaults */
|
|
47
57
|
position: fixed;
|
|
48
58
|
inset: unset;
|
|
49
|
-
|
|
50
|
-
|
|
59
|
+
inset-block-end: var(--bds-space_xl, 2rem);
|
|
60
|
+
inset-inline-end: var(--bds-space_xl, 2rem);
|
|
51
61
|
display: flex;
|
|
52
62
|
flex-direction: column;
|
|
53
63
|
gap: var(--bds-space_m, 1rem);
|
|
@@ -58,34 +68,36 @@ export class BdsToastProvider extends LitElement {
|
|
|
58
68
|
|
|
59
69
|
.toast {
|
|
60
70
|
padding: var(--bds-space_m) var(--bds-space_l);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
71
|
+
/* Layered cascade: public overrides (--toast_color, --toast_on-color) win
|
|
72
|
+
over per-variant defaults. Variants write to the inner --toast_*--variant slot. */
|
|
73
|
+
background-color: var(--toast_color, var(--toast_color--variant, var(--bds-color_bg)));
|
|
74
|
+
color: var(--toast_on-color, var(--toast_on-color--variant, var(--bds-color_on-bg)));
|
|
75
|
+
border-radius: var(--toast_radius, var(--bds-border_radius--s));
|
|
76
|
+
box-shadow: var(--toast_shadow, var(--bds-shadow_s));
|
|
65
77
|
display: flex;
|
|
66
78
|
align-items: center;
|
|
67
79
|
gap: var(--bds-space_m);
|
|
68
|
-
min-
|
|
80
|
+
min-inline-size: min(300px, calc(100svw - 2 * var(--bds-space_xl)));
|
|
69
81
|
}
|
|
70
82
|
|
|
71
83
|
.toast.--variant_success {
|
|
72
|
-
--toast_color: var(--bds-color_success);
|
|
73
|
-
--toast_on-color: var(--bds-color_on-success);
|
|
84
|
+
--toast_color--variant: var(--toast_color--success, var(--bds-color_success));
|
|
85
|
+
--toast_on-color--variant: var(--toast_on-color--success, var(--bds-color_on-success));
|
|
74
86
|
}
|
|
75
87
|
|
|
76
88
|
.toast.--variant_warning {
|
|
77
|
-
--toast_color: var(--bds-color_warning);
|
|
78
|
-
--toast_on-color: var(--bds-color_on-warning);
|
|
89
|
+
--toast_color--variant: var(--toast_color--warning, var(--bds-color_warning));
|
|
90
|
+
--toast_on-color--variant: var(--toast_on-color--warning, var(--bds-color_on-warning));
|
|
79
91
|
}
|
|
80
92
|
|
|
81
93
|
.toast.--variant_info {
|
|
82
|
-
--toast_color: var(--bds-color_bg--subtle);
|
|
83
|
-
--toast_on-color: var(--bds-color_on-bg--subtle);
|
|
94
|
+
--toast_color--variant: var(--toast_color--info, var(--bds-color_bg--subtle));
|
|
95
|
+
--toast_on-color--variant: var(--toast_on-color--info, var(--bds-color_on-bg--subtle));
|
|
84
96
|
}
|
|
85
97
|
|
|
86
98
|
.toast.--variant_error {
|
|
87
|
-
--toast_color: var(--bds-color_error);
|
|
88
|
-
--toast_on-color: var(--bds-color_on-error);
|
|
99
|
+
--toast_color--variant: var(--toast_color--error, var(--bds-color_error));
|
|
100
|
+
--toast_on-color--variant: var(--toast_on-color--error, var(--bds-color_on-error));
|
|
89
101
|
}
|
|
90
102
|
|
|
91
103
|
.message {
|
|
@@ -93,22 +105,35 @@ export class BdsToastProvider extends LitElement {
|
|
|
93
105
|
}
|
|
94
106
|
|
|
95
107
|
.close {
|
|
108
|
+
position: relative;
|
|
96
109
|
display: flex;
|
|
97
110
|
align-items: center;
|
|
98
111
|
justify-content: center;
|
|
99
112
|
flex-shrink: 0;
|
|
100
|
-
|
|
101
|
-
|
|
113
|
+
inline-size: 1.25rem;
|
|
114
|
+
block-size: 1.25rem;
|
|
102
115
|
padding: 0;
|
|
103
116
|
background: none;
|
|
104
117
|
border: none;
|
|
105
118
|
cursor: pointer;
|
|
106
|
-
color:
|
|
119
|
+
color: currentcolor;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Expands the hit area to >=44px (WCAG 2.5.5) without growing the visible icon. */
|
|
123
|
+
.close::after {
|
|
124
|
+
content: '';
|
|
125
|
+
position: absolute;
|
|
126
|
+
inset: -0.75rem;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.close:focus-visible {
|
|
130
|
+
outline: var(--bds-outline_default);
|
|
131
|
+
outline-offset: var(--bds-outline_offset);
|
|
107
132
|
}
|
|
108
133
|
|
|
109
134
|
.close svg {
|
|
110
|
-
|
|
111
|
-
|
|
135
|
+
inline-size: 1rem;
|
|
136
|
+
block-size: 1rem;
|
|
112
137
|
}
|
|
113
138
|
`;
|
|
114
139
|
|
|
@@ -135,6 +160,10 @@ export class BdsToastProvider extends LitElement {
|
|
|
135
160
|
|
|
136
161
|
// Sync the popover's top-layer visibility with the toast queue —
|
|
137
162
|
// mirrors Toast.tsx's useEffect([toasts.length]).
|
|
163
|
+
// Both showPopover() and hidePopover() throw an InvalidStateError when called
|
|
164
|
+
// in a redundant state (e.g. showPopover() while a 2nd/3rd toast is queued and
|
|
165
|
+
// the popover is already open, or hidePopover() before any toast has shown) —
|
|
166
|
+
// catch silently in both directions.
|
|
138
167
|
updated() {
|
|
139
168
|
const container = this.shadowRoot?.querySelector<HTMLElement & {
|
|
140
169
|
showPopover?: () => void;
|
|
@@ -142,7 +171,7 @@ export class BdsToastProvider extends LitElement {
|
|
|
142
171
|
}>('[popover]');
|
|
143
172
|
if (!container) return;
|
|
144
173
|
if (this._toasts.length > 0) {
|
|
145
|
-
container.showPopover?.();
|
|
174
|
+
try { container.showPopover?.(); } catch { /* already open */ }
|
|
146
175
|
} else {
|
|
147
176
|
try { container.hidePopover?.(); } catch { /* already hidden on initial mount */ }
|
|
148
177
|
}
|
|
@@ -46,4 +46,75 @@ describe('bds-tooltip', () => {
|
|
|
46
46
|
expect(el.querySelector('button')!.textContent).toBe('Help');
|
|
47
47
|
cleanup(el);
|
|
48
48
|
});
|
|
49
|
+
|
|
50
|
+
it('associates the trigger with the tooltip content via aria-describedby', async () => {
|
|
51
|
+
const el = await fixture('<bds-tooltip content="Helpful text"><button type="button">?</button></bds-tooltip>');
|
|
52
|
+
// Wait for the deferred connectedCallback wiring (Promise.resolve().then(...)).
|
|
53
|
+
await Promise.resolve();
|
|
54
|
+
await Promise.resolve();
|
|
55
|
+
|
|
56
|
+
const trigger = el.querySelector('button')!;
|
|
57
|
+
const describedById = trigger.getAttribute('aria-describedby');
|
|
58
|
+
expect(describedById).toBeTruthy();
|
|
59
|
+
|
|
60
|
+
const descEl = el.querySelector(`#${describedById}`);
|
|
61
|
+
expect(descEl).not.toBeNull();
|
|
62
|
+
expect(descEl!.textContent).toBe('Helpful text');
|
|
63
|
+
// The description node must live in the light DOM (same tree scope as the
|
|
64
|
+
// trigger) — aria-describedby cannot reference an id inside the shadow root.
|
|
65
|
+
expect(descEl!.getRootNode()).toBe(document);
|
|
66
|
+
cleanup(el);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('keeps a consumer-provided aria-describedby alongside the generated one', async () => {
|
|
70
|
+
const el = await fixture(
|
|
71
|
+
'<bds-tooltip content="Tip"><button type="button" aria-describedby="external-hint">?</button></bds-tooltip>',
|
|
72
|
+
);
|
|
73
|
+
await Promise.resolve();
|
|
74
|
+
await Promise.resolve();
|
|
75
|
+
|
|
76
|
+
const trigger = el.querySelector('button')!;
|
|
77
|
+
const ids = trigger.getAttribute('aria-describedby')!.split(/\s+/);
|
|
78
|
+
expect(ids).toContain('external-hint');
|
|
79
|
+
expect(ids.length).toBe(2);
|
|
80
|
+
cleanup(el);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('updates the hidden description text when content changes', async () => {
|
|
84
|
+
const el = await fixture('<bds-tooltip content="First"><button type="button">?</button></bds-tooltip>') as BdsTooltip;
|
|
85
|
+
await Promise.resolve();
|
|
86
|
+
await Promise.resolve();
|
|
87
|
+
|
|
88
|
+
el.content = 'Second';
|
|
89
|
+
await el.updateComplete;
|
|
90
|
+
|
|
91
|
+
const trigger = el.querySelector('button')!;
|
|
92
|
+
const describedById = trigger.getAttribute('aria-describedby')!;
|
|
93
|
+
const descEl = el.querySelector(`#${describedById}`);
|
|
94
|
+
expect(descEl!.textContent).toBe('Second');
|
|
95
|
+
cleanup(el);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('does not add aria-describedby when content is empty', async () => {
|
|
99
|
+
const el = await fixture('<bds-tooltip><button type="button">?</button></bds-tooltip>');
|
|
100
|
+
await Promise.resolve();
|
|
101
|
+
await Promise.resolve();
|
|
102
|
+
|
|
103
|
+
const trigger = el.querySelector('button')!;
|
|
104
|
+
expect(trigger.hasAttribute('aria-describedby')).toBe(false);
|
|
105
|
+
cleanup(el);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('hides the shadow-DOM tooltip bubble from assistive technology', async () => {
|
|
109
|
+
const el = await fixture('<bds-tooltip content="Helpful text"><button type="button">?</button></bds-tooltip>');
|
|
110
|
+
const tooltip = el.shadowRoot!.querySelector('[role="tooltip"]')!;
|
|
111
|
+
expect(tooltip.getAttribute('aria-hidden')).toBe('true');
|
|
112
|
+
cleanup(el);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('does not hardcode a z-index fallback (token parity with React Tooltip)', () => {
|
|
116
|
+
const cssText = BdsTooltip.styles.cssText;
|
|
117
|
+
expect(cssText).toMatch(/z-index:\s*var\(--bds-z-index_popover\);/);
|
|
118
|
+
expect(cssText).not.toContain('200');
|
|
119
|
+
});
|
|
49
120
|
});
|
|
@@ -16,9 +16,14 @@ export type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
|
|
|
16
16
|
* (default) — the trigger element (button, icon, etc.)
|
|
17
17
|
*
|
|
18
18
|
* Accessibility:
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* `aria-describedby` id references only resolve within the same tree scope, so a
|
|
20
|
+
* slotted (light-DOM) trigger can never reference an id that lives inside this
|
|
21
|
+
* element's shadow root. To keep the description reachable, a visually-hidden
|
|
22
|
+
* `<span>` holding `content` is appended as a light-DOM child of the host and its
|
|
23
|
+
* id is written into `aria-describedby` on the slotted trigger — both elements then
|
|
24
|
+
* share the light DOM's tree scope, so the reference resolves in every browser. The
|
|
25
|
+
* shadow-DOM tooltip bubble stays `aria-hidden="true"`; it is purely the visual
|
|
26
|
+
* presentation, shown on `:hover`/`:focus-within`.
|
|
22
27
|
*
|
|
23
28
|
* @example
|
|
24
29
|
* <bds-tooltip content="More information" placement="bottom">
|
|
@@ -27,6 +32,16 @@ export type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
|
|
|
27
32
|
*/
|
|
28
33
|
export class BdsTooltip extends LitElement {
|
|
29
34
|
static styles = css`
|
|
35
|
+
/* Shadow DOM does not inherit the global box-sizing reset, so set it
|
|
36
|
+
here; otherwise elements with an explicit size + padding size as
|
|
37
|
+
content-box and layouts drift from the React (light-DOM) versions. */
|
|
38
|
+
:host,
|
|
39
|
+
*,
|
|
40
|
+
*::before,
|
|
41
|
+
*::after {
|
|
42
|
+
box-sizing: border-box;
|
|
43
|
+
}
|
|
44
|
+
|
|
30
45
|
:host {
|
|
31
46
|
display: inline-flex;
|
|
32
47
|
align-items: center;
|
|
@@ -35,7 +50,7 @@ export class BdsTooltip extends LitElement {
|
|
|
35
50
|
|
|
36
51
|
.tooltip {
|
|
37
52
|
position: absolute;
|
|
38
|
-
z-index: var(--bds-z-index_popover
|
|
53
|
+
z-index: var(--bds-z-index_popover);
|
|
39
54
|
padding: var(--bds-space_xxs) var(--bds-space_xs);
|
|
40
55
|
border-radius: var(--tooltip_radius, var(--bds-border_radius--full));
|
|
41
56
|
background-color: var(--tooltip_bg, var(--bds-color_on-bg));
|
|
@@ -101,18 +116,98 @@ export class BdsTooltip extends LitElement {
|
|
|
101
116
|
declare content: string;
|
|
102
117
|
declare placement: TooltipPlacement;
|
|
103
118
|
|
|
119
|
+
private readonly _uid = Math.random().toString(36).slice(2, 8);
|
|
120
|
+
/** Visually-hidden light-DOM node holding `content`, referenced by the trigger's aria-describedby. */
|
|
121
|
+
private _descEl: HTMLSpanElement | null = null;
|
|
122
|
+
|
|
104
123
|
constructor() {
|
|
105
124
|
super();
|
|
106
125
|
this.content = '';
|
|
107
126
|
this.placement = 'top';
|
|
108
127
|
}
|
|
109
128
|
|
|
129
|
+
connectedCallback() {
|
|
130
|
+
super.connectedCallback();
|
|
131
|
+
// Deferred: on declarative parsing the trigger may not exist as a child yet
|
|
132
|
+
// when connectedCallback fires (upgrade happens before children are parsed).
|
|
133
|
+
Promise.resolve().then(this._syncDescription);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
disconnectedCallback() {
|
|
137
|
+
super.disconnectedCallback();
|
|
138
|
+
this._teardownDescription();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
updated(changed: Map<string, unknown>) {
|
|
142
|
+
super.updated(changed);
|
|
143
|
+
if (changed.has('content')) {
|
|
144
|
+
this._syncDescription();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private get _descId(): string {
|
|
149
|
+
return `bds-tooltip-desc-${this._uid}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Ensures the hidden description node exists (or is torn down) and re-wires the trigger. */
|
|
153
|
+
private _syncDescription = () => {
|
|
154
|
+
if (!this.isConnected) return;
|
|
155
|
+
|
|
156
|
+
if (!this.content) {
|
|
157
|
+
this._teardownDescription();
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let desc = this._descEl;
|
|
162
|
+
if (!desc) {
|
|
163
|
+
desc = document.createElement('span');
|
|
164
|
+
desc.id = this._descId;
|
|
165
|
+
desc.style.cssText =
|
|
166
|
+
'position:absolute;inline-size:1px;block-size:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;';
|
|
167
|
+
this.append(desc);
|
|
168
|
+
this._descEl = desc;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
desc.textContent = this.content;
|
|
172
|
+
this._describeTrigger();
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
private _describeTrigger() {
|
|
176
|
+
const id = this._descId;
|
|
177
|
+
for (const el of Array.from(this.children)) {
|
|
178
|
+
if (el === this._descEl) continue;
|
|
179
|
+
const existing = el.getAttribute('aria-describedby');
|
|
180
|
+
const ids = existing ? existing.split(/\s+/).filter(Boolean) : [];
|
|
181
|
+
if (!ids.includes(id)) {
|
|
182
|
+
el.setAttribute('aria-describedby', [...ids, id].join(' '));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private _teardownDescription() {
|
|
188
|
+
const id = this._descId;
|
|
189
|
+
for (const el of Array.from(this.children)) {
|
|
190
|
+
if (el === this._descEl) continue;
|
|
191
|
+
const existing = el.getAttribute('aria-describedby');
|
|
192
|
+
if (!existing) continue;
|
|
193
|
+
const ids = existing.split(/\s+/).filter(part => part && part !== id);
|
|
194
|
+
if (ids.length) {
|
|
195
|
+
el.setAttribute('aria-describedby', ids.join(' '));
|
|
196
|
+
} else {
|
|
197
|
+
el.removeAttribute('aria-describedby');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
this._descEl?.remove();
|
|
201
|
+
this._descEl = null;
|
|
202
|
+
}
|
|
203
|
+
|
|
110
204
|
render() {
|
|
111
205
|
return html`
|
|
112
|
-
<slot></slot>
|
|
206
|
+
<slot @slotchange=${this._syncDescription}></slot>
|
|
113
207
|
<span
|
|
114
208
|
class="tooltip --placement_${this.placement}"
|
|
115
209
|
role="tooltip"
|
|
210
|
+
aria-hidden="true"
|
|
116
211
|
>${this.content}</span>
|
|
117
212
|
`;
|
|
118
213
|
}
|