@boostdev/design-system-react-native 0.1.0 → 0.2.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 (191) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/LICENSE +15 -0
  3. package/README.md +104 -2
  4. package/dist/index.cjs +100 -57
  5. package/dist/index.d.cts +61 -6
  6. package/dist/index.d.ts +61 -6
  7. package/dist/index.js +100 -57
  8. package/package.json +30 -10
  9. package/src/components/interaction/Button/Button.mdx +91 -0
  10. package/src/components/interaction/Button/Button.spec.tsx +55 -0
  11. package/src/components/interaction/Button/Button.stories.tsx +141 -0
  12. package/src/components/interaction/Button/Button.tsx +105 -0
  13. package/src/components/interaction/Dialog/Dialog.mdx +61 -0
  14. package/src/components/interaction/Dialog/Dialog.spec.tsx +73 -0
  15. package/src/components/interaction/Dialog/Dialog.stories.tsx +53 -0
  16. package/src/components/interaction/Dialog/Dialog.tsx +128 -0
  17. package/src/components/interaction/DropdownMenu/DropdownMenu.mdx +74 -0
  18. package/src/components/interaction/DropdownMenu/DropdownMenu.spec.tsx +78 -0
  19. package/src/components/interaction/DropdownMenu/DropdownMenu.stories.tsx +51 -0
  20. package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +254 -0
  21. package/src/components/interaction/Popover/Popover.mdx +61 -0
  22. package/src/components/interaction/Popover/Popover.spec.tsx +73 -0
  23. package/src/components/interaction/Popover/Popover.stories.tsx +44 -0
  24. package/src/components/interaction/Popover/Popover.tsx +87 -0
  25. package/src/components/interaction/Rating/Rating.mdx +55 -0
  26. package/src/components/interaction/Rating/Rating.spec.tsx +38 -0
  27. package/src/components/interaction/Rating/Rating.stories.tsx +37 -0
  28. package/src/components/interaction/Rating/Rating.tsx +50 -0
  29. package/src/components/interaction/SideSheet/SideSheet.mdx +60 -0
  30. package/src/components/interaction/SideSheet/SideSheet.spec.tsx +80 -0
  31. package/src/components/interaction/SideSheet/SideSheet.stories.tsx +33 -0
  32. package/src/components/interaction/SideSheet/SideSheet.tsx +175 -0
  33. package/src/components/interaction/Toast/Toast.mdx +81 -0
  34. package/src/components/interaction/Toast/Toast.spec.tsx +80 -0
  35. package/src/components/interaction/Toast/Toast.stories.tsx +47 -0
  36. package/src/components/interaction/Toast/Toast.tsx +202 -0
  37. package/src/components/interaction/form/Checkbox/Checkbox.mdx +74 -0
  38. package/src/components/interaction/form/Checkbox/Checkbox.spec.tsx +42 -0
  39. package/src/components/interaction/form/Checkbox/Checkbox.stories.tsx +34 -0
  40. package/src/components/interaction/form/Checkbox/Checkbox.tsx +80 -0
  41. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.mdx +78 -0
  42. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.spec.tsx +57 -0
  43. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.stories.tsx +96 -0
  44. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +61 -0
  45. package/src/components/interaction/form/Combobox/Combobox.mdx +86 -0
  46. package/src/components/interaction/form/Combobox/Combobox.spec.tsx +60 -0
  47. package/src/components/interaction/form/Combobox/Combobox.stories.tsx +99 -0
  48. package/src/components/interaction/form/Combobox/Combobox.tsx +211 -0
  49. package/src/components/interaction/form/FileInput/FileInput.mdx +86 -0
  50. package/src/components/interaction/form/FileInput/FileInput.spec.tsx +73 -0
  51. package/src/components/interaction/form/FileInput/FileInput.stories.tsx +69 -0
  52. package/src/components/interaction/form/FileInput/FileInput.tsx +129 -0
  53. package/src/components/interaction/form/FormInput/FormInput.mdx +81 -0
  54. package/src/components/interaction/form/FormInput/FormInput.spec.tsx +51 -0
  55. package/src/components/interaction/form/FormInput/FormInput.stories.tsx +54 -0
  56. package/src/components/interaction/form/FormInput/FormInput.tsx +122 -0
  57. package/src/components/interaction/form/NumberInput/NumberInput.mdx +87 -0
  58. package/src/components/interaction/form/NumberInput/NumberInput.spec.tsx +63 -0
  59. package/src/components/interaction/form/NumberInput/NumberInput.stories.tsx +63 -0
  60. package/src/components/interaction/form/NumberInput/NumberInput.tsx +198 -0
  61. package/src/components/interaction/form/Radio/Radio.spec.tsx +79 -0
  62. package/src/components/interaction/form/Radio/Radio.stories.tsx +44 -0
  63. package/src/components/interaction/form/Radio/Radio.tsx +79 -0
  64. package/src/components/interaction/form/RadioGroup/RadioGroup.mdx +79 -0
  65. package/src/components/interaction/form/RadioGroup/RadioGroup.spec.tsx +66 -0
  66. package/src/components/interaction/form/RadioGroup/RadioGroup.stories.tsx +96 -0
  67. package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +61 -0
  68. package/src/components/interaction/form/SegmentedControl/SegmentedControl.mdx +78 -0
  69. package/src/components/interaction/form/SegmentedControl/SegmentedControl.spec.tsx +82 -0
  70. package/src/components/interaction/form/SegmentedControl/SegmentedControl.stories.tsx +99 -0
  71. package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +121 -0
  72. package/src/components/interaction/form/Select/Select.mdx +85 -0
  73. package/src/components/interaction/form/Select/Select.spec.tsx +64 -0
  74. package/src/components/interaction/form/Select/Select.stories.tsx +75 -0
  75. package/src/components/interaction/form/Select/Select.tsx +252 -0
  76. package/src/components/interaction/form/Slider/Slider.mdx +83 -0
  77. package/src/components/interaction/form/Slider/Slider.spec.tsx +51 -0
  78. package/src/components/interaction/form/Slider/Slider.stories.tsx +58 -0
  79. package/src/components/interaction/form/Slider/Slider.tsx +188 -0
  80. package/src/components/interaction/form/Switch/Switch.spec.tsx +60 -0
  81. package/src/components/interaction/form/Switch/Switch.stories.tsx +33 -0
  82. package/src/components/interaction/form/Switch/Switch.tsx +59 -0
  83. package/src/components/interaction/form/Textarea/Textarea.mdx +76 -0
  84. package/src/components/interaction/form/Textarea/Textarea.spec.tsx +50 -0
  85. package/src/components/interaction/form/Textarea/Textarea.stories.tsx +50 -0
  86. package/src/components/interaction/form/Textarea/Textarea.tsx +108 -0
  87. package/src/components/interaction/form/atoms/Label.spec.tsx +39 -0
  88. package/src/components/interaction/form/atoms/Label.stories.tsx +18 -0
  89. package/src/components/interaction/form/atoms/Label.tsx +29 -0
  90. package/src/components/interaction/form/atoms/Message.spec.tsx +30 -0
  91. package/src/components/interaction/form/atoms/Message.stories.tsx +22 -0
  92. package/src/components/interaction/form/atoms/Message.tsx +34 -0
  93. package/src/components/layout/ButtonGroup/ButtonGroup.spec.tsx +83 -0
  94. package/src/components/layout/ButtonGroup/ButtonGroup.stories.tsx +44 -0
  95. package/src/components/layout/ButtonGroup/ButtonGroup.tsx +44 -0
  96. package/src/components/layout/Card/Card.spec.tsx +102 -0
  97. package/src/components/layout/Card/Card.stories.tsx +51 -0
  98. package/src/components/layout/Card/Card.tsx +89 -0
  99. package/src/components/layout/IconWrapper/IconWrapper.spec.tsx +39 -0
  100. package/src/components/layout/IconWrapper/IconWrapper.stories.tsx +37 -0
  101. package/src/components/layout/IconWrapper/IconWrapper.tsx +20 -0
  102. package/src/components/layout/SectionHeader/SectionHeader.spec.tsx +51 -0
  103. package/src/components/layout/SectionHeader/SectionHeader.stories.tsx +36 -0
  104. package/src/components/layout/SectionHeader/SectionHeader.tsx +79 -0
  105. package/src/components/ui/Accordion/Accordion.mdx +65 -0
  106. package/src/components/ui/Accordion/Accordion.spec.tsx +69 -0
  107. package/src/components/ui/Accordion/Accordion.stories.tsx +52 -0
  108. package/src/components/ui/Accordion/Accordion.tsx +141 -0
  109. package/src/components/ui/Alert/Alert.mdx +62 -0
  110. package/src/components/ui/Alert/Alert.spec.tsx +76 -0
  111. package/src/components/ui/Alert/Alert.stories.tsx +41 -0
  112. package/src/components/ui/Alert/Alert.tsx +94 -0
  113. package/src/components/ui/Avatar/Avatar.mdx +50 -0
  114. package/src/components/ui/Avatar/Avatar.spec.tsx +47 -0
  115. package/src/components/ui/Avatar/Avatar.stories.tsx +32 -0
  116. package/src/components/ui/Avatar/Avatar.tsx +85 -0
  117. package/src/components/ui/Badge/Badge.mdx +54 -0
  118. package/src/components/ui/Badge/Badge.spec.tsx +45 -0
  119. package/src/components/ui/Badge/Badge.stories.tsx +34 -0
  120. package/src/components/ui/Badge/Badge.tsx +50 -0
  121. package/src/components/ui/Breadcrumb/Breadcrumb.mdx +52 -0
  122. package/src/components/ui/Breadcrumb/Breadcrumb.spec.tsx +49 -0
  123. package/src/components/ui/Breadcrumb/Breadcrumb.stories.tsx +46 -0
  124. package/src/components/ui/Breadcrumb/Breadcrumb.tsx +83 -0
  125. package/src/components/ui/Calendar/Calendar.mdx +58 -0
  126. package/src/components/ui/Calendar/Calendar.spec.tsx +92 -0
  127. package/src/components/ui/Calendar/Calendar.stories.tsx +45 -0
  128. package/src/components/ui/Calendar/Calendar.tsx +294 -0
  129. package/src/components/ui/Carousel/Carousel.mdx +55 -0
  130. package/src/components/ui/Carousel/Carousel.spec.tsx +55 -0
  131. package/src/components/ui/Carousel/Carousel.stories.tsx +59 -0
  132. package/src/components/ui/Carousel/Carousel.tsx +184 -0
  133. package/src/components/ui/Collapsible/Collapsible.mdx +53 -0
  134. package/src/components/ui/Collapsible/Collapsible.spec.tsx +68 -0
  135. package/src/components/ui/Collapsible/Collapsible.stories.tsx +48 -0
  136. package/src/components/ui/Collapsible/Collapsible.tsx +139 -0
  137. package/src/components/ui/DescriptionList/DescriptionList.mdx +50 -0
  138. package/src/components/ui/DescriptionList/DescriptionList.spec.tsx +37 -0
  139. package/src/components/ui/DescriptionList/DescriptionList.stories.tsx +48 -0
  140. package/src/components/ui/DescriptionList/DescriptionList.tsx +54 -0
  141. package/src/components/ui/Link/Link.mdx +53 -0
  142. package/src/components/ui/Link/Link.spec.tsx +35 -0
  143. package/src/components/ui/Link/Link.stories.tsx +46 -0
  144. package/src/components/ui/Link/Link.tsx +48 -0
  145. package/src/components/ui/Loading/Loading.spec.tsx +24 -0
  146. package/src/components/ui/Loading/Loading.stories.tsx +31 -0
  147. package/src/components/ui/Loading/Loading.tsx +29 -0
  148. package/src/components/ui/NotificationBanner/NotificationBanner.spec.tsx +85 -0
  149. package/src/components/ui/NotificationBanner/NotificationBanner.stories.tsx +37 -0
  150. package/src/components/ui/NotificationBanner/NotificationBanner.tsx +76 -0
  151. package/src/components/ui/Pagination/Pagination.mdx +62 -0
  152. package/src/components/ui/Pagination/Pagination.spec.tsx +86 -0
  153. package/src/components/ui/Pagination/Pagination.stories.tsx +47 -0
  154. package/src/components/ui/Pagination/Pagination.tsx +157 -0
  155. package/src/components/ui/Progress/Progress.spec.tsx +56 -0
  156. package/src/components/ui/Progress/Progress.stories.tsx +34 -0
  157. package/src/components/ui/Progress/Progress.tsx +84 -0
  158. package/src/components/ui/ProgressCircle/ProgressCircle.mdx +63 -0
  159. package/src/components/ui/ProgressCircle/ProgressCircle.spec.tsx +39 -0
  160. package/src/components/ui/ProgressCircle/ProgressCircle.stories.tsx +36 -0
  161. package/src/components/ui/ProgressCircle/ProgressCircle.tsx +176 -0
  162. package/src/components/ui/Separator/Separator.spec.tsx +39 -0
  163. package/src/components/ui/Separator/Separator.stories.tsx +38 -0
  164. package/src/components/ui/Separator/Separator.tsx +32 -0
  165. package/src/components/ui/Skeleton/Skeleton.spec.tsx +39 -0
  166. package/src/components/ui/Skeleton/Skeleton.stories.tsx +31 -0
  167. package/src/components/ui/Skeleton/Skeleton.tsx +42 -0
  168. package/src/components/ui/Table/Table.mdx +72 -0
  169. package/src/components/ui/Table/Table.spec.tsx +83 -0
  170. package/src/components/ui/Table/Table.stories.tsx +55 -0
  171. package/src/components/ui/Table/Table.tsx +196 -0
  172. package/src/components/ui/Tabs/Tabs.mdx +65 -0
  173. package/src/components/ui/Tabs/Tabs.spec.tsx +65 -0
  174. package/src/components/ui/Tabs/Tabs.stories.tsx +57 -0
  175. package/src/components/ui/Tabs/Tabs.tsx +133 -0
  176. package/src/components/ui/Tooltip/Tooltip.mdx +47 -0
  177. package/src/components/ui/Tooltip/Tooltip.spec.tsx +37 -0
  178. package/src/components/ui/Tooltip/Tooltip.stories.tsx +38 -0
  179. package/src/components/ui/Tooltip/Tooltip.tsx +57 -0
  180. package/src/components/ui/Typography/Typography.mdx +56 -0
  181. package/src/components/ui/Typography/Typography.spec.tsx +49 -0
  182. package/src/components/ui/Typography/Typography.stories.tsx +36 -0
  183. package/src/components/ui/Typography/Typography.tsx +65 -0
  184. package/src/index.ts +86 -0
  185. package/src/native/ReactNavigationTheme.mdx +140 -0
  186. package/src/native/ReactNavigationTheme.spec.tsx +35 -0
  187. package/src/native/ReactNavigationTheme.ts +102 -0
  188. package/src/native/ThemeContext.spec.tsx +57 -0
  189. package/src/native/ThemeContext.tsx +48 -0
  190. package/src/native/tokens.ts +13 -0
  191. package/src/test/setup.ts +1 -0
