@boostdev/design-system-components 1.1.0 → 1.1.1
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.d.ts +1 -0
- package/dist/web-components.js +10 -0
- package/package.json +1 -1
- 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/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-popover.spec.ts +49 -1
- package/src/web-components/interaction/bds-popover.ts +12 -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
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Meta } from '@storybook/blocks';
|
|
2
|
+
|
|
3
|
+
<Meta title="React Native/Overview" />
|
|
4
|
+
|
|
5
|
+
# React Native
|
|
6
|
+
|
|
7
|
+
> **Status: alpha** — components and APIs may change before stable release.
|
|
8
|
+
|
|
9
|
+
`@boostdev/components` ships native implementations for 19 components. These use React Native primitives (`View`, `Text`, `Pressable`, `StyleSheet`) rather than HTML elements, and consume a dedicated token layer instead of CSS custom properties.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @boostdev/components
|
|
15
|
+
# or
|
|
16
|
+
pnpm add @boostdev/components
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Metro automatically resolves the native entry point via the `"react-native"` export condition in `package.json` — no extra configuration needed.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
// Import is identical to the web version:
|
|
23
|
+
import { Button, Alert, Badge, Avatar, Typography } from '@boostdev/components';
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Setup — ThemeProvider
|
|
27
|
+
|
|
28
|
+
Wrap your app (or the relevant subtree) in `ThemeProvider` to give native components access to the design token context:
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { ThemeProvider } from '@boostdev/components/native/ThemeContext';
|
|
32
|
+
|
|
33
|
+
export default function App() {
|
|
34
|
+
return (
|
|
35
|
+
<ThemeProvider>
|
|
36
|
+
{/* your app */}
|
|
37
|
+
</ThemeProvider>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`ThemeProvider` reads the current colour scheme via `useColorScheme()` and applies the corresponding dark/light token set automatically.
|
|
43
|
+
|
|
44
|
+
## Design Tokens
|
|
45
|
+
|
|
46
|
+
Native components consume tokens from `src/native/tokens.ts` — a JS object mirroring the CSS custom properties from `@boostdev/design-system-foundation`. Token categories:
|
|
47
|
+
|
|
48
|
+
| Category | Example |
|
|
49
|
+
|----------|---------|
|
|
50
|
+
| `colors` | `tokens.colors.bg`, `tokens.colors.interactive` |
|
|
51
|
+
| `spacing` | `tokens.spacing.xs`, `tokens.spacing.m` |
|
|
52
|
+
| `font` | `tokens.font.sizeBody`, `tokens.font.weightBold` |
|
|
53
|
+
| `border` | `tokens.border.radiusS`, `tokens.border.radiusM` |
|
|
54
|
+
| `zIndex` | `tokens.zIndex.modal` |
|
|
55
|
+
| `animation` | `tokens.animation.duration` |
|
|
56
|
+
|
|
57
|
+
Tokens are accessed inside components via `useTheme()`:
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { useTheme } from '@boostdev/components/native/ThemeContext';
|
|
61
|
+
|
|
62
|
+
function MyComponent() {
|
|
63
|
+
const { tokens } = useTheme();
|
|
64
|
+
return <View style={{ backgroundColor: tokens.colors.bg }} />;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Available Components
|
|
69
|
+
|
|
70
|
+
### UI
|
|
71
|
+
|
|
72
|
+
| Component | Description |
|
|
73
|
+
|-----------|-------------|
|
|
74
|
+
| `Typography` | Text with semantic variants: `h1`, `h2`, `h3`, `body`, `body_s` |
|
|
75
|
+
| `Badge` | Short status label with 5 semantic variants |
|
|
76
|
+
| `Avatar` | Initials-based avatar with 3 sizes |
|
|
77
|
+
| `Alert` | Inline status message with optional title and dismiss |
|
|
78
|
+
| `NotificationBanner` | Full-width banner notification |
|
|
79
|
+
| `Progress` | Linear progress bar |
|
|
80
|
+
| `Skeleton` | Loading placeholder |
|
|
81
|
+
| `Loading` | Animated loading spinner |
|
|
82
|
+
| `Separator` | Visual divider |
|
|
83
|
+
|
|
84
|
+
### Layout
|
|
85
|
+
|
|
86
|
+
| Component | Description |
|
|
87
|
+
|-----------|-------------|
|
|
88
|
+
| `Card` | Elevated content container |
|
|
89
|
+
| `SectionHeader` | Section heading with optional action |
|
|
90
|
+
| `ButtonGroup` | Horizontal or vertical button row |
|
|
91
|
+
| `IconWrapper` | Consistent icon sizing wrapper |
|
|
92
|
+
|
|
93
|
+
### Interaction
|
|
94
|
+
|
|
95
|
+
| Component | Description |
|
|
96
|
+
|-----------|-------------|
|
|
97
|
+
| `Button` | Primary action element with `onPress` |
|
|
98
|
+
|
|
99
|
+
### Form
|
|
100
|
+
|
|
101
|
+
| Component | Description |
|
|
102
|
+
|-----------|-------------|
|
|
103
|
+
| `Checkbox` | Controlled checkbox with label, hint, and error |
|
|
104
|
+
| `Radio` | Controlled radio button |
|
|
105
|
+
| `Switch` | Toggle switch |
|
|
106
|
+
| `Label` | Form field label atom |
|
|
107
|
+
| `Message` | Form field hint/error atom |
|
|
108
|
+
|
|
109
|
+
## Key differences from the web components
|
|
110
|
+
|
|
111
|
+
| Web | React Native |
|
|
112
|
+
|-----|-------------|
|
|
113
|
+
| `onClick` | `onPress` |
|
|
114
|
+
| `aria-label` | `accessibilityLabel` |
|
|
115
|
+
| CSS `className` | `StyleSheet` / `style` prop |
|
|
116
|
+
| `children` as string/HTML | `children` must be `<Text>` nodes (RN restriction) |
|
|
117
|
+
| Design tokens via CSS custom properties | Design tokens via `useTheme()` JS object |
|
|
118
|
+
|
|
119
|
+
## Dark mode
|
|
120
|
+
|
|
121
|
+
`ThemeProvider` subscribes to `useColorScheme()` automatically. No extra work is needed — components adapt their colours when the device switches between light and dark mode.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './BdsButton.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# <bds-button>
|
|
7
|
+
|
|
8
|
+
Framework-agnostic button custom element. Renders as `<button>` or `<a>` depending on whether `href` is set. Matches the React `Button` component in variants, sizes, and theming.
|
|
9
|
+
|
|
10
|
+
> **Status: experimental** — API may change before stable release.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
// Register all web components at once
|
|
16
|
+
import '@boostdev/components/web-components';
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or in HTML without a bundler:
|
|
20
|
+
|
|
21
|
+
```html
|
|
22
|
+
<script type="module">
|
|
23
|
+
import '@boostdev/components/web-components';
|
|
24
|
+
</script>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
|
|
29
|
+
### All variants & sizes
|
|
30
|
+
<Canvas of={Stories.AllVariants} />
|
|
31
|
+
|
|
32
|
+
### Default
|
|
33
|
+
<Canvas of={Stories.Default} />
|
|
34
|
+
|
|
35
|
+
### Ghost
|
|
36
|
+
<Canvas of={Stories.Ghost} />
|
|
37
|
+
|
|
38
|
+
### Sizes
|
|
39
|
+
<Canvas of={Stories.Small} />
|
|
40
|
+
<Canvas of={Stories.Medium} />
|
|
41
|
+
<Canvas of={Stories.Large} />
|
|
42
|
+
|
|
43
|
+
### Pulse (CTA emphasis)
|
|
44
|
+
<Canvas of={Stories.WithPulse} />
|
|
45
|
+
|
|
46
|
+
### Disabled
|
|
47
|
+
<Canvas of={Stories.Disabled} />
|
|
48
|
+
|
|
49
|
+
## Props
|
|
50
|
+
|
|
51
|
+
<ArgTypes of={Stories} />
|
|
52
|
+
|
|
53
|
+
## Attributes
|
|
54
|
+
|
|
55
|
+
| Attribute | Type | Default | Description |
|
|
56
|
+
|-----------|------|---------|-------------|
|
|
57
|
+
| `variant` | `"default" \| "ghost"` | `"default"` | Visual style — filled or transparent outlined |
|
|
58
|
+
| `size` | `"small" \| "medium" \| "large"` | `"medium"` | Height and padding scale |
|
|
59
|
+
| `disabled` | boolean | `false` | Prevents interaction, sets `aria-disabled` |
|
|
60
|
+
| `href` | string | — | Renders as `<a>` when set |
|
|
61
|
+
| `target` | string | — | Forwarded to `<a>` when `href` is set |
|
|
62
|
+
| `rel` | string | — | Forwarded to `<a>` when `href` is set |
|
|
63
|
+
| `type` | `"button" \| "submit" \| "reset"` | `"button"` | Native button type |
|
|
64
|
+
| `has-pulse` | boolean | `false` | Adds animated ring for CTA emphasis |
|
|
65
|
+
| `aria-label` | string | — | Accessible label (required for icon-only buttons) |
|
|
66
|
+
|
|
67
|
+
## Slots
|
|
68
|
+
|
|
69
|
+
| Slot | Description |
|
|
70
|
+
|------|-------------|
|
|
71
|
+
| `(default)` | Button label text |
|
|
72
|
+
| `icon-start` | Icon placed before the label |
|
|
73
|
+
| `icon-end` | Icon placed after the label |
|
|
74
|
+
|
|
75
|
+
## CSS Custom Properties
|
|
76
|
+
|
|
77
|
+
| Property | Description |
|
|
78
|
+
|----------|-------------|
|
|
79
|
+
| `--button_color` | Identity colour — fills the default variant, outlines the ghost variant |
|
|
80
|
+
| `--button_on-color` | Text/icon colour on the button surface. Pair with `--button_color` |
|
|
81
|
+
|
|
82
|
+
## Invoker Commands API
|
|
83
|
+
|
|
84
|
+
`<bds-button>` can control a `<bds-popover>` declaratively using `commandfor` and `command`:
|
|
85
|
+
|
|
86
|
+
```html
|
|
87
|
+
<bds-button commandfor="my-popover" command="--toggle">Open popover</bds-button>
|
|
88
|
+
<bds-popover id="my-popover">
|
|
89
|
+
<p>Popover content</p>
|
|
90
|
+
</bds-popover>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Usage in plain HTML
|
|
94
|
+
|
|
95
|
+
```html
|
|
96
|
+
<bds-button variant="default" size="medium">Save</bds-button>
|
|
97
|
+
<bds-button variant="ghost">Cancel</bds-button>
|
|
98
|
+
<bds-button href="/dashboard">Go to dashboard</bds-button>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Accessibility
|
|
102
|
+
|
|
103
|
+
- Always provide a label via slot content or `aria-label` (required for icon-only buttons)
|
|
104
|
+
- `disabled` sets `aria-disabled="true"` and prevents click events
|
|
105
|
+
- When rendered as `<a>`, `href` must point to a valid destination
|
|
106
|
+
- `has-pulse` animation respects `prefers-reduced-motion`
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './BdsPopover.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# <bds-popover>
|
|
7
|
+
|
|
8
|
+
Framework-agnostic popover custom element. A non-modal floating panel anchored to a trigger element. Uses CSS Anchor Positioning for automatic repositioning when near viewport edges.
|
|
9
|
+
|
|
10
|
+
> **Status: experimental** — API may change before stable release.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import '@boostdev/components/web-components';
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Examples
|
|
19
|
+
|
|
20
|
+
### Default (bottom)
|
|
21
|
+
<Canvas of={Stories.Default} />
|
|
22
|
+
|
|
23
|
+
### Top
|
|
24
|
+
<Canvas of={Stories.Top} />
|
|
25
|
+
|
|
26
|
+
### Right
|
|
27
|
+
<Canvas of={Stories.Right} />
|
|
28
|
+
|
|
29
|
+
### Left
|
|
30
|
+
<Canvas of={Stories.Left} />
|
|
31
|
+
|
|
32
|
+
### With rich content
|
|
33
|
+
<Canvas of={Stories.WithRichContent} />
|
|
34
|
+
|
|
35
|
+
### All placements
|
|
36
|
+
<Canvas of={Stories.AllPlacements} />
|
|
37
|
+
|
|
38
|
+
## Props
|
|
39
|
+
|
|
40
|
+
<ArgTypes of={Stories} />
|
|
41
|
+
|
|
42
|
+
## Attributes
|
|
43
|
+
|
|
44
|
+
| Attribute | Type | Default | Description |
|
|
45
|
+
|-----------|------|---------|-------------|
|
|
46
|
+
| `placement` | `"bottom" \| "top" \| "left" \| "right"` | `"bottom"` | Preferred panel position relative to the trigger |
|
|
47
|
+
| `open` | boolean | `false` | Controls panel visibility; reflects on the element |
|
|
48
|
+
|
|
49
|
+
## Slots
|
|
50
|
+
|
|
51
|
+
| Slot | Description |
|
|
52
|
+
|------|-------------|
|
|
53
|
+
| `trigger` | Element that opens and closes the panel on click |
|
|
54
|
+
| `(default)` | Panel content |
|
|
55
|
+
|
|
56
|
+
## Events
|
|
57
|
+
|
|
58
|
+
| Event | Detail | Bubbles | Composed | Description |
|
|
59
|
+
|-------|--------|---------|----------|-------------|
|
|
60
|
+
| `bds-open` | — | Yes | Yes | Fired when the panel opens |
|
|
61
|
+
| `bds-close` | — | Yes | Yes | Fired when the panel closes |
|
|
62
|
+
|
|
63
|
+
## Behaviour
|
|
64
|
+
|
|
65
|
+
- Clicking the trigger toggles the panel open/closed
|
|
66
|
+
- Pressing `Escape` closes an open panel
|
|
67
|
+
- Clicking outside the element closes the panel
|
|
68
|
+
- CSS Anchor Positioning automatically tries fallback placements (`@position-try`) when the preferred placement would overflow the viewport
|
|
69
|
+
- Each instance gets a unique CSS anchor name — multiple popovers on the same page don't interfere
|
|
70
|
+
|
|
71
|
+
## Invoker Commands API
|
|
72
|
+
|
|
73
|
+
Control the popover declaratively from any `<button>` using `commandfor` and `command` — no JavaScript required:
|
|
74
|
+
|
|
75
|
+
```html
|
|
76
|
+
<button commandfor="my-popover" command="--toggle">Open</button>
|
|
77
|
+
<button commandfor="my-popover" command="--open">Open only</button>
|
|
78
|
+
<button commandfor="my-popover" command="--close">Close only</button>
|
|
79
|
+
|
|
80
|
+
<bds-popover id="my-popover">
|
|
81
|
+
<p>Panel content</p>
|
|
82
|
+
</bds-popover>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Usage in plain HTML
|
|
86
|
+
|
|
87
|
+
```html
|
|
88
|
+
<bds-popover placement="bottom">
|
|
89
|
+
<button slot="trigger">Open menu</button>
|
|
90
|
+
<nav>
|
|
91
|
+
<a href="/profile">Profile</a>
|
|
92
|
+
<a href="/settings">Settings</a>
|
|
93
|
+
<a href="/logout">Log out</a>
|
|
94
|
+
</nav>
|
|
95
|
+
</bds-popover>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Listen for open/close events:
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
const popover = document.querySelector('bds-popover');
|
|
102
|
+
popover.addEventListener('bds-open', () => console.log('opened'));
|
|
103
|
+
popover.addEventListener('bds-close', () => console.log('closed'));
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## CSS Custom Properties
|
|
107
|
+
|
|
108
|
+
| Property | Default | Description |
|
|
109
|
+
|----------|---------|-------------|
|
|
110
|
+
| `--popover_color` | `--bds-color_bg` | Panel background |
|
|
111
|
+
| `--popover_on-color` | `--bds-color_on-bg` | Panel text colour |
|
|
112
|
+
| `--popover_shadow` | `--bds-shadow_m` | Panel drop shadow |
|
|
113
|
+
| `--popover_z-index` | `100` | Stacking order |
|
|
114
|
+
|
|
115
|
+
## Accessibility
|
|
116
|
+
|
|
117
|
+
- The panel has `role="region"` and `aria-label="Popover"`
|
|
118
|
+
- The trigger element retains its own role (e.g. `<button>`) — no ARIA is added by the component
|
|
119
|
+
- For menus, use a `<nav>` or `role="menu"` inside the default slot as appropriate
|
|
120
|
+
- The component does not trap focus — use `<bds-dialog>` for modal, focus-trapped overlays
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './BdsToastProvider.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# <bds-toast-provider>
|
|
7
|
+
|
|
8
|
+
Framework-agnostic toast notification provider. Wrap your page content in `<bds-toast-provider>` to enable toast notifications that render in the browser top layer (above fixed/sticky elements). Auto-dismisses after 5 seconds.
|
|
9
|
+
|
|
10
|
+
> **Status: experimental** — API may change before stable release.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import '@boostdev/components/web-components';
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Examples
|
|
19
|
+
|
|
20
|
+
### Static API
|
|
21
|
+
<Canvas of={Stories.Default} />
|
|
22
|
+
|
|
23
|
+
### Event API (no import required)
|
|
24
|
+
<Canvas of={Stories.EventAPI} />
|
|
25
|
+
|
|
26
|
+
## Slots
|
|
27
|
+
|
|
28
|
+
| Slot | Description |
|
|
29
|
+
|------|-------------|
|
|
30
|
+
| `(default)` | Page content. `<bds-toast-provider>` renders as `display: contents` so it is invisible in the layout |
|
|
31
|
+
|
|
32
|
+
## Three ways to show a toast
|
|
33
|
+
|
|
34
|
+
### 1. Static method (import required)
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
import { BdsToastProvider } from '@boostdev/components/web-components';
|
|
38
|
+
|
|
39
|
+
BdsToastProvider.show('File saved', 'success');
|
|
40
|
+
BdsToastProvider.show('Something went wrong', 'error');
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. Instance method
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
const provider = document.querySelector('bds-toast-provider');
|
|
47
|
+
provider.showToast('Settings updated', 'info');
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 3. Document event (no import required)
|
|
51
|
+
|
|
52
|
+
Useful in frameworks or contexts where importing the class is inconvenient:
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
document.dispatchEvent(new CustomEvent('bds-show-toast', {
|
|
56
|
+
detail: { message: 'Copied to clipboard', variant: 'success' },
|
|
57
|
+
}));
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Toast variants
|
|
61
|
+
|
|
62
|
+
| Variant | Use for |
|
|
63
|
+
|---------|---------|
|
|
64
|
+
| `info` | Neutral status updates |
|
|
65
|
+
| `success` | Completed actions |
|
|
66
|
+
| `warning` | Potential issues the user should know about |
|
|
67
|
+
| `error` | Failures requiring attention |
|
|
68
|
+
|
|
69
|
+
## Setup in plain HTML
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<bds-toast-provider>
|
|
73
|
+
<!-- all your page content goes here -->
|
|
74
|
+
<main>…</main>
|
|
75
|
+
</bds-toast-provider>
|
|
76
|
+
|
|
77
|
+
<script type="module">
|
|
78
|
+
import '@boostdev/components/web-components';
|
|
79
|
+
// or use the document event API — no import needed
|
|
80
|
+
</script>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Behaviour
|
|
84
|
+
|
|
85
|
+
- Toasts render in the browser top layer via the Popover API (`popover="manual"`) — they appear above `position: fixed` elements and `z-index` stacking
|
|
86
|
+
- Each toast auto-dismisses after **5000 ms**
|
|
87
|
+
- Multiple toasts stack vertically and each has its own dismissal timer
|
|
88
|
+
- A visible dismiss (×) button is always present on each toast
|
|
89
|
+
|
|
90
|
+
## Accessibility
|
|
91
|
+
|
|
92
|
+
- Each toast renders with `role="status"` + `aria-live="polite"` by default so screen readers announce it without interrupting the user
|
|
93
|
+
- `error` toasts use `role="alert"` + `aria-live="assertive"` for immediate announcement
|
|
94
|
+
- The dismiss button has an accessible label
|
|
@@ -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
|
}
|
|
@@ -12,3 +12,11 @@ export async function fixture(html: string): Promise<HTMLElement> {
|
|
|
12
12
|
export function cleanup(el: HTMLElement) {
|
|
13
13
|
el.remove();
|
|
14
14
|
}
|
|
15
|
+
|
|
16
|
+
/** Create a synthetic Invoker Commands API `command` event for testing. */
|
|
17
|
+
export function makeCommandEvent(command: string): Event {
|
|
18
|
+
const event = new Event('command', { bubbles: false, cancelable: true });
|
|
19
|
+
Object.defineProperty(event, 'command', { value: command, configurable: true });
|
|
20
|
+
Object.defineProperty(event, 'source', { value: null, configurable: true });
|
|
21
|
+
return event;
|
|
22
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './BdsAlert.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# <bds-alert>
|
|
7
|
+
|
|
8
|
+
Framework-agnostic alert custom element for inline status messages. Supports four semantic variants, an optional title, and a dismissible mode.
|
|
9
|
+
|
|
10
|
+
> **Status: experimental** — API may change before stable release.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import '@boostdev/components/web-components';
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Examples
|
|
19
|
+
|
|
20
|
+
### All variants
|
|
21
|
+
<Canvas of={Stories.AllVariants} />
|
|
22
|
+
|
|
23
|
+
### Info
|
|
24
|
+
<Canvas of={Stories.Info} />
|
|
25
|
+
|
|
26
|
+
### Success
|
|
27
|
+
<Canvas of={Stories.Success} />
|
|
28
|
+
|
|
29
|
+
### Warning
|
|
30
|
+
<Canvas of={Stories.Warning} />
|
|
31
|
+
|
|
32
|
+
### Error
|
|
33
|
+
<Canvas of={Stories.Error} />
|
|
34
|
+
|
|
35
|
+
### With title
|
|
36
|
+
<Canvas of={Stories.WithTitle} />
|
|
37
|
+
|
|
38
|
+
### Dismissible
|
|
39
|
+
<Canvas of={Stories.Dismissible} />
|
|
40
|
+
|
|
41
|
+
## Props
|
|
42
|
+
|
|
43
|
+
<ArgTypes of={Stories} />
|
|
44
|
+
|
|
45
|
+
## Attributes
|
|
46
|
+
|
|
47
|
+
| Attribute | Type | Default | Description |
|
|
48
|
+
|-----------|------|---------|-------------|
|
|
49
|
+
| `variant` | `"info" \| "success" \| "warning" \| "error"` | `"info"` | Semantic colour and icon |
|
|
50
|
+
| `title` | string | — | Optional heading displayed above body content |
|
|
51
|
+
| `dismissible` | boolean | `false` | Shows a dismiss (×) button |
|
|
52
|
+
|
|
53
|
+
## Slots
|
|
54
|
+
|
|
55
|
+
| Slot | Description |
|
|
56
|
+
|------|-------------|
|
|
57
|
+
| `(default)` | Alert body text or content |
|
|
58
|
+
| `icon` | Custom icon, placed left of the content area |
|
|
59
|
+
|
|
60
|
+
## Events
|
|
61
|
+
|
|
62
|
+
| Event | Detail | Bubbles | Composed | Description |
|
|
63
|
+
|-------|--------|---------|----------|-------------|
|
|
64
|
+
| `bds-dismiss` | — | Yes | Yes | Fired when the dismiss button is clicked |
|
|
65
|
+
|
|
66
|
+
## Usage in plain HTML
|
|
67
|
+
|
|
68
|
+
```html
|
|
69
|
+
<bds-alert variant="success" title="Saved">
|
|
70
|
+
Your changes have been saved successfully.
|
|
71
|
+
</bds-alert>
|
|
72
|
+
|
|
73
|
+
<bds-alert variant="error" dismissible>
|
|
74
|
+
Something went wrong. Please try again.
|
|
75
|
+
</bds-alert>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Listen for dismiss:
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
document.querySelector('bds-alert').addEventListener('bds-dismiss', () => {
|
|
82
|
+
// remove or hide the element
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Accessibility
|
|
87
|
+
|
|
88
|
+
- `error` and `warning` use `role="alert"` + `aria-live="assertive"` — content is announced immediately
|
|
89
|
+
- `info` and `success` use `role="status"` + `aria-live="polite"` — content is announced at the next opportunity
|
|
90
|
+
- The dismiss button has an accessible label; do not rely solely on its icon
|