@boostdev/design-system-components 2.4.1 → 2.5.0

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 (201) hide show
  1. package/AGENTS.md +4 -20
  2. package/README.md +3 -23
  3. package/dist/client.cjs +128 -62
  4. package/dist/client.css +537 -537
  5. package/dist/client.d.cts +11 -1
  6. package/dist/client.d.ts +11 -1
  7. package/dist/client.js +128 -62
  8. package/dist/index.cjs +128 -62
  9. package/dist/index.css +537 -537
  10. package/dist/index.d.cts +11 -1
  11. package/dist/index.d.ts +11 -1
  12. package/dist/index.js +128 -62
  13. package/dist/web-components/index.js +36 -4
  14. package/package.json +2 -10
  15. package/src/components/layout/Grid/Grid.mdx +20 -2
  16. package/src/components/layout/Grid/Grid.spec.tsx +206 -4
  17. package/src/components/layout/Grid/Grid.tsx +15 -1
  18. package/src/components/layout/Grid/masonry.ts +148 -10
  19. package/dist/native/index.cjs +0 -4337
  20. package/dist/native/index.d.cts +0 -574
  21. package/dist/native/index.d.ts +0 -574
  22. package/dist/native/index.js +0 -4421
  23. package/src/components/interaction/Button/Button.native.mdx +0 -91
  24. package/src/components/interaction/Button/Button.native.spec.tsx +0 -55
  25. package/src/components/interaction/Button/Button.native.stories.tsx +0 -141
  26. package/src/components/interaction/Button/Button.native.tsx +0 -105
  27. package/src/components/interaction/Dialog/Dialog.native.mdx +0 -61
  28. package/src/components/interaction/Dialog/Dialog.native.spec.tsx +0 -73
  29. package/src/components/interaction/Dialog/Dialog.native.stories.tsx +0 -53
  30. package/src/components/interaction/Dialog/Dialog.native.tsx +0 -128
  31. package/src/components/interaction/Drawer/Drawer.native.mdx +0 -58
  32. package/src/components/interaction/Drawer/Drawer.native.spec.tsx +0 -81
  33. package/src/components/interaction/Drawer/Drawer.native.stories.tsx +0 -33
  34. package/src/components/interaction/Drawer/Drawer.native.tsx +0 -175
  35. package/src/components/interaction/DropdownMenu/DropdownMenu.native.mdx +0 -74
  36. package/src/components/interaction/DropdownMenu/DropdownMenu.native.spec.tsx +0 -78
  37. package/src/components/interaction/DropdownMenu/DropdownMenu.native.stories.tsx +0 -51
  38. package/src/components/interaction/DropdownMenu/DropdownMenu.native.tsx +0 -254
  39. package/src/components/interaction/Popover/Popover.native.mdx +0 -61
  40. package/src/components/interaction/Popover/Popover.native.spec.tsx +0 -73
  41. package/src/components/interaction/Popover/Popover.native.stories.tsx +0 -44
  42. package/src/components/interaction/Popover/Popover.native.tsx +0 -87
  43. package/src/components/interaction/Rating/Rating.native.mdx +0 -55
  44. package/src/components/interaction/Rating/Rating.native.spec.tsx +0 -38
  45. package/src/components/interaction/Rating/Rating.native.stories.tsx +0 -37
  46. package/src/components/interaction/Rating/Rating.native.tsx +0 -50
  47. package/src/components/interaction/Toast/Toast.native.mdx +0 -81
  48. package/src/components/interaction/Toast/Toast.native.spec.tsx +0 -80
  49. package/src/components/interaction/Toast/Toast.native.stories.tsx +0 -47
  50. package/src/components/interaction/Toast/Toast.native.tsx +0 -202
  51. package/src/components/interaction/form/Checkbox/Checkbox.native.mdx +0 -74
  52. package/src/components/interaction/form/Checkbox/Checkbox.native.spec.tsx +0 -42
  53. package/src/components/interaction/form/Checkbox/Checkbox.native.stories.tsx +0 -34
  54. package/src/components/interaction/form/Checkbox/Checkbox.native.tsx +0 -80
  55. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.mdx +0 -78
  56. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.spec.tsx +0 -57
  57. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.stories.tsx +0 -96
  58. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.native.tsx +0 -61
  59. package/src/components/interaction/form/Combobox/Combobox.native.mdx +0 -86
  60. package/src/components/interaction/form/Combobox/Combobox.native.spec.tsx +0 -60
  61. package/src/components/interaction/form/Combobox/Combobox.native.stories.tsx +0 -99
  62. package/src/components/interaction/form/Combobox/Combobox.native.tsx +0 -211
  63. package/src/components/interaction/form/FileInput/FileInput.native.mdx +0 -86
  64. package/src/components/interaction/form/FileInput/FileInput.native.spec.tsx +0 -73
  65. package/src/components/interaction/form/FileInput/FileInput.native.stories.tsx +0 -69
  66. package/src/components/interaction/form/FileInput/FileInput.native.tsx +0 -129
  67. package/src/components/interaction/form/FormInput/FormInput.native.mdx +0 -81
  68. package/src/components/interaction/form/FormInput/FormInput.native.spec.tsx +0 -51
  69. package/src/components/interaction/form/FormInput/FormInput.native.stories.tsx +0 -54
  70. package/src/components/interaction/form/FormInput/FormInput.native.tsx +0 -122
  71. package/src/components/interaction/form/NumberInput/NumberInput.native.mdx +0 -87
  72. package/src/components/interaction/form/NumberInput/NumberInput.native.spec.tsx +0 -63
  73. package/src/components/interaction/form/NumberInput/NumberInput.native.stories.tsx +0 -63
  74. package/src/components/interaction/form/NumberInput/NumberInput.native.tsx +0 -198
  75. package/src/components/interaction/form/Radio/Radio.native.spec.tsx +0 -79
  76. package/src/components/interaction/form/Radio/Radio.native.stories.tsx +0 -44
  77. package/src/components/interaction/form/Radio/Radio.native.tsx +0 -79
  78. package/src/components/interaction/form/RadioGroup/RadioGroup.native.mdx +0 -79
  79. package/src/components/interaction/form/RadioGroup/RadioGroup.native.spec.tsx +0 -66
  80. package/src/components/interaction/form/RadioGroup/RadioGroup.native.stories.tsx +0 -96
  81. package/src/components/interaction/form/RadioGroup/RadioGroup.native.tsx +0 -61
  82. package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.mdx +0 -78
  83. package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.spec.tsx +0 -82
  84. package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.stories.tsx +0 -99
  85. package/src/components/interaction/form/SegmentedControl/SegmentedControl.native.tsx +0 -121
  86. package/src/components/interaction/form/Select/Select.native.mdx +0 -85
  87. package/src/components/interaction/form/Select/Select.native.spec.tsx +0 -64
  88. package/src/components/interaction/form/Select/Select.native.stories.tsx +0 -75
  89. package/src/components/interaction/form/Select/Select.native.tsx +0 -252
  90. package/src/components/interaction/form/Slider/Slider.native.mdx +0 -83
  91. package/src/components/interaction/form/Slider/Slider.native.spec.tsx +0 -51
  92. package/src/components/interaction/form/Slider/Slider.native.stories.tsx +0 -58
  93. package/src/components/interaction/form/Slider/Slider.native.tsx +0 -188
  94. package/src/components/interaction/form/Switch/Switch.native.spec.tsx +0 -60
  95. package/src/components/interaction/form/Switch/Switch.native.stories.tsx +0 -33
  96. package/src/components/interaction/form/Switch/Switch.native.tsx +0 -59
  97. package/src/components/interaction/form/Textarea/Textarea.native.mdx +0 -76
  98. package/src/components/interaction/form/Textarea/Textarea.native.spec.tsx +0 -50
  99. package/src/components/interaction/form/Textarea/Textarea.native.stories.tsx +0 -50
  100. package/src/components/interaction/form/Textarea/Textarea.native.tsx +0 -108
  101. package/src/components/interaction/form/atoms/Label.native.spec.tsx +0 -39
  102. package/src/components/interaction/form/atoms/Label.native.stories.tsx +0 -18
  103. package/src/components/interaction/form/atoms/Label.native.tsx +0 -29
  104. package/src/components/interaction/form/atoms/Message.native.spec.tsx +0 -30
  105. package/src/components/interaction/form/atoms/Message.native.stories.tsx +0 -22
  106. package/src/components/interaction/form/atoms/Message.native.tsx +0 -34
  107. package/src/components/layout/ButtonGroup/ButtonGroup.native.spec.tsx +0 -83
  108. package/src/components/layout/ButtonGroup/ButtonGroup.native.stories.tsx +0 -44
  109. package/src/components/layout/ButtonGroup/ButtonGroup.native.tsx +0 -44
  110. package/src/components/layout/Card/Card.native.spec.tsx +0 -102
  111. package/src/components/layout/Card/Card.native.stories.tsx +0 -51
  112. package/src/components/layout/Card/Card.native.tsx +0 -89
  113. package/src/components/layout/IconWrapper/IconWrapper.native.spec.tsx +0 -39
  114. package/src/components/layout/IconWrapper/IconWrapper.native.stories.tsx +0 -37
  115. package/src/components/layout/IconWrapper/IconWrapper.native.tsx +0 -20
  116. package/src/components/layout/SectionHeader/SectionHeader.native.spec.tsx +0 -51
  117. package/src/components/layout/SectionHeader/SectionHeader.native.stories.tsx +0 -36
  118. package/src/components/layout/SectionHeader/SectionHeader.native.tsx +0 -79
  119. package/src/components/ui/Accordion/Accordion.native.mdx +0 -65
  120. package/src/components/ui/Accordion/Accordion.native.spec.tsx +0 -69
  121. package/src/components/ui/Accordion/Accordion.native.stories.tsx +0 -52
  122. package/src/components/ui/Accordion/Accordion.native.tsx +0 -141
  123. package/src/components/ui/Alert/Alert.native.mdx +0 -62
  124. package/src/components/ui/Alert/Alert.native.spec.tsx +0 -76
  125. package/src/components/ui/Alert/Alert.native.stories.tsx +0 -41
  126. package/src/components/ui/Alert/Alert.native.tsx +0 -94
  127. package/src/components/ui/Avatar/Avatar.native.mdx +0 -50
  128. package/src/components/ui/Avatar/Avatar.native.spec.tsx +0 -47
  129. package/src/components/ui/Avatar/Avatar.native.stories.tsx +0 -32
  130. package/src/components/ui/Avatar/Avatar.native.tsx +0 -85
  131. package/src/components/ui/Badge/Badge.native.mdx +0 -54
  132. package/src/components/ui/Badge/Badge.native.spec.tsx +0 -45
  133. package/src/components/ui/Badge/Badge.native.stories.tsx +0 -34
  134. package/src/components/ui/Badge/Badge.native.tsx +0 -50
  135. package/src/components/ui/Breadcrumb/Breadcrumb.native.mdx +0 -52
  136. package/src/components/ui/Breadcrumb/Breadcrumb.native.spec.tsx +0 -49
  137. package/src/components/ui/Breadcrumb/Breadcrumb.native.stories.tsx +0 -46
  138. package/src/components/ui/Breadcrumb/Breadcrumb.native.tsx +0 -83
  139. package/src/components/ui/Calendar/Calendar.native.mdx +0 -58
  140. package/src/components/ui/Calendar/Calendar.native.spec.tsx +0 -92
  141. package/src/components/ui/Calendar/Calendar.native.stories.tsx +0 -45
  142. package/src/components/ui/Calendar/Calendar.native.tsx +0 -294
  143. package/src/components/ui/Carousel/Carousel.native.mdx +0 -55
  144. package/src/components/ui/Carousel/Carousel.native.spec.tsx +0 -55
  145. package/src/components/ui/Carousel/Carousel.native.stories.tsx +0 -59
  146. package/src/components/ui/Carousel/Carousel.native.tsx +0 -184
  147. package/src/components/ui/Collapsible/Collapsible.native.mdx +0 -53
  148. package/src/components/ui/Collapsible/Collapsible.native.spec.tsx +0 -68
  149. package/src/components/ui/Collapsible/Collapsible.native.stories.tsx +0 -48
  150. package/src/components/ui/Collapsible/Collapsible.native.tsx +0 -139
  151. package/src/components/ui/DescriptionList/DescriptionList.native.mdx +0 -50
  152. package/src/components/ui/DescriptionList/DescriptionList.native.spec.tsx +0 -37
  153. package/src/components/ui/DescriptionList/DescriptionList.native.stories.tsx +0 -48
  154. package/src/components/ui/DescriptionList/DescriptionList.native.tsx +0 -54
  155. package/src/components/ui/Link/Link.native.mdx +0 -53
  156. package/src/components/ui/Link/Link.native.spec.tsx +0 -35
  157. package/src/components/ui/Link/Link.native.stories.tsx +0 -46
  158. package/src/components/ui/Link/Link.native.tsx +0 -48
  159. package/src/components/ui/Loading/Loading.native.spec.tsx +0 -24
  160. package/src/components/ui/Loading/Loading.native.stories.tsx +0 -31
  161. package/src/components/ui/Loading/Loading.native.tsx +0 -29
  162. package/src/components/ui/NotificationBanner/NotificationBanner.native.spec.tsx +0 -85
  163. package/src/components/ui/NotificationBanner/NotificationBanner.native.stories.tsx +0 -37
  164. package/src/components/ui/NotificationBanner/NotificationBanner.native.tsx +0 -76
  165. package/src/components/ui/Pagination/Pagination.native.mdx +0 -62
  166. package/src/components/ui/Pagination/Pagination.native.spec.tsx +0 -86
  167. package/src/components/ui/Pagination/Pagination.native.stories.tsx +0 -47
  168. package/src/components/ui/Pagination/Pagination.native.tsx +0 -157
  169. package/src/components/ui/Progress/Progress.native.spec.tsx +0 -56
  170. package/src/components/ui/Progress/Progress.native.stories.tsx +0 -34
  171. package/src/components/ui/Progress/Progress.native.tsx +0 -84
  172. package/src/components/ui/ProgressCircle/ProgressCircle.native.mdx +0 -63
  173. package/src/components/ui/ProgressCircle/ProgressCircle.native.spec.tsx +0 -39
  174. package/src/components/ui/ProgressCircle/ProgressCircle.native.stories.tsx +0 -36
  175. package/src/components/ui/ProgressCircle/ProgressCircle.native.tsx +0 -176
  176. package/src/components/ui/Separator/Separator.native.spec.tsx +0 -39
  177. package/src/components/ui/Separator/Separator.native.stories.tsx +0 -38
  178. package/src/components/ui/Separator/Separator.native.tsx +0 -32
  179. package/src/components/ui/Skeleton/Skeleton.native.spec.tsx +0 -39
  180. package/src/components/ui/Skeleton/Skeleton.native.stories.tsx +0 -31
  181. package/src/components/ui/Skeleton/Skeleton.native.tsx +0 -42
  182. package/src/components/ui/Table/Table.native.mdx +0 -72
  183. package/src/components/ui/Table/Table.native.spec.tsx +0 -83
  184. package/src/components/ui/Table/Table.native.stories.tsx +0 -55
  185. package/src/components/ui/Table/Table.native.tsx +0 -196
  186. package/src/components/ui/Tabs/Tabs.native.mdx +0 -65
  187. package/src/components/ui/Tabs/Tabs.native.spec.tsx +0 -65
  188. package/src/components/ui/Tabs/Tabs.native.stories.tsx +0 -57
  189. package/src/components/ui/Tabs/Tabs.native.tsx +0 -133
  190. package/src/components/ui/Tooltip/Tooltip.native.mdx +0 -47
  191. package/src/components/ui/Tooltip/Tooltip.native.spec.tsx +0 -37
  192. package/src/components/ui/Tooltip/Tooltip.native.stories.tsx +0 -38
  193. package/src/components/ui/Tooltip/Tooltip.native.tsx +0 -57
  194. package/src/components/ui/Typography/Typography.native.mdx +0 -56
  195. package/src/components/ui/Typography/Typography.native.spec.tsx +0 -49
  196. package/src/components/ui/Typography/Typography.native.stories.tsx +0 -36
  197. package/src/components/ui/Typography/Typography.native.tsx +0 -65
  198. package/src/native/ThemeContext.tsx +0 -28
  199. package/src/native/tokens.ts +0 -13
  200. package/src/native.ts +0 -76
  201. package/src/stories/ReactNative.mdx +0 -156
