@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,82 @@
|
|
|
1
|
+
import { fixture, cleanup } from '../test/helpers';
|
|
2
|
+
import { BdsNotificationBanner } from './bds-notification-banner';
|
|
3
|
+
|
|
4
|
+
describe('bds-notification-banner', () => {
|
|
5
|
+
it('is registered as a custom element', () => {
|
|
6
|
+
expect(customElements.get('bds-notification-banner')).toBe(BdsNotificationBanner);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('defaults to variant="info"', async () => {
|
|
10
|
+
const el = await fixture('<bds-notification-banner>Message</bds-notification-banner>');
|
|
11
|
+
expect(el.getAttribute('variant')).toBe('info');
|
|
12
|
+
cleanup(el);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('renders banner with role="status" for info variant', async () => {
|
|
16
|
+
const el = await fixture('<bds-notification-banner variant="info">Info</bds-notification-banner>');
|
|
17
|
+
const banner = el.shadowRoot!.querySelector('.banner');
|
|
18
|
+
expect(banner!.getAttribute('role')).toBe('status');
|
|
19
|
+
expect(banner!.getAttribute('aria-live')).toBe('polite');
|
|
20
|
+
cleanup(el);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('renders banner with role="alert" for error variant', async () => {
|
|
24
|
+
const el = await fixture('<bds-notification-banner variant="error">Error</bds-notification-banner>');
|
|
25
|
+
const banner = el.shadowRoot!.querySelector('.banner');
|
|
26
|
+
expect(banner!.getAttribute('role')).toBe('alert');
|
|
27
|
+
expect(banner!.getAttribute('aria-live')).toBe('assertive');
|
|
28
|
+
cleanup(el);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('renders banner with role="alert" for warning variant', async () => {
|
|
32
|
+
const el = await fixture('<bds-notification-banner variant="warning">Warning</bds-notification-banner>');
|
|
33
|
+
const banner = el.shadowRoot!.querySelector('.banner');
|
|
34
|
+
expect(banner!.getAttribute('role')).toBe('alert');
|
|
35
|
+
cleanup(el);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('does not render dismiss button by default', async () => {
|
|
39
|
+
const el = await fixture('<bds-notification-banner>Message</bds-notification-banner>');
|
|
40
|
+
expect(el.shadowRoot!.querySelector('button.dismiss')).toBeNull();
|
|
41
|
+
cleanup(el);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('renders dismiss button when dismissible is set', async () => {
|
|
45
|
+
const el = await fixture('<bds-notification-banner dismissible>Message</bds-notification-banner>');
|
|
46
|
+
const btn = el.shadowRoot!.querySelector('button.dismiss');
|
|
47
|
+
expect(btn).not.toBeNull();
|
|
48
|
+
expect(btn!.getAttribute('aria-label')).toBe('Dismiss notification');
|
|
49
|
+
cleanup(el);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('uses custom dismiss-label when set', async () => {
|
|
53
|
+
const el = await fixture('<bds-notification-banner dismissible dismiss-label="Close banner">Message</bds-notification-banner>');
|
|
54
|
+
const btn = el.shadowRoot!.querySelector('button.dismiss');
|
|
55
|
+
expect(btn!.getAttribute('aria-label')).toBe('Close banner');
|
|
56
|
+
cleanup(el);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('fires bds-dismiss when dismiss button is clicked', async () => {
|
|
60
|
+
const el = await fixture('<bds-notification-banner dismissible>Message</bds-notification-banner>');
|
|
61
|
+
const btn = el.shadowRoot!.querySelector('button.dismiss')!;
|
|
62
|
+
const handler = vi.fn();
|
|
63
|
+
el.addEventListener('bds-dismiss', handler);
|
|
64
|
+
btn.dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }));
|
|
65
|
+
expect(handler).toHaveBeenCalledOnce();
|
|
66
|
+
cleanup(el);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('renders slotted content', async () => {
|
|
70
|
+
const el = await fixture('<bds-notification-banner>Hello world</bds-notification-banner>');
|
|
71
|
+
expect(el.textContent?.trim()).toBe('Hello world');
|
|
72
|
+
cleanup(el);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('reflects variant property updates', async () => {
|
|
76
|
+
const el = await fixture('<bds-notification-banner>Message</bds-notification-banner>') as BdsNotificationBanner;
|
|
77
|
+
el.variant = 'success';
|
|
78
|
+
await el.updateComplete;
|
|
79
|
+
expect(el.getAttribute('variant')).toBe('success');
|
|
80
|
+
cleanup(el);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { LitElement, css, html, nothing } from 'lit';
|
|
2
|
+
|
|
3
|
+
export type NotificationBannerVariant = 'info' | 'success' | 'warning' | 'error';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `<bds-notification-banner>` — framework-agnostic Notification Banner custom element.
|
|
7
|
+
*
|
|
8
|
+
* Attributes:
|
|
9
|
+
* variant — "info" (default) | "success" | "warning" | "error"
|
|
10
|
+
* dismissible — boolean; shows a dismiss button when present
|
|
11
|
+
* dismiss-label — aria-label for the dismiss button (default: "Dismiss notification")
|
|
12
|
+
*
|
|
13
|
+
* Slots:
|
|
14
|
+
* (default) — banner body content
|
|
15
|
+
* action — optional action area (e.g. a button)
|
|
16
|
+
*
|
|
17
|
+
* Events:
|
|
18
|
+
* bds-dismiss — fired (bubbles, composed) when the dismiss button is clicked
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* <bds-notification-banner variant="success">
|
|
22
|
+
* Your changes have been saved.
|
|
23
|
+
* <button slot="action" type="button">Undo</button>
|
|
24
|
+
* </bds-notification-banner>
|
|
25
|
+
*/
|
|
26
|
+
export class BdsNotificationBanner extends LitElement {
|
|
27
|
+
static styles = css`
|
|
28
|
+
:host {
|
|
29
|
+
display: block;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.banner {
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
gap: var(--bds-space_m);
|
|
36
|
+
padding: var(--bds-space_s) var(--bds-space_m);
|
|
37
|
+
background-color: var(--banner_bg, var(--bds-color_blue--subtle));
|
|
38
|
+
color: var(--banner_text, var(--bds-color_on-blue--subtle));
|
|
39
|
+
border-block-end: 3px solid var(--banner_border, var(--bds-color_blue));
|
|
40
|
+
font-size: var(--bds-font_size--body);
|
|
41
|
+
line-height: var(--bds-font_line-height--body);
|
|
42
|
+
inline-size: 100%;
|
|
43
|
+
box-sizing: border-box;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
:host([variant='info']) .banner {
|
|
47
|
+
--banner_bg: var(--bds-color_blue--subtle);
|
|
48
|
+
--banner_text: var(--bds-color_on-blue--subtle);
|
|
49
|
+
--banner_border: var(--bds-color_blue);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
:host([variant='success']) .banner {
|
|
53
|
+
--banner_bg: var(--bds-color_success--subtle);
|
|
54
|
+
--banner_text: var(--bds-color_on-success--subtle);
|
|
55
|
+
--banner_border: var(--bds-color_success);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
:host([variant='warning']) .banner {
|
|
59
|
+
--banner_bg: var(--bds-color_orange--subtle);
|
|
60
|
+
--banner_text: var(--bds-color_on-orange--subtle);
|
|
61
|
+
--banner_border: var(--bds-color_warning);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
:host([variant='error']) .banner {
|
|
65
|
+
--banner_bg: rgb(from var(--bds-color_error) r g b / 12%);
|
|
66
|
+
--banner_text: var(--bds-color_on-bg);
|
|
67
|
+
--banner_border: var(--bds-color_error);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.content {
|
|
71
|
+
flex: 1;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.action {
|
|
75
|
+
flex-shrink: 0;
|
|
76
|
+
display: none;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.action.has-content {
|
|
80
|
+
display: block;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.dismiss {
|
|
84
|
+
all: unset;
|
|
85
|
+
display: flex;
|
|
86
|
+
align-items: center;
|
|
87
|
+
justify-content: center;
|
|
88
|
+
flex-shrink: 0;
|
|
89
|
+
cursor: pointer;
|
|
90
|
+
color: currentcolor;
|
|
91
|
+
border-radius: 50%;
|
|
92
|
+
transition: var(--bds-animation_transition);
|
|
93
|
+
--bdc_color: currentcolor;
|
|
94
|
+
outline: var(--bdc-outline_width) solid var(--bdc_color);
|
|
95
|
+
outline-offset: var(--bdc-outline_offset);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.dismiss svg {
|
|
99
|
+
inline-size: 1rem;
|
|
100
|
+
block-size: 1rem;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.dismiss:focus-visible {
|
|
104
|
+
outline: var(--bds-outline_default);
|
|
105
|
+
outline-offset: var(--bds-outline_offset);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@media (hover: hover) and (pointer: fine) {
|
|
109
|
+
.dismiss:hover {
|
|
110
|
+
opacity: 0.7;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
`;
|
|
114
|
+
|
|
115
|
+
static properties = {
|
|
116
|
+
variant: { type: String, reflect: true },
|
|
117
|
+
dismissible: { type: Boolean, reflect: true },
|
|
118
|
+
dismissLabel: { type: String, attribute: 'dismiss-label' },
|
|
119
|
+
_actionFilled: { state: true },
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
declare variant: NotificationBannerVariant;
|
|
123
|
+
declare dismissible: boolean;
|
|
124
|
+
declare dismissLabel: string;
|
|
125
|
+
declare private _actionFilled: boolean;
|
|
126
|
+
|
|
127
|
+
constructor() {
|
|
128
|
+
super();
|
|
129
|
+
this.variant = 'info';
|
|
130
|
+
this.dismissible = false;
|
|
131
|
+
this.dismissLabel = 'Dismiss notification';
|
|
132
|
+
this._actionFilled = false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private _onActionSlotChange(e: Event) {
|
|
136
|
+
const slot = e.target as HTMLSlotElement;
|
|
137
|
+
this._actionFilled = slot.assignedNodes({ flatten: true }).length > 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private get _isUrgent() {
|
|
141
|
+
return this.variant === 'error' || this.variant === 'warning';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private _handleDismiss() {
|
|
145
|
+
this.dispatchEvent(new CustomEvent('bds-dismiss', { bubbles: true, composed: true }));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
render() {
|
|
149
|
+
return html`
|
|
150
|
+
<div
|
|
151
|
+
class="banner"
|
|
152
|
+
role=${this._isUrgent ? 'alert' : 'status'}
|
|
153
|
+
aria-live=${this._isUrgent ? 'assertive' : 'polite'}
|
|
154
|
+
aria-atomic="true"
|
|
155
|
+
>
|
|
156
|
+
<div class="content"><slot></slot></div>
|
|
157
|
+
<div class="action ${this._actionFilled ? 'has-content' : ''}">
|
|
158
|
+
<slot name="action" @slotchange=${this._onActionSlotChange}></slot>
|
|
159
|
+
</div>
|
|
160
|
+
${this.dismissible
|
|
161
|
+
? html`
|
|
162
|
+
<button
|
|
163
|
+
type="button"
|
|
164
|
+
class="dismiss"
|
|
165
|
+
@click=${this._handleDismiss}
|
|
166
|
+
aria-label=${this.dismissLabel}
|
|
167
|
+
>
|
|
168
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
169
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M18 6L6 18M6 6l12 12" />
|
|
170
|
+
</svg>
|
|
171
|
+
</button>
|
|
172
|
+
`
|
|
173
|
+
: nothing}
|
|
174
|
+
</div>
|
|
175
|
+
`;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
customElements.define('bds-notification-banner', BdsNotificationBanner);
|
|
180
|
+
|
|
181
|
+
declare global {
|
|
182
|
+
interface HTMLElementTagNameMap {
|
|
183
|
+
'bds-notification-banner': BdsNotificationBanner;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { fixture, cleanup } from '../test/helpers';
|
|
2
|
+
import { BdsProgressCircle } from './bds-progress-circle';
|
|
3
|
+
|
|
4
|
+
describe('bds-progress-circle', () => {
|
|
5
|
+
it('is registered as a custom element', () => {
|
|
6
|
+
expect(customElements.get('bds-progress-circle')).toBe(BdsProgressCircle);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('defaults to max=100 and size="medium"', async () => {
|
|
10
|
+
const el = await fixture('<bds-progress-circle value="50" label="Loading"></bds-progress-circle>') as BdsProgressCircle;
|
|
11
|
+
expect(el.max).toBe(100);
|
|
12
|
+
expect(el.getAttribute('size')).toBe('medium');
|
|
13
|
+
cleanup(el);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('renders a progressbar wrapper in shadow DOM', async () => {
|
|
17
|
+
const el = await fixture('<bds-progress-circle value="50" label="Loading"></bds-progress-circle>');
|
|
18
|
+
const wrapper = el.shadowRoot!.querySelector('[role="progressbar"]');
|
|
19
|
+
expect(wrapper).not.toBeNull();
|
|
20
|
+
cleanup(el);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('sets aria-label and aria-valuenow', async () => {
|
|
24
|
+
const el = await fixture('<bds-progress-circle value="75" label="Upload"></bds-progress-circle>');
|
|
25
|
+
const wrapper = el.shadowRoot!.querySelector('[role="progressbar"]');
|
|
26
|
+
expect(wrapper!.getAttribute('aria-label')).toBe('Upload');
|
|
27
|
+
expect(wrapper!.getAttribute('aria-valuenow')).toBe('75');
|
|
28
|
+
cleanup(el);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('renders two SVG circles (track and fill)', async () => {
|
|
32
|
+
const el = await fixture('<bds-progress-circle value="50" label="Loading"></bds-progress-circle>');
|
|
33
|
+
const circles = el.shadowRoot!.querySelectorAll('circle');
|
|
34
|
+
expect(circles.length).toBe(2);
|
|
35
|
+
cleanup(el);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('does not render the value text by default', async () => {
|
|
39
|
+
const el = await fixture('<bds-progress-circle value="50" label="Loading"></bds-progress-circle>');
|
|
40
|
+
expect(el.shadowRoot!.querySelector('.value')).toBeNull();
|
|
41
|
+
cleanup(el);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('renders percentage text when show-value is set', async () => {
|
|
45
|
+
const el = await fixture('<bds-progress-circle value="75" label="Loading" show-value></bds-progress-circle>');
|
|
46
|
+
const value = el.shadowRoot!.querySelector('.value');
|
|
47
|
+
expect(value).not.toBeNull();
|
|
48
|
+
expect(value!.textContent).toBe('75%');
|
|
49
|
+
cleanup(el);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('SVG has aria-hidden="true"', async () => {
|
|
53
|
+
const el = await fixture('<bds-progress-circle value="50" label="Loading"></bds-progress-circle>');
|
|
54
|
+
const svg = el.shadowRoot!.querySelector('svg');
|
|
55
|
+
expect(svg!.getAttribute('aria-hidden')).toBe('true');
|
|
56
|
+
cleanup(el);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { LitElement, css, html, nothing } from 'lit';
|
|
2
|
+
|
|
3
|
+
const SIZE_PX = { small: 40, medium: 64, large: 96 } as const;
|
|
4
|
+
const STROKE_WIDTH = 4;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `<bds-progress-circle>` — framework-agnostic circular Progress custom element.
|
|
8
|
+
*
|
|
9
|
+
* Attributes:
|
|
10
|
+
* value — current progress value (required)
|
|
11
|
+
* max — maximum value (default: 100)
|
|
12
|
+
* label — accessible label for the progress bar
|
|
13
|
+
* show-value — boolean; when set, displays the percentage in the centre
|
|
14
|
+
* size — "small" | "medium" (default) | "large"
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* <bds-progress-circle value="75" label="Loading" show-value></bds-progress-circle>
|
|
18
|
+
*/
|
|
19
|
+
export class BdsProgressCircle extends LitElement {
|
|
20
|
+
static styles = css`
|
|
21
|
+
:host {
|
|
22
|
+
display: inline-flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
justify-content: center;
|
|
25
|
+
position: relative;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.svg {
|
|
29
|
+
display: block;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.track {
|
|
33
|
+
stroke: var(--bds-color_bg--subtle);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.fill {
|
|
37
|
+
stroke: var(--progressCircle_color-active, var(--bdc-color_active));
|
|
38
|
+
transition: stroke-dashoffset var(--bds-animation_transition-duration) var(--bds-animation_easing);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.value {
|
|
42
|
+
position: absolute;
|
|
43
|
+
font-size: var(--bds-font_size--body--s);
|
|
44
|
+
font-weight: var(--bds-font_weight--semibold);
|
|
45
|
+
font-variant-numeric: tabular-nums;
|
|
46
|
+
color: var(--bds-color_on-bg);
|
|
47
|
+
line-height: 1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
:host([size='small']) .value { font-size: 0.5rem; }
|
|
51
|
+
:host([size='large']) .value { font-size: var(--bds-font_size--body); }
|
|
52
|
+
|
|
53
|
+
@media (prefers-reduced-motion: reduce) {
|
|
54
|
+
.fill {
|
|
55
|
+
transition: none;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
static properties = {
|
|
61
|
+
value: { type: Number },
|
|
62
|
+
max: { type: Number },
|
|
63
|
+
label: { type: String },
|
|
64
|
+
showValue: { type: Boolean, attribute: 'show-value', reflect: true },
|
|
65
|
+
size: { type: String, reflect: true },
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
declare value: number;
|
|
69
|
+
declare max: number;
|
|
70
|
+
declare label: string;
|
|
71
|
+
declare showValue: boolean;
|
|
72
|
+
declare size: 'small' | 'medium' | 'large';
|
|
73
|
+
|
|
74
|
+
constructor() {
|
|
75
|
+
super();
|
|
76
|
+
this.value = 0;
|
|
77
|
+
this.max = 100;
|
|
78
|
+
this.label = '';
|
|
79
|
+
this.showValue = false;
|
|
80
|
+
this.size = 'medium';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
render() {
|
|
84
|
+
const percentage = Math.min(100, Math.max(0, (this.value / this.max) * 100));
|
|
85
|
+
const px = SIZE_PX[this.size] ?? SIZE_PX.medium;
|
|
86
|
+
const radius = (px - STROKE_WIDTH * 2) / 2;
|
|
87
|
+
const circumference = 2 * Math.PI * radius;
|
|
88
|
+
const offset = circumference - (percentage / 100) * circumference;
|
|
89
|
+
const cx = px / 2;
|
|
90
|
+
|
|
91
|
+
return html`
|
|
92
|
+
<div
|
|
93
|
+
role="progressbar"
|
|
94
|
+
aria-label=${this.label}
|
|
95
|
+
aria-valuenow=${this.value}
|
|
96
|
+
aria-valuemin="0"
|
|
97
|
+
aria-valuemax=${this.max}
|
|
98
|
+
style="position: relative; display: inline-flex; align-items: center; justify-content: center;"
|
|
99
|
+
>
|
|
100
|
+
<svg
|
|
101
|
+
class="svg"
|
|
102
|
+
width=${px}
|
|
103
|
+
height=${px}
|
|
104
|
+
viewBox="0 0 ${px} ${px}"
|
|
105
|
+
aria-hidden="true"
|
|
106
|
+
>
|
|
107
|
+
<circle
|
|
108
|
+
class="track"
|
|
109
|
+
cx=${cx}
|
|
110
|
+
cy=${cx}
|
|
111
|
+
r=${radius}
|
|
112
|
+
stroke-width=${STROKE_WIDTH}
|
|
113
|
+
fill="none"
|
|
114
|
+
/>
|
|
115
|
+
<circle
|
|
116
|
+
class="fill"
|
|
117
|
+
cx=${cx}
|
|
118
|
+
cy=${cx}
|
|
119
|
+
r=${radius}
|
|
120
|
+
stroke-width=${STROKE_WIDTH}
|
|
121
|
+
fill="none"
|
|
122
|
+
stroke-dasharray=${circumference}
|
|
123
|
+
stroke-dashoffset=${offset}
|
|
124
|
+
stroke-linecap="round"
|
|
125
|
+
transform="rotate(-90 ${cx} ${cx})"
|
|
126
|
+
/>
|
|
127
|
+
</svg>
|
|
128
|
+
${this.showValue
|
|
129
|
+
? html`<span class="value" aria-hidden="true">${Math.round(percentage)}%</span>`
|
|
130
|
+
: nothing}
|
|
131
|
+
</div>
|
|
132
|
+
`;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
customElements.define('bds-progress-circle', BdsProgressCircle);
|
|
137
|
+
|
|
138
|
+
declare global {
|
|
139
|
+
interface HTMLElementTagNameMap {
|
|
140
|
+
'bds-progress-circle': BdsProgressCircle;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { fixture, cleanup } from '../test/helpers';
|
|
2
|
+
import { BdsProgress } from './bds-progress';
|
|
3
|
+
|
|
4
|
+
describe('bds-progress', () => {
|
|
5
|
+
it('is registered as a custom element', () => {
|
|
6
|
+
expect(customElements.get('bds-progress')).toBe(BdsProgress);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('defaults to max=100 and size="medium"', async () => {
|
|
10
|
+
const el = await fixture('<bds-progress value="50" label="Loading"></bds-progress>') as BdsProgress;
|
|
11
|
+
expect(el.max).toBe(100);
|
|
12
|
+
expect(el.getAttribute('size')).toBe('medium');
|
|
13
|
+
cleanup(el);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('renders a progressbar track in shadow DOM', async () => {
|
|
17
|
+
const el = await fixture('<bds-progress value="50" label="Loading"></bds-progress>');
|
|
18
|
+
const track = el.shadowRoot!.querySelector('.track');
|
|
19
|
+
expect(track).not.toBeNull();
|
|
20
|
+
expect(track!.getAttribute('role')).toBe('progressbar');
|
|
21
|
+
cleanup(el);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('sets aria-label, aria-valuenow, aria-valuemax on the track', async () => {
|
|
25
|
+
const el = await fixture('<bds-progress value="60" max="200" label="Upload"></bds-progress>');
|
|
26
|
+
const track = el.shadowRoot!.querySelector('.track');
|
|
27
|
+
expect(track!.getAttribute('aria-label')).toBe('Upload');
|
|
28
|
+
expect(track!.getAttribute('aria-valuenow')).toBe('60');
|
|
29
|
+
expect(track!.getAttribute('aria-valuemax')).toBe('200');
|
|
30
|
+
cleanup(el);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('does not render the label row by default', async () => {
|
|
34
|
+
const el = await fixture('<bds-progress value="50" label="Loading"></bds-progress>');
|
|
35
|
+
expect(el.shadowRoot!.querySelector('.label-row')).toBeNull();
|
|
36
|
+
cleanup(el);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('renders label row when show-label is set', async () => {
|
|
40
|
+
const el = await fixture('<bds-progress value="50" label="Upload" show-label></bds-progress>');
|
|
41
|
+
const labelRow = el.shadowRoot!.querySelector('.label-row');
|
|
42
|
+
expect(labelRow).not.toBeNull();
|
|
43
|
+
expect(labelRow!.querySelector('.label')!.textContent).toBe('Upload');
|
|
44
|
+
cleanup(el);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('clamps percentage to 100 for value > max', async () => {
|
|
48
|
+
const el = await fixture('<bds-progress value="150" label="Done" show-label></bds-progress>');
|
|
49
|
+
const valueSpan = el.shadowRoot!.querySelector('.value');
|
|
50
|
+
expect(valueSpan!.textContent).toBe('100%');
|
|
51
|
+
cleanup(el);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('clamps percentage to 0 for negative value', async () => {
|
|
55
|
+
const el = await fixture('<bds-progress value="-10" label="Done" show-label></bds-progress>');
|
|
56
|
+
const valueSpan = el.shadowRoot!.querySelector('.value');
|
|
57
|
+
expect(valueSpan!.textContent).toBe('0%');
|
|
58
|
+
cleanup(el);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('reflects size attribute', async () => {
|
|
62
|
+
const el = await fixture('<bds-progress value="50" label="Loading" size="large"></bds-progress>') as BdsProgress;
|
|
63
|
+
expect(el.getAttribute('size')).toBe('large');
|
|
64
|
+
cleanup(el);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { LitElement, css, html, nothing } from 'lit';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `<bds-progress>` — framework-agnostic Progress bar custom element.
|
|
5
|
+
*
|
|
6
|
+
* Attributes:
|
|
7
|
+
* value — current progress value (required)
|
|
8
|
+
* max — maximum value (default: 100)
|
|
9
|
+
* label — accessible label for the progress bar
|
|
10
|
+
* show-label — boolean; when set, displays the label and percentage above the bar
|
|
11
|
+
* size — "small" | "medium" (default) | "large"
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* <bds-progress value="60" label="Upload progress" show-label></bds-progress>
|
|
15
|
+
*/
|
|
16
|
+
export class BdsProgress extends LitElement {
|
|
17
|
+
static styles = css`
|
|
18
|
+
:host {
|
|
19
|
+
display: block;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.container {
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
gap: var(--bds-space_xxs);
|
|
26
|
+
inline-size: 100%;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.label-row {
|
|
30
|
+
display: flex;
|
|
31
|
+
justify-content: space-between;
|
|
32
|
+
align-items: baseline;
|
|
33
|
+
font-size: var(--bds-font_size--body--s);
|
|
34
|
+
color: var(--bds-color_on-bg);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.value {
|
|
38
|
+
font-variant-numeric: tabular-nums;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.track {
|
|
42
|
+
inline-size: 100%;
|
|
43
|
+
block-size: var(--progress_height, var(--bds-space_xs));
|
|
44
|
+
background-color: var(--bds-color_bg--subtle);
|
|
45
|
+
border-radius: 999px;
|
|
46
|
+
overflow: hidden;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
:host([size='small']) .track { --progress_height: var(--bds-space_xxs); }
|
|
50
|
+
:host([size='medium']) .track { --progress_height: var(--bds-space_xs); }
|
|
51
|
+
:host([size='large']) .track { --progress_height: var(--bds-space_s); }
|
|
52
|
+
|
|
53
|
+
.fill {
|
|
54
|
+
block-size: 100%;
|
|
55
|
+
background-color: var(--progress_color-active, var(--bdc-color_active));
|
|
56
|
+
border-radius: 999px;
|
|
57
|
+
transition: inline-size var(--bds-animation_transition-duration) var(--bds-animation_easing);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@media (prefers-reduced-motion: reduce) {
|
|
61
|
+
.fill {
|
|
62
|
+
transition: none;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
static properties = {
|
|
68
|
+
value: { type: Number },
|
|
69
|
+
max: { type: Number },
|
|
70
|
+
label: { type: String },
|
|
71
|
+
showLabel: { type: Boolean, attribute: 'show-label', reflect: true },
|
|
72
|
+
size: { type: String, reflect: true },
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
declare value: number;
|
|
76
|
+
declare max: number;
|
|
77
|
+
declare label: string;
|
|
78
|
+
declare showLabel: boolean;
|
|
79
|
+
declare size: 'small' | 'medium' | 'large';
|
|
80
|
+
|
|
81
|
+
constructor() {
|
|
82
|
+
super();
|
|
83
|
+
this.value = 0;
|
|
84
|
+
this.max = 100;
|
|
85
|
+
this.label = '';
|
|
86
|
+
this.showLabel = false;
|
|
87
|
+
this.size = 'medium';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private get _percentage(): number {
|
|
91
|
+
return Math.min(100, Math.max(0, (this.value / this.max) * 100));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
render() {
|
|
95
|
+
const pct = this._percentage;
|
|
96
|
+
return html`
|
|
97
|
+
<div class="container">
|
|
98
|
+
${this.showLabel
|
|
99
|
+
? html`
|
|
100
|
+
<div class="label-row">
|
|
101
|
+
<span class="label">${this.label}</span>
|
|
102
|
+
<span class="value">${Math.round(pct)}%</span>
|
|
103
|
+
</div>
|
|
104
|
+
`
|
|
105
|
+
: nothing}
|
|
106
|
+
<div
|
|
107
|
+
class="track"
|
|
108
|
+
role="progressbar"
|
|
109
|
+
aria-label=${this.label}
|
|
110
|
+
aria-valuenow=${this.value}
|
|
111
|
+
aria-valuemin="0"
|
|
112
|
+
aria-valuemax=${this.max}
|
|
113
|
+
>
|
|
114
|
+
<div class="fill" style="inline-size: ${pct}%"></div>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
customElements.define('bds-progress', BdsProgress);
|
|
122
|
+
|
|
123
|
+
declare global {
|
|
124
|
+
interface HTMLElementTagNameMap {
|
|
125
|
+
'bds-progress': BdsProgress;
|
|
126
|
+
}
|
|
127
|
+
}
|