@oztix/roadie-components 0.2.1 → 1.1.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 -2
- package/dist/Button.js.map +1 -1
- package/dist/Code.d.ts +15 -5
- package/dist/Code.js +1 -2
- package/dist/Code.js.map +1 -1
- package/dist/Heading.d.ts +23 -6
- package/dist/Heading.js +1 -2
- package/dist/Heading.js.map +1 -1
- package/dist/Text.d.ts +24 -37
- package/dist/Text.js +1 -2
- package/dist/Text.js.map +1 -1
- package/dist/View.d.ts +7 -70
- package/dist/View.js +1 -2
- package/dist/View.js.map +1 -1
- package/dist/_chunks/chunk-6FIUWNC7.js +2 -0
- package/dist/_chunks/chunk-6FIUWNC7.js.map +1 -0
- package/dist/_chunks/chunk-GSK3G4DW.js +2 -0
- package/dist/_chunks/chunk-GSK3G4DW.js.map +1 -0
- package/dist/_chunks/chunk-P5L5LN6E.js +2 -0
- package/dist/_chunks/chunk-P5L5LN6E.js.map +1 -0
- package/dist/_chunks/chunk-RJEJUZ3O.js +2 -0
- package/dist/_chunks/chunk-RJEJUZ3O.js.map +1 -0
- package/dist/_chunks/chunk-VDMZIGT2.js +2 -0
- package/dist/_chunks/chunk-VDMZIGT2.js.map +1 -0
- package/dist/hooks/index.d.ts +27 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index.d.ts +5 -6
- package/dist/index.js +1 -6
- package/dist/index.js.map +1 -1
- package/package.json +33 -21
- 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/hooks/index.ts +1 -0
- package/src/hooks/useColorMode.ts +37 -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
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oztix/roadie-components",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "The components for Roadie Design System at Oztix",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/TicketSolutionsPtyLtd/roadie"
|
|
9
|
+
},
|
|
6
10
|
"main": "./dist/index.js",
|
|
7
11
|
"module": "./dist/index.js",
|
|
8
12
|
"types": "./dist/index.d.ts",
|
|
@@ -11,11 +15,21 @@
|
|
|
11
15
|
"types": "./dist/index.d.ts",
|
|
12
16
|
"import": "./dist/index.js",
|
|
13
17
|
"require": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./hooks": {
|
|
20
|
+
"types": "./dist/hooks/index.d.ts",
|
|
21
|
+
"import": "./dist/hooks/index.js",
|
|
22
|
+
"require": "./dist/hooks/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./src/*": {
|
|
25
|
+
"types": "./src/*",
|
|
26
|
+
"import": "./src/*"
|
|
14
27
|
}
|
|
15
28
|
},
|
|
16
29
|
"sideEffects": false,
|
|
17
30
|
"files": [
|
|
18
|
-
"dist"
|
|
31
|
+
"dist",
|
|
32
|
+
"src"
|
|
19
33
|
],
|
|
20
34
|
"publishConfig": {
|
|
21
35
|
"access": "public"
|
|
@@ -25,24 +39,23 @@
|
|
|
25
39
|
"react-dom": "^19.0.0"
|
|
26
40
|
},
|
|
27
41
|
"dependencies": {
|
|
28
|
-
"@
|
|
29
|
-
"
|
|
42
|
+
"@ark-ui/react": "5.26.2",
|
|
43
|
+
"@oztix/roadie-core": "1.1.0"
|
|
30
44
|
},
|
|
31
45
|
"devDependencies": {
|
|
32
|
-
"@pandacss/dev": "
|
|
33
|
-
"@testing-library/jest-dom": "6.1
|
|
34
|
-
"@testing-library/react": "
|
|
35
|
-
"@testing-library/user-event": "14.
|
|
36
|
-
"@types/node": "
|
|
37
|
-
"@types/react": "19.
|
|
38
|
-
"@types/react-dom": "19.
|
|
39
|
-
"@vitejs/plugin-react": "
|
|
40
|
-
"@vitest/coverage-v8": "
|
|
41
|
-
"@vitest/ui": "
|
|
42
|
-
"jsdom": "
|
|
43
|
-
"tsup": "8.
|
|
44
|
-
"
|
|
45
|
-
"vitest": "1.6.0"
|
|
46
|
+
"@pandacss/dev": "1.4.3",
|
|
47
|
+
"@testing-library/jest-dom": "6.9.1",
|
|
48
|
+
"@testing-library/react": "16.3.0",
|
|
49
|
+
"@testing-library/user-event": "14.6.1",
|
|
50
|
+
"@types/node": "24.9.2",
|
|
51
|
+
"@types/react": "19.2.2",
|
|
52
|
+
"@types/react-dom": "19.2.2",
|
|
53
|
+
"@vitejs/plugin-react": "5.1.0",
|
|
54
|
+
"@vitest/coverage-v8": "4.0.4",
|
|
55
|
+
"@vitest/ui": "4.0.4",
|
|
56
|
+
"jsdom": "27.0.1",
|
|
57
|
+
"tsup": "8.5.0",
|
|
58
|
+
"vitest": "4.0.4"
|
|
46
59
|
},
|
|
47
60
|
"author": {
|
|
48
61
|
"name": "Luke Brooker",
|
|
@@ -63,15 +76,14 @@
|
|
|
63
76
|
"build": "tsup",
|
|
64
77
|
"clean": "rm -rf node_modules dist",
|
|
65
78
|
"dev": "tsup --watch",
|
|
66
|
-
"pretest": "panda codegen",
|
|
67
79
|
"test": "vitest run",
|
|
68
80
|
"test:watch": "vitest",
|
|
69
81
|
"test:coverage": "vitest run --coverage",
|
|
70
82
|
"test:ui": "vitest --ui",
|
|
71
83
|
"format": "prettier --ignore-path ../../.prettierignore --write \"**/*.{ts,tsx,js,jsx,json,css,md}\"",
|
|
72
84
|
"format:check": "prettier --ignore-path ../../.prettierignore --check \"**/*.{ts,tsx,js,jsx,json,css,md}\"",
|
|
73
|
-
"lint": "eslint
|
|
74
|
-
"lint:fix": "eslint
|
|
85
|
+
"lint": "eslint .",
|
|
86
|
+
"lint:fix": "eslint . --fix",
|
|
75
87
|
"typecheck": "tsc --noEmit"
|
|
76
88
|
}
|
|
77
89
|
}
|
|
@@ -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'
|