@boostdev/design-system-components 1.2.0 → 1.2.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.
Files changed (126) hide show
  1. package/AGENTS.md +25 -4
  2. package/dist/client.cjs +58 -54
  3. package/dist/client.css +503 -525
  4. package/dist/client.js +58 -54
  5. package/dist/index.cjs +58 -54
  6. package/dist/index.css +503 -525
  7. package/dist/index.js +58 -54
  8. package/dist/web-components/{chunk-6MH5UWUD.js → chunk-2FGATTGT.js} +5 -0
  9. package/dist/web-components/{chunk-5IPHEONG.js → chunk-6I2DBFQ7.js} +1 -0
  10. package/dist/web-components/{chunk-DI46Q2EA.js → chunk-OCODKRVZ.js} +57 -82
  11. package/dist/web-components/index.d.ts +571 -1
  12. package/dist/web-components/index.js +1501 -4
  13. package/dist/web-components/interaction/bds-collapsible.js +1 -1
  14. package/dist/web-components/interaction/bds-drawer.js +1 -1
  15. package/dist/web-components/interaction/form/bds-segmented-control.js +1 -1
  16. package/package.json +1 -1
  17. package/src/components/interaction/Drawer/Drawer.module.css +37 -62
  18. package/src/components/interaction/Drawer/Drawer.tsx +21 -17
  19. package/src/components/interaction/form/SegmentedControl/SegmentedControl.mdx +8 -4
  20. package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +13 -6
  21. package/src/stories/Introduction.mdx +2 -1
  22. package/src/web-components/index.ts +12 -0
  23. package/src/web-components/interaction/BdsAccordion.mdx +80 -28
  24. package/src/web-components/interaction/BdsAccordion.stories.tsx +67 -58
  25. package/src/web-components/interaction/BdsCollapsible.mdx +93 -23
  26. package/src/web-components/interaction/BdsCollapsible.stories.tsx +29 -48
  27. package/src/web-components/interaction/BdsDialog.mdx +88 -27
  28. package/src/web-components/interaction/BdsDialog.stories.tsx +129 -47
  29. package/src/web-components/interaction/BdsDrawer.mdx +85 -27
  30. package/src/web-components/interaction/BdsDrawer.stories.tsx +161 -31
  31. package/src/web-components/interaction/BdsDropdownMenu.mdx +108 -0
  32. package/src/web-components/interaction/BdsDropdownMenu.stories.tsx +91 -0
  33. package/src/web-components/interaction/BdsSkipLink.mdx +72 -16
  34. package/src/web-components/interaction/BdsSkipLink.stories.tsx +47 -34
  35. package/src/web-components/interaction/BdsTabs.mdx +97 -27
  36. package/src/web-components/interaction/BdsTabs.stories.tsx +69 -55
  37. package/src/web-components/interaction/BdsTooltip.mdx +84 -18
  38. package/src/web-components/interaction/BdsTooltip.stories.tsx +49 -30
  39. package/src/web-components/interaction/bds-collapsible.ts +1 -0
  40. package/src/web-components/interaction/bds-drawer.ts +59 -82
  41. package/src/web-components/interaction/bds-dropdown-menu-item.ts +124 -0
  42. package/src/web-components/interaction/bds-dropdown-menu.spec.ts +102 -0
  43. package/src/web-components/interaction/bds-dropdown-menu.ts +200 -0
  44. package/src/web-components/interaction/form/BdsCheckbox.mdx +57 -20
  45. package/src/web-components/interaction/form/BdsCheckbox.stories.tsx +35 -36
  46. package/src/web-components/interaction/form/BdsCheckboxGroup.mdx +70 -0
  47. package/src/web-components/interaction/form/BdsCheckboxGroup.stories.tsx +81 -0
  48. package/src/web-components/interaction/form/BdsCombobox.mdx +52 -33
  49. package/src/web-components/interaction/form/BdsCombobox.stories.tsx +55 -53
  50. package/src/web-components/interaction/form/BdsFileInput.mdx +59 -24
  51. package/src/web-components/interaction/form/BdsFileInput.stories.tsx +38 -53
  52. package/src/web-components/interaction/form/BdsFormInput.mdx +98 -0
  53. package/src/web-components/interaction/form/BdsFormInput.stories.tsx +139 -0
  54. package/src/web-components/interaction/form/BdsNumberInput.mdx +51 -22
  55. package/src/web-components/interaction/form/BdsNumberInput.stories.tsx +24 -46
  56. package/src/web-components/interaction/form/BdsRadio.mdx +53 -22
  57. package/src/web-components/interaction/form/BdsRadio.stories.tsx +19 -51
  58. package/src/web-components/interaction/form/BdsRadioGroup.mdx +73 -0
  59. package/src/web-components/interaction/form/BdsRadioGroup.stories.tsx +88 -0
  60. package/src/web-components/interaction/form/BdsSegmentedControl.mdx +76 -23
  61. package/src/web-components/interaction/form/BdsSegmentedControl.stories.tsx +64 -54
  62. package/src/web-components/interaction/form/BdsSelect.mdx +59 -21
  63. package/src/web-components/interaction/form/BdsSelect.stories.tsx +47 -56
  64. package/src/web-components/interaction/form/BdsSlider.mdx +55 -27
  65. package/src/web-components/interaction/form/BdsSlider.stories.tsx +23 -44
  66. package/src/web-components/interaction/form/BdsSwitch.mdx +53 -20
  67. package/src/web-components/interaction/form/BdsSwitch.stories.tsx +22 -37
  68. package/src/web-components/interaction/form/BdsTextarea.mdx +54 -22
  69. package/src/web-components/interaction/form/BdsTextarea.stories.tsx +35 -44
  70. package/src/web-components/interaction/form/bds-checkbox-group.spec.ts +55 -0
  71. package/src/web-components/interaction/form/bds-checkbox-group.ts +117 -0
  72. package/src/web-components/interaction/form/bds-form-input.spec.ts +83 -0
  73. package/src/web-components/interaction/form/bds-form-input.ts +268 -0
  74. package/src/web-components/interaction/form/bds-radio-group.spec.ts +62 -0
  75. package/src/web-components/interaction/form/bds-radio-group.ts +142 -0
  76. package/src/web-components/interaction/form/bds-segmented-control.ts +5 -0
  77. package/src/web-components/ui/BdsAvatar.mdx +29 -20
  78. package/src/web-components/ui/BdsAvatar.stories.tsx +14 -28
  79. package/src/web-components/ui/BdsBreadcrumb.mdx +22 -19
  80. package/src/web-components/ui/BdsBreadcrumb.stories.tsx +11 -21
  81. package/src/web-components/ui/BdsButtonGroup.mdx +69 -0
  82. package/src/web-components/ui/BdsButtonGroup.stories.tsx +66 -0
  83. package/src/web-components/ui/BdsCalendar.mdx +93 -0
  84. package/src/web-components/ui/BdsCalendar.stories.tsx +63 -0
  85. package/src/web-components/ui/BdsCard.mdx +30 -25
  86. package/src/web-components/ui/BdsCard.stories.tsx +19 -42
  87. package/src/web-components/ui/BdsCarousel.mdx +83 -0
  88. package/src/web-components/ui/BdsCarousel.stories.tsx +96 -0
  89. package/src/web-components/ui/BdsDescriptionList.mdx +13 -16
  90. package/src/web-components/ui/BdsDescriptionList.stories.tsx +34 -49
  91. package/src/web-components/ui/BdsIconWrapper.mdx +20 -20
  92. package/src/web-components/ui/BdsIconWrapper.stories.tsx +46 -41
  93. package/src/web-components/ui/BdsLink.mdx +21 -17
  94. package/src/web-components/ui/BdsLink.stories.tsx +19 -20
  95. package/src/web-components/ui/BdsLoading.mdx +9 -13
  96. package/src/web-components/ui/BdsLoading.stories.tsx +8 -10
  97. package/src/web-components/ui/BdsNotificationBanner.mdx +32 -37
  98. package/src/web-components/ui/BdsNotificationBanner.stories.tsx +38 -47
  99. package/src/web-components/ui/BdsPagination.mdx +74 -0
  100. package/src/web-components/ui/BdsPagination.stories.tsx +36 -0
  101. package/src/web-components/ui/BdsProgress.mdx +24 -17
  102. package/src/web-components/ui/BdsProgress.stories.tsx +55 -29
  103. package/src/web-components/ui/BdsProgressCircle.mdx +24 -18
  104. package/src/web-components/ui/BdsProgressCircle.stories.tsx +17 -29
  105. package/src/web-components/ui/BdsRating.mdx +22 -22
  106. package/src/web-components/ui/BdsRating.stories.tsx +19 -13
  107. package/src/web-components/ui/BdsSectionHeader.mdx +31 -28
  108. package/src/web-components/ui/BdsSectionHeader.stories.tsx +21 -23
  109. package/src/web-components/ui/BdsSeparator.mdx +22 -8
  110. package/src/web-components/ui/BdsSeparator.stories.tsx +27 -19
  111. package/src/web-components/ui/BdsSkeleton.mdx +20 -26
  112. package/src/web-components/ui/BdsSkeleton.stories.tsx +23 -23
  113. package/src/web-components/ui/BdsTable.mdx +81 -0
  114. package/src/web-components/ui/BdsTable.stories.tsx +83 -0
  115. package/src/web-components/ui/BdsTypography.mdx +20 -29
  116. package/src/web-components/ui/BdsTypography.stories.tsx +19 -18
  117. package/src/web-components/ui/bds-button-group.spec.ts +40 -0
  118. package/src/web-components/ui/bds-button-group.ts +78 -0
  119. package/src/web-components/ui/bds-calendar.spec.ts +91 -0
  120. package/src/web-components/ui/bds-calendar.ts +427 -0
  121. package/src/web-components/ui/bds-carousel.spec.ts +64 -0
  122. package/src/web-components/ui/bds-carousel.ts +296 -0
  123. package/src/web-components/ui/bds-pagination.spec.ts +67 -0
  124. package/src/web-components/ui/bds-pagination.ts +197 -0
  125. package/src/web-components/ui/bds-table.spec.ts +45 -0
  126. package/src/web-components/ui/bds-table.ts +96 -0
