@titan-design/react-ui 0.1.0 → 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/package.json +10 -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
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { View } from 'react-native'
|
|
3
|
+
import {
|
|
4
|
+
Typography,
|
|
5
|
+
Heading,
|
|
6
|
+
Paragraph,
|
|
7
|
+
Caption,
|
|
8
|
+
Label,
|
|
9
|
+
Overline,
|
|
10
|
+
} from './Typography'
|
|
11
|
+
|
|
12
|
+
const meta: Meta<typeof Typography> = {
|
|
13
|
+
title: 'Custom/Typography',
|
|
14
|
+
component: Typography,
|
|
15
|
+
tags: ['autodocs'],
|
|
16
|
+
argTypes: {
|
|
17
|
+
variant: {
|
|
18
|
+
control: 'select',
|
|
19
|
+
options: [
|
|
20
|
+
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
|
21
|
+
'body1', 'body2', 'subtitle1', 'subtitle2',
|
|
22
|
+
'caption', 'overline', 'button',
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
color: {
|
|
26
|
+
control: 'select',
|
|
27
|
+
options: [
|
|
28
|
+
'primary', 'secondary', 'tertiary', 'disabled',
|
|
29
|
+
'inverse', 'success', 'error', 'warning', 'info', 'inherit',
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
align: {
|
|
33
|
+
control: 'select',
|
|
34
|
+
options: ['left', 'center', 'right', 'justify'],
|
|
35
|
+
},
|
|
36
|
+
noWrap: { control: 'boolean' },
|
|
37
|
+
gutterBottom: { control: 'boolean' },
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default meta
|
|
42
|
+
type Story = StoryObj<typeof Typography>
|
|
43
|
+
|
|
44
|
+
export const Default: Story = {
|
|
45
|
+
args: {
|
|
46
|
+
variant: 'body1',
|
|
47
|
+
children: 'This is body1 text',
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const AllHeadings: Story = {
|
|
52
|
+
render: () => (
|
|
53
|
+
<View style={{ gap: 16 }}>
|
|
54
|
+
<Typography variant="h1">Heading 1</Typography>
|
|
55
|
+
<Typography variant="h2">Heading 2</Typography>
|
|
56
|
+
<Typography variant="h3">Heading 3</Typography>
|
|
57
|
+
<Typography variant="h4">Heading 4</Typography>
|
|
58
|
+
<Typography variant="h5">Heading 5</Typography>
|
|
59
|
+
<Typography variant="h6">Heading 6</Typography>
|
|
60
|
+
</View>
|
|
61
|
+
),
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const AllBody: Story = {
|
|
65
|
+
render: () => (
|
|
66
|
+
<View style={{ gap: 16, maxWidth: 400 }}>
|
|
67
|
+
<Typography variant="body1">
|
|
68
|
+
Body 1 - This is the default body text style. It's used for main content
|
|
69
|
+
and paragraphs throughout the application.
|
|
70
|
+
</Typography>
|
|
71
|
+
<Typography variant="body2">
|
|
72
|
+
Body 2 - This is a smaller body text style. It's useful for secondary
|
|
73
|
+
content or when you need more compact text.
|
|
74
|
+
</Typography>
|
|
75
|
+
<Typography variant="subtitle1">
|
|
76
|
+
Subtitle 1 - Semi-bold text for emphasis
|
|
77
|
+
</Typography>
|
|
78
|
+
<Typography variant="subtitle2">
|
|
79
|
+
Subtitle 2 - Smaller semi-bold text
|
|
80
|
+
</Typography>
|
|
81
|
+
</View>
|
|
82
|
+
),
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const AllUtility: Story = {
|
|
86
|
+
render: () => (
|
|
87
|
+
<View style={{ gap: 16 }}>
|
|
88
|
+
<Typography variant="caption">Caption - Small helper text</Typography>
|
|
89
|
+
<Typography variant="overline">OVERLINE - SECTION LABEL</Typography>
|
|
90
|
+
<Typography variant="button">Button Text</Typography>
|
|
91
|
+
</View>
|
|
92
|
+
),
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const AllColors: Story = {
|
|
96
|
+
render: () => (
|
|
97
|
+
<View style={{ gap: 8 }}>
|
|
98
|
+
<Typography color="primary">Primary text color</Typography>
|
|
99
|
+
<Typography color="secondary">Secondary text color</Typography>
|
|
100
|
+
<Typography color="tertiary">Tertiary text color</Typography>
|
|
101
|
+
<Typography color="disabled">Disabled text color</Typography>
|
|
102
|
+
<Typography color="success">Success text color</Typography>
|
|
103
|
+
<Typography color="error">Error text color</Typography>
|
|
104
|
+
<Typography color="warning">Warning text color</Typography>
|
|
105
|
+
<Typography color="info">Info text color</Typography>
|
|
106
|
+
</View>
|
|
107
|
+
),
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const HeadingComponent: Story = {
|
|
111
|
+
render: () => (
|
|
112
|
+
<View style={{ gap: 8 }}>
|
|
113
|
+
<Heading level={1}>Heading Level 1</Heading>
|
|
114
|
+
<Heading level={2}>Heading Level 2</Heading>
|
|
115
|
+
<Heading level={3}>Heading Level 3</Heading>
|
|
116
|
+
</View>
|
|
117
|
+
),
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const ParagraphComponent: Story = {
|
|
121
|
+
render: () => (
|
|
122
|
+
<View style={{ gap: 16, maxWidth: 400 }}>
|
|
123
|
+
<Paragraph>
|
|
124
|
+
This is a regular paragraph using the default body1 variant.
|
|
125
|
+
It's great for main content.
|
|
126
|
+
</Paragraph>
|
|
127
|
+
<Paragraph small>
|
|
128
|
+
This is a small paragraph using the body2 variant.
|
|
129
|
+
It's useful for secondary content.
|
|
130
|
+
</Paragraph>
|
|
131
|
+
</View>
|
|
132
|
+
),
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export const UtilityComponents: Story = {
|
|
136
|
+
render: () => (
|
|
137
|
+
<View style={{ gap: 12 }}>
|
|
138
|
+
<Overline>Section Title</Overline>
|
|
139
|
+
<Label>Form Label</Label>
|
|
140
|
+
<Caption>Helper text or caption</Caption>
|
|
141
|
+
</View>
|
|
142
|
+
),
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export const WithTruncation: Story = {
|
|
146
|
+
render: () => (
|
|
147
|
+
<View style={{ width: 200 }}>
|
|
148
|
+
<Typography noWrap>
|
|
149
|
+
This is a very long text that will be truncated with an ellipsis
|
|
150
|
+
</Typography>
|
|
151
|
+
</View>
|
|
152
|
+
),
|
|
153
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { render, screen } from '@testing-library/react'
|
|
3
|
+
import { axe } from 'jest-axe'
|
|
4
|
+
import { Typography, Heading, Paragraph, Caption, Label, Overline } from './Typography'
|
|
5
|
+
|
|
6
|
+
describe('Typography', () => {
|
|
7
|
+
it('renders children correctly', () => {
|
|
8
|
+
render(<Typography>Hello World</Typography>)
|
|
9
|
+
expect(screen.getByText('Hello World')).toBeInTheDocument()
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('renders different variants', () => {
|
|
13
|
+
const { rerender } = render(<Typography variant="h1">Heading</Typography>)
|
|
14
|
+
expect(screen.getByRole('heading')).toBeInTheDocument()
|
|
15
|
+
|
|
16
|
+
rerender(<Typography variant="body1">Body text</Typography>)
|
|
17
|
+
expect(screen.getByText('Body text')).toBeInTheDocument()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('applies color styles', () => {
|
|
21
|
+
render(<Typography color="secondary">Secondary text</Typography>)
|
|
22
|
+
expect(screen.getByText('Secondary text')).toBeInTheDocument()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('applies text alignment', () => {
|
|
26
|
+
render(<Typography align="center">Centered text</Typography>)
|
|
27
|
+
expect(screen.getByText('Centered text')).toBeInTheDocument()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('applies noWrap truncation', () => {
|
|
31
|
+
render(<Typography noWrap>Long text that should truncate</Typography>)
|
|
32
|
+
const text = screen.getByText('Long text that should truncate')
|
|
33
|
+
// On web, noWrap renders as CSS truncation styles rather than numberOfLines attribute
|
|
34
|
+
expect(text).toBeInTheDocument()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe('Heading component', () => {
|
|
38
|
+
it('renders correct heading level', () => {
|
|
39
|
+
const { rerender } = render(<Heading level={1}>H1</Heading>)
|
|
40
|
+
expect(screen.getByRole('heading')).toBeInTheDocument()
|
|
41
|
+
|
|
42
|
+
rerender(<Heading level={3}>H3</Heading>)
|
|
43
|
+
expect(screen.getByRole('heading')).toBeInTheDocument()
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
describe('Paragraph component', () => {
|
|
48
|
+
it('renders body1 by default', () => {
|
|
49
|
+
render(<Paragraph>Regular paragraph</Paragraph>)
|
|
50
|
+
expect(screen.getByText('Regular paragraph')).toBeInTheDocument()
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('renders body2 when small prop is true', () => {
|
|
54
|
+
render(<Paragraph small>Small paragraph</Paragraph>)
|
|
55
|
+
expect(screen.getByText('Small paragraph')).toBeInTheDocument()
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
describe('Caption component', () => {
|
|
60
|
+
it('renders with caption variant', () => {
|
|
61
|
+
render(<Caption>Caption text</Caption>)
|
|
62
|
+
expect(screen.getByText('Caption text')).toBeInTheDocument()
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
describe('Label component', () => {
|
|
67
|
+
it('renders with subtitle2 variant', () => {
|
|
68
|
+
render(<Label>Label text</Label>)
|
|
69
|
+
expect(screen.getByText('Label text')).toBeInTheDocument()
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
describe('Overline component', () => {
|
|
74
|
+
it('renders with overline variant', () => {
|
|
75
|
+
render(<Overline>OVERLINE TEXT</Overline>)
|
|
76
|
+
expect(screen.getByText('OVERLINE TEXT')).toBeInTheDocument()
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
describe('accessibility', () => {
|
|
81
|
+
it('headings have no accessibility violations', async () => {
|
|
82
|
+
const { container } = render(
|
|
83
|
+
<div>
|
|
84
|
+
<Typography variant="h1">Main Title</Typography>
|
|
85
|
+
<Typography variant="h2">Section Title</Typography>
|
|
86
|
+
</div>
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
const results = await axe(container)
|
|
90
|
+
expect(results).toHaveNoViolations()
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('body text has no accessibility violations', async () => {
|
|
94
|
+
const { container } = render(
|
|
95
|
+
<Typography variant="body1">Body text content</Typography>
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
const results = await axe(container)
|
|
99
|
+
expect(results).toHaveNoViolations()
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('headings have correct accessibility role', () => {
|
|
103
|
+
render(<Typography variant="h2">Section Heading</Typography>)
|
|
104
|
+
expect(screen.getByRole('heading')).toBeInTheDocument()
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
})
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Text, type TextProps } from 'react-native'
|
|
3
|
+
import { cn } from '../../../utils/cn'
|
|
4
|
+
|
|
5
|
+
export type TypographyVariant =
|
|
6
|
+
| 'h1'
|
|
7
|
+
| 'h2'
|
|
8
|
+
| 'h3'
|
|
9
|
+
| 'h4'
|
|
10
|
+
| 'h5'
|
|
11
|
+
| 'h6'
|
|
12
|
+
| 'body1'
|
|
13
|
+
| 'body2'
|
|
14
|
+
| 'subtitle1'
|
|
15
|
+
| 'subtitle2'
|
|
16
|
+
| 'caption'
|
|
17
|
+
| 'overline'
|
|
18
|
+
| 'button'
|
|
19
|
+
|
|
20
|
+
export type TypographyColor =
|
|
21
|
+
| 'primary'
|
|
22
|
+
| 'secondary'
|
|
23
|
+
| 'tertiary'
|
|
24
|
+
| 'disabled'
|
|
25
|
+
| 'inverse'
|
|
26
|
+
| 'success'
|
|
27
|
+
| 'error'
|
|
28
|
+
| 'warning'
|
|
29
|
+
| 'info'
|
|
30
|
+
| 'inherit'
|
|
31
|
+
|
|
32
|
+
export type TypographyAlign = 'left' | 'center' | 'right' | 'justify'
|
|
33
|
+
|
|
34
|
+
export type TypographyMarginBottom = 'none' | 'sm' | 'md' | 'lg'
|
|
35
|
+
|
|
36
|
+
export interface TypographyProps extends TextProps {
|
|
37
|
+
/** Typography variant */
|
|
38
|
+
variant?: TypographyVariant
|
|
39
|
+
/** Text color */
|
|
40
|
+
color?: TypographyColor
|
|
41
|
+
/** Text alignment */
|
|
42
|
+
align?: TypographyAlign
|
|
43
|
+
/** Whether text should truncate with ellipsis */
|
|
44
|
+
truncate?: boolean
|
|
45
|
+
/** @deprecated Use truncate instead */
|
|
46
|
+
noWrap?: boolean
|
|
47
|
+
/** Margin bottom spacing */
|
|
48
|
+
marginBottom?: TypographyMarginBottom
|
|
49
|
+
/** @deprecated Use marginBottom instead */
|
|
50
|
+
gutterBottom?: boolean
|
|
51
|
+
/** Maximum number of lines before truncating */
|
|
52
|
+
maxLines?: number
|
|
53
|
+
/** Additional className */
|
|
54
|
+
className?: string
|
|
55
|
+
children?: React.ReactNode
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Variant styles matching the existing theme
|
|
59
|
+
const variantStyles: Record<TypographyVariant, string> = {
|
|
60
|
+
h1: 'font-heading text-5xl font-bold leading-tight',
|
|
61
|
+
h2: 'font-heading text-4xl font-bold leading-tight',
|
|
62
|
+
h3: 'font-heading text-3xl font-bold leading-tight',
|
|
63
|
+
h4: 'font-heading text-2xl font-bold leading-tight',
|
|
64
|
+
h5: 'font-heading text-xl font-semibold leading-tight',
|
|
65
|
+
h6: 'font-heading text-lg font-semibold leading-tight',
|
|
66
|
+
body1: 'font-body text-base font-normal leading-normal',
|
|
67
|
+
body2: 'font-body text-sm font-normal leading-normal',
|
|
68
|
+
subtitle1: 'font-body text-base font-medium leading-relaxed',
|
|
69
|
+
subtitle2: 'font-body text-sm font-medium leading-normal',
|
|
70
|
+
caption: 'font-body text-xs font-normal leading-loose',
|
|
71
|
+
overline: 'font-body text-xs font-semibold uppercase tracking-widest leading-relaxed',
|
|
72
|
+
button: 'font-sans text-sm font-semibold leading-relaxed',
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const colorStyles: Record<TypographyColor, string> = {
|
|
76
|
+
primary: 'text-text-primary',
|
|
77
|
+
secondary: 'text-text-secondary',
|
|
78
|
+
tertiary: 'text-text-tertiary',
|
|
79
|
+
disabled: 'text-text-disabled',
|
|
80
|
+
inverse: 'text-text-inverse',
|
|
81
|
+
success: 'text-status-success',
|
|
82
|
+
error: 'text-status-error',
|
|
83
|
+
warning: 'text-status-warning',
|
|
84
|
+
info: 'text-status-info',
|
|
85
|
+
inherit: '', // No color class, inherit from parent
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const alignStyles: Record<TypographyAlign, string> = {
|
|
89
|
+
left: 'text-left',
|
|
90
|
+
center: 'text-center',
|
|
91
|
+
right: 'text-right',
|
|
92
|
+
justify: 'text-justify',
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Heading variants for accessibility role
|
|
96
|
+
const headingVariants: TypographyVariant[] = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
|
|
97
|
+
|
|
98
|
+
const marginBottomStyles: Record<TypographyMarginBottom, string> = {
|
|
99
|
+
none: '',
|
|
100
|
+
sm: 'mb-1',
|
|
101
|
+
md: 'mb-2',
|
|
102
|
+
lg: 'mb-4',
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Typography component for consistent text styling.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* <Typography variant="h1">Page Title</Typography>
|
|
110
|
+
* <Typography variant="body1" color="secondary">Body text content</Typography>
|
|
111
|
+
* <Typography variant="caption" marginBottom="md">Small caption text</Typography>
|
|
112
|
+
* <Typography truncate maxLines={2}>Long text that will truncate...</Typography>
|
|
113
|
+
*/
|
|
114
|
+
export function Typography({
|
|
115
|
+
variant = 'body1',
|
|
116
|
+
color = 'primary',
|
|
117
|
+
align,
|
|
118
|
+
truncate = false,
|
|
119
|
+
noWrap = false, // deprecated, kept for backward compatibility
|
|
120
|
+
marginBottom,
|
|
121
|
+
gutterBottom = false, // deprecated, kept for backward compatibility
|
|
122
|
+
maxLines,
|
|
123
|
+
className,
|
|
124
|
+
children,
|
|
125
|
+
...props
|
|
126
|
+
}: TypographyProps) {
|
|
127
|
+
const isHeading = headingVariants.includes(variant)
|
|
128
|
+
const shouldTruncate = truncate || noWrap
|
|
129
|
+
const mb = marginBottom || (gutterBottom ? 'md' : 'none')
|
|
130
|
+
|
|
131
|
+
// Determine number of lines
|
|
132
|
+
let numLines: number | undefined
|
|
133
|
+
if (maxLines) {
|
|
134
|
+
numLines = maxLines
|
|
135
|
+
} else if (shouldTruncate) {
|
|
136
|
+
numLines = 1
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<Text
|
|
141
|
+
accessibilityRole={isHeading ? 'header' : 'text'}
|
|
142
|
+
numberOfLines={numLines}
|
|
143
|
+
className={cn(
|
|
144
|
+
variantStyles[variant],
|
|
145
|
+
color !== 'inherit' && colorStyles[color],
|
|
146
|
+
align && alignStyles[align],
|
|
147
|
+
shouldTruncate && 'truncate',
|
|
148
|
+
marginBottomStyles[mb],
|
|
149
|
+
className
|
|
150
|
+
)}
|
|
151
|
+
{...props}
|
|
152
|
+
>
|
|
153
|
+
{children}
|
|
154
|
+
</Text>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Convenience components for common typography patterns
|
|
159
|
+
|
|
160
|
+
export interface HeadingProps extends Omit<TypographyProps, 'variant'> {
|
|
161
|
+
/** Heading level (1-6) */
|
|
162
|
+
level?: 1 | 2 | 3 | 4 | 5 | 6
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Heading component - convenience wrapper for Typography with heading variants.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* <Heading level={1}>Page Title</Heading>
|
|
170
|
+
* <Heading level={2} gutterBottom>Section Title</Heading>
|
|
171
|
+
*/
|
|
172
|
+
export function Heading({ level = 1, ...props }: HeadingProps) {
|
|
173
|
+
const variant = `h${level}` as TypographyVariant
|
|
174
|
+
return <Typography variant={variant} {...props} />
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface ParagraphProps extends Omit<TypographyProps, 'variant'> {
|
|
178
|
+
/** Use smaller body2 variant */
|
|
179
|
+
small?: boolean
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Paragraph component - convenience wrapper for body text.
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* <Paragraph>This is body text content.</Paragraph>
|
|
187
|
+
* <Paragraph small>This is smaller body text.</Paragraph>
|
|
188
|
+
*/
|
|
189
|
+
export function Paragraph({ small = false, ...props }: ParagraphProps) {
|
|
190
|
+
return <Typography variant={small ? 'body2' : 'body1'} {...props} />
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface CaptionProps extends Omit<TypographyProps, 'variant'> {}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Caption component - for small helper/secondary text.
|
|
197
|
+
*/
|
|
198
|
+
export function Caption(props: CaptionProps) {
|
|
199
|
+
return <Typography variant="caption" color="secondary" {...props} />
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export interface LabelProps extends Omit<TypographyProps, 'variant'> {}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Label component - for form labels and similar.
|
|
206
|
+
*/
|
|
207
|
+
export function Label(props: LabelProps) {
|
|
208
|
+
return <Typography variant="subtitle2" {...props} />
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export interface OverlineProps extends Omit<TypographyProps, 'variant'> {}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Overline component - for section labels, categories.
|
|
215
|
+
*/
|
|
216
|
+
export function Overline(props: OverlineProps) {
|
|
217
|
+
return <Typography variant="overline" color="secondary" {...props} />
|
|
218
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export {
|
|
2
|
+
Typography,
|
|
3
|
+
Heading,
|
|
4
|
+
Paragraph,
|
|
5
|
+
Caption,
|
|
6
|
+
Label,
|
|
7
|
+
Overline,
|
|
8
|
+
} from './Typography'
|
|
9
|
+
export type {
|
|
10
|
+
TypographyProps,
|
|
11
|
+
TypographyVariant,
|
|
12
|
+
TypographyColor,
|
|
13
|
+
TypographyAlign,
|
|
14
|
+
HeadingProps,
|
|
15
|
+
ParagraphProps,
|
|
16
|
+
CaptionProps,
|
|
17
|
+
LabelProps,
|
|
18
|
+
OverlineProps,
|
|
19
|
+
} from './Typography'
|