@titan-design/react-ui 0.0.2 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-NA3EES23.mjs → chunk-MGW37MPO.mjs} +26 -4
- package/dist/chunk-MGW37MPO.mjs.map +1 -0
- package/dist/index.d.mts +115 -2
- package/dist/index.d.ts +115 -2
- package/dist/index.js +262 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +225 -3
- package/dist/index.mjs.map +1 -1
- package/dist/theme/index.d.mts +124 -109
- package/dist/theme/index.d.ts +124 -109
- package/dist/theme/index.js +24 -1
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +1 -1
- package/package.json +12 -5
- package/src/components/custom/DateTime/DateTime.stories.tsx +180 -0
- package/src/components/custom/DateTime/DateTime.test.tsx +152 -0
- package/src/components/custom/DateTime/DateTime.tsx +235 -0
- package/src/components/custom/DateTime/index.ts +2 -0
- package/src/components/custom/EmptyState/EmptyState.stories.tsx +99 -0
- package/src/components/custom/EmptyState/EmptyState.test.tsx +106 -0
- package/src/components/custom/EmptyState/EmptyState.tsx +68 -0
- package/src/components/custom/EmptyState/index.ts +2 -0
- package/src/components/custom/Metric/Metric.stories.tsx +133 -0
- package/src/components/custom/Metric/Metric.test.tsx +120 -0
- package/src/components/custom/Metric/Metric.tsx +92 -0
- package/src/components/custom/Metric/index.ts +2 -0
- package/src/components/custom/Sidebar/Sidebar.stories.tsx +227 -0
- package/src/components/custom/Sidebar/Sidebar.test.tsx +185 -0
- package/src/components/custom/Sidebar/Sidebar.tsx +257 -0
- package/src/components/custom/Sidebar/index.ts +18 -0
- package/src/components/custom/Table/Table.stories.tsx +481 -0
- package/src/components/custom/Table/Table.test.tsx +531 -0
- package/src/components/custom/Table/Table.tsx +696 -0
- package/src/components/custom/Table/index.ts +22 -0
- package/src/components/custom/Typography/Typography.stories.tsx +153 -0
- package/src/components/custom/Typography/Typography.test.tsx +107 -0
- package/src/components/custom/Typography/Typography.tsx +218 -0
- package/src/components/custom/Typography/index.ts +19 -0
- package/src/components/custom/index.ts +8 -0
- package/src/components/custom/stepper/Stepper.stories.tsx +245 -0
- package/src/components/custom/stepper/Stepper.test.tsx +230 -0
- package/src/components/custom/stepper/Stepper.tsx +250 -0
- package/src/components/custom/stepper/index.ts +10 -0
- package/src/components/index.ts +5 -0
- package/src/components/ui/alert/Alert.stories.tsx +154 -0
- package/src/components/ui/alert/Alert.test.tsx +202 -0
- package/src/components/ui/alert/Alert.tsx +170 -0
- package/src/components/ui/alert/index.ts +8 -0
- package/src/components/ui/autocomplete/Autocomplete.stories.tsx +299 -0
- package/src/components/ui/autocomplete/Autocomplete.test.tsx +247 -0
- package/src/components/ui/autocomplete/Autocomplete.tsx +332 -0
- package/src/components/ui/autocomplete/index.ts +2 -0
- package/src/components/ui/avatar/Avatar.stories.tsx +105 -0
- package/src/components/ui/avatar/Avatar.test.tsx +135 -0
- package/src/components/ui/avatar/Avatar.tsx +168 -0
- package/src/components/ui/avatar/index.ts +2 -0
- package/src/components/ui/badge/Badge.stories.tsx +100 -0
- package/src/components/ui/badge/Badge.test.tsx +86 -0
- package/src/components/ui/badge/Badge.tsx +105 -0
- package/src/components/ui/badge/index.ts +2 -0
- package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +70 -0
- package/src/components/ui/breadcrumbs/Breadcrumbs.test.tsx +156 -0
- package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +127 -0
- package/src/components/ui/breadcrumbs/index.ts +2 -0
- package/src/components/ui/button/Button.stories.tsx +216 -0
- package/src/components/ui/button/Button.test.tsx +132 -0
- package/src/components/ui/button/Button.tsx +242 -0
- package/src/components/ui/button/index.ts +10 -0
- package/src/components/ui/card/Card.stories.tsx +443 -0
- package/src/components/ui/card/Card.test.tsx +265 -0
- package/src/components/ui/card/Card.tsx +380 -0
- package/src/components/ui/card/index.ts +20 -0
- package/src/components/ui/checkbox/Checkbox.stories.tsx +126 -0
- package/src/components/ui/checkbox/Checkbox.test.tsx +152 -0
- package/src/components/ui/checkbox/Checkbox.tsx +163 -0
- package/src/components/ui/checkbox/index.ts +2 -0
- package/src/components/ui/chip/Chip.stories.tsx +112 -0
- package/src/components/ui/chip/Chip.test.tsx +119 -0
- package/src/components/ui/chip/Chip.tsx +151 -0
- package/src/components/ui/chip/index.ts +2 -0
- package/src/components/ui/collapse/Collapse.stories.tsx +313 -0
- package/src/components/ui/collapse/Collapse.test.tsx +298 -0
- package/src/components/ui/collapse/Collapse.tsx +275 -0
- package/src/components/ui/collapse/index.ts +18 -0
- package/src/components/ui/data-row/DataRow.stories.tsx +78 -0
- package/src/components/ui/data-row/DataRow.test.tsx +78 -0
- package/src/components/ui/data-row/DataRow.tsx +29 -0
- package/src/components/ui/data-row/index.ts +2 -0
- package/src/components/ui/divider/Divider.stories.tsx +71 -0
- package/src/components/ui/divider/Divider.test.tsx +55 -0
- package/src/components/ui/divider/Divider.tsx +37 -0
- package/src/components/ui/divider/index.ts +2 -0
- package/src/components/ui/drawer/Drawer.stories.tsx +228 -0
- package/src/components/ui/drawer/Drawer.test.tsx +142 -0
- package/src/components/ui/drawer/Drawer.tsx +228 -0
- package/src/components/ui/drawer/index.ts +9 -0
- package/src/components/ui/form-field/FormField.stories.tsx +260 -0
- package/src/components/ui/form-field/FormField.test.tsx +280 -0
- package/src/components/ui/form-field/FormField.tsx +308 -0
- package/src/components/ui/form-field/index.ts +2 -0
- package/src/components/ui/help-tip/HelpTip.stories.tsx +250 -0
- package/src/components/ui/help-tip/HelpTip.test.tsx +198 -0
- package/src/components/ui/help-tip/HelpTip.tsx +229 -0
- package/src/components/ui/help-tip/index.ts +2 -0
- package/src/components/ui/icon-box/IconBox.stories.tsx +139 -0
- package/src/components/ui/icon-box/IconBox.test.tsx +120 -0
- package/src/components/ui/icon-box/IconBox.tsx +57 -0
- package/src/components/ui/icon-box/index.ts +1 -0
- package/src/components/ui/index.ts +36 -0
- package/src/components/ui/input/Input.stories.tsx +192 -0
- package/src/components/ui/input/Input.test.tsx +86 -0
- package/src/components/ui/input/Input.tsx +273 -0
- package/src/components/ui/input/index.ts +2 -0
- package/src/components/ui/link/Link.stories.tsx +78 -0
- package/src/components/ui/link/Link.test.tsx +94 -0
- package/src/components/ui/link/Link.tsx +86 -0
- package/src/components/ui/link/index.ts +2 -0
- package/src/components/ui/list-item/ListItem.stories.tsx +146 -0
- package/src/components/ui/list-item/ListItem.test.tsx +128 -0
- package/src/components/ui/list-item/ListItem.tsx +89 -0
- package/src/components/ui/list-item/index.ts +14 -0
- package/src/components/ui/menu/Menu.stories.tsx +180 -0
- package/src/components/ui/menu/Menu.test.tsx +353 -0
- package/src/components/ui/menu/Menu.tsx +222 -0
- package/src/components/ui/menu/index.ts +2 -0
- package/src/components/ui/modal/Modal.stories.tsx +350 -0
- package/src/components/ui/modal/Modal.test.tsx +159 -0
- package/src/components/ui/modal/Modal.tsx +254 -0
- package/src/components/ui/modal/index.ts +19 -0
- package/src/components/ui/popover/Popover.stories.tsx +190 -0
- package/src/components/ui/popover/Popover.test.tsx +277 -0
- package/src/components/ui/popover/Popover.tsx +162 -0
- package/src/components/ui/popover/index.ts +2 -0
- package/src/components/ui/progress/Progress.stories.tsx +190 -0
- package/src/components/ui/progress/Progress.test.tsx +181 -0
- package/src/components/ui/progress/Progress.tsx +320 -0
- package/src/components/ui/progress/index.ts +9 -0
- package/src/components/ui/radio/Radio.stories.tsx +199 -0
- package/src/components/ui/radio/Radio.test.tsx +166 -0
- package/src/components/ui/radio/Radio.tsx +200 -0
- package/src/components/ui/radio/index.ts +2 -0
- package/src/components/ui/section/Section.stories.tsx +111 -0
- package/src/components/ui/section/Section.test.tsx +121 -0
- package/src/components/ui/section/Section.tsx +52 -0
- package/src/components/ui/section/index.ts +6 -0
- package/src/components/ui/select/Select.stories.tsx +286 -0
- package/src/components/ui/select/Select.test.tsx +180 -0
- package/src/components/ui/select/Select.tsx +236 -0
- package/src/components/ui/select/index.ts +2 -0
- package/src/components/ui/skeleton/Skeleton.stories.tsx +153 -0
- package/src/components/ui/skeleton/Skeleton.test.tsx +162 -0
- package/src/components/ui/skeleton/Skeleton.tsx +229 -0
- package/src/components/ui/skeleton/index.ts +16 -0
- package/src/components/ui/spinner/Spinner.stories.tsx +67 -0
- package/src/components/ui/spinner/Spinner.test.tsx +67 -0
- package/src/components/ui/spinner/Spinner.tsx +72 -0
- package/src/components/ui/spinner/index.ts +2 -0
- package/src/components/ui/stack/Stack.stories.tsx +127 -0
- package/src/components/ui/stack/Stack.test.tsx +184 -0
- package/src/components/ui/stack/Stack.tsx +76 -0
- package/src/components/ui/stack/index.ts +2 -0
- package/src/components/ui/surface/Surface.stories.tsx +91 -0
- package/src/components/ui/surface/Surface.test.tsx +63 -0
- package/src/components/ui/surface/Surface.tsx +51 -0
- package/src/components/ui/surface/index.ts +1 -0
- package/src/components/ui/switch/Switch.stories.tsx +111 -0
- package/src/components/ui/switch/Switch.test.tsx +112 -0
- package/src/components/ui/switch/Switch.tsx +103 -0
- package/src/components/ui/switch/index.ts +2 -0
- package/src/components/ui/tabs/Tabs.stories.tsx +213 -0
- package/src/components/ui/tabs/Tabs.test.tsx +192 -0
- package/src/components/ui/tabs/Tabs.tsx +253 -0
- package/src/components/ui/tabs/index.ts +16 -0
- package/src/components/ui/toast/Toast.stories.tsx +255 -0
- package/src/components/ui/toast/Toast.test.tsx +207 -0
- package/src/components/ui/toast/Toast.tsx +327 -0
- package/src/components/ui/toast/index.ts +8 -0
- package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +415 -0
- package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +224 -0
- package/src/components/ui/toolbar-button/ToolbarButton.tsx +307 -0
- package/src/components/ui/toolbar-button/index.ts +7 -0
- package/src/components/ui/tooltip/Tooltip.stories.tsx +126 -0
- package/src/components/ui/tooltip/Tooltip.test.tsx +179 -0
- package/src/components/ui/tooltip/Tooltip.tsx +143 -0
- package/src/components/ui/tooltip/index.ts +2 -0
- package/src/index.ts +10 -0
- package/src/test/setup.ts +19 -0
- package/src/theme/Colors.stories.tsx +258 -0
- package/src/theme/config.ts +193 -0
- package/src/theme/elevation.stories.tsx +346 -0
- package/src/theme/elevation.test.ts +27 -0
- package/src/theme/elevation.ts +567 -0
- package/src/theme/index.ts +39 -0
- package/src/theme/shadows.stories.tsx +523 -0
- package/src/theme/shadows.ts +439 -0
- package/src/theme/tokens/index.ts +2 -0
- package/src/theme/tokens/primitives.ts +354 -0
- package/src/theme/tokens/semantic.ts +381 -0
- package/src/types/jest-axe.d.ts +16 -0
- package/src/types/nativewind.d.ts +54 -0
- package/src/utils/cn.ts +14 -0
- package/src/utils/colors.ts +140 -0
- package/src/utils/form.ts +188 -0
- package/src/utils/index.ts +29 -0
- package/src/utils/useTheme.ts +72 -0
- package/dist/chunk-NA3EES23.mjs.map +0 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react'
|
|
3
|
+
import { axe } from 'jest-axe'
|
|
4
|
+
import { View, Text } from 'react-native'
|
|
5
|
+
import {
|
|
6
|
+
ListItem,
|
|
7
|
+
ListItemIcon,
|
|
8
|
+
ListItemContent,
|
|
9
|
+
ListItemTrailing,
|
|
10
|
+
ListItemDivider,
|
|
11
|
+
} from './ListItem'
|
|
12
|
+
|
|
13
|
+
function MockIcon({ size = 20 }: { size?: number; className?: string }) {
|
|
14
|
+
return <View testID="mock-icon" style={{ width: size, height: size }} />
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function ChevronIcon({ size = 16 }: { size?: number; className?: string }) {
|
|
18
|
+
return <Text>{'>'}</Text>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('ListItem', () => {
|
|
22
|
+
it('renders compound children', () => {
|
|
23
|
+
render(
|
|
24
|
+
<ListItem>
|
|
25
|
+
<ListItemIcon icon={MockIcon} />
|
|
26
|
+
<ListItemContent title="Profile" subtitle="Edit your profile" />
|
|
27
|
+
<ListItemTrailing>
|
|
28
|
+
<Text>{'>'}</Text>
|
|
29
|
+
</ListItemTrailing>
|
|
30
|
+
</ListItem>
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
expect(screen.getByTestId('mock-icon')).toBeInTheDocument()
|
|
34
|
+
expect(screen.getByText('Profile')).toBeInTheDocument()
|
|
35
|
+
expect(screen.getByText('Edit your profile')).toBeInTheDocument()
|
|
36
|
+
expect(screen.getByText('>')).toBeInTheDocument()
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('handles press events when onPress is provided', () => {
|
|
40
|
+
const onPress = vi.fn()
|
|
41
|
+
render(
|
|
42
|
+
<ListItem onPress={onPress} testID="list-item">
|
|
43
|
+
<ListItemContent title="Pressable item" />
|
|
44
|
+
</ListItem>
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
fireEvent.click(screen.getByTestId('list-item'))
|
|
48
|
+
expect(onPress).toHaveBeenCalledTimes(1)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('uses View when onPress is not provided', () => {
|
|
52
|
+
const { container } = render(
|
|
53
|
+
<ListItem testID="list-item">
|
|
54
|
+
<ListItemContent title="Static item" />
|
|
55
|
+
</ListItem>
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
// Without onPress, should not have button role
|
|
59
|
+
expect(screen.queryByRole('button')).not.toBeInTheDocument()
|
|
60
|
+
expect(screen.getByText('Static item')).toBeInTheDocument()
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
describe('ListItemContent', () => {
|
|
65
|
+
it('renders title', () => {
|
|
66
|
+
render(<ListItemContent title="Settings" />)
|
|
67
|
+
expect(screen.getByText('Settings')).toBeInTheDocument()
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('renders title and subtitle', () => {
|
|
71
|
+
render(<ListItemContent title="Wi-Fi" subtitle="Connected" />)
|
|
72
|
+
expect(screen.getByText('Wi-Fi')).toBeInTheDocument()
|
|
73
|
+
expect(screen.getByText('Connected')).toBeInTheDocument()
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('omits subtitle when not provided', () => {
|
|
77
|
+
render(<ListItemContent title="Bluetooth" />)
|
|
78
|
+
expect(screen.getByText('Bluetooth')).toBeInTheDocument()
|
|
79
|
+
expect(screen.queryByText('Connected')).not.toBeInTheDocument()
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
describe('ListItemTrailing', () => {
|
|
84
|
+
it('renders trailing content', () => {
|
|
85
|
+
render(
|
|
86
|
+
<ListItemTrailing>
|
|
87
|
+
<Text>Badge</Text>
|
|
88
|
+
</ListItemTrailing>
|
|
89
|
+
)
|
|
90
|
+
expect(screen.getByText('Badge')).toBeInTheDocument()
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
describe('ListItemDivider', () => {
|
|
95
|
+
it('renders with inset by default', () => {
|
|
96
|
+
const { container } = render(<ListItemDivider testID="divider" />)
|
|
97
|
+
expect(screen.getByTestId('divider')).toBeInTheDocument()
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('renders full-width when inset is false', () => {
|
|
101
|
+
render(<ListItemDivider inset={false} testID="divider-full" />)
|
|
102
|
+
expect(screen.getByTestId('divider-full')).toBeInTheDocument()
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
describe('ListItem accessibility', () => {
|
|
107
|
+
it('has no accessibility violations', async () => {
|
|
108
|
+
const { container } = render(
|
|
109
|
+
<ListItem>
|
|
110
|
+
<ListItemContent title="Accessible item" subtitle="With subtitle" />
|
|
111
|
+
</ListItem>
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
const results = await axe(container)
|
|
115
|
+
expect(results).toHaveNoViolations()
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('has no accessibility violations when pressable', async () => {
|
|
119
|
+
const { container } = render(
|
|
120
|
+
<ListItem onPress={() => {}} accessibilityRole="button">
|
|
121
|
+
<ListItemContent title="Pressable item" />
|
|
122
|
+
</ListItem>
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
const results = await axe(container)
|
|
126
|
+
expect(results).toHaveNoViolations()
|
|
127
|
+
})
|
|
128
|
+
})
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { View, Text, Pressable, type ViewProps, type PressableProps } from 'react-native'
|
|
3
|
+
import { cn } from '../../../utils/cn'
|
|
4
|
+
|
|
5
|
+
// --- ListItem (container) ---
|
|
6
|
+
|
|
7
|
+
export interface ListItemProps extends PressableProps {
|
|
8
|
+
className?: string
|
|
9
|
+
children: React.ReactNode
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function ListItem({ className, children, onPress, ...props }: ListItemProps) {
|
|
13
|
+
const Container = onPress ? Pressable : View
|
|
14
|
+
const containerProps = onPress ? { onPress, ...props } : props
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<Container
|
|
18
|
+
className={cn('flex-row items-center py-3 px-4 min-h-[48px]', className)}
|
|
19
|
+
{...(containerProps as any)}
|
|
20
|
+
>
|
|
21
|
+
{children}
|
|
22
|
+
</Container>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// --- ListItemIcon (leading slot) ---
|
|
27
|
+
|
|
28
|
+
export interface ListItemIconProps extends ViewProps {
|
|
29
|
+
icon: React.ComponentType<{ size?: number; className?: string }>
|
|
30
|
+
className?: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function ListItemIcon({ icon: Icon, className, ...props }: ListItemIconProps) {
|
|
34
|
+
return (
|
|
35
|
+
<View className={cn('mr-3 items-center justify-center', className)} {...props}>
|
|
36
|
+
<Icon size={20} className="text-text-secondary" />
|
|
37
|
+
</View>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// --- ListItemContent (title + subtitle) ---
|
|
42
|
+
|
|
43
|
+
export interface ListItemContentProps extends ViewProps {
|
|
44
|
+
title: string
|
|
45
|
+
subtitle?: string
|
|
46
|
+
className?: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function ListItemContent({ title, subtitle, className, ...props }: ListItemContentProps) {
|
|
50
|
+
return (
|
|
51
|
+
<View className={cn('flex-1 justify-center', className)} {...props}>
|
|
52
|
+
<Text className="text-sm font-medium text-text-primary">{title}</Text>
|
|
53
|
+
{subtitle && (
|
|
54
|
+
<Text className="text-xs text-text-secondary mt-0.5">{subtitle}</Text>
|
|
55
|
+
)}
|
|
56
|
+
</View>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// --- ListItemTrailing (right-side slot) ---
|
|
61
|
+
|
|
62
|
+
export interface ListItemTrailingProps extends ViewProps {
|
|
63
|
+
className?: string
|
|
64
|
+
children: React.ReactNode
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function ListItemTrailing({ className, children, ...props }: ListItemTrailingProps) {
|
|
68
|
+
return (
|
|
69
|
+
<View className={cn('ml-3 items-center justify-center', className)} {...props}>
|
|
70
|
+
{children}
|
|
71
|
+
</View>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// --- ListItemDivider ---
|
|
76
|
+
|
|
77
|
+
export interface ListItemDividerProps extends ViewProps {
|
|
78
|
+
inset?: boolean
|
|
79
|
+
className?: string
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function ListItemDivider({ inset = true, className, ...props }: ListItemDividerProps) {
|
|
83
|
+
return (
|
|
84
|
+
<View
|
|
85
|
+
className={cn('h-px bg-divider', inset ? 'ml-14' : '', className)}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export {
|
|
2
|
+
ListItem,
|
|
3
|
+
ListItemIcon,
|
|
4
|
+
ListItemContent,
|
|
5
|
+
ListItemTrailing,
|
|
6
|
+
ListItemDivider,
|
|
7
|
+
} from './ListItem'
|
|
8
|
+
export type {
|
|
9
|
+
ListItemProps,
|
|
10
|
+
ListItemIconProps,
|
|
11
|
+
ListItemContentProps,
|
|
12
|
+
ListItemTrailingProps,
|
|
13
|
+
ListItemDividerProps,
|
|
14
|
+
} from './ListItem'
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { View, Text } from 'react-native'
|
|
3
|
+
import { Menu, MenuTrigger, MenuList, MenuItem, MenuDivider, MenuGroup } from './Menu'
|
|
4
|
+
import { Button, ButtonText } from '../button/Button'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Menu> = {
|
|
7
|
+
title: 'Components/Menu',
|
|
8
|
+
component: Menu,
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
argTypes: {
|
|
11
|
+
isOpen: {
|
|
12
|
+
control: 'boolean',
|
|
13
|
+
description: 'Controlled open state',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
decorators: [
|
|
17
|
+
(Story) => (
|
|
18
|
+
<View style={{ minHeight: 300, padding: 16 }}>
|
|
19
|
+
<Story />
|
|
20
|
+
</View>
|
|
21
|
+
),
|
|
22
|
+
],
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default meta
|
|
26
|
+
type Story = StoryObj<typeof Menu>
|
|
27
|
+
|
|
28
|
+
export const Default: Story = {
|
|
29
|
+
render: () => (
|
|
30
|
+
<Menu>
|
|
31
|
+
<MenuTrigger>
|
|
32
|
+
<Button variant="outline" color="primary">
|
|
33
|
+
<ButtonText>Options</ButtonText>
|
|
34
|
+
</Button>
|
|
35
|
+
</MenuTrigger>
|
|
36
|
+
<MenuList>
|
|
37
|
+
<MenuItem onPress={() => {}}>Edit</MenuItem>
|
|
38
|
+
<MenuItem onPress={() => {}}>Duplicate</MenuItem>
|
|
39
|
+
<MenuItem onPress={() => {}}>Share</MenuItem>
|
|
40
|
+
</MenuList>
|
|
41
|
+
</Menu>
|
|
42
|
+
),
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const WithGroups: Story = {
|
|
46
|
+
render: () => (
|
|
47
|
+
<Menu>
|
|
48
|
+
<MenuTrigger>
|
|
49
|
+
<Button variant="outline" color="primary">
|
|
50
|
+
<ButtonText>Actions</ButtonText>
|
|
51
|
+
</Button>
|
|
52
|
+
</MenuTrigger>
|
|
53
|
+
<MenuList>
|
|
54
|
+
<MenuGroup label="Edit">
|
|
55
|
+
<MenuItem onPress={() => {}}>Cut</MenuItem>
|
|
56
|
+
<MenuItem onPress={() => {}}>Copy</MenuItem>
|
|
57
|
+
<MenuItem onPress={() => {}}>Paste</MenuItem>
|
|
58
|
+
</MenuGroup>
|
|
59
|
+
<MenuDivider />
|
|
60
|
+
<MenuGroup label="View">
|
|
61
|
+
<MenuItem onPress={() => {}}>Zoom In</MenuItem>
|
|
62
|
+
<MenuItem onPress={() => {}}>Zoom Out</MenuItem>
|
|
63
|
+
<MenuItem onPress={() => {}}>Reset Zoom</MenuItem>
|
|
64
|
+
</MenuGroup>
|
|
65
|
+
</MenuList>
|
|
66
|
+
</Menu>
|
|
67
|
+
),
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function EditIcon({ size = 14 }: { size?: number }) {
|
|
71
|
+
return (
|
|
72
|
+
<Text style={{ fontSize: size, lineHeight: size }}>✎</Text>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function TrashIcon({ size = 14 }: { size?: number }) {
|
|
77
|
+
return (
|
|
78
|
+
<Text style={{ fontSize: size, lineHeight: size }}>🗑</Text>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function CopyIcon({ size = 14 }: { size?: number }) {
|
|
83
|
+
return (
|
|
84
|
+
<Text style={{ fontSize: size, lineHeight: size }}>📋</Text>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const WithIcons: Story = {
|
|
89
|
+
render: () => (
|
|
90
|
+
<Menu>
|
|
91
|
+
<MenuTrigger>
|
|
92
|
+
<Button variant="outline" color="primary">
|
|
93
|
+
<ButtonText>More</ButtonText>
|
|
94
|
+
</Button>
|
|
95
|
+
</MenuTrigger>
|
|
96
|
+
<MenuList>
|
|
97
|
+
<MenuItem icon={<EditIcon />} onPress={() => {}}>Edit</MenuItem>
|
|
98
|
+
<MenuItem icon={<CopyIcon />} onPress={() => {}}>Duplicate</MenuItem>
|
|
99
|
+
<MenuDivider />
|
|
100
|
+
<MenuItem icon={<TrashIcon />} onPress={() => {}} isDestructive>Delete</MenuItem>
|
|
101
|
+
</MenuList>
|
|
102
|
+
</Menu>
|
|
103
|
+
),
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export const DestructiveItem: Story = {
|
|
107
|
+
render: () => (
|
|
108
|
+
<Menu>
|
|
109
|
+
<MenuTrigger>
|
|
110
|
+
<Button variant="outline" color="primary">
|
|
111
|
+
<ButtonText>Manage</ButtonText>
|
|
112
|
+
</Button>
|
|
113
|
+
</MenuTrigger>
|
|
114
|
+
<MenuList>
|
|
115
|
+
<MenuItem onPress={() => {}}>Edit Profile</MenuItem>
|
|
116
|
+
<MenuItem onPress={() => {}}>Change Password</MenuItem>
|
|
117
|
+
<MenuDivider />
|
|
118
|
+
<MenuItem onPress={() => {}} isDestructive>Delete Account</MenuItem>
|
|
119
|
+
</MenuList>
|
|
120
|
+
</Menu>
|
|
121
|
+
),
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export const WithDividers: Story = {
|
|
125
|
+
render: () => (
|
|
126
|
+
<Menu>
|
|
127
|
+
<MenuTrigger>
|
|
128
|
+
<Button variant="outline" color="primary">
|
|
129
|
+
<ButtonText>File</ButtonText>
|
|
130
|
+
</Button>
|
|
131
|
+
</MenuTrigger>
|
|
132
|
+
<MenuList>
|
|
133
|
+
<MenuItem onPress={() => {}}>New File</MenuItem>
|
|
134
|
+
<MenuItem onPress={() => {}}>Open File</MenuItem>
|
|
135
|
+
<MenuDivider />
|
|
136
|
+
<MenuItem onPress={() => {}}>Save</MenuItem>
|
|
137
|
+
<MenuItem onPress={() => {}}>Save As</MenuItem>
|
|
138
|
+
<MenuDivider />
|
|
139
|
+
<MenuItem onPress={() => {}}>Export</MenuItem>
|
|
140
|
+
</MenuList>
|
|
141
|
+
</Menu>
|
|
142
|
+
),
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export const DisabledItems: Story = {
|
|
146
|
+
render: () => (
|
|
147
|
+
<Menu>
|
|
148
|
+
<MenuTrigger>
|
|
149
|
+
<Button variant="outline" color="primary">
|
|
150
|
+
<ButtonText>Edit</ButtonText>
|
|
151
|
+
</Button>
|
|
152
|
+
</MenuTrigger>
|
|
153
|
+
<MenuList>
|
|
154
|
+
<MenuItem onPress={() => {}}>Undo</MenuItem>
|
|
155
|
+
<MenuItem onPress={() => {}} isDisabled>Redo</MenuItem>
|
|
156
|
+
<MenuDivider />
|
|
157
|
+
<MenuItem onPress={() => {}}>Cut</MenuItem>
|
|
158
|
+
<MenuItem onPress={() => {}}>Copy</MenuItem>
|
|
159
|
+
<MenuItem onPress={() => {}} isDisabled>Paste</MenuItem>
|
|
160
|
+
</MenuList>
|
|
161
|
+
</Menu>
|
|
162
|
+
),
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export const Controlled: Story = {
|
|
166
|
+
render: () => (
|
|
167
|
+
<Menu isOpen>
|
|
168
|
+
<MenuTrigger>
|
|
169
|
+
<Button variant="outline" color="primary">
|
|
170
|
+
<ButtonText>Always Open</ButtonText>
|
|
171
|
+
</Button>
|
|
172
|
+
</MenuTrigger>
|
|
173
|
+
<MenuList>
|
|
174
|
+
<MenuItem onPress={() => {}}>Item One</MenuItem>
|
|
175
|
+
<MenuItem onPress={() => {}}>Item Two</MenuItem>
|
|
176
|
+
<MenuItem onPress={() => {}}>Item Three</MenuItem>
|
|
177
|
+
</MenuList>
|
|
178
|
+
</Menu>
|
|
179
|
+
),
|
|
180
|
+
}
|