@boostdev/design-system-components 1.2.2 → 1.2.4

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 (215) hide show
  1. package/AGENTS.md +5 -0
  2. package/README.md +50 -5
  3. package/dist/client.cjs +50 -50
  4. package/dist/client.css +506 -503
  5. package/dist/client.js +50 -50
  6. package/dist/index.cjs +50 -50
  7. package/dist/index.css +506 -503
  8. package/dist/index.js +50 -50
  9. package/dist/native/index.cjs +3692 -352
  10. package/dist/native/index.d.cts +359 -3
  11. package/dist/native/index.d.ts +359 -3
  12. package/dist/native/index.js +3811 -364
  13. package/package.json +3 -3
  14. package/src/components/interaction/Button/Button.module.css +1 -4
  15. package/src/components/interaction/Button/Button.native.stories.tsx +10 -12
  16. package/src/components/interaction/Button/Button.native.tsx +3 -3
  17. package/src/components/interaction/Dialog/Dialog.module.css +1 -1
  18. package/src/components/interaction/Dialog/Dialog.native.mdx +61 -0
  19. package/src/components/interaction/Dialog/Dialog.native.spec.tsx +73 -0
  20. package/src/components/interaction/Dialog/Dialog.native.stories.tsx +53 -0
  21. package/src/components/interaction/Dialog/Dialog.native.tsx +128 -0
  22. package/src/components/interaction/Drawer/Drawer.module.css +1 -1
  23. package/src/components/interaction/Drawer/Drawer.native.mdx +58 -0
  24. package/src/components/interaction/Drawer/Drawer.native.spec.tsx +81 -0
  25. package/src/components/interaction/Drawer/Drawer.native.stories.tsx +33 -0
  26. package/src/components/interaction/Drawer/Drawer.native.tsx +175 -0
  27. package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +4 -0
  28. package/src/components/interaction/DropdownMenu/DropdownMenu.native.mdx +74 -0
  29. package/src/components/interaction/DropdownMenu/DropdownMenu.native.spec.tsx +78 -0
  30. package/src/components/interaction/DropdownMenu/DropdownMenu.native.stories.tsx +51 -0
  31. package/src/components/interaction/DropdownMenu/DropdownMenu.native.tsx +254 -0
  32. package/src/components/interaction/DropdownMenu/DropdownMenu.stories.tsx +4 -4
  33. package/src/components/interaction/Popover/Popover.native.mdx +61 -0
  34. package/src/components/interaction/Popover/Popover.native.spec.tsx +73 -0
  35. package/src/components/interaction/Popover/Popover.native.stories.tsx +44 -0
  36. package/src/components/interaction/Popover/Popover.native.tsx +87 -0
  37. package/src/components/interaction/Rating/Rating.native.mdx +55 -0
  38. package/src/components/interaction/Rating/Rating.native.spec.tsx +38 -0
  39. package/src/components/interaction/Rating/Rating.native.stories.tsx +37 -0
  40. package/src/components/interaction/Rating/Rating.native.tsx +50 -0
  41. package/src/components/interaction/Toast/Toast.native.mdx +81 -0
  42. package/src/components/interaction/Toast/Toast.native.spec.tsx +80 -0
  43. package/src/components/interaction/Toast/Toast.native.stories.tsx +47 -0
  44. package/src/components/interaction/Toast/Toast.native.tsx +202 -0
  45. package/src/components/interaction/form/Checkbox/Checkbox.native.stories.tsx +2 -4
  46. package/src/components/interaction/form/Checkbox/Checkbox.native.tsx +1 -1
  47. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.mdx +78 -0
  48. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.spec.tsx +57 -0
  49. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.stories.tsx +96 -0
  50. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.tsx +61 -0
  51. package/src/components/interaction/form/Combobox/Combobox.module.css +1 -1
  52. package/src/components/interaction/form/Combobox/Combobox.native.mdx +86 -0
  53. package/src/components/interaction/form/Combobox/Combobox.native.spec.tsx +60 -0
  54. package/src/components/interaction/form/Combobox/Combobox.native.stories.tsx +99 -0
  55. package/src/components/interaction/form/Combobox/Combobox.native.tsx +211 -0
  56. package/src/components/interaction/form/FileInput/FileInput.native.mdx +86 -0
  57. package/src/components/interaction/form/FileInput/FileInput.native.spec.tsx +73 -0
  58. package/src/components/interaction/form/FileInput/FileInput.native.stories.tsx +69 -0
  59. package/src/components/interaction/form/FileInput/FileInput.native.tsx +129 -0
  60. package/src/components/interaction/form/FormInput/FormInput.module.css +1 -1
  61. package/src/components/interaction/form/FormInput/FormInput.native.mdx +81 -0
  62. package/src/components/interaction/form/FormInput/FormInput.native.spec.tsx +51 -0
  63. package/src/components/interaction/form/FormInput/FormInput.native.stories.tsx +54 -0
  64. package/src/components/interaction/form/FormInput/FormInput.native.tsx +122 -0
  65. package/src/components/interaction/form/NumberInput/NumberInput.module.css +1 -1
  66. package/src/components/interaction/form/NumberInput/NumberInput.native.mdx +87 -0
  67. package/src/components/interaction/form/NumberInput/NumberInput.native.spec.tsx +63 -0
  68. package/src/components/interaction/form/NumberInput/NumberInput.native.stories.tsx +63 -0
  69. package/src/components/interaction/form/NumberInput/NumberInput.native.tsx +198 -0
  70. package/src/components/interaction/form/Radio/Radio.native.spec.tsx +79 -0
  71. package/src/components/interaction/form/Radio/Radio.native.stories.tsx +12 -14
  72. package/src/components/interaction/form/Radio/Radio.native.tsx +1 -1
  73. package/src/components/interaction/form/RadioGroup/RadioGroup.native.mdx +79 -0
  74. package/src/components/interaction/form/RadioGroup/RadioGroup.native.spec.tsx +66 -0
  75. package/src/components/interaction/form/RadioGroup/RadioGroup.native.stories.tsx +96 -0
  76. package/src/components/interaction/form/RadioGroup/RadioGroup.native.tsx +61 -0
  77. package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +5 -5
  78. package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.mdx +78 -0
  79. package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.spec.tsx +82 -0
  80. package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.stories.tsx +99 -0
  81. package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.tsx +121 -0
  82. package/src/components/interaction/form/Select/Select.module.css +1 -1
  83. package/src/components/interaction/form/Select/Select.native.mdx +85 -0
  84. package/src/components/interaction/form/Select/Select.native.spec.tsx +64 -0
  85. package/src/components/interaction/form/Select/Select.native.stories.tsx +75 -0
  86. package/src/components/interaction/form/Select/Select.native.tsx +252 -0
  87. package/src/components/interaction/form/Slider/Slider.module.css +4 -4
  88. package/src/components/interaction/form/Slider/Slider.native.mdx +83 -0
  89. package/src/components/interaction/form/Slider/Slider.native.spec.tsx +51 -0
  90. package/src/components/interaction/form/Slider/Slider.native.stories.tsx +58 -0
  91. package/src/components/interaction/form/Slider/Slider.native.tsx +188 -0
  92. package/src/components/interaction/form/Switch/Switch.module.css +3 -3
  93. package/src/components/interaction/form/Switch/Switch.native.stories.tsx +2 -4
  94. package/src/components/interaction/form/Textarea/Textarea.native.mdx +76 -0
  95. package/src/components/interaction/form/Textarea/Textarea.native.spec.tsx +50 -0
  96. package/src/components/interaction/form/Textarea/Textarea.native.stories.tsx +50 -0
  97. package/src/components/interaction/form/Textarea/Textarea.native.tsx +108 -0
  98. package/src/components/interaction/form/atoms/Label.native.spec.tsx +39 -0
  99. package/src/components/interaction/form/atoms/Label.native.stories.tsx +1 -1
  100. package/src/components/interaction/form/atoms/Message.native.stories.tsx +1 -1
  101. package/src/components/layout/ButtonGroup/ButtonGroup.native.spec.tsx +83 -0
  102. package/src/components/layout/ButtonGroup/ButtonGroup.native.stories.tsx +13 -19
  103. package/src/components/layout/Card/Card.native.spec.tsx +102 -0
  104. package/src/components/layout/Card/Card.native.stories.tsx +9 -11
  105. package/src/components/layout/Card/Card.native.tsx +1 -1
  106. package/src/components/layout/IconWrapper/IconWrapper.module.css +1 -1
  107. package/src/components/layout/IconWrapper/IconWrapper.native.stories.tsx +7 -11
  108. package/src/components/layout/SectionHeader/SectionHeader.native.spec.tsx +51 -0
  109. package/src/components/layout/SectionHeader/SectionHeader.native.stories.tsx +6 -8
  110. package/src/components/ui/Accordion/Accordion.native.mdx +65 -0
  111. package/src/components/ui/Accordion/Accordion.native.spec.tsx +69 -0
  112. package/src/components/ui/Accordion/Accordion.native.stories.tsx +52 -0
  113. package/src/components/ui/Accordion/Accordion.native.tsx +141 -0
  114. package/src/components/ui/Alert/Alert.module.css +1 -1
  115. package/src/components/ui/Alert/Alert.native.spec.tsx +76 -0
  116. package/src/components/ui/Alert/Alert.native.stories.tsx +8 -10
  117. package/src/components/ui/Alert/Alert.native.tsx +7 -7
  118. package/src/components/ui/Avatar/Avatar.module.css +1 -1
  119. package/src/components/ui/Avatar/Avatar.native.stories.tsx +6 -8
  120. package/src/components/ui/Avatar/Avatar.native.tsx +2 -2
  121. package/src/components/ui/Badge/Badge.module.css +1 -1
  122. package/src/components/ui/Badge/Badge.native.spec.tsx +45 -0
  123. package/src/components/ui/Badge/Badge.native.stories.tsx +6 -8
  124. package/src/components/ui/Badge/Badge.native.tsx +2 -2
  125. package/src/components/ui/Breadcrumb/Breadcrumb.native.mdx +52 -0
  126. package/src/components/ui/Breadcrumb/Breadcrumb.native.spec.tsx +49 -0
  127. package/src/components/ui/Breadcrumb/Breadcrumb.native.stories.tsx +46 -0
  128. package/src/components/ui/Breadcrumb/Breadcrumb.native.tsx +83 -0
  129. package/src/components/ui/Calendar/Calendar.module.css +2 -2
  130. package/src/components/ui/Calendar/Calendar.native.mdx +58 -0
  131. package/src/components/ui/Calendar/Calendar.native.spec.tsx +92 -0
  132. package/src/components/ui/Calendar/Calendar.native.stories.tsx +45 -0
  133. package/src/components/ui/Calendar/Calendar.native.tsx +294 -0
  134. package/src/components/ui/Carousel/Carousel.module.css +1 -1
  135. package/src/components/ui/Carousel/Carousel.native.mdx +55 -0
  136. package/src/components/ui/Carousel/Carousel.native.spec.tsx +55 -0
  137. package/src/components/ui/Carousel/Carousel.native.stories.tsx +59 -0
  138. package/src/components/ui/Carousel/Carousel.native.tsx +184 -0
  139. package/src/components/ui/Collapsible/Collapsible.native.mdx +53 -0
  140. package/src/components/ui/Collapsible/Collapsible.native.spec.tsx +68 -0
  141. package/src/components/ui/Collapsible/Collapsible.native.stories.tsx +48 -0
  142. package/src/components/ui/Collapsible/Collapsible.native.tsx +139 -0
  143. package/src/components/ui/DescriptionList/DescriptionList.native.mdx +50 -0
  144. package/src/components/ui/DescriptionList/DescriptionList.native.spec.tsx +37 -0
  145. package/src/components/ui/DescriptionList/DescriptionList.native.stories.tsx +48 -0
  146. package/src/components/ui/DescriptionList/DescriptionList.native.tsx +54 -0
  147. package/src/components/ui/Link/Link.native.mdx +53 -0
  148. package/src/components/ui/Link/Link.native.spec.tsx +35 -0
  149. package/src/components/ui/Link/Link.native.stories.tsx +46 -0
  150. package/src/components/ui/Link/Link.native.tsx +48 -0
  151. package/src/components/ui/Loading/Loading.module.css +1 -1
  152. package/src/components/ui/Loading/Loading.native.stories.tsx +6 -8
  153. package/src/components/ui/NotificationBanner/NotificationBanner.module.css +1 -1
  154. package/src/components/ui/NotificationBanner/NotificationBanner.native.spec.tsx +85 -0
  155. package/src/components/ui/NotificationBanner/NotificationBanner.native.stories.tsx +8 -10
  156. package/src/components/ui/NotificationBanner/NotificationBanner.native.tsx +7 -7
  157. package/src/components/ui/Pagination/Pagination.module.css +5 -1
  158. package/src/components/ui/Pagination/Pagination.native.mdx +62 -0
  159. package/src/components/ui/Pagination/Pagination.native.spec.tsx +86 -0
  160. package/src/components/ui/Pagination/Pagination.native.stories.tsx +47 -0
  161. package/src/components/ui/Pagination/Pagination.native.tsx +157 -0
  162. package/src/components/ui/Progress/Progress.module.css +2 -2
  163. package/src/components/ui/Progress/Progress.native.spec.tsx +56 -0
  164. package/src/components/ui/Progress/Progress.native.stories.tsx +1 -1
  165. package/src/components/ui/Progress/Progress.native.tsx +2 -2
  166. package/src/components/ui/ProgressCircle/ProgressCircle.native.mdx +63 -0
  167. package/src/components/ui/ProgressCircle/ProgressCircle.native.spec.tsx +39 -0
  168. package/src/components/ui/ProgressCircle/ProgressCircle.native.stories.tsx +36 -0
  169. package/src/components/ui/ProgressCircle/ProgressCircle.native.tsx +176 -0
  170. package/src/components/ui/Separator/Separator.native.spec.tsx +39 -0
  171. package/src/components/ui/Separator/Separator.native.stories.tsx +10 -14
  172. package/src/components/ui/Skeleton/Skeleton.native.spec.tsx +39 -0
  173. package/src/components/ui/Skeleton/Skeleton.native.stories.tsx +6 -8
  174. package/src/components/ui/Skeleton/Skeleton.native.tsx +2 -1
  175. package/src/components/ui/Table/Table.native.mdx +72 -0
  176. package/src/components/ui/Table/Table.native.spec.tsx +83 -0
  177. package/src/components/ui/Table/Table.native.stories.tsx +55 -0
  178. package/src/components/ui/Table/Table.native.tsx +196 -0
  179. package/src/components/ui/Tabs/Tabs.native.mdx +65 -0
  180. package/src/components/ui/Tabs/Tabs.native.spec.tsx +65 -0
  181. package/src/components/ui/Tabs/Tabs.native.stories.tsx +57 -0
  182. package/src/components/ui/Tabs/Tabs.native.tsx +133 -0
  183. package/src/components/ui/Tooltip/Tooltip.module.css +1 -1
  184. package/src/components/ui/Tooltip/Tooltip.native.mdx +47 -0
  185. package/src/components/ui/Tooltip/Tooltip.native.spec.tsx +37 -0
  186. package/src/components/ui/Tooltip/Tooltip.native.stories.tsx +38 -0
  187. package/src/components/ui/Tooltip/Tooltip.native.tsx +57 -0
  188. package/src/components/ui/Typography/Typography.native.spec.tsx +49 -0
  189. package/src/components/ui/Typography/Typography.native.stories.tsx +8 -10
  190. package/src/native/ThemeContext.tsx +3 -3
  191. package/src/native.ts +37 -0
  192. package/src/stories/ReactNative.mdx +48 -13
  193. package/src/web-components/interaction/BdsAccordion.stories.tsx +1 -1
  194. package/src/web-components/interaction/BdsCollapsible.stories.tsx +1 -1
  195. package/src/web-components/interaction/BdsSkipLink.stories.tsx +1 -1
  196. package/src/web-components/interaction/BdsTabs.stories.tsx +1 -1
  197. package/src/web-components/interaction/BdsTooltip.stories.tsx +1 -1
  198. package/src/web-components/interaction/form/BdsCheckbox.stories.tsx +1 -1
  199. package/src/web-components/interaction/form/BdsCheckboxGroup.stories.tsx +1 -1
  200. package/src/web-components/interaction/form/BdsCombobox.stories.tsx +1 -1
  201. package/src/web-components/interaction/form/BdsFileInput.stories.tsx +1 -1
  202. package/src/web-components/interaction/form/BdsFormInput.stories.tsx +1 -1
  203. package/src/web-components/interaction/form/BdsNumberInput.stories.tsx +1 -1
  204. package/src/web-components/interaction/form/BdsRadio.stories.tsx +1 -1
  205. package/src/web-components/interaction/form/BdsRadioGroup.stories.tsx +1 -1
  206. package/src/web-components/interaction/form/BdsSegmentedControl.stories.tsx +1 -1
  207. package/src/web-components/interaction/form/BdsSelect.stories.tsx +1 -1
  208. package/src/web-components/interaction/form/BdsSlider.stories.tsx +1 -1
  209. package/src/web-components/interaction/form/BdsSwitch.stories.tsx +1 -1
  210. package/src/web-components/interaction/form/BdsTextarea.stories.tsx +1 -1
  211. package/src/web-components/ui/BdsButtonGroup.stories.tsx +1 -1
  212. package/src/web-components/ui/BdsCard.stories.tsx +1 -1
  213. package/src/web-components/ui/BdsIconWrapper.stories.tsx +1 -1
  214. package/src/web-components/ui/BdsRating.stories.tsx +1 -1
  215. package/src/web-components/ui/BdsSectionHeader.stories.tsx +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boostdev/design-system-components",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "BoostDev React component library: accessible, token-driven components built on @boostdev/design-system-foundation",