@@ -1,72 +0,0 @@
1
- import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
- import * as Stories from './Table.native.stories';
3
-
4
- <Meta of={Stories} />
5
-
6
- # Table (React Native)
7
-
8
- React Native table component for displaying structured data in rows and columns. Supports optional sortable columns, captions, and horizontal scrolling for wide tables. Alternating row backgrounds improve readability.
9
-
10
- > **Status: alpha** -- API may change before stable release.
11
-
12
- ## Usage
13
-
14
- ```tsx
15
- import { Table } from '@boostdev/design-system-components';
16
-
17
- <Table
18
- columns={[
19
- { key: 'name', header: 'Name' },
20
- { key: 'email', header: 'Email' },
21
- ]}
22
- rows={[
23
- { name: 'Alice', email: 'alice@example.com' },
24
- { name: 'Bob', email: 'bob@example.com' },
25
- ]}
26
- />
27
- ```
28
-
29
- ## Examples
30
-
31
- ### Default
32
- <Canvas of={Stories.Default} />
33
-
34
- ### With caption
35
- <Canvas of={Stories.WithCaption} />
36
-
37
- ### Sortable columns
38
- <Canvas of={Stories.Sortable} />
39
-
40
- ### Empty table
41
- <Canvas of={Stories.Empty} />
42
-
43
- ## Props
44
-
45
- <ArgTypes of={Stories} />
46
-
47
- ## Props reference
48
-
49
- | Prop | Type | Default | Description |
50
- |------|------|---------|-------------|
51
- | `columns` | `TableColumn<Row>[]` | -- | Column definitions |
52
- | `rows` | `Row[]` | -- | Data rows |
53
- | `caption` | `string` | -- | Optional table caption |
54
- | `sortKey` | `string` | -- | Currently sorted column key |
55
- | `sortDirection` | `"asc" \| "desc"` | -- | Current sort direction |
56
- | `onSort` | `(key: string, direction: "asc" \| "desc") => void` | -- | Called when a sortable header is pressed |
57
- | `style` | `StyleProp<ViewStyle>` | -- | Custom style for the scroll wrapper |
58
-
59
- ### TableColumn
60
-
61
- | Prop | Type | Description |
62
- |------|------|-------------|
63
- | `key` | `string` | Column key matching row property |
64
- | `header` | `ReactNode` | Header cell content |
65
- | `sortable` | `boolean` | Enables sort button on header |
66
- | `render` | `(row: Row) => ReactNode` | Custom cell renderer |
67
-
68
- ## Accessibility
69
-
70
- - Sortable headers have `button` role with descriptive labels
71
- - Sort indicators are hidden from the accessibility tree
72
- - Table scrolls horizontally when content exceeds the viewport width
@@ -1,83 +0,0 @@
1
- import { render, screen, fireEvent } from '@testing-library/react';
2
- import { ThemeProvider } from '../../../native/ThemeContext';
3
- import { Table } from './Table.native';
4
-
5
- function renderWithTheme(ui: React.ReactElement) {
6
- return render(<ThemeProvider>{ui}</ThemeProvider>);
7
- }
8
-
9
- const columns = [
10
- { key: 'name', header: 'Name' },
11
- { key: 'age', header: 'Age' },
12
- ];
13
-
14
- const rows = [
15
- { name: 'Alice', age: 29 },
16
- { name: 'Bob', age: 34 },
17
- ];
18
-
19
- describe('Table (native)', () => {
20
- it('renders column headers', () => {
21
- renderWithTheme(<Table columns={columns} rows={rows} />);
22
- expect(screen.getByText('Name')).toBeInTheDocument();
23
- expect(screen.getByText('Age')).toBeInTheDocument();
24
- });
25
-
26
- it('renders row data', () => {
27
- renderWithTheme(<Table columns={columns} rows={rows} />);
28
- expect(screen.getByText('Alice')).toBeInTheDocument();
29
- expect(screen.getByText('29')).toBeInTheDocument();
30
- expect(screen.getByText('Bob')).toBeInTheDocument();
31
- expect(screen.getByText('34')).toBeInTheDocument();
32
- });
33
-
34
- it('renders a caption when provided', () => {
35
- renderWithTheme(<Table columns={columns} rows={rows} caption="Team" />);
36
- expect(screen.getByText('Team')).toBeInTheDocument();
37
- });
38
-
39
- it('renders an empty table without errors', () => {
40
- renderWithTheme(<Table columns={columns} rows={[]} />);
41
- expect(screen.getByText('Name')).toBeInTheDocument();
42
- expect(screen.getByText('Age')).toBeInTheDocument();
43
- });
44
-
45
- it('calls onSort when a sortable column header is pressed', () => {
46
- const onSort = vi.fn();
47
- const sortableColumns = [
48
- { key: 'name', header: 'Name', sortable: true },
49
- { key: 'age', header: 'Age' },
50
- ];
51
- renderWithTheme(
52
- <Table columns={sortableColumns} rows={rows} onSort={onSort} />,
53
- );
54
- fireEvent.click(screen.getByRole('button', { name: /Sort by Name/ }));
55
- expect(onSort).toHaveBeenCalledWith('name', 'asc');
56
- });
57
-
58
- it('toggles sort direction on subsequent presses', () => {
59
- const onSort = vi.fn();
60
- const sortableColumns = [
61
- { key: 'name', header: 'Name', sortable: true },
62
- ];
63
- renderWithTheme(
64
- <Table
65
- columns={sortableColumns}
66
- rows={rows}
67
- sortKey="name"
68
- sortDirection="asc"
69
- onSort={onSort}
70
- />,
71
- );
72
- fireEvent.click(screen.getByRole('button', { name: /Sort by Name/ }));
73
- expect(onSort).toHaveBeenCalledWith('name', 'desc');
74
- });
75
-
76
- it('uses custom render function for cells', () => {
77
- const customColumns = [
78
- { key: 'name', header: 'Name', render: (row: { name: string }) => `Mr. ${row.name}` },
79
- ];
80
- renderWithTheme(<Table columns={customColumns} rows={[{ name: 'Alice' }]} />);
81
- expect(screen.getByText('Mr. Alice')).toBeInTheDocument();
82
- });
83
- });
@@ -1,55 +0,0 @@
1
- import React from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react';
3
- import { ThemeProvider } from '../../../native/ThemeContext';
4
- import { Table } from './Table.native';
5
-
6
- interface Person { name: string; role: string; age: number }
7
-
8
- const meta = {
9
- tags: ['!stable', 'alpha'],
10
- title: 'React Native/UI/Table',
11
- component: Table<Person>,
12
- decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
13
- parameters: { layout: 'padded' },
14
- } satisfies Meta<typeof Table<Person>>;
15
-
16
- export default meta;
17
- type Story = StoryObj<typeof meta>;
18
-
19
- const columns = [
20
- { key: 'name', header: 'Name' },
21
- { key: 'role', header: 'Role' },
22
- { key: 'age', header: 'Age' },
23
- ];
24
-
25
- const rows: Person[] = [
26
- { name: 'Alice Johnson', role: 'Engineer', age: 29 },
27
- { name: 'Bob Smith', role: 'Designer', age: 34 },
28
- { name: 'Carol White', role: 'Manager', age: 41 },
29
- { name: 'Dave Brown', role: 'Analyst', age: 26 },
30
- ];
31
-
32
- export const Default: Story = {
33
- args: { columns, rows },
34
- };
35
-
36
- export const WithCaption: Story = {
37
- args: { columns, rows, caption: 'Team Members' },
38
- };
39
-
40
- export const Sortable: Story = {
41
- args: {
42
- columns: [
43
- { key: 'name', header: 'Name', sortable: true },
44
- { key: 'role', header: 'Role' },
45
- { key: 'age', header: 'Age', sortable: true },
46
- ],
47
- rows,
48
- sortKey: 'name',
49
- sortDirection: 'asc',
50
- },
51
- };
52
-
53
- export const Empty: Story = {
54
- args: { columns, rows: [] },
55
- };
@@ -1,196 +0,0 @@
1
- import { ReactNode } from 'react';
2
- import {
3
- StyleSheet,
4
- View,
5
- Text,
6
- ScrollView,
7
- Pressable,
8
- StyleProp,
9
- ViewStyle,
10
- } from 'react-native';
11
- import { spacing, font, border, lineHeight } from '../../../native/tokens';
12
- import { useTheme } from '../../../native/ThemeContext';
13
-
14
- export interface TableColumn<Row> {
15
- key: string;
16
- header: ReactNode;
17
- sortable?: boolean;
18
- render?: (row: Row) => ReactNode;
19
- }
20
-
21
- interface TableProps<Row extends Record<string, unknown>> {
22
- columns: TableColumn<Row>[];
23
- rows: Row[];
24
- caption?: string;
25
- sortKey?: string;
26
- sortDirection?: 'asc' | 'desc';
27
- onSort?: (key: string, direction: 'asc' | 'desc') => void;
28
- style?: StyleProp<ViewStyle>;
29
- }
30
-
31
- const styles = StyleSheet.create({
32
- wrapper: {
33
- borderRadius: border.radius.s,
34
- overflow: 'hidden',
35
- },
36
- caption: {
37
- fontSize: font.size.bodyS,
38
- fontFamily: font.family.body,
39
- fontWeight: font.weight.semibold,
40
- lineHeight: lineHeight.bodyS,
41
- paddingVertical: spacing.xs,
42
- paddingHorizontal: spacing.m,
43
- },
44
- headerRow: {
45
- flexDirection: 'row',
46
- },
47
- headerCell: {
48
- flex: 1,
49
- minWidth: 100,
50
- paddingVertical: spacing.s,
51
- paddingHorizontal: spacing.m,
52
- flexDirection: 'row',
53
- alignItems: 'center',
54
- gap: spacing.xxs,
55
- },
56
- headerText: {
57
- fontSize: font.size.bodyS,
58
- fontFamily: font.family.body,
59
- fontWeight: font.weight.semibold,
60
- lineHeight: lineHeight.bodyS,
61
- },
62
- sortIndicator: {
63
- fontSize: font.size.bodyS,
64
- },
65
- row: {
66
- flexDirection: 'row',
67
- borderTopWidth: 1,
68
- minHeight: 44,
69
- },
70
- cell: {
71
- flex: 1,
72
- minWidth: 100,
73
- paddingVertical: spacing.s,
74
- paddingHorizontal: spacing.m,
75
- justifyContent: 'center',
76
- },
77
- cellText: {
78
- fontSize: font.size.body,
79
- fontFamily: font.family.body,
80
- fontWeight: font.weight.body,
81
- lineHeight: lineHeight.body,
82
- },
83
- pressed: {
84
- opacity: 0.75,
85
- },
86
- });
87
-
88
- export function Table<Row extends Record<string, unknown>>({
89
- columns,
90
- rows,
91
- caption,
92
- sortKey,
93
- sortDirection,
94
- onSort,
95
- style,
96
- }: Readonly<TableProps<Row>>) {
97
- const { colors } = useTheme();
98
-
99
- const handleSort = (key: string) => {
100
- if (!onSort) return;
101
- const nextDirection = sortKey === key && sortDirection === 'asc' ? 'desc' : 'asc';
102
- onSort(key, nextDirection);
103
- };
104
-
105
- const getSortLabel = (col: TableColumn<Row>): string => {
106
- const base = `Sort by ${String(col.header)}`;
107
- if (sortKey !== col.key) return base;
108
- return `${base}, currently ${sortDirection === 'asc' ? 'ascending' : 'descending'}`;
109
- };
110
-
111
- return (
112
- <ScrollView
113
- horizontal
114
- showsHorizontalScrollIndicator={true}
115
- style={[styles.wrapper, { backgroundColor: colors.colorBg }, style]}
116
- >
117
- <View>
118
- {caption && (
119
- <Text style={[styles.caption, { color: colors.colorOnBgSubtle }]}>
120
- {caption}
121
- </Text>
122
- )}
123
-
124
- <View style={[styles.headerRow, { backgroundColor: colors.colorBgSubtle }]}>
125
- {columns.map(col =>
126
- col.sortable ? (
127
- <Pressable
128
- key={col.key}
129
- onPress={() => handleSort(col.key)}
130
- accessibilityRole="button"
131
- accessibilityLabel={getSortLabel(col)}
132
- style={({ pressed }) => [styles.headerCell, pressed && styles.pressed]}
133
- >
134
- <Text style={[styles.headerText, { color: colors.colorOnBg }]}>
135
- {col.header}
136
- </Text>
137
- <Text
138
- style={[
139
- styles.sortIndicator,
140
- {
141
- color: sortKey === col.key
142
- ? colors.colorInteractive
143
- : colors.colorOnBgSubtle,
144
- },
145
- ]}
146
- importantForAccessibility="no"
147
- accessibilityElementsHidden={true}
148
- >
149
- {sortKey === col.key
150
- ? sortDirection === 'asc' ? '▲' : '▼'
151
- : '▽'}
152
- </Text>
153
- </Pressable>
154
- ) : (
155
- <View key={col.key} style={styles.headerCell}>
156
- <Text style={[styles.headerText, { color: colors.colorOnBg }]}>
157
- {col.header}
158
- </Text>
159
- </View>
160
- ),
161
- )}
162
- </View>
163
-
164
- {rows.map((row, rowIndex) => (
165
- <View
166
- key={rowIndex}
167
- style={[
168
- styles.row,
169
- { borderTopColor: colors.colorBgSubtle },
170
- rowIndex % 2 === 1 && { backgroundColor: colors.colorBgSubtle },
171
- ]}
172
- accessibilityRole="none"
173
- >
174
- {columns.map(col => (
175
- <View key={col.key} style={styles.cell}>
176
- {col.render ? (
177
- typeof col.render(row) === 'string' ? (
178
- <Text style={[styles.cellText, { color: colors.colorOnBg }]}>
179
- {col.render(row)}
180
- </Text>
181
- ) : (
182
- col.render(row)
183
- )
184
- ) : (
185
- <Text style={[styles.cellText, { color: colors.colorOnBg }]}>
186
- {String(row[col.key] ?? '')}
187
- </Text>
188
- )}
189
- </View>
190
- ))}
191
- </View>
192
- ))}
193
- </View>
194
- </ScrollView>
195
- );
196
- }
@@ -1,65 +0,0 @@
1
- import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
- import * as Stories from './Tabs.native.stories';
3
-
4
- <Meta of={Stories} />
5
-
6
- # Tabs (React Native)
7
-
8
- React Native tabs component with a horizontally scrollable tab bar and content panels. Active tab is indicated with an underline in the interactive color.
9
-
10
- > **Status: alpha** -- API may change before stable release.
11
-
12
- ## Usage
13
-
14
- ```tsx
15
- import { Tabs } from '@boostdev/design-system-components';
16
-
17
- <Tabs
18
- tabs={[
19
- { id: 'one', label: 'Tab One', content: <Text>Content one</Text> },
20
- { id: 'two', label: 'Tab Two', content: <Text>Content two</Text> },
21
- ]}
22
- />
23
- ```
24
-
25
- ## Examples
26
-
27
- ### Default
28
- <Canvas of={Stories.Default} />
29
-
30
- ### With default tab
31
- <Canvas of={Stories.WithDefaultTab} />
32
-
33
- ### With disabled tab
34
- <Canvas of={Stories.WithDisabledTab} />
35
-
36
- ### Many tabs (scrollable)
37
- <Canvas of={Stories.ManyTabs} />
38
-
39
- ## Props
40
-
41
- <ArgTypes of={Stories} />
42
-
43
- ## Props reference
44
-
45
- | Prop | Type | Default | Description |
46
- |------|------|---------|-------------|
47
- | `tabs` | `TabItem[]` | -- | Array of tab definitions |
48
- | `defaultTab` | `string` | First tab ID | ID of initially active tab |
49
- | `style` | `StyleProp<ViewStyle>` | -- | Custom style for the container |
50
-
51
- ### TabItem
52
-
53
- | Prop | Type | Description |
54
- |------|------|-------------|
55
- | `id` | `string` | Unique identifier |
56
- | `label` | `ReactNode` | Tab button label |
57
- | `content` | `ReactNode` | Panel content |
58
- | `disabled` | `boolean` | Prevents selection |
59
-
60
- ## Accessibility
61
-
62
- - Tab bar uses `tabbar` accessibility role
63
- - Each tab button uses `tab` role with `selected` state
64
- - Content area uses `tabpanel` role
65
- - Disabled tabs have `disabled` accessibility state and reduced opacity
@@ -1,65 +0,0 @@
1
- import { render, screen, fireEvent } from '@testing-library/react';
2
- import { ThemeProvider } from '../../../native/ThemeContext';
3
- import { Tabs } from './Tabs.native';
4
-
5
- function renderWithTheme(ui: React.ReactElement) {
6
- return render(<ThemeProvider>{ui}</ThemeProvider>);
7
- }
8
-
9
- const tabs = [
10
- { id: 'one', label: 'Tab One', content: <span>Content One</span> },
11
- { id: 'two', label: 'Tab Two', content: <span>Content Two</span> },
12
- { id: 'three', label: 'Tab Three', content: <span>Content Three</span> },
13
- ];
14
-
15
- describe('Tabs (native)', () => {
16
- it('renders all tab labels', () => {
17
- renderWithTheme(<Tabs tabs={tabs} />);
18
- expect(screen.getByText('Tab One')).toBeInTheDocument();
19
- expect(screen.getByText('Tab Two')).toBeInTheDocument();
20
- expect(screen.getByText('Tab Three')).toBeInTheDocument();
21
- });
22
-
23
- it('shows first tab content by default', () => {
24
- renderWithTheme(<Tabs tabs={tabs} />);
25
- expect(screen.getByText('Content One')).toBeInTheDocument();
26
- expect(screen.queryByText('Content Two')).not.toBeInTheDocument();
27
- });
28
-
29
- it('respects defaultTab prop', () => {
30
- renderWithTheme(<Tabs tabs={tabs} defaultTab="two" />);
31
- expect(screen.queryByText('Content One')).not.toBeInTheDocument();
32
- expect(screen.getByText('Content Two')).toBeInTheDocument();
33
- });
34
-
35
- it('switches content when a tab is pressed', () => {
36
- renderWithTheme(<Tabs tabs={tabs} />);
37
- fireEvent.click(screen.getByText('Tab Two'));
38
- expect(screen.queryByText('Content One')).not.toBeInTheDocument();
39
- expect(screen.getByText('Content Two')).toBeInTheDocument();
40
- });
41
-
42
- it('renders tab roles for all tabs', () => {
43
- renderWithTheme(<Tabs tabs={tabs} />);
44
- expect(screen.getByRole('tab', { name: 'Tab One' })).toBeInTheDocument();
45
- expect(screen.getByRole('tab', { name: 'Tab Two' })).toBeInTheDocument();
46
- expect(screen.getByRole('tab', { name: 'Tab Three' })).toBeInTheDocument();
47
- });
48
-
49
- it('disables tabs with disabled flag', () => {
50
- const withDisabled = [
51
- ...tabs,
52
- { id: 'four', label: 'Disabled Tab', content: <span>Nope</span>, disabled: true },
53
- ];
54
- renderWithTheme(<Tabs tabs={withDisabled} />);
55
- expect(screen.getByRole('tab', { name: 'Disabled Tab' })).toHaveAttribute(
56
- 'aria-disabled',
57
- 'true',
58
- );
59
- });
60
-
61
- it('renders tabpanel role for active content', () => {
62
- renderWithTheme(<Tabs tabs={tabs} />);
63
- expect(screen.getByRole('tabpanel')).toBeInTheDocument();
64
- });
65
- });
@@ -1,57 +0,0 @@
1
- import React from 'react';
2
- import { Text } from 'react-native';
3
- import type { Meta, StoryObj } from '@storybook/react';
4
- import { ThemeProvider } from '../../../native/ThemeContext';
5
- import { Tabs } from './Tabs.native';
6
-
7
- const meta = {
8
- tags: ['!stable', 'alpha'],
9
- title: 'React Native/UI/Tabs',
10
- component: Tabs,
11
- decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
12
- parameters: { layout: 'padded' },
13
- } satisfies Meta<typeof Tabs>;
14
-
15
- export default meta;
16
- type Story = StoryObj<typeof meta>;
17
-
18
- const sampleTabs = [
19
- { id: 'overview', label: 'Overview', content: <Text>Overview content goes here.</Text> },
20
- { id: 'features', label: 'Features', content: <Text>Feature list and descriptions.</Text> },
21
- { id: 'pricing', label: 'Pricing', content: <Text>Pricing tiers and plan details.</Text> },
22
- ];
23
-
24
- export const Default: Story = {
25
- args: {
26
- tabs: sampleTabs,
27
- },
28
- };
29
-
30
- export const WithDefaultTab: Story = {
31
- args: {
32
- tabs: sampleTabs,
33
- defaultTab: 'features',
34
- },
35
- };
36
-
37
- export const WithDisabledTab: Story = {
38
- args: {
39
- tabs: [
40
- ...sampleTabs,
41
- { id: 'admin', label: 'Admin', content: <Text>Admin panel.</Text>, disabled: true },
42
- ],
43
- },
44
- };
45
-
46
- export const ManyTabs: Story = {
47
- args: {
48
- tabs: [
49
- { id: 'home', label: 'Home', content: <Text>Home content</Text> },
50
- { id: 'discover', label: 'Discover', content: <Text>Discover content</Text> },
51
- { id: 'trending', label: 'Trending', content: <Text>Trending content</Text> },
52
- { id: 'saved', label: 'Saved', content: <Text>Saved content</Text> },
53
- { id: 'settings', label: 'Settings', content: <Text>Settings content</Text> },
54
- { id: 'profile', label: 'Profile', content: <Text>Profile content</Text> },
55
- ],
56
- },
57
- };