@boostdev/design-system-components 1.1.0 → 1.1.2
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 +46 -5
- package/dist/client.cjs +206 -113
- package/dist/client.css +664 -624
- package/dist/client.d.cts +9 -3
- package/dist/client.d.ts +9 -3
- package/dist/client.js +241 -149
- package/dist/index.cjs +206 -113
- package/dist/index.css +664 -624
- package/dist/index.d.cts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +241 -149
- package/dist/web-components/chunk-2QA62WCQ.js +48 -0
- package/dist/web-components/chunk-4BX6AWXX.js +146 -0
- package/dist/web-components/chunk-57TRNCWQ.js +93 -0
- package/dist/web-components/chunk-5IPHEONG.js +139 -0
- package/dist/web-components/chunk-5R6IFELV.js +63 -0
- package/dist/web-components/chunk-5TFWOPLH.js +49 -0
- package/dist/web-components/chunk-6I3CFC26.js +89 -0
- package/dist/web-components/chunk-6T3RKUKG.js +65 -0
- package/dist/web-components/chunk-7FMR53BW.js +127 -0
- package/dist/web-components/chunk-A67LCT7C.js +134 -0
- package/dist/web-components/chunk-AGKJTMJI.js +56 -0
- package/dist/web-components/chunk-BQLZ7I4G.js +56 -0
- package/dist/web-components/chunk-CAPI25CU.js +53 -0
- package/dist/web-components/chunk-CJ5ZOO42.js +229 -0
- package/dist/web-components/chunk-CR5PGUHV.js +109 -0
- package/dist/web-components/chunk-ECW3HHWA.js +185 -0
- package/dist/web-components/chunk-HGBNDR22.js +98 -0
- package/dist/web-components/chunk-IK3PTFZ4.js +133 -0
- package/dist/web-components/chunk-IQGLIQSO.js +188 -0
- package/dist/web-components/chunk-ITHLKR7Q.js +105 -0
- package/dist/web-components/chunk-K5CE6HSB.js +169 -0
- package/dist/web-components/chunk-L3XHOMN6.js +111 -0
- package/dist/{web-components.js → web-components/chunk-LGEZYFUU.js} +62 -834
- package/dist/web-components/chunk-LSC4N56Q.js +168 -0
- package/dist/web-components/chunk-LY62SAVN.js +64 -0
- package/dist/web-components/chunk-O4UFI2RX.js +244 -0
- package/dist/web-components/chunk-O7I63SQX.js +124 -0
- package/dist/web-components/chunk-OHUTLSIQ.js +69 -0
- package/dist/web-components/chunk-OQTRTMAB.js +27 -0
- package/dist/web-components/chunk-OTX4COSV.js +187 -0
- package/dist/web-components/chunk-PY62FTRW.js +179 -0
- package/dist/web-components/chunk-Q4T5BJT6.js +94 -0
- package/dist/web-components/chunk-QDRH46TF.js +281 -0
- package/dist/web-components/chunk-QDST57JC.js +119 -0
- package/dist/web-components/chunk-QPOKHLMQ.js +45 -0
- package/dist/web-components/chunk-SOQQ7RVJ.js +88 -0
- package/dist/web-components/chunk-UFFCKVZE.js +149 -0
- package/dist/web-components/chunk-VM3YHGQ2.js +152 -0
- package/dist/web-components/chunk-VMMVWLXL.js +53 -0
- package/dist/web-components/chunk-VSS5ITNE.js +109 -0
- package/dist/web-components/chunk-XIENXR7H.js +118 -0
- package/dist/web-components/chunk-Y4MAXTMA.js +80 -0
- package/dist/web-components/chunk-YLVTEY23.js +165 -0
- package/dist/web-components/globals.d.ts +2 -0
- package/dist/web-components/globals.js +1 -0
- package/dist/web-components/index.d.ts +41 -0
- package/dist/web-components/index.js +165 -0
- package/dist/web-components/interaction/bds-accordion-item.d.ts +54 -0
- package/dist/web-components/interaction/bds-accordion-item.js +7 -0
- package/dist/web-components/interaction/bds-accordion.d.ts +50 -0
- package/dist/web-components/interaction/bds-accordion.js +7 -0
- package/dist/web-components/interaction/bds-button.d.ts +100 -0
- package/dist/web-components/interaction/bds-button.js +7 -0
- package/dist/web-components/interaction/bds-collapsible.d.ts +52 -0
- package/dist/web-components/interaction/bds-collapsible.js +7 -0
- package/dist/web-components/interaction/bds-dialog.d.ts +56 -0
- package/dist/web-components/interaction/bds-dialog.js +7 -0
- package/dist/web-components/interaction/bds-drawer.d.ts +64 -0
- package/dist/web-components/interaction/bds-drawer.js +7 -0
- package/dist/web-components/interaction/bds-popover.d.ts +62 -0
- package/dist/web-components/interaction/bds-popover.js +7 -0
- package/dist/web-components/interaction/bds-skip-link.d.ts +36 -0
- package/dist/web-components/interaction/bds-skip-link.js +7 -0
- package/dist/web-components/interaction/bds-tab-panel.d.ts +26 -0
- package/dist/web-components/interaction/bds-tab-panel.js +7 -0
- package/dist/web-components/interaction/bds-tab.d.ts +45 -0
- package/dist/web-components/interaction/bds-tab.js +7 -0
- package/dist/web-components/interaction/bds-tabs.d.ts +46 -0
- package/dist/web-components/interaction/bds-tabs.js +7 -0
- package/dist/web-components/interaction/bds-toast-provider.d.ts +57 -0
- package/dist/web-components/interaction/bds-toast-provider.js +7 -0
- package/dist/web-components/interaction/bds-tooltip.d.ts +50 -0
- package/dist/web-components/interaction/bds-tooltip.js +7 -0
- package/dist/web-components/interaction/form/bds-checkbox.d.ts +70 -0
- package/dist/web-components/interaction/form/bds-checkbox.js +7 -0
- package/dist/web-components/interaction/form/bds-combobox.d.ts +84 -0
- package/dist/web-components/interaction/form/bds-combobox.js +7 -0
- package/dist/web-components/interaction/form/bds-file-input.d.ts +62 -0
- package/dist/web-components/interaction/form/bds-file-input.js +7 -0
- package/dist/web-components/interaction/form/bds-number-input.d.ts +68 -0
- package/dist/web-components/interaction/form/bds-number-input.js +7 -0
- package/dist/web-components/interaction/form/bds-radio.d.ts +68 -0
- package/dist/web-components/interaction/form/bds-radio.js +7 -0
- package/dist/web-components/interaction/form/bds-segmented-control.d.ts +71 -0
- package/dist/web-components/interaction/form/bds-segmented-control.js +7 -0
- package/dist/web-components/interaction/form/bds-select.d.ts +79 -0
- package/dist/web-components/interaction/form/bds-select.js +7 -0
- package/dist/web-components/interaction/form/bds-slider.d.ts +70 -0
- package/dist/web-components/interaction/form/bds-slider.js +7 -0
- package/dist/web-components/interaction/form/bds-switch.d.ts +67 -0
- package/dist/web-components/interaction/form/bds-switch.js +7 -0
- package/dist/web-components/interaction/form/bds-textarea.d.ts +70 -0
- package/dist/web-components/interaction/form/bds-textarea.js +7 -0
- package/dist/web-components/ui/bds-alert.d.ts +66 -0
- package/dist/web-components/ui/bds-alert.js +7 -0
- package/dist/web-components/ui/bds-avatar.d.ts +50 -0
- package/dist/web-components/ui/bds-avatar.js +7 -0
- package/dist/web-components/ui/bds-badge.d.ts +35 -0
- package/dist/web-components/ui/bds-badge.js +7 -0
- package/dist/web-components/ui/bds-breadcrumb.d.ts +35 -0
- package/dist/web-components/ui/bds-breadcrumb.js +7 -0
- package/dist/web-components/ui/bds-card.d.ts +72 -0
- package/dist/web-components/ui/bds-card.js +7 -0
- package/dist/web-components/ui/bds-description-list.d.ts +42 -0
- package/dist/web-components/ui/bds-description-list.js +7 -0
- package/dist/web-components/ui/bds-icon-wrapper.d.ts +39 -0
- package/dist/web-components/ui/bds-icon-wrapper.js +7 -0
- package/dist/web-components/ui/bds-link.d.ts +59 -0
- package/dist/web-components/ui/bds-link.js +7 -0
- package/dist/web-components/ui/bds-loading.d.ts +32 -0
- package/dist/web-components/ui/bds-loading.js +7 -0
- package/dist/web-components/ui/bds-notification-banner.d.ts +61 -0
- package/dist/web-components/ui/bds-notification-banner.js +7 -0
- package/dist/web-components/ui/bds-progress-circle.d.ts +53 -0
- package/dist/web-components/ui/bds-progress-circle.js +7 -0
- package/dist/web-components/ui/bds-progress.d.ts +54 -0
- package/dist/web-components/ui/bds-progress.js +7 -0
- package/dist/web-components/ui/bds-rating.d.ts +36 -0
- package/dist/web-components/ui/bds-rating.js +7 -0
- package/dist/web-components/ui/bds-section-header.d.ts +53 -0
- package/dist/web-components/ui/bds-section-header.js +8 -0
- package/dist/web-components/ui/bds-separator.d.ts +32 -0
- package/dist/web-components/ui/bds-separator.js +7 -0
- package/dist/web-components/ui/bds-skeleton.d.ts +22 -0
- package/dist/web-components/ui/bds-skeleton.js +7 -0
- package/dist/web-components/ui/bds-typography.d.ts +44 -0
- package/dist/web-components/ui/bds-typography.js +8 -0
- package/package.json +169 -4
- package/src/components/interaction/Button/Button.module.css +1 -1
- package/src/components/interaction/Button/Button.native.mdx +72 -0
- package/src/components/interaction/Command/Command.module.css +7 -7
- package/src/components/interaction/Command/Command.spec.tsx +37 -0
- package/src/components/interaction/Command/Command.tsx +37 -2
- package/src/components/interaction/Dialog/Dialog.module.css +10 -13
- package/src/components/interaction/Dialog/Dialog.spec.tsx +53 -1
- package/src/components/interaction/Dialog/Dialog.stories.tsx +17 -0
- package/src/components/interaction/Dialog/Dialog.tsx +50 -16
- package/src/components/interaction/Drawer/Drawer.module.css +11 -11
- package/src/components/interaction/Drawer/Drawer.spec.tsx +48 -1
- package/src/components/interaction/Drawer/Drawer.tsx +38 -4
- package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +6 -6
- package/src/components/interaction/Popover/Popover.module.css +74 -15
- package/src/components/interaction/Popover/Popover.spec.tsx +94 -59
- package/src/components/interaction/Popover/Popover.tsx +44 -60
- package/src/components/interaction/Toast/Toast.module.css +7 -7
- package/src/components/interaction/form/Checkbox/Checkbox.module.css +6 -6
- package/src/components/interaction/form/Checkbox/Checkbox.native.mdx +74 -0
- package/src/components/interaction/form/Combobox/Combobox.module.css +7 -8
- package/src/components/interaction/form/FileInput/FileInput.module.css +4 -4
- package/src/components/interaction/form/NumberInput/NumberInput.module.css +1 -1
- package/src/components/interaction/form/Radio/Radio.module.css +6 -6
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +5 -6
- package/src/components/interaction/form/Select/Select.module.css +3 -3
- package/src/components/interaction/form/Slider/Slider.module.css +6 -6
- package/src/components/interaction/form/Switch/Switch.module.css +8 -8
- package/src/components/interaction/form/Textarea/Textarea.module.css +1 -1
- package/src/components/layout/IconWrapper/IconWrapper.module.css +2 -2
- package/src/components/ui/Accordion/Accordion.module.css +5 -5
- package/src/components/ui/Alert/Alert.native.mdx +62 -0
- package/src/components/ui/Avatar/Avatar.native.mdx +50 -0
- package/src/components/ui/Badge/Badge.native.mdx +54 -0
- package/src/components/ui/Calendar/Calendar.module.css +7 -7
- package/src/components/ui/Carousel/Carousel.module.css +5 -5
- package/src/components/ui/Collapsible/Collapsible.module.css +4 -4
- package/src/components/ui/Link/Link.module.css +2 -2
- package/src/components/ui/Loading/Loading.module.css +7 -7
- package/src/components/ui/NotificationBanner/NotificationBanner.module.css +3 -3
- package/src/components/ui/Pagination/Pagination.module.css +6 -6
- package/src/components/ui/Progress/Progress.module.css +5 -5
- package/src/components/ui/Separator/Separator.module.css +3 -3
- package/src/components/ui/Skeleton/Skeleton.module.css +1 -1
- package/src/components/ui/Table/Table.module.css +9 -9
- package/src/components/ui/Tabs/Tabs.module.css +3 -3
- package/src/components/ui/Tooltip/Tooltip.module.css +8 -8
- package/src/components/ui/Typography/Typography.native.mdx +56 -0
- package/src/polyfill-invoker-commands.ts +68 -0
- package/src/react-augment.d.ts +19 -0
- package/src/stories/ReactNative.mdx +121 -0
- package/src/web-components/index.ts +47 -0
- package/src/web-components/interaction/BdsButton.mdx +106 -0
- package/src/web-components/interaction/BdsPopover.mdx +120 -0
- package/src/web-components/interaction/BdsToastProvider.mdx +94 -0
- package/src/web-components/interaction/bds-accordion-item.spec.ts +75 -0
- package/src/web-components/interaction/bds-accordion-item.ts +181 -0
- package/src/web-components/interaction/bds-accordion.spec.ts +73 -0
- package/src/web-components/interaction/bds-accordion.ts +99 -0
- package/src/web-components/interaction/bds-collapsible.spec.ts +56 -0
- package/src/web-components/interaction/bds-collapsible.ts +170 -0
- package/src/web-components/interaction/bds-dialog.spec.ts +119 -0
- package/src/web-components/interaction/bds-dialog.ts +224 -0
- package/src/web-components/interaction/bds-drawer.spec.ts +113 -0
- package/src/web-components/interaction/bds-drawer.ts +266 -0
- package/src/web-components/interaction/bds-popover.spec.ts +49 -1
- package/src/web-components/interaction/bds-popover.ts +12 -0
- package/src/web-components/interaction/bds-skip-link.spec.ts +42 -0
- package/src/web-components/interaction/bds-skip-link.ts +74 -0
- package/src/web-components/interaction/bds-tab-panel.spec.ts +32 -0
- package/src/web-components/interaction/bds-tab-panel.ts +38 -0
- package/src/web-components/interaction/bds-tab.spec.ts +71 -0
- package/src/web-components/interaction/bds-tab.ts +110 -0
- package/src/web-components/interaction/bds-tabs.spec.ts +120 -0
- package/src/web-components/interaction/bds-tabs.ts +152 -0
- package/src/web-components/interaction/bds-tooltip.spec.ts +49 -0
- package/src/web-components/interaction/bds-tooltip.ts +127 -0
- package/src/web-components/interaction/form/bds-checkbox.spec.ts +63 -0
- package/src/web-components/interaction/form/bds-checkbox.ts +168 -0
- package/src/web-components/interaction/form/bds-combobox.spec.ts +117 -0
- package/src/web-components/interaction/form/bds-combobox.ts +336 -0
- package/src/web-components/interaction/form/bds-file-input.spec.ts +58 -0
- package/src/web-components/interaction/form/bds-file-input.ts +209 -0
- package/src/web-components/interaction/form/bds-number-input.spec.ts +87 -0
- package/src/web-components/interaction/form/bds-number-input.ts +221 -0
- package/src/web-components/interaction/form/bds-radio.spec.ts +70 -0
- package/src/web-components/interaction/form/bds-radio.ts +157 -0
- package/src/web-components/interaction/form/bds-segmented-control.spec.ts +74 -0
- package/src/web-components/interaction/form/bds-segmented-control.ts +211 -0
- package/src/web-components/interaction/form/bds-select.spec.ts +74 -0
- package/src/web-components/interaction/form/bds-select.ts +190 -0
- package/src/web-components/interaction/form/bds-slider.spec.ts +41 -0
- package/src/web-components/interaction/form/bds-slider.ts +162 -0
- package/src/web-components/interaction/form/bds-switch.spec.ts +62 -0
- package/src/web-components/interaction/form/bds-switch.ts +166 -0
- package/src/web-components/interaction/form/bds-textarea.spec.ts +55 -0
- package/src/web-components/interaction/form/bds-textarea.ts +143 -0
- package/src/web-components/test/helpers.ts +8 -0
- package/src/web-components/ui/BdsAlert.mdx +90 -0
- package/src/web-components/ui/BdsBadge.mdx +74 -0
- package/src/web-components/ui/bds-avatar.spec.ts +75 -0
- package/src/web-components/ui/bds-avatar.ts +122 -0
- package/src/web-components/ui/bds-breadcrumb.spec.ts +55 -0
- package/src/web-components/ui/bds-breadcrumb.ts +114 -0
- package/src/web-components/ui/bds-card.spec.ts +70 -0
- package/src/web-components/ui/bds-card.ts +156 -0
- package/src/web-components/ui/bds-description-list.spec.ts +36 -0
- package/src/web-components/ui/bds-description-list.ts +79 -0
- package/src/web-components/ui/bds-icon-wrapper.spec.ts +21 -0
- package/src/web-components/ui/bds-icon-wrapper.ts +68 -0
- package/src/web-components/ui/bds-link.spec.ts +72 -0
- package/src/web-components/ui/bds-link.ts +138 -0
- package/src/web-components/ui/bds-loading.spec.ts +38 -0
- package/src/web-components/ui/bds-loading.ts +76 -0
- package/src/web-components/ui/bds-notification-banner.spec.ts +82 -0
- package/src/web-components/ui/bds-notification-banner.ts +185 -0
- package/src/web-components/ui/bds-progress-circle.spec.ts +58 -0
- package/src/web-components/ui/bds-progress-circle.ts +142 -0
- package/src/web-components/ui/bds-progress.spec.ts +66 -0
- package/src/web-components/ui/bds-progress.ts +127 -0
- package/src/web-components/ui/bds-rating.spec.ts +70 -0
- package/src/web-components/ui/bds-rating.ts +80 -0
- package/src/web-components/ui/bds-section-header.spec.ts +57 -0
- package/src/web-components/ui/bds-section-header.ts +96 -0
- package/src/web-components/ui/bds-separator.spec.ts +50 -0
- package/src/web-components/ui/bds-separator.ts +66 -0
- package/src/web-components/ui/bds-skeleton.spec.ts +22 -0
- package/src/web-components/ui/bds-skeleton.ts +55 -0
- package/src/web-components/ui/bds-typography.spec.ts +76 -0
- package/src/web-components/ui/bds-typography.ts +112 -0
- package/dist/web-components.d.ts +0 -303
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { LitElement, css, html } from 'lit';
|
|
2
|
+
|
|
3
|
+
const FOCUSABLE = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
|
|
4
|
+
|
|
5
|
+
export type DrawerSide = 'left' | 'right';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `<bds-drawer>` — framework-agnostic Drawer (slide-in panel) custom element.
|
|
9
|
+
*
|
|
10
|
+
* Same modal infrastructure as `<bds-dialog>` but positioned as a side panel.
|
|
11
|
+
* Uses a native `<dialog>` element with a slide-in inner panel.
|
|
12
|
+
*
|
|
13
|
+
* Attributes:
|
|
14
|
+
* open — boolean; controlled open state
|
|
15
|
+
* side — "left" | "right" (default: "right")
|
|
16
|
+
*
|
|
17
|
+
* Slots:
|
|
18
|
+
* title — drawer title text
|
|
19
|
+
* (default) — drawer body content
|
|
20
|
+
*
|
|
21
|
+
* Events:
|
|
22
|
+
* bds-open — fired (bubbles, composed) when the drawer opens
|
|
23
|
+
* bds-close — fired (bubbles, composed) when the drawer closes
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* <bds-drawer id="my-drawer" side="right">
|
|
27
|
+
* <span slot="title">Settings</span>
|
|
28
|
+
* <p>Drawer content</p>
|
|
29
|
+
* </bds-drawer>
|
|
30
|
+
* <button commandfor="my-drawer" command="show-modal">Open</button>
|
|
31
|
+
*/
|
|
32
|
+
export class BdsDrawer extends LitElement {
|
|
33
|
+
static styles = css`
|
|
34
|
+
:host {
|
|
35
|
+
display: contents;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.drawer {
|
|
39
|
+
position: fixed;
|
|
40
|
+
inset: 0;
|
|
41
|
+
margin: 0;
|
|
42
|
+
padding: 0;
|
|
43
|
+
inline-size: 100%;
|
|
44
|
+
max-inline-size: 100%;
|
|
45
|
+
block-size: 100%;
|
|
46
|
+
max-block-size: 100%;
|
|
47
|
+
border: none;
|
|
48
|
+
background: transparent;
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.drawer::backdrop {
|
|
53
|
+
background-color: var(--color_backdrop, rgb(0 0 0 / 50%));
|
|
54
|
+
backdrop-filter: blur(3px);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.panel {
|
|
58
|
+
position: absolute;
|
|
59
|
+
inset-block: 0;
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
inline-size: min(28rem, 90vw);
|
|
63
|
+
block-size: 100%;
|
|
64
|
+
background-color: var(--drawer_color, var(--bds-color_bg));
|
|
65
|
+
color: var(--drawer_on-color, var(--bds-color_on-bg));
|
|
66
|
+
box-shadow: var(--bds-shadow_xl);
|
|
67
|
+
overflow: hidden;
|
|
68
|
+
transition: var(--bds-animation_transition);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
:host([side='right']) .panel {
|
|
72
|
+
inset-inline-end: 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
:host([side='left']) .panel,
|
|
76
|
+
:host(:not([side])) .panel {
|
|
77
|
+
inset-inline-start: 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.header {
|
|
81
|
+
display: flex;
|
|
82
|
+
align-items: center;
|
|
83
|
+
justify-content: space-between;
|
|
84
|
+
padding: var(--bds-space_m) var(--bds-space_l);
|
|
85
|
+
border-block-end: 1px solid currentcolor;
|
|
86
|
+
flex-shrink: 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.title {
|
|
90
|
+
font-weight: var(--bds-font_weight--semibold);
|
|
91
|
+
font-size: var(--bds-font_size--body);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.close-button {
|
|
95
|
+
all: unset;
|
|
96
|
+
display: flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
justify-content: center;
|
|
99
|
+
inline-size: 2rem;
|
|
100
|
+
block-size: 2rem;
|
|
101
|
+
border-radius: 50%;
|
|
102
|
+
cursor: pointer;
|
|
103
|
+
transition: var(--bds-animation_transition);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.close-button svg {
|
|
107
|
+
inline-size: 1.25rem;
|
|
108
|
+
block-size: 1.25rem;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.close-button:focus-visible {
|
|
112
|
+
outline: var(--bds-outline_default);
|
|
113
|
+
outline-offset: var(--bds-outline_offset);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@media (hover: hover) and (pointer: fine) {
|
|
117
|
+
.close-button:hover {
|
|
118
|
+
background-color: var(--bds-color_bg--subtle);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.body {
|
|
123
|
+
flex: 1;
|
|
124
|
+
overflow-block: auto;
|
|
125
|
+
padding: var(--bds-space_l);
|
|
126
|
+
display: flex;
|
|
127
|
+
flex-direction: column;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@media (prefers-reduced-motion: reduce) {
|
|
131
|
+
.panel {
|
|
132
|
+
transition: none;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
`;
|
|
136
|
+
|
|
137
|
+
static properties = {
|
|
138
|
+
open: { type: Boolean, reflect: true },
|
|
139
|
+
side: { type: String, reflect: true },
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
declare open: boolean;
|
|
143
|
+
declare side: DrawerSide;
|
|
144
|
+
|
|
145
|
+
private _triggerEl: Element | null = null;
|
|
146
|
+
|
|
147
|
+
constructor() {
|
|
148
|
+
super();
|
|
149
|
+
this.open = false;
|
|
150
|
+
this.side = 'right';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
connectedCallback() {
|
|
154
|
+
super.connectedCallback();
|
|
155
|
+
this.addEventListener('command', this._onCommand);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
disconnectedCallback() {
|
|
159
|
+
super.disconnectedCallback();
|
|
160
|
+
this.removeEventListener('command', this._onCommand);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
updated(changed: Map<string, unknown>) {
|
|
164
|
+
super.updated(changed);
|
|
165
|
+
if (changed.has('open')) {
|
|
166
|
+
const dialog = this._dialog;
|
|
167
|
+
if (!dialog) return;
|
|
168
|
+
if (this.open && !dialog.open) {
|
|
169
|
+
this._triggerEl = document.activeElement;
|
|
170
|
+
dialog.showModal();
|
|
171
|
+
document.body.style.overflow = 'hidden';
|
|
172
|
+
} else if (!this.open && dialog.open) {
|
|
173
|
+
dialog.close();
|
|
174
|
+
document.body.style.overflow = '';
|
|
175
|
+
(this._triggerEl as HTMLElement | null)?.focus();
|
|
176
|
+
this._triggerEl = null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private get _dialog(): HTMLDialogElement | null {
|
|
182
|
+
return this.shadowRoot?.querySelector('dialog') ?? null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private _onCommand = (e: Event) => {
|
|
186
|
+
const command = (e as CommandEvent).command;
|
|
187
|
+
if (command === 'show-modal') this._openDrawer();
|
|
188
|
+
else if (command === 'close') this._closeDrawer();
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
private _openDrawer() {
|
|
192
|
+
if (this.open) return;
|
|
193
|
+
this._triggerEl = document.activeElement;
|
|
194
|
+
this.open = true;
|
|
195
|
+
this.dispatchEvent(new CustomEvent('bds-open', { bubbles: true, composed: true }));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private _closeDrawer() {
|
|
199
|
+
if (!this.open) return;
|
|
200
|
+
this.open = false;
|
|
201
|
+
this.dispatchEvent(new CustomEvent('bds-close', { bubbles: true, composed: true }));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private _handleBackdropClick(e: MouseEvent) {
|
|
205
|
+
if (e.target === this._dialog) this._closeDrawer();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
private _handleCancel(e: Event) {
|
|
209
|
+
e.preventDefault();
|
|
210
|
+
this._closeDrawer();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private _handleKeydown(e: KeyboardEvent) {
|
|
214
|
+
if (e.key !== 'Tab') return;
|
|
215
|
+
const dialog = this._dialog;
|
|
216
|
+
if (!dialog) return;
|
|
217
|
+
const focusable = Array.from(dialog.querySelectorAll<HTMLElement>(FOCUSABLE));
|
|
218
|
+
if (focusable.length === 0) return;
|
|
219
|
+
const first = focusable[0];
|
|
220
|
+
const last = focusable[focusable.length - 1];
|
|
221
|
+
if (e.shiftKey) {
|
|
222
|
+
if (document.activeElement === first) { e.preventDefault(); last.focus(); }
|
|
223
|
+
} else {
|
|
224
|
+
if (document.activeElement === last) { e.preventDefault(); first.focus(); }
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
render() {
|
|
229
|
+
return html`
|
|
230
|
+
<dialog
|
|
231
|
+
class="drawer"
|
|
232
|
+
aria-modal="true"
|
|
233
|
+
@click=${this._handleBackdropClick}
|
|
234
|
+
@cancel=${this._handleCancel}
|
|
235
|
+
@keydown=${this._handleKeydown}
|
|
236
|
+
>
|
|
237
|
+
<div class="panel">
|
|
238
|
+
<div class="header">
|
|
239
|
+
<span class="title"><slot name="title"></slot></span>
|
|
240
|
+
<button
|
|
241
|
+
type="button"
|
|
242
|
+
class="close-button"
|
|
243
|
+
aria-label="Close drawer"
|
|
244
|
+
@click=${this._closeDrawer}
|
|
245
|
+
>
|
|
246
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
247
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M18 6L6 18M6 6l12 12" />
|
|
248
|
+
</svg>
|
|
249
|
+
</button>
|
|
250
|
+
</div>
|
|
251
|
+
<div class="body">
|
|
252
|
+
<slot></slot>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
</dialog>
|
|
256
|
+
`;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
customElements.define('bds-drawer', BdsDrawer);
|
|
261
|
+
|
|
262
|
+
declare global {
|
|
263
|
+
interface HTMLElementTagNameMap {
|
|
264
|
+
'bds-drawer': BdsDrawer;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fixture, cleanup } from '../test/helpers';
|
|
1
|
+
import { fixture, cleanup, makeCommandEvent } from '../test/helpers';
|
|
2
2
|
import { BdsPopover } from './bds-popover';
|
|
3
3
|
|
|
4
4
|
describe('bds-popover', () => {
|
|
@@ -114,6 +114,54 @@ describe('bds-popover', () => {
|
|
|
114
114
|
cleanup(el);
|
|
115
115
|
});
|
|
116
116
|
|
|
117
|
+
describe('Invoker Commands API (command event on host)', () => {
|
|
118
|
+
it('opens on --open command', async () => {
|
|
119
|
+
const el = await fixture('<bds-popover id="p1"><button slot="trigger">T</button></bds-popover>') as BdsPopover;
|
|
120
|
+
el.dispatchEvent(makeCommandEvent('--open'));
|
|
121
|
+
await el.updateComplete;
|
|
122
|
+
expect(el.open).toBe(true);
|
|
123
|
+
cleanup(el);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('closes on --close command', async () => {
|
|
127
|
+
const el = await fixture('<bds-popover id="p2" open><button slot="trigger">T</button></bds-popover>') as BdsPopover;
|
|
128
|
+
await el.updateComplete;
|
|
129
|
+
el.dispatchEvent(makeCommandEvent('--close'));
|
|
130
|
+
await el.updateComplete;
|
|
131
|
+
expect(el.open).toBe(false);
|
|
132
|
+
cleanup(el);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('toggles on --toggle command', async () => {
|
|
136
|
+
const el = await fixture('<bds-popover id="p3"><button slot="trigger">T</button></bds-popover>') as BdsPopover;
|
|
137
|
+
el.dispatchEvent(makeCommandEvent('--toggle'));
|
|
138
|
+
await el.updateComplete;
|
|
139
|
+
expect(el.open).toBe(true);
|
|
140
|
+
el.dispatchEvent(makeCommandEvent('--toggle'));
|
|
141
|
+
await el.updateComplete;
|
|
142
|
+
expect(el.open).toBe(false);
|
|
143
|
+
cleanup(el);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('fires bds-open event on --open command', async () => {
|
|
147
|
+
const el = await fixture('<bds-popover id="p4"><button slot="trigger">T</button></bds-popover>') as BdsPopover;
|
|
148
|
+
const handler = vi.fn();
|
|
149
|
+
el.addEventListener('bds-open', handler);
|
|
150
|
+
el.dispatchEvent(makeCommandEvent('--open'));
|
|
151
|
+
await el.updateComplete;
|
|
152
|
+
expect(handler).toHaveBeenCalledOnce();
|
|
153
|
+
cleanup(el);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('ignores unknown commands', async () => {
|
|
157
|
+
const el = await fixture('<bds-popover id="p5"><button slot="trigger">T</button></bds-popover>') as BdsPopover;
|
|
158
|
+
el.dispatchEvent(makeCommandEvent('--unknown'));
|
|
159
|
+
await el.updateComplete;
|
|
160
|
+
expect(el.open).toBe(false);
|
|
161
|
+
cleanup(el);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
117
165
|
it('two instances get different anchor names', async () => {
|
|
118
166
|
const a = await fixture('<bds-popover><button slot="trigger">A</button></bds-popover>');
|
|
119
167
|
const b = await fixture('<bds-popover><button slot="trigger">B</button></bds-popover>');
|
|
@@ -157,18 +157,28 @@ export class BdsPopover extends LitElement {
|
|
|
157
157
|
this.style.setProperty('--bds-popover-anchor', this._anchorName);
|
|
158
158
|
document.addEventListener('keydown', this._onKeydown);
|
|
159
159
|
document.addEventListener('mousedown', this._onOutsideClick);
|
|
160
|
+
// Invoker Commands API — host is in light DOM so commandfor can reference its id.
|
|
161
|
+
this.addEventListener('command', this._onCommand);
|
|
160
162
|
}
|
|
161
163
|
|
|
162
164
|
disconnectedCallback() {
|
|
163
165
|
super.disconnectedCallback();
|
|
164
166
|
document.removeEventListener('keydown', this._onKeydown);
|
|
165
167
|
document.removeEventListener('mousedown', this._onOutsideClick);
|
|
168
|
+
this.removeEventListener('command', this._onCommand);
|
|
166
169
|
}
|
|
167
170
|
|
|
168
171
|
private _onKeydown = (e: KeyboardEvent) => {
|
|
169
172
|
if (this.open && e.key === 'Escape') this._close();
|
|
170
173
|
};
|
|
171
174
|
|
|
175
|
+
private _onCommand = (e: Event) => {
|
|
176
|
+
const command = (e as CommandEvent).command;
|
|
177
|
+
if (command === '--toggle') this._toggle();
|
|
178
|
+
else if (command === '--open') this._open();
|
|
179
|
+
else if (command === '--close') this._close();
|
|
180
|
+
};
|
|
181
|
+
|
|
172
182
|
private _onOutsideClick = (e: MouseEvent) => {
|
|
173
183
|
if (this.open && !this.contains(e.target as Node)) this._close();
|
|
174
184
|
};
|
|
@@ -182,11 +192,13 @@ export class BdsPopover extends LitElement {
|
|
|
182
192
|
}
|
|
183
193
|
|
|
184
194
|
private _open() {
|
|
195
|
+
if (this.open) return;
|
|
185
196
|
this.open = true;
|
|
186
197
|
this.dispatchEvent(new CustomEvent('bds-open', { bubbles: true, composed: true }));
|
|
187
198
|
}
|
|
188
199
|
|
|
189
200
|
private _close() {
|
|
201
|
+
if (!this.open) return;
|
|
190
202
|
this.open = false;
|
|
191
203
|
this.dispatchEvent(new CustomEvent('bds-close', { bubbles: true, composed: true }));
|
|
192
204
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { fixture, cleanup } from '../test/helpers';
|
|
2
|
+
import { BdsSkipLink } from './bds-skip-link';
|
|
3
|
+
|
|
4
|
+
describe('bds-skip-link', () => {
|
|
5
|
+
it('is registered as a custom element', () => {
|
|
6
|
+
expect(customElements.get('bds-skip-link')).toBe(BdsSkipLink);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('defaults to href="#main"', async () => {
|
|
10
|
+
const el = await fixture('<bds-skip-link></bds-skip-link>') as BdsSkipLink;
|
|
11
|
+
expect(el.href).toBe('#main');
|
|
12
|
+
cleanup(el);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('renders an <a> element in shadow DOM', async () => {
|
|
16
|
+
const el = await fixture('<bds-skip-link></bds-skip-link>');
|
|
17
|
+
const a = el.shadowRoot!.querySelector('a.skip-link');
|
|
18
|
+
expect(a).not.toBeNull();
|
|
19
|
+
cleanup(el);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('uses the href attribute on the anchor', async () => {
|
|
23
|
+
const el = await fixture('<bds-skip-link href="#content"></bds-skip-link>');
|
|
24
|
+
const a = el.shadowRoot!.querySelector('a');
|
|
25
|
+
expect(a!.getAttribute('href')).toBe('#content');
|
|
26
|
+
cleanup(el);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('renders default slot text "Skip to main content"', async () => {
|
|
30
|
+
const el = await fixture('<bds-skip-link></bds-skip-link>');
|
|
31
|
+
// The slot default content is not visible in textContent from light DOM.
|
|
32
|
+
// Verify the anchor exists and the slot is present.
|
|
33
|
+
expect(el.shadowRoot!.querySelector('slot')).not.toBeNull();
|
|
34
|
+
cleanup(el);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('renders slotted link text', async () => {
|
|
38
|
+
const el = await fixture('<bds-skip-link href="#main">Go to main</bds-skip-link>');
|
|
39
|
+
expect(el.textContent?.trim()).toBe('Go to main');
|
|
40
|
+
cleanup(el);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { LitElement, css, html } from 'lit';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `<bds-skip-link>` — framework-agnostic Skip Navigation Link custom element.
|
|
5
|
+
*
|
|
6
|
+
* Visually hidden until focused. Allows keyboard users to skip repetitive
|
|
7
|
+
* navigation and jump directly to main content.
|
|
8
|
+
*
|
|
9
|
+
* Attributes:
|
|
10
|
+
* href — the target anchor (default: "#main")
|
|
11
|
+
*
|
|
12
|
+
* Slots:
|
|
13
|
+
* (default) — link text (default: "Skip to main content")
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* <bds-skip-link href="#main-content">Skip to main content</bds-skip-link>
|
|
17
|
+
*/
|
|
18
|
+
export class BdsSkipLink extends LitElement {
|
|
19
|
+
static styles = css`
|
|
20
|
+
:host {
|
|
21
|
+
display: block;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.skip-link {
|
|
25
|
+
position: absolute;
|
|
26
|
+
inset-block-start: var(--bds-space_s);
|
|
27
|
+
inset-inline-start: var(--bds-space_s);
|
|
28
|
+
z-index: 9999;
|
|
29
|
+
padding: var(--bds-space_xs) var(--bds-space_m);
|
|
30
|
+
background-color: var(--bds-color_bg);
|
|
31
|
+
color: var(--bds-color_on-bg);
|
|
32
|
+
border-radius: var(--bds-border_radius--s);
|
|
33
|
+
font-weight: var(--bds-font_weight--semibold);
|
|
34
|
+
font-size: var(--bds-font_size--body);
|
|
35
|
+
text-decoration: none;
|
|
36
|
+
box-shadow: var(--bds-shadow_m);
|
|
37
|
+
transform: translateY(calc(-100% - var(--bds-space_s)));
|
|
38
|
+
transition: transform var(--bds-animation_transition-duration) var(--bds-animation_easing);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.skip-link:focus-visible {
|
|
42
|
+
transform: translateY(0);
|
|
43
|
+
outline: var(--bds-outline_default);
|
|
44
|
+
outline-offset: var(--bds-outline_offset);
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
static properties = {
|
|
49
|
+
href: { type: String },
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
declare href: string;
|
|
53
|
+
|
|
54
|
+
constructor() {
|
|
55
|
+
super();
|
|
56
|
+
this.href = '#main';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
render() {
|
|
60
|
+
return html`
|
|
61
|
+
<a class="skip-link" href=${this.href}>
|
|
62
|
+
<slot>Skip to main content</slot>
|
|
63
|
+
</a>
|
|
64
|
+
`;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
customElements.define('bds-skip-link', BdsSkipLink);
|
|
69
|
+
|
|
70
|
+
declare global {
|
|
71
|
+
interface HTMLElementTagNameMap {
|
|
72
|
+
'bds-skip-link': BdsSkipLink;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { fixture, cleanup } from '../test/helpers';
|
|
2
|
+
import { BdsTabPanel } from './bds-tab-panel';
|
|
3
|
+
|
|
4
|
+
describe('bds-tab-panel', () => {
|
|
5
|
+
it('is registered as a custom element', () => {
|
|
6
|
+
expect(customElements.get('bds-tab-panel')).toBe(BdsTabPanel);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('renders slotted content', async () => {
|
|
10
|
+
const el = await fixture('<bds-tab-panel>Panel content</bds-tab-panel>');
|
|
11
|
+
expect(el.textContent?.trim()).toBe('Panel content');
|
|
12
|
+
cleanup(el);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('renders a slot in shadow DOM', async () => {
|
|
16
|
+
const el = await fixture('<bds-tab-panel>Content</bds-tab-panel>');
|
|
17
|
+
expect(el.shadowRoot!.querySelector('slot')).not.toBeNull();
|
|
18
|
+
cleanup(el);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('is visible by default (no hidden attribute)', async () => {
|
|
22
|
+
const el = await fixture('<bds-tab-panel>Content</bds-tab-panel>');
|
|
23
|
+
expect(el.hasAttribute('hidden')).toBe(false);
|
|
24
|
+
cleanup(el);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('applies hidden attribute when set externally', async () => {
|
|
28
|
+
const el = await fixture('<bds-tab-panel hidden>Content</bds-tab-panel>');
|
|
29
|
+
expect(el.hasAttribute('hidden')).toBe(true);
|
|
30
|
+
cleanup(el);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { LitElement, css, html } from 'lit';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `<bds-tab-panel>` — child element of `<bds-tabs>`.
|
|
5
|
+
*
|
|
6
|
+
* Contains the content displayed for the associated tab. The `hidden`
|
|
7
|
+
* state is managed by the parent `<bds-tabs>` element.
|
|
8
|
+
*
|
|
9
|
+
* Slots:
|
|
10
|
+
* (default) — panel content
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* <bds-tab-panel>Panel content here</bds-tab-panel>
|
|
14
|
+
*/
|
|
15
|
+
export class BdsTabPanel extends LitElement {
|
|
16
|
+
static styles = css`
|
|
17
|
+
:host {
|
|
18
|
+
display: block;
|
|
19
|
+
padding-block-start: var(--bds-space_m);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
:host([hidden]) {
|
|
23
|
+
display: none;
|
|
24
|
+
}
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
render() {
|
|
28
|
+
return html`<slot></slot>`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
customElements.define('bds-tab-panel', BdsTabPanel);
|
|
33
|
+
|
|
34
|
+
declare global {
|
|
35
|
+
interface HTMLElementTagNameMap {
|
|
36
|
+
'bds-tab-panel': BdsTabPanel;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { fixture, cleanup } from '../test/helpers';
|
|
2
|
+
import { BdsTab } from './bds-tab';
|
|
3
|
+
|
|
4
|
+
describe('bds-tab', () => {
|
|
5
|
+
it('is registered as a custom element', () => {
|
|
6
|
+
expect(customElements.get('bds-tab')).toBe(BdsTab);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('renders a button with role="tab"', async () => {
|
|
10
|
+
const el = await fixture('<bds-tab>Label</bds-tab>');
|
|
11
|
+
const btn = el.shadowRoot!.querySelector('button');
|
|
12
|
+
expect(btn).not.toBeNull();
|
|
13
|
+
expect(btn!.getAttribute('role')).toBe('tab');
|
|
14
|
+
cleanup(el);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('is not selected by default', async () => {
|
|
18
|
+
const el = await fixture('<bds-tab>Label</bds-tab>') as BdsTab;
|
|
19
|
+
expect(el.selected).toBe(false);
|
|
20
|
+
expect(el.shadowRoot!.querySelector('button')!.getAttribute('aria-selected')).toBe('false');
|
|
21
|
+
cleanup(el);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('has tabindex="-1" when not selected', async () => {
|
|
25
|
+
const el = await fixture('<bds-tab>Label</bds-tab>');
|
|
26
|
+
const btn = el.shadowRoot!.querySelector('button')!;
|
|
27
|
+
expect(btn.getAttribute('tabindex')).toBe('-1');
|
|
28
|
+
cleanup(el);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('has tabindex="0" when selected', async () => {
|
|
32
|
+
const el = await fixture('<bds-tab selected>Label</bds-tab>');
|
|
33
|
+
const btn = el.shadowRoot!.querySelector('button')!;
|
|
34
|
+
expect(btn.getAttribute('tabindex')).toBe('0');
|
|
35
|
+
cleanup(el);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('sets aria-selected="true" when selected', async () => {
|
|
39
|
+
const el = await fixture('<bds-tab selected>Label</bds-tab>');
|
|
40
|
+
const btn = el.shadowRoot!.querySelector('button')!;
|
|
41
|
+
expect(btn.getAttribute('aria-selected')).toBe('true');
|
|
42
|
+
cleanup(el);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('fires bds-tab-select when clicked', async () => {
|
|
46
|
+
const el = await fixture('<bds-tab>Label</bds-tab>');
|
|
47
|
+
const btn = el.shadowRoot!.querySelector('button')!;
|
|
48
|
+
const handler = vi.fn();
|
|
49
|
+
el.addEventListener('bds-tab-select', handler);
|
|
50
|
+
btn.click();
|
|
51
|
+
expect(handler).toHaveBeenCalledOnce();
|
|
52
|
+
cleanup(el);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('does not fire bds-tab-select when disabled', async () => {
|
|
56
|
+
const el = await fixture('<bds-tab disabled>Label</bds-tab>');
|
|
57
|
+
const btn = el.shadowRoot!.querySelector('button')!;
|
|
58
|
+
const handler = vi.fn();
|
|
59
|
+
el.addEventListener('bds-tab-select', handler);
|
|
60
|
+
btn.dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }));
|
|
61
|
+
expect(handler).not.toHaveBeenCalled();
|
|
62
|
+
cleanup(el);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('button is disabled when disabled attribute is set', async () => {
|
|
66
|
+
const el = await fixture('<bds-tab disabled>Label</bds-tab>');
|
|
67
|
+
const btn = el.shadowRoot!.querySelector('button')!;
|
|
68
|
+
expect(btn.hasAttribute('disabled')).toBe(true);
|
|
69
|
+
cleanup(el);
|
|
70
|
+
});
|
|
71
|
+
});
|