5
5
  "keywords": [
6
6
  "React",
@@ -271,7 +271,7 @@
271
271
  "typecheck": "tsc --noEmit",
272
272
  "lint": "eslint src",
273
273
  "lint:css": "stylelint \"src/**/*.css\"",
274
- "storybook": "storybook dev -p 6006 --no-manager-cache",
274
+ "storybook": "storybook dev -p 6006",
275
275
  "build-storybook": "storybook build",
276
276
  "test": "vitest run",
277
277
  "test:watch": "vitest",
@@ -279,7 +279,7 @@
279
279
  "changelog:unreleased": "pnpm exec git-cliff --unreleased"
280
280
  },
281
281
  "devDependencies": {
282
- "@boostdev/design-system-foundation": "^1.0.4",
282
+ "@boostdev/design-system-foundation": "^1.0.5",
283
283
  "@modelcontextprotocol/sdk": "^1.27.0",
284
284
  "@storybook/addon-docs": "8.6.14",
285
285
  "@storybook/addon-essentials": "^8.6.14",
@@ -36,7 +36,7 @@
36
36
  padding-inline: var(--bds-space_l);
37
37
  display: inline-flex;
38
38
  gap: var(--bds-space_xs);
39
- border-radius: var(--button_border_radius--s, var(--bds-border_radius--s));
39
+ border-radius: var(--button_radius, var(--bds-border_radius--full));
40
40
  align-items: center;
41
41
  cursor: pointer;
42
42
  scroll-padding-block-end: var(--bds-space_l);
@@ -76,7 +76,6 @@
76
76
  .button.--size_small {
77
77
  --button_height: 2.25em;
78
78
  --button_font-size: var(--bds-font_size--body--s);
79
- --button_border_radius--s: var(--bds-border_radius--xs);
80
79
 
81
80
  padding-inline: var(--bds-space_m);
82
81
  }
@@ -89,7 +88,6 @@
89
88
 
90
89
  .button.--size_large {
91
90
  --button_font-size: var(--bds-font_size--heading-3);
92
- --button_border_radius--s: var(--bds-border_radius--m);
93
91
 
94
92
  padding-inline: var(--bds-space_xl);
95
93
  }
@@ -137,7 +135,6 @@
137
135
  .button:focus-visible {
138
136
  outline: var(--bds-outline_default);
139
137
  outline-offset: var(--bds-outline_offset);
140
- border-radius: var(--bds-border_radius--s);
141
138
  }
142
139
 
143
140
  /* Default hover: outline appears, bg becomes page surface */
@@ -7,7 +7,7 @@ const meta = {
7
7
  tags: ['!stable', 'alpha'],
8
8
  title: 'React Native/Interaction/Button',
9
9
  component: Button,
10
- decorators: [(Story) => <ThemeProvider><Story /></ThemeProvider>],
10
+ decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
11
11
  parameters: { layout: 'centered' },
12
12
  argTypes: {
13
13
  variant: { control: 'select', options: ['default', 'ghost'] },
@@ -27,16 +27,14 @@ export const Large: Story = { args: { children: 'Large', size: 'large' } };
27
27
  export const Disabled: Story = { args: { children: 'Disabled', disabled: true } };
28
28
  export const AllVariants: Story = {
29
29
  render: () => (
30
- <ThemeProvider>
31
- <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
32
- {(['default', 'ghost'] as const).map(v => (
33
- <div key={v} style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
34
- {(['small', 'medium', 'large'] as const).map(s => (
35
- <Button key={s} variant={v} size={s}>{v} {s}</Button>
36
- ))}
37
- </div>
38
- ))}
39
- </div>
40
- </ThemeProvider>
30
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
31
+ {(['default', 'ghost'] as const).map(v => (
32
+ <div key={v} style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
33
+ {(['small', 'medium', 'large'] as const).map(s => (
34
+ <Button key={s} variant={v} size={s}>{v} {s}</Button>
35
+ ))}
36
+ </div>
37
+ ))}
38
+ </div>
41
39
  ),
42
40
  };
@@ -19,9 +19,9 @@ export interface ButtonProps {
19
19
  }
20
20
 
21
21
  const sizeTokens: Record<ButtonSize, { height: number; px: number; fontSize: number; radius: number }> = {
22
- small: { height: 36, px: spacing.m, fontSize: font.size.bodyS, radius: border.radius.xs },
23
- medium: { height: 48, px: spacing.m, fontSize: font.size.body, radius: border.radius.s },
24
- large: { height: 56, px: spacing.xxl, fontSize: font.size.heading3, radius: border.radius.m },
22
+ small: { height: 36, px: spacing.m, fontSize: font.size.bodyS, radius: border.radius.full },
23
+ medium: { height: 48, px: spacing.m, fontSize: font.size.body, radius: border.radius.full },
24
+ large: { height: 56, px: spacing.xxl, fontSize: font.size.heading3, radius: border.radius.full },
25
25
  };
26
26
 
27
27
  const styles = StyleSheet.create({
@@ -33,7 +33,7 @@
33
33
  display: flex;
34
34
  align-items: center;
35
35
  justify-content: center;
36
- border-radius: 50%;
36
+ border-radius: var(--dialog_close-radius, var(--bds-border_radius--circle));
37
37
  background-color: var(--bds-color_bg);
38
38
  color: currentcolor;
39
39
  border: var(--dialog_close-border, none);
@@ -0,0 +1,61 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './Dialog.native.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Dialog (React Native)
7
+
8
+ A modal dialog overlay that centers content on screen with a semi-transparent backdrop. Built on React Native `Modal` with `transparent` background.
9
+
10
+ > **Status: alpha** — API may change before stable release.
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ import { Dialog } from '@boostdev/components';
16
+
17
+ const [open, setOpen] = useState(false);
18
+
19
+ <Button onPress={() => setOpen(true)}>Open</Button>
20
+ <Dialog isOpen={open} onClose={() => setOpen(false)} title="Confirm">
21
+ <Text>Are you sure?</Text>
22
+ </Dialog>
23
+ ```
24
+
25
+ Wrap your tree in `ThemeProvider` (once, at the app root):
26
+
27
+ ```tsx
28
+ import { ThemeProvider } from '@boostdev/components/native/ThemeContext';
29
+
30
+ export default function App() {
31
+ return <ThemeProvider><YourApp /></ThemeProvider>;
32
+ }
33
+ ```
34
+
35
+ ## Examples
36
+
37
+ ### Default
38
+ <Canvas of={Stories.Default} />
39
+
40
+ ### Without title
41
+ <Canvas of={Stories.WithoutTitle} />
42
+
43
+ ## Props
44
+
45
+ <ArgTypes of={Stories} />
46
+
47
+ ## Props reference
48
+
49
+ | Prop | Type | Default | Description |
50
+ |------|------|---------|-------------|
51
+ | `isOpen` | `boolean` | `false` | Controls visibility of the dialog |
52
+ | `onClose` | `() => void` | — | Called when backdrop or close button is pressed |
53
+ | `title` | `string` | — | Optional title displayed at the top |
54
+ | `children` | `ReactNode` | — | Dialog body content |
55
+ | `style` | `StyleProp<ViewStyle>` | — | Override panel styles |
56
+
57
+ ## Accessibility
58
+
59
+ - Backdrop press and close button both call `onClose`
60
+ - Close button has `accessibilityLabel="Close dialog"`
61
+ - Android back button triggers `onRequestClose` which calls `onClose`
@@ -0,0 +1,73 @@
1
+ import { render, screen, fireEvent } from '@testing-library/react';
2
+ import { ThemeProvider } from '../../../native/ThemeContext';
3
+ import { Dialog } from './Dialog.native';
4
+
5
+ function renderWithTheme(ui: React.ReactElement) {
6
+ return render(<ThemeProvider>{ui}</ThemeProvider>);
7
+ }
8
+
9
+ describe('Dialog (native)', () => {
10
+ it('renders children when open', () => {
11
+ renderWithTheme(
12
+ <Dialog isOpen>
13
+ <span>Dialog body</span>
14
+ </Dialog>,
15
+ );
16
+ expect(screen.getByText('Dialog body')).toBeInTheDocument();
17
+ });
18
+
19
+ it('does not render children when closed', () => {
20
+ renderWithTheme(
21
+ <Dialog isOpen={false}>
22
+ <span>Hidden content</span>
23
+ </Dialog>,
24
+ );
25
+ expect(screen.queryByText('Hidden content')).not.toBeInTheDocument();
26
+ });
27
+
28
+ it('renders the title when provided', () => {
29
+ renderWithTheme(
30
+ <Dialog isOpen title="Confirm action">
31
+ <span>Body</span>
32
+ </Dialog>,
33
+ );
34
+ expect(screen.getByText('Confirm action')).toBeInTheDocument();
35
+ });
36
+
37
+ it('renders a close button with accessible label', () => {
38
+ renderWithTheme(
39
+ <Dialog isOpen onClose={() => {}}>
40
+ <span>Body</span>
41
+ </Dialog>,
42
+ );
43
+ const closeButtons = screen.getAllByRole('button', { name: 'Close dialog' });
44
+ expect(closeButtons.length).toBeGreaterThanOrEqual(1);
45
+ });
46
+
47
+ it('calls onClose when close button is pressed', () => {
48
+ const onClose = vi.fn();
49
+ renderWithTheme(
50
+ <Dialog isOpen onClose={onClose}>
51
+ <span>Body</span>
52
+ </Dialog>,
53
+ );
54
+ const closeButtons = screen.getAllByRole('button', { name: 'Close dialog' });
55
+ // The inner close button (not the backdrop)
56
+ const innerClose = closeButtons[closeButtons.length - 1];
57
+ fireEvent.click(innerClose);
58
+ expect(onClose).toHaveBeenCalledTimes(1);
59
+ });
60
+
61
+ it('calls onClose when backdrop is pressed', () => {
62
+ const onClose = vi.fn();
63
+ renderWithTheme(
64
+ <Dialog isOpen onClose={onClose}>
65
+ <span>Body</span>
66
+ </Dialog>,
67
+ );
68
+ const closeButtons = screen.getAllByRole('button', { name: 'Close dialog' });
69
+ // The first one is the backdrop
70
+ fireEvent.click(closeButtons[0]);
71
+ expect(onClose).toHaveBeenCalledTimes(1);
72
+ });
73
+ });
@@ -0,0 +1,53 @@
1
+ import React, { useState } from 'react';
2
+ import { Text, View } from 'react-native';
3
+ import type { Meta, StoryObj } from '@storybook/react';
4
+ import { ThemeProvider } from '../../../native/ThemeContext';
5
+ import { Dialog } from './Dialog.native';
6
+ import { Button } from '../Button/Button.native';
7
+
8
+ const meta = {
9
+ tags: ['!stable', 'alpha'],
10
+ title: 'React Native/Interaction/Dialog',
11
+ component: Dialog,
12
+ decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
13
+ parameters: { layout: 'centered' },
14
+ } satisfies Meta<typeof Dialog>;
15
+
16
+ export default meta;
17
+ type Story = StoryObj<typeof meta>;
18
+
19
+ function DialogDemo() {
20
+ const [open, setOpen] = useState(false);
21
+ return (
22
+ <>
23
+ <Button onPress={() => setOpen(true)}>Open Dialog</Button>
24
+ <Dialog isOpen={open} onClose={() => setOpen(false)} title="Confirmation">
25
+ <Text>Are you sure you want to proceed with this action?</Text>
26
+ <View style={{ flexDirection: 'row', gap: 8, marginTop: 16 }}>
27
+ <Button variant="outline" onPress={() => setOpen(false)}>Cancel</Button>
28
+ <Button onPress={() => setOpen(false)}>Confirm</Button>
29
+ </View>
30
+ </Dialog>
31
+ </>
32
+ );
33
+ }
34
+
35
+ function DialogWithoutTitleDemo() {
36
+ const [open, setOpen] = useState(false);
37
+ return (
38
+ <>
39
+ <Button onPress={() => setOpen(true)}>Open Dialog (no title)</Button>
40
+ <Dialog isOpen={open} onClose={() => setOpen(false)}>
41
+ <Text>This dialog has no title, just content.</Text>
42
+ </Dialog>
43
+ </>
44
+ );
45
+ }
46
+
47
+ export const Default: Story = {
48
+ render: () => <DialogDemo />,
49
+ };
50
+
51
+ export const WithoutTitle: Story = {
52
+ render: () => <DialogWithoutTitleDemo />,
53
+ };
@@ -0,0 +1,128 @@
1
+ import { ReactNode } from 'react';
2
+ import {
3
+ Modal,
4
+ View,
5
+ Pressable,
6
+ Text,
7
+ StyleSheet,
8
+ StyleProp,
9
+ ViewStyle,
10
+ } from 'react-native';
11
+ import { spacing, font, border } from '../../../native/tokens';
12
+ import { useTheme } from '../../../native/ThemeContext';
13
+
14
+ interface DialogProps {
15
+ isOpen?: boolean;
16
+ onClose?: () => void;
17
+ title?: string;
18
+ children?: ReactNode;
19
+ style?: StyleProp<ViewStyle>;
20
+ }
21
+
22
+ const styles = StyleSheet.create({
23
+ overlay: {
24
+ flex: 1,
25
+ justifyContent: 'center',
26
+ alignItems: 'center',
27
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
28
+ padding: spacing.m,
29
+ },
30
+ panel: {
31
+ width: '100%',
32
+ maxWidth: 480,
33
+ borderRadius: border.radius.m,
34
+ padding: spacing.l,
35
+ paddingTop: spacing.xl,
36
+ shadowColor: '#000',
37
+ shadowOffset: { width: 0, height: 4 },
38
+ shadowOpacity: 0.15,
39
+ shadowRadius: 12,
40
+ elevation: 8,
41
+ },
42
+ header: {
43
+ flexDirection: 'row',
44
+ justifyContent: 'space-between',
45
+ alignItems: 'center',
46
+ marginBottom: spacing.m,
47
+ },
48
+ title: {
49
+ fontFamily: font.family.body,
50
+ fontSize: font.size.heading3,
51
+ fontWeight: font.weight.semibold,
52
+ flex: 1,
53
+ },
54
+ closeButton: {
55
+ position: 'absolute',
56
+ top: spacing.s,
57
+ end: spacing.s,
58
+ width: 44,
59
+ height: 44,
60
+ alignItems: 'center',
61
+ justifyContent: 'center',
62
+ zIndex: 1,
63
+ },
64
+ closeText: {
65
+ fontFamily: font.family.body,
66
+ fontSize: 20,
67
+ lineHeight: 24,
68
+ },
69
+ });
70
+
71
+ export function Dialog({
72
+ isOpen = false,
73
+ onClose,
74
+ title,
75
+ children,
76
+ style,
77
+ }: Readonly<DialogProps>) {
78
+ const { colors } = useTheme();
79
+
80
+ return (
81
+ <Modal
82
+ visible={isOpen}
83
+ transparent
84
+ animationType="fade"
85
+ onRequestClose={onClose}
86
+ >
87
+ <Pressable
88
+ style={styles.overlay}
89
+ onPress={onClose}
90
+ accessibilityRole="button"
91
+ accessibilityLabel="Close dialog"
92
+ >
93
+ <Pressable
94
+ style={[styles.panel, { backgroundColor: colors.colorGreySubtle }, style]}
95
+ onPress={() => {
96
+ /* prevent close when pressing content */
97
+ }}
98
+ accessibilityRole="none"
99
+ >
100
+ <Pressable
101
+ style={styles.closeButton}
102
+ onPress={onClose}
103
+ accessibilityRole="button"
104
+ accessibilityLabel="Close dialog"
105
+ hitSlop={8}
106
+ >
107
+ <Text
108
+ style={[styles.closeText, { color: colors.colorOnBgSubtle }]}
109
+ aria-hidden
110
+ >
111
+ {'\u2715'}
112
+ </Text>
113
+ </Pressable>
114
+
115
+ {!!title && (
116
+ <View style={styles.header}>
117
+ <Text style={[styles.title, { color: colors.colorOnGreySubtle }]}>
118
+ {title}
119
+ </Text>
120
+ </View>
121
+ )}
122
+
123
+ {children}
124
+ </Pressable>
125
+ </Pressable>
126
+ </Modal>
127
+ );
128
+ }
@@ -78,7 +78,7 @@
78
78
  justify-content: center;
79
79
  inline-size: 2rem;
80
80
  block-size: 2rem;
81
- border-radius: 50%;
81
+ border-radius: var(--drawer_close-radius, var(--bds-border_radius--circle));
82
82
  cursor: pointer;
83
83
  transition: var(--bds-animation_transition);
84
84
  }
@@ -0,0 +1,58 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './Drawer.native.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Drawer (React Native)
7
+
8
+ A sliding panel that slides in from the bottom of the screen. Built on React Native `Modal` with `Animated` for smooth slide and backdrop fade transitions.
9
+
10
+ > **Status: alpha** — API may change before stable release.
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ import { Drawer } from '@boostdev/components';
16
+
17
+ const [open, setOpen] = useState(false);
18
+
19
+ <Button onPress={() => setOpen(true)}>Open</Button>
20
+ <Drawer isOpen={open} onClose={() => setOpen(false)} title="Settings">
21
+ <Text>Drawer content here.</Text>
22
+ </Drawer>
23
+ ```
24
+
25
+ Wrap your tree in `ThemeProvider` (once, at the app root):
26
+
27
+ ```tsx
28
+ import { ThemeProvider } from '@boostdev/components/native/ThemeContext';
29
+
30
+ export default function App() {
31
+ return <ThemeProvider><YourApp /></ThemeProvider>;
32
+ }
33
+ ```
34
+
35
+ ## Examples
36
+
37
+ <Canvas of={Stories.Default} />
38
+
39
+ ## Props
40
+
41
+ <ArgTypes of={Stories} />
42
+
43
+ ## Props reference
44
+
45
+ | Prop | Type | Default | Description |
46
+ |------|------|---------|-------------|
47
+ | `isOpen` | `boolean` | — | Controls visibility of the drawer |
48
+ | `onClose` | `() => void` | — | Called when backdrop or close button is pressed |
49
+ | `title` | `ReactNode` | — | Title displayed in the drawer header |
50
+ | `children` | `ReactNode` | — | Drawer body content |
51
+ | `style` | `StyleProp<ViewStyle>` | — | Override panel styles |
52
+
53
+ ## Accessibility
54
+
55
+ - Backdrop press and close button both call `onClose`
56
+ - Close button has `accessibilityLabel="Close drawer"`
57
+ - Android back button triggers `onRequestClose` which calls `onClose`
58
+ - Backdrop fades in, panel slides from bottom
@@ -0,0 +1,81 @@
1
+ import { render, screen, fireEvent } from '@testing-library/react';
2
+ import { ThemeProvider } from '../../../native/ThemeContext';
3
+ import { Drawer } from './Drawer.native';
4
+
5
+ function renderWithTheme(ui: React.ReactElement) {
6
+ return render(<ThemeProvider>{ui}</ThemeProvider>);
7
+ }
8
+
9
+ describe('Drawer (native)', () => {
10
+ it('renders children when open', () => {
11
+ renderWithTheme(
12
+ <Drawer isOpen onClose={() => {}}>
13
+ <span>Drawer content</span>
14
+ </Drawer>,
15
+ );
16
+ expect(screen.getByText('Drawer content')).toBeInTheDocument();
17
+ });
18
+
19
+ it('does not render children when closed', () => {
20
+ renderWithTheme(
21
+ <Drawer isOpen={false} onClose={() => {}}>
22
+ <span>Hidden drawer</span>
23
+ </Drawer>,
24
+ );
25
+ expect(screen.queryByText('Hidden drawer')).not.toBeInTheDocument();
26
+ });
27
+
28
+ it('renders the title when provided', () => {
29
+ renderWithTheme(
30
+ <Drawer isOpen onClose={() => {}} title="Settings">
31
+ <span>Body</span>
32
+ </Drawer>,
33
+ );
34
+ expect(screen.getByText('Settings')).toBeInTheDocument();
35
+ });
36
+
37
+ it('renders a close button with accessible label', () => {
38
+ renderWithTheme(
39
+ <Drawer isOpen onClose={() => {}}>
40
+ <span>Body</span>
41
+ </Drawer>,
42
+ );
43
+ const closeButtons = screen.getAllByRole('button', { name: 'Close drawer' });
44
+ expect(closeButtons.length).toBeGreaterThanOrEqual(1);
45
+ });
46
+
47
+ it('calls onClose when close button is pressed', () => {
48
+ const onClose = vi.fn();
49
+ renderWithTheme(
50
+ <Drawer isOpen onClose={onClose} title="Test">
51
+ <span>Body</span>
52
+ </Drawer>,
53
+ );
54
+ const closeButtons = screen.getAllByRole('button', { name: 'Close drawer' });
55
+ // The inner close button (not the backdrop)
56
+ const innerClose = closeButtons[closeButtons.length - 1];
57
+ fireEvent.click(innerClose);
58
+ expect(onClose).toHaveBeenCalledTimes(1);
59
+ });
60
+
61
+ it('calls onClose when backdrop is pressed', () => {
62
+ const onClose = vi.fn();
63
+ renderWithTheme(
64
+ <Drawer isOpen onClose={onClose}>
65
+ <span>Body</span>
66
+ </Drawer>,
67
+ );
68
+ const closeButtons = screen.getAllByRole('button', { name: 'Close drawer' });
69
+ fireEvent.click(closeButtons[0]);
70
+ expect(onClose).toHaveBeenCalledTimes(1);
71
+ });
72
+
73
+ it('renders panel when open', () => {
74
+ const { container } = renderWithTheme(
75
+ <Drawer isOpen onClose={() => {}}>
76
+ <span>Body</span>
77
+ </Drawer>,
78
+ );
79
+ expect(container).toBeInTheDocument();
80
+ });
81
+ });
@@ -0,0 +1,33 @@
1
+ import React, { useState } from 'react';
2
+ import { Text } from 'react-native';
3
+ import type { Meta, StoryObj } from '@storybook/react';
4
+ import { ThemeProvider } from '../../../native/ThemeContext';
5
+ import { Drawer } from './Drawer.native';
6
+ import { Button } from '../Button/Button.native';
7
+
8
+ const meta = {
9
+ tags: ['!stable', 'alpha'],
10
+ title: 'React Native/Interaction/Drawer',
11
+ component: Drawer,
12
+ decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
13
+ parameters: { layout: 'centered' },
14
+ } satisfies Meta<typeof Drawer>;
15
+
16
+ export default meta;
17
+ type Story = StoryObj<typeof meta>;
18
+
19
+ function DrawerDemo() {
20
+ const [open, setOpen] = useState(false);
21
+ return (
22
+ <>
23
+ <Button onPress={() => setOpen(true)}>Open Drawer</Button>
24
+ <Drawer isOpen={open} onClose={() => setOpen(false)} title="Settings">
25
+ <Text>Drawer content slides in from the bottom.</Text>
26
+ </Drawer>
27
+ </>
28
+ );
29
+ }
30
+
31
+ export const Default: Story = {
32
+ render: () => <DrawerDemo />,
33
+ };