@@ -0,0 +1,133 @@
1
+ import { ReactNode, useState } from 'react';
2
+ import {
3
+ AccessibilityRole,
4
+ StyleSheet,
5
+ View,
6
+ Text,
7
+ Pressable,
8
+ ScrollView,
9
+ StyleProp,
10
+ ViewStyle,
11
+ } from 'react-native';
12
+ import { spacing, font, lineHeight } from '../../../native/tokens';
13
+ import { useTheme } from '../../../native/ThemeContext';
14
+
15
+ export interface TabItem {
16
+ id: string;
17
+ label: ReactNode;
18
+ content: ReactNode;
19
+ disabled?: boolean;
20
+ }
21
+
22
+ interface TabsProps {
23
+ tabs: TabItem[];
24
+ defaultTab?: string;
25
+ style?: StyleProp<ViewStyle>;
26
+ }
27
+
28
+ const styles = StyleSheet.create({
29
+ container: {
30
+ overflow: 'hidden',
31
+ },
32
+ tabList: {
33
+ flexDirection: 'row',
34
+ borderBottomWidth: 2,
35
+ },
36
+ tabButton: {
37
+ minHeight: 44,
38
+ paddingHorizontal: spacing.m,
39
+ paddingVertical: spacing.s,
40
+ alignItems: 'center',
41
+ justifyContent: 'center',
42
+ borderBottomWidth: 2,
43
+ marginBottom: -2,
44
+ },
45
+ tabLabel: {
46
+ fontSize: font.size.body,
47
+ fontFamily: font.family.body,
48
+ lineHeight: lineHeight.body,
49
+ },
50
+ panel: {
51
+ paddingVertical: spacing.m,
52
+ },
53
+ pressed: {
54
+ opacity: 0.75,
55
+ },
56
+ disabled: {
57
+ opacity: 0.4,
58
+ },
59
+ });
60
+
61
+ export function Tabs({
62
+ tabs,
63
+ defaultTab,
64
+ style,
65
+ }: Readonly<TabsProps>) {
66
+ const { colors } = useTheme();
67
+ const [activeTab, setActiveTab] = useState(defaultTab ?? tabs[0]?.id);
68
+
69
+ return (
70
+ <View style={[styles.container, style]}>
71
+ <ScrollView
72
+ horizontal
73
+ showsHorizontalScrollIndicator={false}
74
+ contentContainerStyle={[styles.tabList, { borderBottomColor: colors.colorBgSubtle }]}
75
+ accessibilityRole="tabbar"
76
+ >
77
+ {tabs.map(tab => {
78
+ const isActive = activeTab === tab.id;
79
+
80
+ return (
81
+ <Pressable
82
+ key={tab.id}
83
+ onPress={() => setActiveTab(tab.id)}
84
+ disabled={tab.disabled}
85
+ accessibilityRole="tab"
86
+ accessibilityState={{
87
+ selected: isActive,
88
+ disabled: tab.disabled,
89
+ }}
90
+ accessibilityLabel={typeof tab.label === 'string' ? tab.label : undefined}
91
+ style={({ pressed }) => [
92
+ styles.tabButton,
93
+ {
94
+ borderBottomColor: isActive
95
+ ? colors.colorInteractive
96
+ : 'transparent',
97
+ },
98
+ tab.disabled && styles.disabled,
99
+ pressed && !tab.disabled && styles.pressed,
100
+ ]}
101
+ >
102
+ <Text
103
+ style={[
104
+ styles.tabLabel,
105
+ {
106
+ color: isActive
107
+ ? colors.colorInteractive
108
+ : colors.colorOnBgSubtle,
109
+ fontWeight: isActive ? font.weight.semibold : font.weight.body,
110
+ },
111
+ ]}
112
+ >
113
+ {tab.label}
114
+ </Text>
115
+ </Pressable>
116
+ );
117
+ })}
118
+ </ScrollView>
119
+
120
+ {tabs.map(tab => {
121
+ const isActive = activeTab === tab.id;
122
+
123
+ if (!isActive) return null;
124
+
125
+ return (
126
+ <View key={tab.id} style={styles.panel} accessibilityRole={'tabpanel' as AccessibilityRole}>
127
+ {tab.content}
128
+ </View>
129
+ );
130
+ })}
131
+ </View>
132
+ );
133
+ }
@@ -0,0 +1,47 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './Tooltip.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Tooltip (React Native)
7
+
8
+ React Native tooltip that appears on long press. Wraps children in a `Pressable` and shows the tooltip content above the trigger element.
9
+
10
+ > **Status: alpha** — API may change before stable release.
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ import { Tooltip } from '@boostdev/design-system-components';
16
+ import { Text } from 'react-native';
17
+
18
+ <Tooltip content="Helpful information">
19
+ <Text>Long press me</Text>
20
+ </Tooltip>
21
+ ```
22
+
23
+ ## Examples
24
+
25
+ ### Default
26
+ <Canvas of={Stories.Default} />
27
+
28
+ ### Long content
29
+ <Canvas of={Stories.LongContent} />
30
+
31
+ ## Props
32
+
33
+ <ArgTypes of={Stories} />
34
+
35
+ ## Props reference
36
+
37
+ | Prop | Type | Default | Description |
38
+ |------|------|---------|-------------|
39
+ | `content` | `string` | — | Tooltip text |
40
+ | `children` | `ReactNode` | — | Trigger element |
41
+ | `style` | `StyleProp<ViewStyle>` | — | Override wrapper styles |
42
+
43
+ ## Accessibility
44
+
45
+ - Tooltip content is exposed via `accessibilityHint` on the pressable trigger
46
+ - When visible, the tooltip view has `accessibilityRole="text"` and `accessibilityLabel`
47
+ - Tooltip appears on long press and dismisses on press out
@@ -0,0 +1,37 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { ThemeProvider } from '../../../native/ThemeContext';
3
+ import { Tooltip } from './Tooltip';
4
+ import { Text } from 'react-native';
5
+
6
+ function renderWithTheme(ui: React.ReactElement) {
7
+ return render(<ThemeProvider>{ui}</ThemeProvider>);
8
+ }
9
+
10
+ describe('Tooltip (native)', () => {
11
+ it('renders children', () => {
12
+ renderWithTheme(
13
+ <Tooltip content="Helpful info">
14
+ <Text>Trigger</Text>
15
+ </Tooltip>,
16
+ );
17
+ expect(screen.getByText('Trigger')).toBeInTheDocument();
18
+ });
19
+
20
+ it('does not show tooltip content by default', () => {
21
+ renderWithTheme(
22
+ <Tooltip content="Helpful info">
23
+ <Text>Trigger</Text>
24
+ </Tooltip>,
25
+ );
26
+ expect(screen.queryByText('Helpful info')).not.toBeInTheDocument();
27
+ });
28
+
29
+ it('renders a wrapper element', () => {
30
+ const { container } = renderWithTheme(
31
+ <Tooltip content="Helpful info">
32
+ <Text>Trigger</Text>
33
+ </Tooltip>,
34
+ );
35
+ expect(container.firstChild).toBeInTheDocument();
36
+ });
37
+ });
@@ -0,0 +1,38 @@
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 { Tooltip } from './Tooltip';
6
+
7
+ const meta = {
8
+ tags: ['!stable', 'alpha'],
9
+ title: 'React Native/UI/Tooltip',
10
+ component: Tooltip,
11
+ decorators: [
12
+ (Story) => (
13
+ <ThemeProvider colorScheme="light">
14
+ <div style={{ padding: '60px 20px 20px' }}>
15
+ <Story />
16
+ </div>
17
+ </ThemeProvider>
18
+ ),
19
+ ],
20
+ parameters: { layout: 'padded' },
21
+ } satisfies Meta<typeof Tooltip>;
22
+
23
+ export default meta;
24
+ type Story = StoryObj<typeof meta>;
25
+
26
+ export const Default: Story = {
27
+ args: {
28
+ content: 'Helpful tooltip text',
29
+ children: <Text>Long press me</Text>,
30
+ },
31
+ };
32
+
33
+ export const LongContent: Story = {
34
+ args: {
35
+ content: 'This is a longer tooltip that provides more detailed information about the element.',
36
+ children: <Text>Long press for details</Text>,
37
+ },
38
+ };
@@ -0,0 +1,57 @@
1
+ import { ReactNode, useState } from 'react';
2
+ import { StyleSheet, View, Text, Pressable, StyleProp, ViewStyle } from 'react-native';
3
+ import { spacing, font, border } from '../../../native/tokens';
4
+ import { useTheme } from '../../../native/ThemeContext';
5
+
6
+ interface TooltipProps {
7
+ content: string;
8
+ children: ReactNode;
9
+ style?: StyleProp<ViewStyle>;
10
+ }
11
+
12
+ const styles = StyleSheet.create({
13
+ wrapper: {
14
+ position: 'relative',
15
+ },
16
+ tooltip: {
17
+ position: 'absolute',
18
+ bottom: '100%',
19
+ start: 0,
20
+ marginBottom: spacing.xxs,
21
+ paddingVertical: spacing.xxs,
22
+ paddingHorizontal: spacing.xs,
23
+ borderRadius: border.radius.full,
24
+ zIndex: 10,
25
+ alignSelf: 'flex-start',
26
+ },
27
+ text: {
28
+ fontSize: font.size.bodyS,
29
+ fontFamily: font.family.body,
30
+ },
31
+ });
32
+
33
+ export function Tooltip({ content, children, style }: Readonly<TooltipProps>) {
34
+ const { colors } = useTheme();
35
+ const [visible, setVisible] = useState(false);
36
+
37
+ return (
38
+ <View style={[styles.wrapper, style]}>
39
+ <Pressable
40
+ onLongPress={() => setVisible(true)}
41
+ onPressOut={() => setVisible(false)}
42
+ accessibilityHint={content}
43
+ >
44
+ {children}
45
+ </Pressable>
46
+ {visible && (
47
+ <View
48
+ style={[styles.tooltip, { backgroundColor: colors.colorOnBg }]}
49
+ accessibilityRole="text"
50
+ accessibilityLabel={content}
51
+ >
52
+ <Text style={[styles.text, { color: colors.colorBg }]}>{content}</Text>
53
+ </View>
54
+ )}
55
+ </View>
56
+ );
57
+ }
@@ -0,0 +1,56 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as Stories from './Typography.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Typography (React Native)
7
+
8
+ React Native typography component wrapping `Text` with semantic variants. Use instead of bare `<Text>` to ensure consistent sizing, weight, and line-height from the design token scale.
9
+
10
+ > **Status: alpha** — API may change before stable release.
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ import { Typography } from '@boostdev/design-system-components';
16
+
17
+ <Typography variant="h1">Page title</Typography>
18
+ <Typography variant="body">Body copy text.</Typography>
19
+ ```
20
+
21
+ ## Examples
22
+
23
+ ### All variants
24
+ <Canvas of={Stories.AllVariants} />
25
+
26
+ ### Heading 1
27
+ <Canvas of={Stories.Heading1} />
28
+
29
+ ### Heading 2
30
+ <Canvas of={Stories.Heading2} />
31
+
32
+ ### Heading 3
33
+ <Canvas of={Stories.Heading3} />
34
+
35
+ ### Body
36
+ <Canvas of={Stories.Body} />
37
+
38
+ ### Body small
39
+ <Canvas of={Stories.BodySmall} />
40
+
41
+ ## Props
42
+
43
+ <ArgTypes of={Stories} />
44
+
45
+ ## Props reference
46
+
47
+ | Prop | Type | Default | Description |
48
+ |------|------|---------|-------------|
49
+ | `variant` | `"h1" \| "h2" \| "h3" \| "body" \| "body_s"` | `"body"` | Typographic scale |
50
+ | `children` | `ReactNode` | — | Text content |
51
+ | `style` | `StyleProp<TextStyle>` | — | Override text styles |
52
+
53
+ ## Accessibility
54
+
55
+ - `h1`/`h2`/`h3` variants set `accessibilityRole="header"` automatically
56
+ - Override with the `accessibilityRole` prop if the visual hierarchy doesn't match the semantic hierarchy
@@ -0,0 +1,49 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { ThemeProvider } from '../../../native/ThemeContext';
3
+ import { Typography } from './Typography';
4
+
5
+ function renderWithTheme(ui: React.ReactElement) {
6
+ return render(<ThemeProvider>{ui}</ThemeProvider>);
7
+ }
8
+
9
+ describe('Typography (native)', () => {
10
+ it('renders children text', () => {
11
+ renderWithTheme(<Typography>Hello world</Typography>);
12
+ expect(screen.getByText('Hello world')).toBeInTheDocument();
13
+ });
14
+
15
+ it('defaults to body variant without heading role', () => {
16
+ renderWithTheme(<Typography>Body text</Typography>);
17
+ expect(screen.getByText('Body text')).toBeInTheDocument();
18
+ expect(screen.queryByRole('heading')).not.toBeInTheDocument();
19
+ });
20
+
21
+ it('has heading role for h1 variant', () => {
22
+ renderWithTheme(<Typography variant="h1">Heading 1</Typography>);
23
+ expect(screen.getByRole('heading')).toBeInTheDocument();
24
+ expect(screen.getByText('Heading 1')).toBeInTheDocument();
25
+ });
26
+
27
+ it('has heading role for h2 variant', () => {
28
+ renderWithTheme(<Typography variant="h2">Heading 2</Typography>);
29
+ expect(screen.getByRole('heading')).toBeInTheDocument();
30
+ expect(screen.getByText('Heading 2')).toBeInTheDocument();
31
+ });
32
+
33
+ it('has heading role for h3 variant', () => {
34
+ renderWithTheme(<Typography variant="h3">Heading 3</Typography>);
35
+ expect(screen.getByRole('heading')).toBeInTheDocument();
36
+ expect(screen.getByText('Heading 3')).toBeInTheDocument();
37
+ });
38
+
39
+ it('does not have heading role for body_s variant', () => {
40
+ renderWithTheme(<Typography variant="body_s">Small body</Typography>);
41
+ expect(screen.getByText('Small body')).toBeInTheDocument();
42
+ expect(screen.queryByRole('heading')).not.toBeInTheDocument();
43
+ });
44
+
45
+ it('accepts custom style', () => {
46
+ renderWithTheme(<Typography style={{ marginTop: 10 }}>Styled</Typography>);
47
+ expect(screen.getByText('Styled')).toBeInTheDocument();
48
+ });
49
+ });
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import { ThemeProvider } from '../../../native/ThemeContext';
4
+ import { Typography } from './Typography';
5
+
6
+ const meta = {
7
+ tags: ['!stable', 'alpha'],
8
+ title: 'React Native/UI/Typography',
9
+ component: Typography,
10
+ decorators: [(Story) => <ThemeProvider colorScheme="light"><Story /></ThemeProvider>],
11
+ parameters: { layout: 'padded' },
12
+ argTypes: {
13
+ variant: { control: 'select', options: ['h1', 'h2', 'h3', 'body', 'body_s'] },
14
+ },
15
+ } satisfies Meta<typeof Typography>;
16
+
17
+ export default meta;
18
+ type Story = StoryObj<typeof meta>;
19
+
20
+ export const Heading1: Story = { args: { children: 'Heading 1', variant: 'h1' } };
21
+ export const Heading2: Story = { args: { children: 'Heading 2', variant: 'h2' } };
22
+ export const Heading3: Story = { args: { children: 'Heading 3', variant: 'h3' } };
23
+ export const Body: Story = { args: { children: 'The quick brown fox jumps over the lazy dog.', variant: 'body' } };
24
+ export const BodySmall: Story = { args: { children: 'The quick brown fox jumps over the lazy dog.', variant: 'body_s' } };
25
+ export const AllVariants: Story = {
26
+ args: { children: '' },
27
+ render: () => (
28
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
29
+ <Typography variant="h1">Heading 1</Typography>
30
+ <Typography variant="h2">Heading 2</Typography>
31
+ <Typography variant="h3">Heading 3</Typography>
32
+ <Typography variant="body">Body — The quick brown fox jumps over the lazy dog.</Typography>
33
+ <Typography variant="body_s">Body Small — The quick brown fox jumps over the lazy dog.</Typography>
34
+ </div>
35
+ ),
36
+ };
@@ -0,0 +1,65 @@
1
+ import { ReactNode } from 'react';
2
+ import { StyleSheet, Text, StyleProp, TextStyle } from 'react-native';
3
+ import { font, lineHeight } from '../../../native/tokens';
4
+ import { useTheme } from '../../../native/ThemeContext';
5
+
6
+ type TypographyVariant = 'h1' | 'h2' | 'h3' | 'body' | 'body_s';
7
+
8
+ interface TypographyProps {
9
+ variant?: TypographyVariant;
10
+ children: ReactNode;
11
+ style?: StyleProp<TextStyle>;
12
+ }
13
+
14
+ const styles = StyleSheet.create({
15
+ base: {
16
+ fontFamily: font.family.body,
17
+ fontWeight: font.weight.body,
18
+ },
19
+ h1: {
20
+ fontSize: font.size.heading1,
21
+ fontFamily: font.family.heading,
22
+ fontWeight: font.weight.heading,
23
+ lineHeight: lineHeight.heading1,
24
+ },
25
+ h2: {
26
+ fontSize: font.size.heading2,
27
+ fontFamily: font.family.heading,
28
+ fontWeight: font.weight.heading,
29
+ lineHeight: lineHeight.heading2,
30
+ },
31
+ h3: {
32
+ fontSize: font.size.heading3,
33
+ fontFamily: font.family.heading,
34
+ fontWeight: font.weight.heading,
35
+ lineHeight: lineHeight.heading3,
36
+ },
37
+ body: {
38
+ fontSize: font.size.body,
39
+ lineHeight: lineHeight.body,
40
+ },
41
+ body_s: {
42
+ fontSize: font.size.bodyS,
43
+ lineHeight: lineHeight.bodyS,
44
+ },
45
+ });
46
+
47
+ const accessibilityRoles: Record<TypographyVariant, 'header' | 'text'> = {
48
+ h1: 'header',
49
+ h2: 'header',
50
+ h3: 'header',
51
+ body: 'text',
52
+ body_s: 'text',
53
+ };
54
+
55
+ export function Typography({ variant = 'body', children, style }: TypographyProps) {
56
+ const { colors } = useTheme();
57
+ return (
58
+ <Text
59
+ accessibilityRole={accessibilityRoles[variant]}
60
+ style={[styles.base, styles[variant], { color: colors.colorOnBg }, style]}
61
+ >
62
+ {children}
63
+ </Text>
64
+ );
65
+ }
package/src/index.ts ADDED
@@ -0,0 +1,86 @@
1
+ // @boostdev/design-system-react-native — public barrel
2
+
3
+ // Infrastructure
4
+ export { ThemeProvider, useTheme } from './native/ThemeContext';
5
+ export type { ThemeTokenOverrides, ThemeColors, ThemeColorName } from './native/ThemeContext';
6
+ export {
7
+ createReactNavigationTheme,
8
+ useReactNavigationTheme,
9
+ } from './native/ReactNavigationTheme';
10
+ export type {
11
+ ReactNavigationTheme,
12
+ ReactNavigationThemeColors,
13
+ ReactNavigationThemeFonts,
14
+ ReactNavigationFontStyle,
15
+ CreateReactNavigationThemeOptions,
16
+ } from './native/ReactNavigationTheme';
17
+
18
+ // UI
19
+ export { Typography } from './components/ui/Typography/Typography';
20
+ export { Badge } from './components/ui/Badge/Badge';
21
+ export { Avatar } from './components/ui/Avatar/Avatar';
22
+ export { Separator } from './components/ui/Separator/Separator';
23
+ export { Loading } from './components/ui/Loading/Loading';
24
+ export { Progress } from './components/ui/Progress/Progress';
25
+ export { Skeleton } from './components/ui/Skeleton/Skeleton';
26
+ export { Alert } from './components/ui/Alert/Alert';
27
+ export { NotificationBanner } from './components/ui/NotificationBanner/NotificationBanner';
28
+ export { Link } from './components/ui/Link/Link';
29
+ export { DescriptionList } from './components/ui/DescriptionList/DescriptionList';
30
+ export type { DescriptionItem } from './components/ui/DescriptionList/DescriptionList';
31
+ export { Breadcrumb } from './components/ui/Breadcrumb/Breadcrumb';
32
+ export type { BreadcrumbItem } from './components/ui/Breadcrumb/Breadcrumb';
33
+ export { ProgressCircle } from './components/ui/ProgressCircle/ProgressCircle';
34
+ export { Tooltip } from './components/ui/Tooltip/Tooltip';
35
+ export { Pagination } from './components/ui/Pagination/Pagination';
36
+ export { Collapsible } from './components/ui/Collapsible/Collapsible';
37
+ export type { CollapsibleProps } from './components/ui/Collapsible/Collapsible';
38
+ export { Accordion } from './components/ui/Accordion/Accordion';
39
+ export type { AccordionItem } from './components/ui/Accordion/Accordion';
40
+ export { Tabs } from './components/ui/Tabs/Tabs';
41
+ export type { TabItem } from './components/ui/Tabs/Tabs';
42
+ export { Table } from './components/ui/Table/Table';
43
+ export type { TableColumn } from './components/ui/Table/Table';
44
+ export { Calendar } from './components/ui/Calendar/Calendar';
45
+ export { Carousel } from './components/ui/Carousel/Carousel';
46
+
47
+ // Layout
48
+ export { Card } from './components/layout/Card/Card';
49
+ export { SectionHeader } from './components/layout/SectionHeader/SectionHeader';
50
+ export { IconWrapper } from './components/layout/IconWrapper/IconWrapper';
51
+ export type { IconWrapperProps } from './components/layout/IconWrapper/IconWrapper';
52
+ export { ButtonContainer, ButtonGroup } from './components/layout/ButtonGroup/ButtonGroup';
53
+ export type { ButtonContainerProps, ButtonGroupProps } from './components/layout/ButtonGroup/ButtonGroup';
54
+
55
+ // Interaction
56
+ export { Button } from './components/interaction/Button/Button';
57
+ export { Rating } from './components/interaction/Rating/Rating';
58
+ export type { ButtonProps } from './components/interaction/Button/Button';
59
+ export { Switch } from './components/interaction/form/Switch/Switch';
60
+ export { Checkbox } from './components/interaction/form/Checkbox/Checkbox';
61
+ export { CheckboxGroup } from './components/interaction/form/CheckboxGroup/CheckboxGroup';
62
+ export { Radio } from './components/interaction/form/Radio/Radio';
63
+ export { RadioGroup } from './components/interaction/form/RadioGroup/RadioGroup';
64
+ export { FileInput } from './components/interaction/form/FileInput/FileInput';
65
+ export { Combobox } from './components/interaction/form/Combobox/Combobox';
66
+ export type { ComboboxOption } from './components/interaction/form/Combobox/Combobox';
67
+ export { SegmentedControl } from './components/interaction/form/SegmentedControl/SegmentedControl';
68
+ export { FormInput } from './components/interaction/form/FormInput/FormInput';
69
+ export { Textarea } from './components/interaction/form/Textarea/Textarea';
70
+ export { Select } from './components/interaction/form/Select/Select';
71
+ export type { SelectOption } from './components/interaction/form/Select/Select';
72
+ export { Slider } from './components/interaction/form/Slider/Slider';
73
+ export { NumberInput } from './components/interaction/form/NumberInput/NumberInput';
74
+ export { Dialog } from './components/interaction/Dialog/Dialog';
75
+ export { SideSheet } from './components/interaction/SideSheet/SideSheet';
76
+ export { ToastProvider, useToast } from './components/interaction/Toast/Toast';
77
+ export { DropdownMenu } from './components/interaction/DropdownMenu/DropdownMenu';
78
+ export type { DropdownMenuItem } from './components/interaction/DropdownMenu/DropdownMenu';
79
+ export { Popover } from './components/interaction/Popover/Popover';
80
+
81
+ // Atoms
82
+ export { Label } from './components/interaction/form/atoms/Label';
83
+ export { Message } from './components/interaction/form/atoms/Message';
84
+
85
+ // Tokens (re-exported for convenience)
86
+ export { colors, spacing, font, border, zIndex, animation, lineHeight } from './native/tokens';