@oztix/roadie-components 0.2.1 → 1.0.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.
- package/dist/Button.d.ts +31 -111
- package/dist/Button.js +1 -1
- package/dist/Code.d.ts +15 -5
- package/dist/Code.js +1 -1
- package/dist/Heading.d.ts +23 -6
- package/dist/Heading.js +1 -1
- package/dist/Text.d.ts +24 -37
- package/dist/Text.js +1 -1
- package/dist/View.d.ts +7 -70
- package/dist/View.js +1 -1
- package/dist/_chunks/chunk-6FIUWNC7.js +9 -0
- package/dist/_chunks/chunk-6FIUWNC7.js.map +1 -0
- package/dist/_chunks/chunk-GSK3G4DW.js +9 -0
- package/dist/_chunks/chunk-GSK3G4DW.js.map +1 -0
- package/dist/_chunks/chunk-P5L5LN6E.js +9 -0
- package/dist/_chunks/chunk-P5L5LN6E.js.map +1 -0
- package/dist/_chunks/chunk-RJEJUZ3O.js +7 -0
- package/dist/_chunks/chunk-RJEJUZ3O.js.map +1 -0
- package/dist/_chunks/chunk-VDMZIGT2.js +10 -0
- package/dist/_chunks/chunk-VDMZIGT2.js.map +1 -0
- package/dist/index.d.ts +5 -6
- package/dist/index.js +5 -5
- package/package.json +10 -5
- package/src/components/Button/Button.test.tsx +156 -0
- package/src/components/Button/index.tsx +48 -0
- package/src/components/Code/Code.test.tsx +85 -0
- package/src/components/Code/index.tsx +37 -0
- package/src/components/Heading/Heading.test.tsx +128 -0
- package/src/components/Heading/index.tsx +52 -0
- package/src/components/Text/Text.test.tsx +121 -0
- package/src/components/Text/index.tsx +54 -0
- package/src/components/View/View.test.tsx +161 -0
- package/src/components/View/index.tsx +12 -0
- package/src/components/index.ts +14 -0
- package/src/index.test.tsx +37 -0
- package/src/index.tsx +5 -0
- package/dist/_chunks/chunk-5EABNA3A.js +0 -8
- package/dist/_chunks/chunk-5EABNA3A.js.map +0 -1
- package/dist/_chunks/chunk-72747LP3.js +0 -8
- package/dist/_chunks/chunk-72747LP3.js.map +0 -1
- package/dist/_chunks/chunk-76TTGNAE.js +0 -8
- package/dist/_chunks/chunk-76TTGNAE.js.map +0 -1
- package/dist/_chunks/chunk-CNVMCX74.js +0 -8
- package/dist/_chunks/chunk-CNVMCX74.js.map +0 -1
- package/dist/_chunks/chunk-JDVDZGUN.js +0 -9
- package/dist/_chunks/chunk-JDVDZGUN.js.map +0 -1
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { render } from '@testing-library/react'
|
|
2
|
+
import userEvent from '@testing-library/user-event'
|
|
3
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
4
|
+
|
|
5
|
+
import { Button } from '.'
|
|
6
|
+
|
|
7
|
+
describe('Button', () => {
|
|
8
|
+
it('renders with default props', () => {
|
|
9
|
+
const { getByText } = render(<Button>Click me</Button>)
|
|
10
|
+
const button = getByText('Click me')
|
|
11
|
+
expect(button).toBeInTheDocument()
|
|
12
|
+
expect(button.tagName.toLowerCase()).toBe('button')
|
|
13
|
+
expect(button).toHaveClass(
|
|
14
|
+
'button',
|
|
15
|
+
'button--emphasis_default',
|
|
16
|
+
'button--size_md'
|
|
17
|
+
)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('renders with different emphasis', () => {
|
|
21
|
+
const { rerender, getByText } = render(
|
|
22
|
+
<Button emphasis='strong' colorPalette='accent'>
|
|
23
|
+
Strong
|
|
24
|
+
</Button>
|
|
25
|
+
)
|
|
26
|
+
let button = getByText('Strong')
|
|
27
|
+
expect(button).toHaveClass(
|
|
28
|
+
'button--emphasis_strong',
|
|
29
|
+
'color-palette_accent'
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
rerender(<Button emphasis='default'>Default</Button>)
|
|
33
|
+
button = getByText('Default')
|
|
34
|
+
expect(button).toHaveClass('button--emphasis_default')
|
|
35
|
+
|
|
36
|
+
rerender(<Button emphasis='subtle'>Subtle</Button>)
|
|
37
|
+
button = getByText('Subtle')
|
|
38
|
+
expect(button).toHaveClass('button--emphasis_subtle')
|
|
39
|
+
|
|
40
|
+
rerender(<Button emphasis='subtler'>subtler</Button>)
|
|
41
|
+
button = getByText('subtler')
|
|
42
|
+
expect(button).toHaveClass('button--emphasis_subtler')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('renders with different sizes', () => {
|
|
46
|
+
const { rerender, getByText } = render(<Button size='sm'>Small</Button>)
|
|
47
|
+
let button = getByText('Small')
|
|
48
|
+
expect(button).toHaveClass('button--size_sm')
|
|
49
|
+
|
|
50
|
+
rerender(<Button size='md'>Medium</Button>)
|
|
51
|
+
button = getByText('Medium')
|
|
52
|
+
expect(button).toHaveClass('button--size_md')
|
|
53
|
+
|
|
54
|
+
rerender(<Button size='lg'>Large</Button>)
|
|
55
|
+
button = getByText('Large')
|
|
56
|
+
expect(button).toHaveClass('button--size_lg')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('renders with different color palettes', () => {
|
|
60
|
+
const { rerender, getByText } = render(
|
|
61
|
+
<Button emphasis='strong' colorPalette='accent'>
|
|
62
|
+
Accent
|
|
63
|
+
</Button>
|
|
64
|
+
)
|
|
65
|
+
let button = getByText('Accent')
|
|
66
|
+
expect(button).toHaveClass('color-palette_accent')
|
|
67
|
+
|
|
68
|
+
rerender(
|
|
69
|
+
<Button emphasis='strong' colorPalette='success'>
|
|
70
|
+
Success
|
|
71
|
+
</Button>
|
|
72
|
+
)
|
|
73
|
+
button = getByText('Success')
|
|
74
|
+
expect(button).toHaveClass('color-palette_success')
|
|
75
|
+
|
|
76
|
+
rerender(
|
|
77
|
+
<Button emphasis='strong' colorPalette='warning'>
|
|
78
|
+
Warning
|
|
79
|
+
</Button>
|
|
80
|
+
)
|
|
81
|
+
button = getByText('Warning')
|
|
82
|
+
expect(button).toHaveClass('color-palette_warning')
|
|
83
|
+
|
|
84
|
+
rerender(
|
|
85
|
+
<Button emphasis='strong' colorPalette='danger'>
|
|
86
|
+
Danger
|
|
87
|
+
</Button>
|
|
88
|
+
)
|
|
89
|
+
button = getByText('Danger')
|
|
90
|
+
expect(button).toHaveClass('color-palette_danger')
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('handles disabled state', () => {
|
|
94
|
+
const { getByText } = render(<Button disabled>Disabled</Button>)
|
|
95
|
+
const button = getByText('Disabled')
|
|
96
|
+
expect(button).toBeDisabled()
|
|
97
|
+
expect(button).toHaveClass('button')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('calls onClick when clicked', async () => {
|
|
101
|
+
const handleClick = vi.fn()
|
|
102
|
+
const user = userEvent.setup()
|
|
103
|
+
|
|
104
|
+
const { getByText } = render(
|
|
105
|
+
<Button onClick={handleClick}>Click me</Button>
|
|
106
|
+
)
|
|
107
|
+
const button = getByText('Click me')
|
|
108
|
+
|
|
109
|
+
await user.click(button)
|
|
110
|
+
expect(handleClick).toHaveBeenCalledTimes(1)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('does not call onClick when disabled', async () => {
|
|
114
|
+
const handleClick = vi.fn()
|
|
115
|
+
const user = userEvent.setup()
|
|
116
|
+
|
|
117
|
+
const { getByText } = render(
|
|
118
|
+
<Button disabled onClick={handleClick}>
|
|
119
|
+
Click me
|
|
120
|
+
</Button>
|
|
121
|
+
)
|
|
122
|
+
const button = getByText('Click me')
|
|
123
|
+
|
|
124
|
+
await user.click(button)
|
|
125
|
+
expect(handleClick).not.toHaveBeenCalled()
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('applies custom className', () => {
|
|
129
|
+
const { getByText } = render(
|
|
130
|
+
<Button className='custom-class'>Custom</Button>
|
|
131
|
+
)
|
|
132
|
+
const button = getByText('Custom')
|
|
133
|
+
expect(button).toHaveClass('custom-class')
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('combines multiple props', () => {
|
|
137
|
+
const { getByText } = render(
|
|
138
|
+
<Button
|
|
139
|
+
emphasis='strong'
|
|
140
|
+
size='lg'
|
|
141
|
+
colorPalette='accent'
|
|
142
|
+
className='custom-class'
|
|
143
|
+
>
|
|
144
|
+
Combined
|
|
145
|
+
</Button>
|
|
146
|
+
)
|
|
147
|
+
const button = getByText('Combined')
|
|
148
|
+
expect(button).toHaveClass(
|
|
149
|
+
'button',
|
|
150
|
+
'button--emphasis_strong',
|
|
151
|
+
'button--size_lg',
|
|
152
|
+
'color-palette_accent',
|
|
153
|
+
'custom-class'
|
|
154
|
+
)
|
|
155
|
+
})
|
|
156
|
+
})
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { ark } from '@ark-ui/react/factory'
|
|
4
|
+
|
|
5
|
+
import type { ColorPalette } from '@oztix/roadie-core'
|
|
6
|
+
import { styled } from '@oztix/roadie-core/jsx'
|
|
7
|
+
import type { HTMLStyledProps } from '@oztix/roadie-core/jsx'
|
|
8
|
+
import { type ButtonVariantProps, button } from '@oztix/roadie-core/recipes'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A button component with various emphasis levels and sizes
|
|
12
|
+
*/
|
|
13
|
+
export interface ButtonProps extends HTMLStyledProps<'button'> {
|
|
14
|
+
/**
|
|
15
|
+
* The visual emphasis of the button
|
|
16
|
+
* @default 'default'
|
|
17
|
+
*/
|
|
18
|
+
emphasis?: ButtonVariantProps['emphasis']
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The size of the button
|
|
22
|
+
* @default 'md'
|
|
23
|
+
*/
|
|
24
|
+
size?: ButtonVariantProps['size']
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The color palette to use for the button
|
|
28
|
+
* @default 'neutral'
|
|
29
|
+
*/
|
|
30
|
+
colorPalette?: ColorPalette
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* When true, the component will pass props to its child component
|
|
34
|
+
*/
|
|
35
|
+
asChild?: boolean
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The content to display
|
|
39
|
+
*/
|
|
40
|
+
children?: React.ReactNode
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const Button = styled(
|
|
44
|
+
ark.button,
|
|
45
|
+
button
|
|
46
|
+
) as React.ForwardRefExoticComponent<ButtonProps>
|
|
47
|
+
|
|
48
|
+
Button.displayName = 'Button'
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { render } from '@testing-library/react'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { Code } from './index'
|
|
5
|
+
|
|
6
|
+
describe('Code', () => {
|
|
7
|
+
it('renders with default props', () => {
|
|
8
|
+
const { getByText } = render(<Code>const x = 42;</Code>)
|
|
9
|
+
const code = getByText('const x = 42;')
|
|
10
|
+
expect(code).toBeInTheDocument()
|
|
11
|
+
expect(code.tagName.toLowerCase()).toBe('code')
|
|
12
|
+
expect(code).toHaveClass('code', 'code--emphasis_default')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('renders with different emphasis', () => {
|
|
16
|
+
const { rerender, getByText } = render(
|
|
17
|
+
<Code emphasis='strong'>strong code</Code>
|
|
18
|
+
)
|
|
19
|
+
let code = getByText('strong code')
|
|
20
|
+
expect(code).toHaveClass('code--emphasis_strong')
|
|
21
|
+
|
|
22
|
+
rerender(<Code emphasis='subtle'>subtle code</Code>)
|
|
23
|
+
code = getByText('subtle code')
|
|
24
|
+
expect(code).toHaveClass('code--emphasis_subtle')
|
|
25
|
+
|
|
26
|
+
rerender(<Code emphasis='subtler'>subtler code</Code>)
|
|
27
|
+
code = getByText('subtler code')
|
|
28
|
+
expect(code).toHaveClass('code--emphasis_subtler')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('renders with different color palettes', () => {
|
|
32
|
+
const { rerender, getByText } = render(
|
|
33
|
+
<Code colorPalette='accent'>accent code</Code>
|
|
34
|
+
)
|
|
35
|
+
let code = getByText('accent code')
|
|
36
|
+
expect(code).toBeInTheDocument()
|
|
37
|
+
|
|
38
|
+
rerender(<Code colorPalette='success'>success code</Code>)
|
|
39
|
+
code = getByText('success code')
|
|
40
|
+
expect(code).toBeInTheDocument()
|
|
41
|
+
|
|
42
|
+
rerender(<Code colorPalette='danger'>danger code</Code>)
|
|
43
|
+
code = getByText('danger code')
|
|
44
|
+
expect(code).toBeInTheDocument()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('applies custom font size', () => {
|
|
48
|
+
const { getByText } = render(<Code fontSize='lg'>Large code</Code>)
|
|
49
|
+
const code = getByText('Large code')
|
|
50
|
+
expect(code).toHaveClass('fs_lg')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('inherits Text props', () => {
|
|
54
|
+
const { getByText } = render(<Code color='accent.fg'>Colored code</Code>)
|
|
55
|
+
const code = getByText('Colored code')
|
|
56
|
+
expect(code).toHaveClass('c_accent.fg')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('applies line clamp', () => {
|
|
60
|
+
const { getByText } = render(<Code lineClamp={2}>Clamped code</Code>)
|
|
61
|
+
const code = getByText('Clamped code')
|
|
62
|
+
expect(code).toHaveClass('lc_2')
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('combines multiple props', () => {
|
|
66
|
+
const { getByText } = render(
|
|
67
|
+
<Code
|
|
68
|
+
emphasis='subtler'
|
|
69
|
+
colorPalette='accent'
|
|
70
|
+
fontSize='lg'
|
|
71
|
+
className='custom-class'
|
|
72
|
+
>
|
|
73
|
+
Combined styles
|
|
74
|
+
</Code>
|
|
75
|
+
)
|
|
76
|
+
const code = getByText('Combined styles')
|
|
77
|
+
expect(code).toHaveClass(
|
|
78
|
+
'code',
|
|
79
|
+
'code--emphasis_subtler',
|
|
80
|
+
'color-palette_accent',
|
|
81
|
+
'fs_lg',
|
|
82
|
+
'custom-class'
|
|
83
|
+
)
|
|
84
|
+
})
|
|
85
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import { ark } from '@ark-ui/react/factory'
|
|
4
|
+
|
|
5
|
+
import type { ColorPalette } from '@oztix/roadie-core'
|
|
6
|
+
import { styled } from '@oztix/roadie-core/jsx'
|
|
7
|
+
import type { HTMLStyledProps } from '@oztix/roadie-core/jsx'
|
|
8
|
+
import { type CodeVariantProps, code } from '@oztix/roadie-core/recipes'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A code component that inherits from Text and renders as a code element
|
|
12
|
+
*/
|
|
13
|
+
export interface CodeProps extends HTMLStyledProps<'code'> {
|
|
14
|
+
/**
|
|
15
|
+
* The appearance of the code block
|
|
16
|
+
* @default 'default'
|
|
17
|
+
*/
|
|
18
|
+
emphasis?: CodeVariantProps['emphasis']
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The color palette to use for the code
|
|
22
|
+
* @default 'neutral'
|
|
23
|
+
*/
|
|
24
|
+
colorPalette?: ColorPalette
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The content to display
|
|
28
|
+
*/
|
|
29
|
+
children?: ReactNode
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const Code = styled(
|
|
33
|
+
ark.code,
|
|
34
|
+
code
|
|
35
|
+
) as React.ForwardRefExoticComponent<CodeProps>
|
|
36
|
+
|
|
37
|
+
Code.displayName = 'Code'
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { render } from '@testing-library/react'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { Heading } from './index'
|
|
5
|
+
|
|
6
|
+
describe('Heading', () => {
|
|
7
|
+
it('renders with default props', () => {
|
|
8
|
+
const { getByText } = render(<Heading>Hello World</Heading>)
|
|
9
|
+
const heading = getByText('Hello World')
|
|
10
|
+
expect(heading).toBeInTheDocument()
|
|
11
|
+
expect(heading.tagName.toLowerCase()).toBe('h2')
|
|
12
|
+
expect(heading).toHaveClass('heading', 'heading--emphasis_default')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('renders with different heading levels', () => {
|
|
16
|
+
const levels: Array<'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'> = [
|
|
17
|
+
'h1',
|
|
18
|
+
'h2',
|
|
19
|
+
'h3',
|
|
20
|
+
'h4',
|
|
21
|
+
'h5',
|
|
22
|
+
'h6'
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
levels.forEach((level) => {
|
|
26
|
+
const { rerender, getByText } = render(
|
|
27
|
+
<Heading as={level}>{level} Heading</Heading>
|
|
28
|
+
)
|
|
29
|
+
const heading = getByText(`${level} Heading`)
|
|
30
|
+
expect(heading).toBeInTheDocument()
|
|
31
|
+
expect(heading.tagName.toLowerCase()).toBe(level)
|
|
32
|
+
expect(heading).toHaveClass('heading')
|
|
33
|
+
rerender(<></>)
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('applies text style', () => {
|
|
38
|
+
const { getByText } = render(
|
|
39
|
+
<Heading textStyle='display.ui.1'>Large Heading</Heading>
|
|
40
|
+
)
|
|
41
|
+
const heading = getByText('Large Heading')
|
|
42
|
+
expect(heading).toHaveClass('textStyle_display.ui.1')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('inherits Text props', () => {
|
|
46
|
+
const { getByTestId } = render(
|
|
47
|
+
<Heading
|
|
48
|
+
color='neutral.fg.subtle'
|
|
49
|
+
data-testid='heading'
|
|
50
|
+
title='tooltip'
|
|
51
|
+
aria-label='Accessible heading'
|
|
52
|
+
>
|
|
53
|
+
Styled Heading
|
|
54
|
+
</Heading>
|
|
55
|
+
)
|
|
56
|
+
const heading = getByTestId('heading')
|
|
57
|
+
expect(heading).toHaveClass('heading', 'c_neutral.fg.subtle')
|
|
58
|
+
expect(heading).toHaveAttribute('title', 'tooltip')
|
|
59
|
+
expect(heading).toHaveAttribute('aria-label', 'Accessible heading')
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('applies line clamp', () => {
|
|
63
|
+
const { getByText } = render(
|
|
64
|
+
<Heading lineClamp={2}>Multi-line heading that should be clamped</Heading>
|
|
65
|
+
)
|
|
66
|
+
const heading = getByText('Multi-line heading that should be clamped')
|
|
67
|
+
expect(heading).toHaveClass('lc_2')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('renders with different emphasis', () => {
|
|
71
|
+
const { rerender, getByText } = render(
|
|
72
|
+
<Heading emphasis='default'>Default Heading</Heading>
|
|
73
|
+
)
|
|
74
|
+
let heading = getByText('Default Heading')
|
|
75
|
+
expect(heading).toHaveClass('heading--emphasis_default')
|
|
76
|
+
|
|
77
|
+
rerender(<Heading emphasis='strong'>Strong Heading</Heading>)
|
|
78
|
+
heading = getByText('Strong Heading')
|
|
79
|
+
expect(heading).toHaveClass('heading--emphasis_strong')
|
|
80
|
+
|
|
81
|
+
rerender(<Heading emphasis='subtle'>Subtle Heading</Heading>)
|
|
82
|
+
heading = getByText('Subtle Heading')
|
|
83
|
+
expect(heading).toHaveClass('heading--emphasis_subtle')
|
|
84
|
+
|
|
85
|
+
rerender(<Heading emphasis='subtler'>Subtler Heading</Heading>)
|
|
86
|
+
heading = getByText('Subtler Heading')
|
|
87
|
+
expect(heading).toHaveClass('heading--emphasis_subtler')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('renders with different color palettes', () => {
|
|
91
|
+
const { rerender, getByText } = render(
|
|
92
|
+
<Heading colorPalette='neutral'>Neutral Heading</Heading>
|
|
93
|
+
)
|
|
94
|
+
let heading = getByText('Neutral Heading')
|
|
95
|
+
expect(heading).toBeInTheDocument()
|
|
96
|
+
|
|
97
|
+
rerender(<Heading colorPalette='brand'>Brand Heading</Heading>)
|
|
98
|
+
heading = getByText('Brand Heading')
|
|
99
|
+
expect(heading).toBeInTheDocument()
|
|
100
|
+
|
|
101
|
+
rerender(<Heading colorPalette='success'>Success Heading</Heading>)
|
|
102
|
+
heading = getByText('Success Heading')
|
|
103
|
+
expect(heading).toBeInTheDocument()
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('combines multiple props', () => {
|
|
107
|
+
const { getByText } = render(
|
|
108
|
+
<Heading
|
|
109
|
+
as='h1'
|
|
110
|
+
textStyle='display.ui.1'
|
|
111
|
+
emphasis='strong'
|
|
112
|
+
colorPalette='brand'
|
|
113
|
+
className='custom-class'
|
|
114
|
+
>
|
|
115
|
+
Combined styles
|
|
116
|
+
</Heading>
|
|
117
|
+
)
|
|
118
|
+
const heading = getByText('Combined styles')
|
|
119
|
+
expect(heading.tagName.toLowerCase()).toBe('h1')
|
|
120
|
+
expect(heading).toHaveClass(
|
|
121
|
+
'heading',
|
|
122
|
+
'heading--emphasis_strong',
|
|
123
|
+
'textStyle_display.ui.1',
|
|
124
|
+
'color-palette_brand',
|
|
125
|
+
'custom-class'
|
|
126
|
+
)
|
|
127
|
+
})
|
|
128
|
+
})
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import { ark } from '@ark-ui/react/factory'
|
|
4
|
+
|
|
5
|
+
import type { ColorPalette } from '@oztix/roadie-core'
|
|
6
|
+
import { type HTMLStyledProps, styled } from '@oztix/roadie-core/jsx'
|
|
7
|
+
import { type HeadingVariantProps, heading } from '@oztix/roadie-core/recipes'
|
|
8
|
+
import type { JsxStyleProps } from '@oztix/roadie-core/types'
|
|
9
|
+
|
|
10
|
+
type HeadingElement = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'label'
|
|
11
|
+
type HeadingStyle = Extract<JsxStyleProps['textStyle'], `display.${string}`>
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A heading component that uses display styles for titles and section headers
|
|
15
|
+
*/
|
|
16
|
+
export interface HeadingProps extends HTMLStyledProps<'h2'> {
|
|
17
|
+
/**
|
|
18
|
+
* The heading element to render
|
|
19
|
+
* @default 'h2'
|
|
20
|
+
*/
|
|
21
|
+
as?: HeadingElement
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The text style to use for the heading
|
|
25
|
+
* @default 'display.ui'
|
|
26
|
+
*/
|
|
27
|
+
textStyle?: HeadingStyle
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The color palette to use for the heading
|
|
31
|
+
* @default 'neutral'
|
|
32
|
+
*/
|
|
33
|
+
colorPalette?: ColorPalette
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Set a sepecific empahasis level for differen fonts like subtitles
|
|
37
|
+
* @default 'default'
|
|
38
|
+
*/
|
|
39
|
+
emphasis?: HeadingVariantProps['emphasis']
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The content to display
|
|
43
|
+
*/
|
|
44
|
+
children?: ReactNode
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const Heading = styled(
|
|
48
|
+
ark.h2,
|
|
49
|
+
heading
|
|
50
|
+
) as React.ForwardRefExoticComponent<HeadingProps>
|
|
51
|
+
|
|
52
|
+
Heading.displayName = 'Heading'
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { render } from '@testing-library/react'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { Text } from './index'
|
|
5
|
+
|
|
6
|
+
describe('Text', () => {
|
|
7
|
+
it('renders with default props', () => {
|
|
8
|
+
const { getByText } = render(<Text>Hello World</Text>)
|
|
9
|
+
const text = getByText('Hello World')
|
|
10
|
+
expect(text).toBeInTheDocument()
|
|
11
|
+
expect(text.tagName.toLowerCase()).toBe('span')
|
|
12
|
+
expect(text).toHaveClass('text', 'text--emphasis_default')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('applies textStyle prop', () => {
|
|
16
|
+
const { getByText } = render(<Text textStyle='heading.1'>Large Text</Text>)
|
|
17
|
+
const text = getByText('Large Text')
|
|
18
|
+
expect(text).toHaveClass('textStyle_heading.1')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('renders with custom element type', () => {
|
|
22
|
+
const { getByText } = render(<Text as='p'>Paragraph Text</Text>)
|
|
23
|
+
const element = getByText('Paragraph Text')
|
|
24
|
+
expect(element.tagName.toLowerCase()).toBe('p')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('renders with different emphasis', () => {
|
|
28
|
+
const { rerender, getByText } = render(
|
|
29
|
+
<Text emphasis='strong'>Strong Text</Text>
|
|
30
|
+
)
|
|
31
|
+
let text = getByText('Strong Text')
|
|
32
|
+
expect(text).toHaveClass('text--emphasis_strong')
|
|
33
|
+
|
|
34
|
+
rerender(<Text emphasis='subtle'>Subtle Text</Text>)
|
|
35
|
+
text = getByText('Subtle Text')
|
|
36
|
+
expect(text).toHaveClass('text--emphasis_subtle')
|
|
37
|
+
|
|
38
|
+
rerender(<Text emphasis='subtler'>subtler Text</Text>)
|
|
39
|
+
text = getByText('subtler Text')
|
|
40
|
+
expect(text).toHaveClass('text--emphasis_subtler')
|
|
41
|
+
|
|
42
|
+
rerender(<Text emphasis='default'>Default Text</Text>)
|
|
43
|
+
text = getByText('Default Text')
|
|
44
|
+
expect(text).toHaveClass('text--emphasis_default')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('renders with different color palettes', () => {
|
|
48
|
+
const { rerender, getByText } = render(
|
|
49
|
+
<Text colorPalette='accent'>Accent Text</Text>
|
|
50
|
+
)
|
|
51
|
+
let text = getByText('Accent Text')
|
|
52
|
+
expect(text).toHaveClass('color-palette_accent')
|
|
53
|
+
|
|
54
|
+
rerender(<Text colorPalette='brand'>Brand Text</Text>)
|
|
55
|
+
text = getByText('Brand Text')
|
|
56
|
+
expect(text).toHaveClass('color-palette_brand')
|
|
57
|
+
|
|
58
|
+
rerender(<Text colorPalette='success'>Success Text</Text>)
|
|
59
|
+
text = getByText('Success Text')
|
|
60
|
+
expect(text).toHaveClass('color-palette_success')
|
|
61
|
+
|
|
62
|
+
rerender(<Text colorPalette='warning'>Warning Text</Text>)
|
|
63
|
+
text = getByText('Warning Text')
|
|
64
|
+
expect(text).toHaveClass('color-palette_warning')
|
|
65
|
+
|
|
66
|
+
rerender(<Text colorPalette='danger'>Danger Text</Text>)
|
|
67
|
+
text = getByText('Danger Text')
|
|
68
|
+
expect(text).toHaveClass('color-palette_danger')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('applies interactive styles', () => {
|
|
72
|
+
const { getByText } = render(<Text interactive>Interactive Text</Text>)
|
|
73
|
+
const text = getByText('Interactive Text')
|
|
74
|
+
expect(text).toHaveClass('text--interactive_true')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('applies line clamp prop', () => {
|
|
78
|
+
const { getByText } = render(
|
|
79
|
+
<Text lineClamp={2}>Multi-line text that should be clamped</Text>
|
|
80
|
+
)
|
|
81
|
+
const text = getByText('Multi-line text that should be clamped')
|
|
82
|
+
expect(text).toHaveClass('lc_2')
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('forwards additional HTML attributes', () => {
|
|
86
|
+
const { getByTestId } = render(
|
|
87
|
+
<Text data-testid='text' title='tooltip' aria-label='Accessible text'>
|
|
88
|
+
Text with attributes
|
|
89
|
+
</Text>
|
|
90
|
+
)
|
|
91
|
+
const text = getByTestId('text')
|
|
92
|
+
expect(text).toHaveClass('text')
|
|
93
|
+
expect(text).toHaveAttribute('title', 'tooltip')
|
|
94
|
+
expect(text).toHaveAttribute('aria-label', 'Accessible text')
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('combines multiple props', () => {
|
|
98
|
+
const { getByText } = render(
|
|
99
|
+
<Text
|
|
100
|
+
textStyle='heading.2'
|
|
101
|
+
emphasis='strong'
|
|
102
|
+
colorPalette='accent'
|
|
103
|
+
interactive
|
|
104
|
+
lineClamp={2}
|
|
105
|
+
className='custom-class'
|
|
106
|
+
>
|
|
107
|
+
Styled Text
|
|
108
|
+
</Text>
|
|
109
|
+
)
|
|
110
|
+
const text = getByText('Styled Text')
|
|
111
|
+
expect(text).toHaveClass(
|
|
112
|
+
'text',
|
|
113
|
+
'text--emphasis_strong',
|
|
114
|
+
'text--interactive_true',
|
|
115
|
+
'textStyle_heading.2',
|
|
116
|
+
'color-palette_accent',
|
|
117
|
+
'lc_2',
|
|
118
|
+
'custom-class'
|
|
119
|
+
)
|
|
120
|
+
})
|
|
121
|
+
})
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import { ark } from '@ark-ui/react/factory'
|
|
4
|
+
|
|
5
|
+
import type { ColorPalette } from '@oztix/roadie-core'
|
|
6
|
+
import { styled } from '@oztix/roadie-core/jsx'
|
|
7
|
+
import type { HTMLStyledProps } from '@oztix/roadie-core/jsx'
|
|
8
|
+
import { type TextVariantProps, text } from '@oztix/roadie-core/recipes'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Text component for displaying content with various styling options
|
|
12
|
+
*/
|
|
13
|
+
export interface TextProps extends HTMLStyledProps<'span'> {
|
|
14
|
+
/**
|
|
15
|
+
* The visual emphasis of the text
|
|
16
|
+
* @default 'default'
|
|
17
|
+
*/
|
|
18
|
+
emphasis?: TextVariantProps['emphasis']
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Whether the text is interactive
|
|
22
|
+
* @default false
|
|
23
|
+
*/
|
|
24
|
+
interactive?: TextVariantProps['interactive']
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The HTML element to render the text as
|
|
28
|
+
* @default 'span'
|
|
29
|
+
*/
|
|
30
|
+
as?: HTMLStyledProps<'span'>['as']
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The color palette to use for the text
|
|
34
|
+
* @default 'neutral'
|
|
35
|
+
*/
|
|
36
|
+
colorPalette?: ColorPalette
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* When true, the component will pass props to its child component
|
|
40
|
+
*/
|
|
41
|
+
asChild?: boolean
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The content to display
|
|
45
|
+
*/
|
|
46
|
+
children?: ReactNode
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const Text = styled(
|
|
50
|
+
ark.span,
|
|
51
|
+
text
|
|
52
|
+
) as React.ForwardRefExoticComponent<TextProps>
|
|
53
|
+
|
|
54
|
+
Text.displayName = 'Text'
|