@boostdev/design-system-components 1.2.3 → 1.2.5
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 +19 -11
- package/README.md +36 -1
- package/dist/client.cjs +50 -50
- package/dist/client.css +470 -470
- package/dist/client.js +50 -50
- package/dist/index.cjs +50 -50
- package/dist/index.css +470 -470
- package/dist/index.js +50 -50
- package/dist/native/index.cjs +3692 -352
- package/dist/native/index.d.cts +359 -3
- package/dist/native/index.d.ts +359 -3
- package/dist/native/index.js +3811 -364
- package/dist/utils.cjs +30 -0
- package/dist/utils.d.cts +8 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.js +5 -0
- package/package.json +10 -4
- package/src/client.ts +6 -0
- package/src/components/interaction/Button/Button.native.stories.tsx +10 -12
- package/src/components/interaction/Button/Button.native.tsx +3 -3
- package/src/components/interaction/Dialog/Dialog.native.mdx +61 -0
- package/src/components/interaction/Dialog/Dialog.native.spec.tsx +73 -0
- package/src/components/interaction/Dialog/Dialog.native.stories.tsx +53 -0
- package/src/components/interaction/Dialog/Dialog.native.tsx +128 -0
- package/src/components/interaction/Drawer/Drawer.native.mdx +58 -0
- package/src/components/interaction/Drawer/Drawer.native.spec.tsx +81 -0
- package/src/components/interaction/Drawer/Drawer.native.stories.tsx +33 -0
- package/src/components/interaction/Drawer/Drawer.native.tsx +175 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.native.mdx +74 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.native.spec.tsx +78 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.native.stories.tsx +51 -0
- package/src/components/interaction/DropdownMenu/DropdownMenu.native.tsx +254 -0
- package/src/components/interaction/Popover/Popover.native.mdx +61 -0
- package/src/components/interaction/Popover/Popover.native.spec.tsx +73 -0
- package/src/components/interaction/Popover/Popover.native.stories.tsx +44 -0
- package/src/components/interaction/Popover/Popover.native.tsx +87 -0
- package/src/components/interaction/Rating/Rating.native.mdx +55 -0
- package/src/components/interaction/Rating/Rating.native.spec.tsx +38 -0
- package/src/components/interaction/Rating/Rating.native.stories.tsx +37 -0
- package/src/components/interaction/Rating/Rating.native.tsx +50 -0
- package/src/components/interaction/Toast/Toast.native.mdx +81 -0
- package/src/components/interaction/Toast/Toast.native.spec.tsx +80 -0
- package/src/components/interaction/Toast/Toast.native.stories.tsx +47 -0
- package/src/components/interaction/Toast/Toast.native.tsx +202 -0
- package/src/components/interaction/form/Checkbox/Checkbox.native.stories.tsx +2 -4
- package/src/components/interaction/form/Checkbox/Checkbox.native.tsx +1 -1
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.mdx +78 -0
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.spec.tsx +57 -0
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.stories.tsx +96 -0
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.tsx +61 -0
- package/src/components/interaction/form/Combobox/Combobox.native.mdx +86 -0
- package/src/components/interaction/form/Combobox/Combobox.native.spec.tsx +60 -0
- package/src/components/interaction/form/Combobox/Combobox.native.stories.tsx +99 -0
- package/src/components/interaction/form/Combobox/Combobox.native.tsx +211 -0
- package/src/components/interaction/form/FileInput/FileInput.native.mdx +86 -0
- package/src/components/interaction/form/FileInput/FileInput.native.spec.tsx +73 -0
- package/src/components/interaction/form/FileInput/FileInput.native.stories.tsx +69 -0
- package/src/components/interaction/form/FileInput/FileInput.native.tsx +129 -0
- package/src/components/interaction/form/FormInput/FormInput.native.mdx +81 -0
- package/src/components/interaction/form/FormInput/FormInput.native.spec.tsx +51 -0
- package/src/components/interaction/form/FormInput/FormInput.native.stories.tsx +54 -0
- package/src/components/interaction/form/FormInput/FormInput.native.tsx +122 -0
- package/src/components/interaction/form/NumberInput/NumberInput.native.mdx +87 -0
- package/src/components/interaction/form/NumberInput/NumberInput.native.spec.tsx +63 -0
- package/src/components/interaction/form/NumberInput/NumberInput.native.stories.tsx +63 -0
- package/src/components/interaction/form/NumberInput/NumberInput.native.tsx +198 -0
- package/src/components/interaction/form/Radio/Radio.native.spec.tsx +79 -0
- package/src/components/interaction/form/Radio/Radio.native.stories.tsx +12 -14
- package/src/components/interaction/form/Radio/Radio.native.tsx +1 -1
- package/src/components/interaction/form/RadioGroup/RadioGroup.native.mdx +79 -0
- package/src/components/interaction/form/RadioGroup/RadioGroup.native.spec.tsx +66 -0
- package/src/components/interaction/form/RadioGroup/RadioGroup.native.stories.tsx +96 -0
- package/src/components/interaction/form/RadioGroup/RadioGroup.native.tsx +61 -0
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.mdx +78 -0
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.spec.tsx +82 -0
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.stories.tsx +99 -0
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.tsx +121 -0
- package/src/components/interaction/form/Select/Select.native.mdx +85 -0
- package/src/components/interaction/form/Select/Select.native.spec.tsx +64 -0
- package/src/components/interaction/form/Select/Select.native.stories.tsx +75 -0
- package/src/components/interaction/form/Select/Select.native.tsx +252 -0
- package/src/components/interaction/form/Slider/Slider.native.mdx +83 -0
- package/src/components/interaction/form/Slider/Slider.native.spec.tsx +51 -0
- package/src/components/interaction/form/Slider/Slider.native.stories.tsx +58 -0
- package/src/components/interaction/form/Slider/Slider.native.tsx +188 -0
- package/src/components/interaction/form/Switch/Switch.native.stories.tsx +2 -4
- package/src/components/interaction/form/Textarea/Textarea.native.mdx +76 -0
- package/src/components/interaction/form/Textarea/Textarea.native.spec.tsx +50 -0
- package/src/components/interaction/form/Textarea/Textarea.native.stories.tsx +50 -0
- package/src/components/interaction/form/Textarea/Textarea.native.tsx +108 -0
- package/src/components/interaction/form/atoms/Label.native.spec.tsx +39 -0
- package/src/components/interaction/form/atoms/Label.native.stories.tsx +1 -1
- package/src/components/interaction/form/atoms/Message.native.stories.tsx +1 -1
- package/src/components/layout/ButtonGroup/ButtonGroup.native.spec.tsx +83 -0
- package/src/components/layout/ButtonGroup/ButtonGroup.native.stories.tsx +13 -19
- package/src/components/layout/Card/Card.native.spec.tsx +102 -0
- package/src/components/layout/Card/Card.native.stories.tsx +9 -11
- package/src/components/layout/Card/Card.native.tsx +1 -1
- package/src/components/layout/IconWrapper/IconWrapper.native.stories.tsx +7 -11
- package/src/components/layout/SectionHeader/SectionHeader.native.spec.tsx +51 -0
- package/src/components/layout/SectionHeader/SectionHeader.native.stories.tsx +6 -8
- package/src/components/ui/Accordion/Accordion.native.mdx +65 -0
- package/src/components/ui/Accordion/Accordion.native.spec.tsx +69 -0
- package/src/components/ui/Accordion/Accordion.native.stories.tsx +52 -0
- package/src/components/ui/Accordion/Accordion.native.tsx +141 -0
- package/src/components/ui/Alert/Alert.native.spec.tsx +76 -0
- package/src/components/ui/Alert/Alert.native.stories.tsx +8 -10
- package/src/components/ui/Alert/Alert.native.tsx +7 -7
- package/src/components/ui/Avatar/Avatar.native.stories.tsx +6 -8
- package/src/components/ui/Avatar/Avatar.native.tsx +2 -2
- package/src/components/ui/Badge/Badge.native.spec.tsx +45 -0
- package/src/components/ui/Badge/Badge.native.stories.tsx +6 -8
- package/src/components/ui/Badge/Badge.native.tsx +2 -2
- package/src/components/ui/Breadcrumb/Breadcrumb.native.mdx +52 -0
- package/src/components/ui/Breadcrumb/Breadcrumb.native.spec.tsx +49 -0
- package/src/components/ui/Breadcrumb/Breadcrumb.native.stories.tsx +46 -0
- package/src/components/ui/Breadcrumb/Breadcrumb.native.tsx +83 -0
- package/src/components/ui/Calendar/Calendar.native.mdx +58 -0
- package/src/components/ui/Calendar/Calendar.native.spec.tsx +92 -0
- package/src/components/ui/Calendar/Calendar.native.stories.tsx +45 -0
- package/src/components/ui/Calendar/Calendar.native.tsx +294 -0
- package/src/components/ui/Carousel/Carousel.native.mdx +55 -0
- package/src/components/ui/Carousel/Carousel.native.spec.tsx +55 -0
- package/src/components/ui/Carousel/Carousel.native.stories.tsx +59 -0
- package/src/components/ui/Carousel/Carousel.native.tsx +184 -0
- package/src/components/ui/Collapsible/Collapsible.native.mdx +53 -0
- package/src/components/ui/Collapsible/Collapsible.native.spec.tsx +68 -0
- package/src/components/ui/Collapsible/Collapsible.native.stories.tsx +48 -0
- package/src/components/ui/Collapsible/Collapsible.native.tsx +139 -0
- package/src/components/ui/DescriptionList/DescriptionList.native.mdx +50 -0
- package/src/components/ui/DescriptionList/DescriptionList.native.spec.tsx +37 -0
- package/src/components/ui/DescriptionList/DescriptionList.native.stories.tsx +48 -0
- package/src/components/ui/DescriptionList/DescriptionList.native.tsx +54 -0
- package/src/components/ui/Link/Link.native.mdx +53 -0
- package/src/components/ui/Link/Link.native.spec.tsx +35 -0
- package/src/components/ui/Link/Link.native.stories.tsx +46 -0
- package/src/components/ui/Link/Link.native.tsx +48 -0
- package/src/components/ui/Loading/Loading.native.stories.tsx +6 -8
- package/src/components/ui/NotificationBanner/NotificationBanner.native.spec.tsx +85 -0
- package/src/components/ui/NotificationBanner/NotificationBanner.native.stories.tsx +8 -10
- package/src/components/ui/NotificationBanner/NotificationBanner.native.tsx +7 -7
- package/src/components/ui/Pagination/Pagination.native.mdx +62 -0
- package/src/components/ui/Pagination/Pagination.native.spec.tsx +86 -0
- package/src/components/ui/Pagination/Pagination.native.stories.tsx +47 -0
- package/src/components/ui/Pagination/Pagination.native.tsx +157 -0
- package/src/components/ui/Progress/Progress.native.spec.tsx +56 -0
- package/src/components/ui/Progress/Progress.native.stories.tsx +1 -1
- package/src/components/ui/Progress/Progress.native.tsx +2 -2
- package/src/components/ui/ProgressCircle/ProgressCircle.native.mdx +63 -0
- package/src/components/ui/ProgressCircle/ProgressCircle.native.spec.tsx +39 -0
- package/src/components/ui/ProgressCircle/ProgressCircle.native.stories.tsx +36 -0
- package/src/components/ui/ProgressCircle/ProgressCircle.native.tsx +176 -0
- package/src/components/ui/Separator/Separator.native.spec.tsx +39 -0
- package/src/components/ui/Separator/Separator.native.stories.tsx +10 -14
- package/src/components/ui/Skeleton/Skeleton.native.spec.tsx +39 -0
- package/src/components/ui/Skeleton/Skeleton.native.stories.tsx +6 -8
- package/src/components/ui/Skeleton/Skeleton.native.tsx +2 -1
- package/src/components/ui/Table/Table.native.mdx +72 -0
- package/src/components/ui/Table/Table.native.spec.tsx +83 -0
- package/src/components/ui/Table/Table.native.stories.tsx +55 -0
- package/src/components/ui/Table/Table.native.tsx +196 -0
- package/src/components/ui/Tabs/Tabs.native.mdx +65 -0
- package/src/components/ui/Tabs/Tabs.native.spec.tsx +65 -0
- package/src/components/ui/Tabs/Tabs.native.stories.tsx +57 -0
- package/src/components/ui/Tabs/Tabs.native.tsx +133 -0
- package/src/components/ui/Tooltip/Tooltip.native.mdx +47 -0
- package/src/components/ui/Tooltip/Tooltip.native.spec.tsx +37 -0
- package/src/components/ui/Tooltip/Tooltip.native.stories.tsx +38 -0
- package/src/components/ui/Tooltip/Tooltip.native.tsx +57 -0
- package/src/components/ui/Typography/Typography.native.spec.tsx +49 -0
- package/src/components/ui/Typography/Typography.native.stories.tsx +8 -10
- package/src/native/ThemeContext.tsx +3 -3
- package/src/native.ts +37 -0
- package/src/stories/ReactNative.mdx +48 -13
- package/src/utils.ts +6 -0
- package/src/web-components/interaction/BdsAccordion.stories.tsx +1 -1
- package/src/web-components/interaction/BdsCollapsible.stories.tsx +1 -1
- package/src/web-components/interaction/BdsSkipLink.stories.tsx +1 -1
- package/src/web-components/interaction/BdsTabs.stories.tsx +1 -1
- package/src/web-components/interaction/BdsTooltip.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsCheckbox.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsCheckboxGroup.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsCombobox.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsFileInput.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsFormInput.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsNumberInput.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsRadio.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsRadioGroup.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsSegmentedControl.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsSelect.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsSlider.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsSwitch.stories.tsx +1 -1
- package/src/web-components/interaction/form/BdsTextarea.stories.tsx +1 -1
- package/src/web-components/ui/BdsButtonGroup.stories.tsx +1 -1
- package/src/web-components/ui/BdsCard.stories.tsx +1 -1
- package/src/web-components/ui/BdsIconWrapper.stories.tsx +1 -1
- package/src/web-components/ui/BdsRating.stories.tsx +1 -1
- package/src/web-components/ui/BdsSectionHeader.stories.tsx +1 -1
package/dist/utils.cjs
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/utils.ts
|
|
21
|
+
var utils_exports = {};
|
|
22
|
+
__export(utils_exports, {
|
|
23
|
+
cn: () => import_design_system_foundation.cn
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(utils_exports);
|
|
26
|
+
var import_design_system_foundation = require("@boostdev/design-system-foundation");
|
|
27
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
28
|
+
0 && (module.exports = {
|
|
29
|
+
cn
|
|
30
|
+
});
|
package/dist/utils.d.cts
ADDED
package/dist/utils.d.ts
ADDED
package/dist/utils.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boostdev/design-system-components",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.5",
|
|
4
4
|
"description": "BoostDev React component library: accessible, token-driven components built on @boostdev/design-system-foundation",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"React",
|
|
@@ -36,6 +36,11 @@
|
|
|
36
36
|
"import": "./dist/client.js",
|
|
37
37
|
"require": "./dist/client.cjs"
|
|
38
38
|
},
|
|
39
|
+
"./utils": {
|
|
40
|
+
"types": "./dist/utils.d.ts",
|
|
41
|
+
"import": "./dist/utils.js",
|
|
42
|
+
"require": "./dist/utils.cjs"
|
|
43
|
+
},
|
|
39
44
|
"./web-components": {
|
|
40
45
|
"types": "./dist/web-components/index.d.ts",
|
|
41
46
|
"import": "./dist/web-components/index.js"
|
|
@@ -279,7 +284,7 @@
|
|
|
279
284
|
"changelog:unreleased": "pnpm exec git-cliff --unreleased"
|
|
280
285
|
},
|
|
281
286
|
"devDependencies": {
|
|
282
|
-
"@boostdev/design-system-foundation": "^1.0.
|
|
287
|
+
"@boostdev/design-system-foundation": "^1.0.6",
|
|
283
288
|
"@modelcontextprotocol/sdk": "^1.27.0",
|
|
284
289
|
"@storybook/addon-docs": "8.6.14",
|
|
285
290
|
"@storybook/addon-essentials": "^8.6.14",
|
|
@@ -310,10 +315,11 @@
|
|
|
310
315
|
"typescript": "^5.8",
|
|
311
316
|
"typescript-eslint": "^8",
|
|
312
317
|
"vite": "^5.4.21",
|
|
313
|
-
"vitest": "^3"
|
|
318
|
+
"vitest": "^3",
|
|
319
|
+
"zod": "^4.3.6"
|
|
314
320
|
},
|
|
315
321
|
"peerDependencies": {
|
|
316
|
-
"@boostdev/design-system-foundation": ">=1.0.
|
|
322
|
+
"@boostdev/design-system-foundation": ">=1.0.6",
|
|
317
323
|
"react": ">=18",
|
|
318
324
|
"react-dom": ">=18",
|
|
319
325
|
"react-native": ">=0.70"
|
package/src/client.ts
CHANGED
|
@@ -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
|
-
<
|
|
31
|
-
|
|
32
|
-
{
|
|
33
|
-
|
|
34
|
-
{
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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.
|
|
23
|
-
medium: { height: 48, px: spacing.m, fontSize: font.size.body, radius: border.radius.
|
|
24
|
-
large: { height: 56, px: spacing.xxl, fontSize: font.size.heading3, radius: border.radius.
|
|
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({
|
|
@@ -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
|
+
}
|
|
@@ -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
|
+
};
|