@@ -3,37 +3,74 @@ import * as Stories from './BdsCheckbox.stories';
3
3
 
4
4
  <Meta of={Stories} />
5
5
 
6
- # Checkbox
6
+ # &lt;bds-checkbox&gt;
7
7
 
8
- `<bds-checkbox>` is a form-associated custom element that participates in HTML forms via the Form-Associated Custom Elements API. Native browser validation (`required`) works without any wrapper.
8
+ Form-associated checkbox custom element. Participates in HTML forms via the Form-Associated Custom Elements API native browser validation (required, etc.) works without any wrapper.
9
9
 
10
- ## Usage
10
+ > **Status: experimental** — API may change before stable release.
11
11
 
12
- ```html
13
- <bds-checkbox name="agree" value="yes" required>I agree to the terms</bds-checkbox>
12
+ ## Installation
13
+
14
+ ```js
15
+ import '@boostdev/components/web-components';
14
16
  ```
15
17
 
18
+ ## When to use
19
+
20
+ Use `<bds-checkbox>` whenever you need a single boolean toggle that participates in a form. For grouping multiple checkboxes under a shared label, wrap them in `<bds-checkbox-group>`.
21
+
22
+ ## Examples
23
+
24
+ ### Default
25
+ <Canvas of={Stories.Default} />
26
+
27
+ ### Checked
28
+ <Canvas of={Stories.Checked} />
29
+
30
+ ### Required
31
+ <Canvas of={Stories.Required} />
32
+
33
+ ### Disabled
34
+ <Canvas of={Stories.Disabled} />
35
+
36
+ ### Visual group (without group wrapper)
37
+ <Canvas of={Stories.WithGroup} />
38
+
39
+ ## Props
40
+
41
+ <ArgTypes of={Stories} />
42
+
16
43
  ## Attributes
17
44
 
18
- | Attribute | Type | Default | Description |
19
- |------------|-----------|---------|-------------------------------------|
20
- | `name` | `string` | `""` | Form field name |
21
- | `value` | `string` | `"on"` | Submitted value when checked |
22
- | `checked` | `boolean` | `false` | Whether the checkbox is checked |
23
- | `required` | `boolean` | `false` | Marks the field as required |
24
- | `disabled` | `boolean` | `false` | Disables interaction |
45
+ | Attribute | Type | Default | Description |
46
+ |------------|---------|---------|--------------------------------------------------|
47
+ | `name` | string | `""` | Form field name |
48
+ | `value` | string | `"on"` | Value submitted when checked |
49
+ | `checked` | boolean | `false` | Whether the checkbox is checked |
50
+ | `required` | boolean | `false` | Marks the field as required |
51
+ | `disabled` | boolean | `false` | Disables the checkbox |
25
52
 
26
53
  ## Slots
27
54
 
28
- | Slot | Description |
29
- |-----------|---------------|
30
- | (default) | Label text |
55
+ | Slot | Description |
56
+ |-------------|--------------------|
57
+ | `(default)` | Label text content |
31
58
 
32
- ## Stories
59
+ ## Events
33
60
 
34
- <Canvas of={Stories.Default} />
35
- <Canvas of={Stories.AllVariants} />
61
+ | Event | Detail | Description |
62
+ |----------|--------|-------------------------------------|
63
+ | `change` | — | Native change event forwarded from the inner `<input>` |
36
64
 
37
- ## Controls
65
+ ## Usage in plain HTML
38
66
 
39
- <ArgTypes of={Stories} />
67
+ ```html
68
+ <bds-checkbox name="agree" value="yes" required>I agree to the terms</bds-checkbox>
69
+ ```
70
+
71
+ ## Accessibility
72
+
73
+ - The inner `<input type="checkbox">` is associated with a `<label>` via matching `id`/`for` attributes
74
+ - `required` triggers native browser validation
75
+ - `disabled` sets `pointer-events: none` and reduces opacity
76
+ - Use `<bds-checkbox-group>` for accessible grouping with `<fieldset>`/`<legend>`
@@ -1,26 +1,32 @@
1
1
  import React from 'react';
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
- import '../../index';
3
+ import '../../index'; // auto-registers all custom elements
4
4
 
5
5
  function BdsCheckbox({
6
- label = 'Check me',
6
+ name,
7
+ value,
7
8
  checked,
8
- disabled,
9
9
  required,
10
+ disabled,
11
+ children,
10
12
  }: {
11
- label?: string;
13
+ name?: string;
14
+ value?: string;
12
15
  checked?: boolean;
13
- disabled?: boolean;
14
16
  required?: boolean;
17
+ disabled?: boolean;
18
+ children?: React.ReactNode;
15
19
  }) {
16
20
  return React.createElement(
17
21
  'bds-checkbox',
18
22
  {
23
+ name,
24
+ value,
19
25
  checked: checked || undefined,
20
- disabled: disabled || undefined,
21
26
  required: required || undefined,
27
+ disabled: disabled || undefined,
22
28
  },
23
- label,
29
+ children,
24
30
  );
25
31
  }
26
32
 
@@ -28,46 +34,39 @@ const meta = {
28
34
  title: 'Web Components/Interaction/Form/Checkbox',
29
35
  component: BdsCheckbox,
30
36
  tags: ['!stable', 'experimental'],
31
- parameters: { layout: 'padded' },
37
+ parameters: { layout: 'centered' },
32
38
  argTypes: {
33
39
  checked: { control: 'boolean' },
34
- disabled: { control: 'boolean' },
35
40
  required: { control: 'boolean' },
41
+ disabled: { control: 'boolean' },
36
42
  },
37
43
  } satisfies Meta<typeof BdsCheckbox>;
38
44
 
39
45
  export default meta;
40
46
  type Story = StoryObj<typeof meta>;
41
47
 
42
- export const Default: Story = { args: { label: 'Accept terms and conditions' } };
43
- export const Checked: Story = { args: { label: 'Already checked', checked: true } };
44
- export const Disabled: Story = { args: { label: 'Disabled option', disabled: true } };
45
- export const DisabledChecked: Story = { args: { label: 'Disabled and checked', checked: true, disabled: true } };
48
+ export const Default: Story = {
49
+ args: { name: 'option', value: 'yes', children: 'Accept terms' },
50
+ };
51
+
52
+ export const Checked: Story = {
53
+ args: { name: 'option', value: 'yes', checked: true, children: 'Checked by default' },
54
+ };
46
55
 
47
- export const AllVariants: Story = {
56
+ export const Disabled: Story = {
57
+ args: { name: 'option', value: 'yes', disabled: true, children: 'Disabled' },
58
+ };
59
+
60
+ export const Required: Story = {
61
+ args: { name: 'option', value: 'yes', required: true, children: 'Required field' },
62
+ };
63
+
64
+ export const WithGroup: Story = {
48
65
  render: () => (
49
- <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
50
- {React.createElement('bds-checkbox', null, 'Unchecked')}
51
- {React.createElement('bds-checkbox', { checked: true }, 'Checked')}
52
- {React.createElement('bds-checkbox', { disabled: true }, 'Disabled')}
53
- {React.createElement('bds-checkbox', { checked: true, disabled: true }, 'Disabled checked')}
54
- {React.createElement('bds-checkbox', { required: true }, 'Required')}
66
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
67
+ <BdsCheckbox name="interests" value="sports">Sports</BdsCheckbox>
68
+ <BdsCheckbox name="interests" value="music" checked>Music</BdsCheckbox>
69
+ <BdsCheckbox name="interests" value="travel">Travel</BdsCheckbox>
55
70
  </div>
56
71
  ),
57
72
  };
58
-
59
- export const Interactive: Story = {
60
- render: () => {
61
- const [checked, setChecked] = React.useState(false);
62
- return (
63
- <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
64
- <p style={{ margin: 0, fontSize: '0.875rem' }}>State: {checked ? 'checked' : 'unchecked'}</p>
65
- {React.createElement(
66
- 'bds-checkbox',
67
- { checked: checked || undefined, onChange: (e: Event) => setChecked((e.target as HTMLInputElement).checked) },
68
- 'Controlled checkbox',
69
- )}
70
- </div>
71
- );
72
- },
73
- };
@@ -0,0 +1,70 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './BdsCheckboxGroup.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # &lt;bds-checkbox-group&gt;
7
+
8
+ Groups related checkboxes with a shared label, hint, and error message. Renders a `<fieldset>` with a `<legend>` for accessible grouping.
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
+ ## When to use
19
+
20
+ Use `<bds-checkbox-group>` when you have multiple related checkboxes that belong under a common heading. The `<fieldset>`/`<legend>` structure is announced by screen readers as a group.
21
+
22
+ ## Examples
23
+
24
+ ### Default
25
+ <Canvas of={Stories.Default} />
26
+
27
+ ### With error
28
+ <Canvas of={Stories.WithError} />
29
+
30
+ ### With hint
31
+ <Canvas of={Stories.WithHint} />
32
+
33
+ ### Disabled
34
+ <Canvas of={Stories.Disabled} />
35
+
36
+ ## Props
37
+
38
+ <ArgTypes of={Stories} />
39
+
40
+ ## Attributes
41
+
42
+ | Attribute | Type | Default | Description |
43
+ |------------|---------|---------|----------------------------------------------------|
44
+ | `label` | string | `""` | Group label rendered as `<legend>` |
45
+ | `error` | string | — | Error message shown below items; takes priority over `hint` |
46
+ | `hint` | string | — | Helper text shown below items |
47
+ | `required` | boolean | `false` | Shows a required indicator on the legend |
48
+ | `disabled` | boolean | `false` | Disables the entire fieldset |
49
+
50
+ ## Slots
51
+
52
+ | Slot | Description |
53
+ |-------------|-----------------------------------|
54
+ | `(default)` | `<bds-checkbox>` elements |
55
+
56
+ ## Usage in plain HTML
57
+
58
+ ```html
59
+ <bds-checkbox-group label="Notification preferences" required>
60
+ <bds-checkbox name="email" value="email">Email</bds-checkbox>
61
+ <bds-checkbox name="sms" value="sms">SMS</bds-checkbox>
62
+ </bds-checkbox-group>
63
+ ```
64
+
65
+ ## Accessibility
66
+
67
+ - Renders a `<fieldset>` with a `<legend>`, which is announced as a group by screen readers
68
+ - `required` attribute adds a visual asterisk indicator to the legend
69
+ - `disabled` on the group disables all contained checkboxes via the native `<fieldset disabled>` behaviour
70
+ - Error messages use `role="alert"` so they are announced immediately
@@ -0,0 +1,81 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import '../../index'; // auto-registers all custom elements
4
+
5
+ function BdsCheckboxGroup({
6
+ label,
7
+ error,
8
+ hint,
9
+ required,
10
+ disabled,
11
+ children,
12
+ }: {
13
+ label?: string;
14
+ error?: string;
15
+ hint?: string;
16
+ required?: boolean;
17
+ disabled?: boolean;
18
+ children?: React.ReactNode;
19
+ }) {
20
+ return React.createElement(
21
+ 'bds-checkbox-group',
22
+ {
23
+ label,
24
+ error,
25
+ hint,
26
+ required: required || undefined,
27
+ disabled: disabled || undefined,
28
+ },
29
+ children,
30
+ );
31
+ }
32
+
33
+ const meta = {
34
+ title: 'Web Components/Interaction/Form/CheckboxGroup',
35
+ component: BdsCheckboxGroup,
36
+ tags: ['!stable', 'experimental'],
37
+ parameters: { layout: 'centered' },
38
+ argTypes: {
39
+ required: { control: 'boolean' },
40
+ disabled: { control: 'boolean' },
41
+ },
42
+ } satisfies Meta<typeof BdsCheckboxGroup>;
43
+
44
+ export default meta;
45
+ type Story = StoryObj<typeof meta>;
46
+
47
+ export const Default: Story = {
48
+ render: () => (
49
+ <BdsCheckboxGroup label="Interests">
50
+ {React.createElement('bds-checkbox', { name: 'sports', value: 'sports' }, 'Sports')}
51
+ {React.createElement('bds-checkbox', { name: 'music', value: 'music' }, 'Music')}
52
+ </BdsCheckboxGroup>
53
+ ),
54
+ };
55
+
56
+ export const WithError: Story = {
57
+ render: () => (
58
+ <BdsCheckboxGroup label="Interests" error="Please select at least one option.">
59
+ {React.createElement('bds-checkbox', { name: 'sports', value: 'sports' }, 'Sports')}
60
+ {React.createElement('bds-checkbox', { name: 'music', value: 'music' }, 'Music')}
61
+ </BdsCheckboxGroup>
62
+ ),
63
+ };
64
+
65
+ export const WithHint: Story = {
66
+ render: () => (
67
+ <BdsCheckboxGroup label="Interests" hint="You can select multiple options.">
68
+ {React.createElement('bds-checkbox', { name: 'sports', value: 'sports' }, 'Sports')}
69
+ {React.createElement('bds-checkbox', { name: 'music', value: 'music' }, 'Music')}
70
+ </BdsCheckboxGroup>
71
+ ),
72
+ };
73
+
74
+ export const Disabled: Story = {
75
+ render: () => (
76
+ <BdsCheckboxGroup label="Interests" disabled>
77
+ {React.createElement('bds-checkbox', { name: 'sports', value: 'sports' }, 'Sports')}
78
+ {React.createElement('bds-checkbox', { name: 'music', value: 'music' }, 'Music')}
79
+ </BdsCheckboxGroup>
80
+ ),
81
+ };
@@ -3,50 +3,69 @@ import * as Stories from './BdsCombobox.stories';
3
3
 
4
4
  <Meta of={Stories} />
5
5
 
6
- # Combobox
6
+ # &lt;bds-combobox&gt;
7
7
 
8
- `<bds-combobox>` is a form-associated autocomplete input. The user types to filter options and selects with Enter or mouse click. Keyboard navigation: ArrowDown/ArrowUp to move focus, Enter to select, Escape to close.
8
+ Form-associated autocomplete combobox. Renders a text input with a filterable dropdown. Keyboard navigation: `ArrowDown`/`ArrowUp` to move focus, `Enter` to select, `Escape` to close.
9
9
 
10
- ## Usage
10
+ > **Status: experimental** — API may change before stable release.
11
11
 
12
- ```html
13
- <bds-combobox
14
- name="country"
15
- placeholder="Search…"
16
- options='[{"value":"nl","label":"Netherlands"},{"value":"be","label":"Belgium"}]'
17
- ></bds-combobox>
18
- ```
12
+ ## Installation
19
13
 
20
- ## Attributes
14
+ ```js
15
+ import '@boostdev/components/web-components';
16
+ ```
21
17
 
22
- | Attribute | Type | Default | Description |
23
- |---------------|-----------|---------|-----------------------------------------------------|
24
- | `name` | `string` | `""` | Form field name |
25
- | `value` | `string` | `""` | Currently selected value |
26
- | `options` | `string` | `"[]"` | JSON array of `{ value, label, disabled? }` objects |
27
- | `placeholder` | `string` | `""` | Input placeholder text |
28
- | `disabled` | `boolean` | `false` | Disables the combobox |
18
+ ## When to use
29
19
 
30
- ## Events
20
+ Use `<bds-combobox>` when users need to search and filter a list of options. For a simpler pick-from-list without search, use `<bds-select>`.
31
21
 
32
- | Event | Detail | Description |
33
- |--------------|----------------------------|----------------------------------|
34
- | `bds-change` | `{ value, label }` | Fired when an option is selected |
22
+ ## Examples
35
23
 
36
- ## Keyboard Navigation
24
+ ### Default
25
+ <Canvas of={Stories.Default} />
37
26
 
38
- | Key | Action |
39
- |-------------|-------------------------|
40
- | `ArrowDown` | Move to next option |
41
- | `ArrowUp` | Move to previous option |
42
- | `Enter` | Select focused option |
43
- | `Escape` | Close the listbox |
27
+ ### With disabled option
28
+ <Canvas of={Stories.WithDisabledOption} />
44
29
 
45
- ## Stories
30
+ ### Required
31
+ <Canvas of={Stories.Required} />
46
32
 
47
- <Canvas of={Stories.Default} />
48
- <Canvas of={Stories.AllVariants} />
33
+ ### Disabled
34
+ <Canvas of={Stories.Disabled} />
49
35
 
50
- ## Controls
36
+ ## Props
51
37
 
52
38
  <ArgTypes of={Stories} />
39
+
40
+ ## Attributes
41
+
42
+ | Attribute | Type | Default | Description |
43
+ |---------------|---------|---------|------------------------------------------------------------------|
44
+ | `name` | string | `""` | Form field name |
45
+ | `value` | string | `""` | Currently selected value |
46
+ | `options` | string | `"[]"` | JSON array of `{ value, label, disabled? }` option objects |
47
+ | `placeholder` | string | `""` | Input placeholder text |
48
+ | `disabled` | boolean | `false` | Disables the entire combobox |
49
+
50
+ ## Events
51
+
52
+ | Event | Detail | Description |
53
+ |--------------|---------------------------------|--------------------------------------|
54
+ | `bds-change` | `{ value: string, label: string }` | Fired when an option is selected |
55
+
56
+ ## Usage in plain HTML
57
+
58
+ ```html
59
+ <bds-combobox
60
+ name="country"
61
+ placeholder="Search country…"
62
+ options='[{"value":"nl","label":"Netherlands"},{"value":"be","label":"Belgium"}]'
63
+ ></bds-combobox>
64
+ ```
65
+
66
+ ## Accessibility
67
+
68
+ - Uses `role="combobox"` on the input with `aria-expanded`, `aria-autocomplete="list"`, and `aria-haspopup="listbox"`
69
+ - Listbox uses `role="listbox"` and each option uses `role="option"` with `aria-selected` and `aria-disabled`
70
+ - Full keyboard navigation: `ArrowDown`/`ArrowUp` to navigate, `Enter` to select, `Escape` to close
71
+ - Participates in HTML forms via the Form-Associated Custom Elements API
@@ -1,30 +1,41 @@
1
1
  import React from 'react';
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
- import type { ComboboxOption } from './bds-combobox';
4
- import '../../index';
3
+ import '../../index'; // auto-registers all custom elements
5
4
 
6
- const COUNTRIES: ComboboxOption[] = [
5
+ const COUNTRIES = JSON.stringify([
7
6
  { value: 'nl', label: 'Netherlands' },
8
7
  { value: 'be', label: 'Belgium' },
9
8
  { value: 'de', label: 'Germany' },
10
9
  { value: 'fr', label: 'France' },
11
- { value: 'es', label: 'Spain' },
12
- { value: 'it', label: 'Italy' },
13
- { value: 'gb', label: 'United Kingdom', disabled: true },
14
- ];
10
+ { value: 'gb', label: 'United Kingdom' },
11
+ ]);
12
+
13
+ const COUNTRIES_WITH_DISABLED = JSON.stringify([
14
+ { value: 'nl', label: 'Netherlands' },
15
+ { value: 'be', label: 'Belgium' },
16
+ { value: 'de', label: 'Germany', disabled: true },
17
+ { value: 'fr', label: 'France' },
18
+ ]);
15
19
 
16
20
  function BdsCombobox({
17
- placeholder = 'Search…',
21
+ name,
22
+ value,
23
+ options,
24
+ placeholder,
18
25
  disabled,
19
26
  }: {
27
+ name?: string;
28
+ value?: string;
29
+ options?: string;
20
30
  placeholder?: string;
21
31
  disabled?: boolean;
22
32
  }) {
23
33
  return React.createElement('bds-combobox', {
24
- options: JSON.stringify(COUNTRIES),
34
+ name,
35
+ value,
36
+ options,
25
37
  placeholder,
26
38
  disabled: disabled || undefined,
27
- style: { display: 'block', maxInlineSize: '280px' },
28
39
  });
29
40
  }
30
41
 
@@ -32,60 +43,51 @@ const meta = {
32
43
  title: 'Web Components/Interaction/Form/Combobox',
33
44
  component: BdsCombobox,
34
45
  tags: ['!stable', 'experimental'],
35
- parameters: { layout: 'padded' },
46
+ parameters: { layout: 'centered' },
36
47
  argTypes: {
37
48
  disabled: { control: 'boolean' },
38
49
  },
50
+ decorators: [
51
+ (Story: React.ComponentType) => (
52
+ <div style={{ width: '280px', paddingBlockEnd: '200px' }}>
53
+ <Story />
54
+ </div>
55
+ ),
56
+ ],
39
57
  } satisfies Meta<typeof BdsCombobox>;
40
58
 
41
59
  export default meta;
42
60
  type Story = StoryObj<typeof meta>;
43
61
 
44
- export const Default: Story = { args: { placeholder: 'Select a country…' } };
45
- export const Disabled: Story = { args: { placeholder: 'Select a country…', disabled: true } };
46
- export const WithValue: Story = {
47
- render: () =>
48
- React.createElement('bds-combobox', {
49
- options: JSON.stringify(COUNTRIES),
50
- value: 'nl',
51
- placeholder: 'Search…',
52
- style: { display: 'block', maxInlineSize: '280px' },
53
- }),
62
+ export const Default: Story = {
63
+ args: {
64
+ name: 'country',
65
+ options: COUNTRIES,
66
+ placeholder: 'Search country…',
67
+ },
54
68
  };
55
69
 
56
- export const AllVariants: Story = {
57
- render: () => (
58
- <div style={{ display: 'flex', flexDirection: 'column', gap: '16px', maxInlineSize: '280px' }}>
59
- <div>
60
- <p style={{ margin: '0 0 4px', fontSize: '0.75rem', color: '#888' }}>Default</p>
61
- {React.createElement('bds-combobox', { options: JSON.stringify(COUNTRIES), placeholder: 'Search…' })}
62
- </div>
63
- <div>
64
- <p style={{ margin: '0 0 4px', fontSize: '0.75rem', color: '#888' }}>With pre-selected value</p>
65
- {React.createElement('bds-combobox', { options: JSON.stringify(COUNTRIES), value: 'be', placeholder: 'Search…' })}
66
- </div>
67
- <div>
68
- <p style={{ margin: '0 0 4px', fontSize: '0.75rem', color: '#888' }}>Disabled</p>
69
- {React.createElement('bds-combobox', { options: JSON.stringify(COUNTRIES), placeholder: 'Search…', disabled: true })}
70
- </div>
71
- </div>
72
- ),
70
+ export const WithDisabledOption: Story = {
71
+ args: {
72
+ name: 'country',
73
+ options: COUNTRIES_WITH_DISABLED,
74
+ placeholder: 'Search country…',
75
+ },
73
76
  };
74
77
 
75
- export const Interactive: Story = {
76
- render: () => {
77
- const [selected, setSelected] = React.useState<{ value: string; label: string } | null>(null);
78
- return (
79
- <div style={{ display: 'flex', flexDirection: 'column', gap: '8px', maxInlineSize: '280px' }}>
80
- <p style={{ margin: '0 0 8px', fontSize: '0.875rem' }}>
81
- Selected: {selected ? `${selected.label} (${selected.value})` : '(none)'}
82
- </p>
83
- {React.createElement('bds-combobox', {
84
- options: JSON.stringify(COUNTRIES),
85
- placeholder: 'Type to filter…',
86
- 'onbds-change': (e: CustomEvent<{ value: string; label: string }>) => setSelected(e.detail),
87
- })}
88
- </div>
89
- );
78
+ export const Required: Story = {
79
+ args: {
80
+ name: 'country',
81
+ options: COUNTRIES,
82
+ placeholder: 'Select a country…',
83
+ },
84
+ };
85
+
86
+ export const Disabled: Story = {
87
+ args: {
88
+ name: 'country',
89
+ options: COUNTRIES,
90
+ placeholder: 'Disabled combobox',
91
+ disabled: true,
90
92
  },
91
93
  };