@oztix/roadie-components 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/dist/Button.d.ts +18 -33
  2. package/dist/Button.js +1 -2
  3. package/dist/Button.js.map +1 -1
  4. package/dist/Code.js +1 -2
  5. package/dist/Code.js.map +1 -1
  6. package/dist/Container.d.ts +34 -0
  7. package/dist/Container.js +2 -0
  8. package/dist/Container.js.map +1 -0
  9. package/dist/Heading.d.ts +7 -2
  10. package/dist/Heading.js +1 -2
  11. package/dist/Heading.js.map +1 -1
  12. package/dist/Highlight.d.ts +44 -0
  13. package/dist/Highlight.js +3 -0
  14. package/dist/Highlight.js.map +1 -0
  15. package/dist/Mark.d.ts +29 -0
  16. package/dist/Mark.js +2 -0
  17. package/dist/Mark.js.map +1 -0
  18. package/dist/SpotIllustration.d.ts +79 -0
  19. package/dist/SpotIllustration.js +2 -0
  20. package/dist/SpotIllustration.js.map +1 -0
  21. package/dist/Text.d.ts +11 -5
  22. package/dist/Text.js +1 -2
  23. package/dist/Text.js.map +1 -1
  24. package/dist/View.js +1 -2
  25. package/dist/View.js.map +1 -1
  26. package/dist/_chunks/chunk-AZZHYO2A.js +3 -0
  27. package/dist/_chunks/chunk-AZZHYO2A.js.map +1 -0
  28. package/dist/_chunks/chunk-JOQJCXYF.js +2 -0
  29. package/dist/_chunks/chunk-JOQJCXYF.js.map +1 -0
  30. package/dist/_chunks/chunk-NMGF2AP6.js +2 -0
  31. package/dist/_chunks/chunk-NMGF2AP6.js.map +1 -0
  32. package/dist/_chunks/chunk-OH4JYS35.js +3 -0
  33. package/dist/_chunks/chunk-OH4JYS35.js.map +1 -0
  34. package/dist/_chunks/chunk-P5L5LN6E.js +1 -8
  35. package/dist/_chunks/chunk-P5L5LN6E.js.map +1 -1
  36. package/dist/_chunks/chunk-RJEJUZ3O.js +1 -6
  37. package/dist/_chunks/chunk-RJEJUZ3O.js.map +1 -1
  38. package/dist/_chunks/chunk-SUDUTP6A.js +3 -0
  39. package/dist/_chunks/chunk-SUDUTP6A.js.map +1 -0
  40. package/dist/_chunks/chunk-YNF56IUK.js +2 -0
  41. package/dist/_chunks/chunk-YNF56IUK.js.map +1 -0
  42. package/dist/_chunks/chunk-ZXS7U3VJ.js +2 -0
  43. package/dist/_chunks/chunk-ZXS7U3VJ.js.map +1 -0
  44. package/dist/hooks/index.d.ts +27 -0
  45. package/dist/hooks/index.js +2 -0
  46. package/dist/hooks/index.js.map +1 -0
  47. package/dist/index.d.ts +8 -2
  48. package/dist/index.js +1 -6
  49. package/dist/index.js.map +1 -1
  50. package/package.json +33 -19
  51. package/src/components/Button/Button.tsx +12 -0
  52. package/src/components/Button/IconButton.test.tsx +234 -0
  53. package/src/components/Button/IconButton.tsx +14 -0
  54. package/src/components/Button/index.tsx +2 -48
  55. package/src/components/Container/Container.test.tsx +241 -0
  56. package/src/components/Container/index.tsx +34 -0
  57. package/src/components/Heading/index.tsx +1 -4
  58. package/src/components/Highlight/Highlight.test.tsx +113 -0
  59. package/src/components/Highlight/index.tsx +96 -0
  60. package/src/components/Mark/Mark.test.tsx +82 -0
  61. package/src/components/Mark/index.tsx +33 -0
  62. package/src/components/SpotIllustration/ArrowUpRight.tsx +9 -0
  63. package/src/components/SpotIllustration/CowboyHat.tsx +6 -0
  64. package/src/components/SpotIllustration/Cursor.tsx +6 -0
  65. package/src/components/SpotIllustration/FlowerSpiral.tsx +9 -0
  66. package/src/components/SpotIllustration/Football.tsx +6 -0
  67. package/src/components/SpotIllustration/Hand.tsx +6 -0
  68. package/src/components/SpotIllustration/Heart.tsx +6 -0
  69. package/src/components/SpotIllustration/HighFive.tsx +6 -0
  70. package/src/components/SpotIllustration/MapPin.tsx +6 -0
  71. package/src/components/SpotIllustration/NoteMusic.tsx +6 -0
  72. package/src/components/SpotIllustration/README.md +280 -0
  73. package/src/components/SpotIllustration/SpotIllustration.test.tsx +179 -0
  74. package/src/components/SpotIllustration/SpotIllustration.tsx +96 -0
  75. package/src/components/SpotIllustration/Ticket.tsx +6 -0
  76. package/src/components/SpotIllustration/WineGlass.tsx +6 -0
  77. package/src/components/SpotIllustration/createSpotIllustration.tsx +46 -0
  78. package/src/components/SpotIllustration/index.tsx +42 -0
  79. package/src/components/SpotIllustration/json/arrow-up-right.json +34 -0
  80. package/src/components/SpotIllustration/json/cowboy-hat.json +34 -0
  81. package/src/components/SpotIllustration/json/cursor.json +34 -0
  82. package/src/components/SpotIllustration/json/flower-spiral.json +38 -0
  83. package/src/components/SpotIllustration/json/football.json +46 -0
  84. package/src/components/SpotIllustration/json/hand.json +22 -0
  85. package/src/components/SpotIllustration/json/heart.json +26 -0
  86. package/src/components/SpotIllustration/json/high-five.json +62 -0
  87. package/src/components/SpotIllustration/json/map-pin.json +26 -0
  88. package/src/components/SpotIllustration/json/note-music.json +42 -0
  89. package/src/components/SpotIllustration/json/ticket.json +42 -0
  90. package/src/components/SpotIllustration/json/wine-glass.json +34 -0
  91. package/src/components/SpotIllustration/svgs/arrow-up-right.svg +9 -0
  92. package/src/components/SpotIllustration/svgs/cowboy-hat.svg +9 -0
  93. package/src/components/SpotIllustration/svgs/cursor.svg +9 -0
  94. package/src/components/SpotIllustration/svgs/flower-spiral.svg +10 -0
  95. package/src/components/SpotIllustration/svgs/football.svg +12 -0
  96. package/src/components/SpotIllustration/svgs/hand.svg +6 -0
  97. package/src/components/SpotIllustration/svgs/heart.svg +7 -0
  98. package/src/components/SpotIllustration/svgs/high-five.svg +16 -0
  99. package/src/components/SpotIllustration/svgs/map-pin.svg +7 -0
  100. package/src/components/SpotIllustration/svgs/note-music.svg +11 -0
  101. package/src/components/SpotIllustration/svgs/ticket.svg +11 -0
  102. package/src/components/SpotIllustration/svgs/wine-glass.svg +9 -0
  103. package/src/components/Text/Text.test.tsx +1 -1
  104. package/src/components/Text/index.tsx +4 -7
  105. package/src/components/index.ts +4 -1
  106. package/src/hooks/index.ts +1 -0
  107. package/src/hooks/useColorMode.ts +37 -0
  108. package/src/index.tsx +4 -0
  109. package/dist/_chunks/chunk-6FIUWNC7.js +0 -9
  110. package/dist/_chunks/chunk-6FIUWNC7.js.map +0 -1
  111. package/dist/_chunks/chunk-GSK3G4DW.js +0 -9
  112. package/dist/_chunks/chunk-GSK3G4DW.js.map +0 -1
  113. package/dist/_chunks/chunk-VDMZIGT2.js +0 -10
  114. package/dist/_chunks/chunk-VDMZIGT2.js.map +0 -1
@@ -0,0 +1,241 @@
1
+ import { render } from '@testing-library/react'
2
+ import { describe, expect, it } from 'vitest'
3
+
4
+ import { Container } from './index'
5
+
6
+ describe('Container', () => {
7
+ it('renders with default props', () => {
8
+ const { getByTestId } = render(
9
+ <Container data-testid='container'>Content</Container>
10
+ )
11
+ const container = getByTestId('container')
12
+ expect(container).toBeInTheDocument()
13
+ expect(container.tagName.toLowerCase()).toBe('div')
14
+ expect(container).toHaveClass(
15
+ 'd_flex',
16
+ 'pos_relative',
17
+ 'flex-d_column',
18
+ 'flex-wrap_nowrap',
19
+ 'ai_stretch',
20
+ 'ac_flex-start',
21
+ 'jc_flex-start',
22
+ 'min-h_0',
23
+ 'min-w_0',
24
+ 'w_full',
25
+ 'mx_auto',
26
+ 'max-w_8xl'
27
+ )
28
+ })
29
+
30
+ it('applies responsive padding by default', () => {
31
+ const { getByTestId } = render(
32
+ <Container data-testid='container'>Content</Container>
33
+ )
34
+ const container = getByTestId('container')
35
+ // Should have responsive padding classes
36
+ expect(container.className).toMatch(/px_/)
37
+ })
38
+
39
+ it('renders with contain true by default (constrained width)', () => {
40
+ const { getByTestId } = render(
41
+ <Container data-testid='container'>Constrained Content</Container>
42
+ )
43
+ const container = getByTestId('container')
44
+ expect(container).toHaveClass('max-w_8xl')
45
+ })
46
+
47
+ it('renders with contain=false (full width)', () => {
48
+ const { getByTestId } = render(
49
+ <Container contain={false} data-testid='container'>
50
+ Full Width Content
51
+ </Container>
52
+ )
53
+ const container = getByTestId('container')
54
+ expect(container).toHaveClass('max-w_full')
55
+ })
56
+
57
+ it('renders with contain prop explicitly set to true', () => {
58
+ const { getByTestId } = render(
59
+ <Container contain data-testid='container'>
60
+ Constrained Content
61
+ </Container>
62
+ )
63
+ const container = getByTestId('container')
64
+ expect(container).toHaveClass('max-w_8xl')
65
+ })
66
+
67
+ it('renders with different HTML elements', () => {
68
+ const elements: Array<'section' | 'article' | 'aside' | 'main'> = [
69
+ 'section',
70
+ 'article',
71
+ 'aside',
72
+ 'main'
73
+ ]
74
+
75
+ elements.forEach((element) => {
76
+ const { rerender, getByTestId } = render(
77
+ <Container as={element} data-testid='container'>
78
+ {element} content
79
+ </Container>
80
+ )
81
+ const container = getByTestId('container')
82
+ expect(container.tagName.toLowerCase()).toBe(element)
83
+ expect(container).toHaveClass(
84
+ 'd_flex',
85
+ 'pos_relative',
86
+ 'flex-d_column',
87
+ 'flex-wrap_nowrap',
88
+ 'ai_stretch',
89
+ 'ac_flex-start',
90
+ 'jc_flex-start',
91
+ 'min-h_0',
92
+ 'min-w_0',
93
+ 'w_full',
94
+ 'mx_auto'
95
+ )
96
+ rerender(<></>)
97
+ })
98
+ })
99
+
100
+ it('applies layout properties', () => {
101
+ const { getByTestId } = render(
102
+ <Container
103
+ data-testid='container'
104
+ display='inline-flex'
105
+ position='absolute'
106
+ flexDirection='row'
107
+ flexWrap='wrap'
108
+ alignItems='center'
109
+ alignContent='center'
110
+ justifyContent='center'
111
+ >
112
+ Styled Container
113
+ </Container>
114
+ )
115
+ const container = getByTestId('container')
116
+ expect(container).toHaveClass(
117
+ 'd_inline-flex',
118
+ 'pos_absolute',
119
+ 'flex-d_row',
120
+ 'flex-wrap_wrap',
121
+ 'ai_center',
122
+ 'ac_center',
123
+ 'jc_center',
124
+ 'min-h_0',
125
+ 'min-w_0'
126
+ )
127
+ })
128
+
129
+ it('applies custom styles and attributes', () => {
130
+ const { getByTestId } = render(
131
+ <Container
132
+ data-testid='container'
133
+ backgroundColor='neutral.bg.subtle'
134
+ padding='200'
135
+ title='tooltip'
136
+ aria-label='Accessible container'
137
+ >
138
+ Custom Container
139
+ </Container>
140
+ )
141
+ const container = getByTestId('container')
142
+ expect(container).toHaveClass('bg-c_neutral.bg.subtle', 'p_200')
143
+ expect(container).toHaveAttribute('title', 'tooltip')
144
+ expect(container).toHaveAttribute('aria-label', 'Accessible container')
145
+ })
146
+
147
+ it('renders nested containers', () => {
148
+ const { getByTestId } = render(
149
+ <Container data-testid='parent'>
150
+ <Container data-testid='child'>Nested Content</Container>
151
+ </Container>
152
+ )
153
+ const parent = getByTestId('parent')
154
+ const child = getByTestId('child')
155
+ expect(parent).toContainElement(child)
156
+ expect(child).toHaveTextContent('Nested Content')
157
+ expect(parent).toHaveClass(
158
+ 'd_flex',
159
+ 'pos_relative',
160
+ 'flex-d_column',
161
+ 'flex-wrap_nowrap',
162
+ 'ai_stretch',
163
+ 'ac_flex-start',
164
+ 'jc_flex-start',
165
+ 'min-h_0',
166
+ 'min-w_0',
167
+ 'w_full',
168
+ 'mx_auto'
169
+ )
170
+ expect(child).toHaveClass(
171
+ 'd_flex',
172
+ 'pos_relative',
173
+ 'flex-d_column',
174
+ 'flex-wrap_nowrap',
175
+ 'ai_stretch',
176
+ 'ac_flex-start',
177
+ 'jc_flex-start',
178
+ 'min-h_0',
179
+ 'min-w_0',
180
+ 'w_full',
181
+ 'mx_auto'
182
+ )
183
+ })
184
+
185
+ it('combines multiple props including contain', () => {
186
+ const { getByTestId } = render(
187
+ <Container
188
+ as='section'
189
+ contain
190
+ display='grid'
191
+ gap='200'
192
+ padding='400'
193
+ backgroundColor='neutral.bg.subtle'
194
+ className='custom-class'
195
+ data-testid='container'
196
+ >
197
+ Combined styles
198
+ </Container>
199
+ )
200
+ const container = getByTestId('container')
201
+ expect(container.tagName.toLowerCase()).toBe('section')
202
+ expect(container).toHaveClass(
203
+ 'd_grid',
204
+ 'gap_200',
205
+ 'p_400',
206
+ 'bg-c_neutral.bg.subtle',
207
+ 'max-w_8xl',
208
+ 'custom-class'
209
+ )
210
+ })
211
+
212
+ it('can override default padding', () => {
213
+ const { getByTestId } = render(
214
+ <Container data-testid='container' px='800'>
215
+ Custom Padding
216
+ </Container>
217
+ )
218
+ const container = getByTestId('container')
219
+ expect(container).toHaveClass('px_800')
220
+ })
221
+
222
+ it('can override default width', () => {
223
+ const { getByTestId } = render(
224
+ <Container data-testid='container' width='auto'>
225
+ Custom Width
226
+ </Container>
227
+ )
228
+ const container = getByTestId('container')
229
+ expect(container).toHaveClass('w_auto')
230
+ })
231
+
232
+ it('can set contain=false for full width', () => {
233
+ const { getByTestId } = render(
234
+ <Container contain={false} data-testid='container'>
235
+ Full Width
236
+ </Container>
237
+ )
238
+ const container = getByTestId('container')
239
+ expect(container).toHaveClass('max-w_full')
240
+ })
241
+ })
@@ -0,0 +1,34 @@
1
+ import { Container as ContainerPattern } from '@oztix/roadie-core/jsx'
2
+
3
+ /**
4
+ * A foundational layout component that provides a centered container with responsive padding
5
+ * and a constrained max-width by default. Perfect for page layouts and content sections.
6
+ *
7
+ * By default, Container has:
8
+ * - Centered content with `mx: auto`
9
+ * - Responsive horizontal padding (300 on mobile, 400 on tablet, 600 on desktop)
10
+ * - Max-width constraint of 7xl (80rem)
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * // Basic usage - constrained width by default
15
+ * <Container>
16
+ * <h1>Page Title</h1>
17
+ * <p>Content with readable max-width</p>
18
+ * </Container>
19
+ * ```
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * // Full-width container
24
+ * <Container contain={false}>
25
+ * <h1>Hero Section</h1>
26
+ * <p>This content spans the full width</p>
27
+ * </Container>
28
+ * ```
29
+ */
30
+ export const Container = ContainerPattern
31
+
32
+ export type ContainerProps = React.ComponentProps<typeof ContainerPattern>
33
+
34
+ Container.displayName = 'Container'
@@ -44,9 +44,6 @@ export interface HeadingProps extends HTMLStyledProps<'h2'> {
44
44
  children?: ReactNode
45
45
  }
46
46
 
47
- export const Heading = styled(
48
- ark.h2,
49
- heading
50
- ) as React.ForwardRefExoticComponent<HeadingProps>
47
+ export const Heading = styled(ark.h2, heading)
51
48
 
52
49
  Heading.displayName = 'Heading'
@@ -0,0 +1,113 @@
1
+ import { render } from '@testing-library/react'
2
+ import { describe, expect, it } from 'vitest'
3
+
4
+ import { Highlight } from './index'
5
+
6
+ describe('Highlight', () => {
7
+ it('renders with text prop', () => {
8
+ const { container } = render(<Highlight text='Hello World' query='World' />)
9
+ expect(container).toHaveTextContent('Hello World')
10
+ const mark = container.querySelector('mark')
11
+ expect(mark).toBeInTheDocument()
12
+ expect(mark).toHaveTextContent('World')
13
+ })
14
+
15
+ it('highlights only matching text when query is provided', () => {
16
+ const { container } = render(
17
+ <Highlight text='The quick brown fox' query='quick' />
18
+ )
19
+ expect(container).toHaveTextContent('The quick brown fox')
20
+ const mark = container.querySelector('mark')
21
+ expect(mark).toHaveTextContent('quick')
22
+ })
23
+
24
+ it('highlights multiple matches when matchAll is true', () => {
25
+ const { container } = render(
26
+ <Highlight text='foo bar foo baz' query='foo' matchAll />
27
+ )
28
+ const marks = container.querySelectorAll('mark')
29
+ expect(marks).toHaveLength(2)
30
+ marks.forEach((mark: Element) => {
31
+ expect(mark).toHaveTextContent('foo')
32
+ })
33
+ })
34
+
35
+ it('highlights with case insensitive matching by default', () => {
36
+ const { container } = render(<Highlight text='Hello World' query='hello' />)
37
+ const mark = container.querySelector('mark')
38
+ expect(mark).toHaveTextContent('Hello')
39
+ })
40
+
41
+ it('respects case when ignoreCase is false', () => {
42
+ const { container } = render(
43
+ <Highlight text='Hello World' query='hello' ignoreCase={false} />
44
+ )
45
+ const mark = container.querySelector('mark')
46
+ expect(mark).toBeNull()
47
+ })
48
+
49
+ it('highlights multiple queries', () => {
50
+ const { container } = render(
51
+ <Highlight text='The quick brown fox' query={['quick', 'fox']} />
52
+ )
53
+ const marks = container.querySelectorAll('mark')
54
+ expect(marks).toHaveLength(2)
55
+ expect(marks[0]).toHaveTextContent('quick')
56
+ expect(marks[1]).toHaveTextContent('fox')
57
+ })
58
+
59
+ it('applies default color palette', () => {
60
+ const { container } = render(
61
+ <Highlight text='Highlighted text' query='Highlighted' />
62
+ )
63
+ const mark = container.querySelector('mark')
64
+ expect(mark).toHaveClass('color-palette_information')
65
+ })
66
+
67
+ it('applies custom color palette', () => {
68
+ const { container } = render(
69
+ <Highlight
70
+ text='Highlighted text'
71
+ query='Highlighted'
72
+ colorPalette='success'
73
+ />
74
+ )
75
+ const mark = container.querySelector('mark')
76
+ expect(mark).toHaveClass('color-palette_success')
77
+ })
78
+
79
+ it('matches exact words when exactMatch is true', () => {
80
+ const { container } = render(
81
+ <Highlight text='foo foobar' query='foo' exactMatch />
82
+ )
83
+ const marks = container.querySelectorAll('mark')
84
+ expect(marks).toHaveLength(1)
85
+ expect(marks[0]).toHaveTextContent('foo')
86
+ })
87
+
88
+ it('forwards additional HTML attributes', () => {
89
+ const { container } = render(
90
+ <Highlight
91
+ text='Hello'
92
+ query='Hello'
93
+ data-testid='highlight'
94
+ title='tooltip'
95
+ />
96
+ )
97
+ const mark = container.querySelector('mark')
98
+ expect(mark).toHaveAttribute('data-testid', 'highlight')
99
+ expect(mark).toHaveAttribute('title', 'tooltip')
100
+ })
101
+
102
+ it('applies custom className', () => {
103
+ const { container } = render(
104
+ <Highlight
105
+ text='Highlighted text'
106
+ query='Highlighted'
107
+ className='custom-class'
108
+ />
109
+ )
110
+ const mark = container.querySelector('mark')
111
+ expect(mark).toHaveClass('mark', 'custom-class')
112
+ })
113
+ })
@@ -0,0 +1,96 @@
1
+ 'use client'
2
+
3
+ import { Fragment, type ReactElement } from 'react'
4
+
5
+ import { useHighlight } from '@ark-ui/react/highlight'
6
+
7
+ import type { ColorPalette } from '@oztix/roadie-core'
8
+ import type { HTMLStyledProps } from '@oztix/roadie-core/jsx'
9
+
10
+ import { Mark } from '../Mark'
11
+
12
+ // Re-export Ark UI types and hook for advanced use cases
13
+ export { useHighlight } from '@ark-ui/react/highlight'
14
+ export type { HighlightChunk, UseHighlightProps } from '@ark-ui/react/highlight'
15
+
16
+ /**
17
+ * Highlight component for highlighting substrings within text
18
+ */
19
+ export interface HighlightProps
20
+ extends Omit<HTMLStyledProps<'mark'>, 'children'> {
21
+ /**
22
+ * The text content to display and potentially highlight
23
+ */
24
+ text: string
25
+
26
+ /**
27
+ * The query string(s) to highlight within the text
28
+ */
29
+ query: string | string[]
30
+
31
+ /**
32
+ * Whether to match whole words only
33
+ * @default false
34
+ */
35
+ exactMatch?: boolean
36
+
37
+ /**
38
+ * Whether to ignore case while matching
39
+ * @default true
40
+ */
41
+ ignoreCase?: boolean
42
+
43
+ /**
44
+ * Whether to match multiple instances of the query
45
+ * @default true
46
+ */
47
+ matchAll?: boolean
48
+
49
+ /**
50
+ * The color palette to use for the highlight
51
+ * @default 'information'
52
+ */
53
+ colorPalette?: ColorPalette
54
+ }
55
+
56
+ export const Highlight = ({
57
+ text,
58
+ query,
59
+ exactMatch = false,
60
+ ignoreCase = true,
61
+ matchAll = true,
62
+ colorPalette = 'information',
63
+ ...props
64
+ }: HighlightProps): ReactElement => {
65
+ // Fast path: if query is empty, just return the text
66
+ const isQueryEmpty =
67
+ !query || (Array.isArray(query) && query.length === 0) || query === ''
68
+
69
+ if (isQueryEmpty) {
70
+ return <>{text}</>
71
+ }
72
+
73
+ const chunks = useHighlight({
74
+ query,
75
+ text,
76
+ exactMatch,
77
+ ignoreCase,
78
+ matchAll
79
+ })
80
+
81
+ return (
82
+ <>
83
+ {chunks.map((chunk, index) =>
84
+ chunk.match ? (
85
+ <Mark key={index} colorPalette={colorPalette} {...props}>
86
+ {chunk.text}
87
+ </Mark>
88
+ ) : (
89
+ <Fragment key={index}>{chunk.text}</Fragment>
90
+ )
91
+ )}
92
+ </>
93
+ )
94
+ }
95
+
96
+ Highlight.displayName = 'Highlight'
@@ -0,0 +1,82 @@
1
+ import { render } from '@testing-library/react'
2
+ import { describe, expect, it } from 'vitest'
3
+
4
+ import { Mark } from './index'
5
+
6
+ describe('Mark', () => {
7
+ it('renders with default props', () => {
8
+ const { container } = render(<Mark>Marked text</Mark>)
9
+ const mark = container.querySelector('mark')
10
+ expect(mark).toBeInTheDocument()
11
+ expect(mark).toHaveTextContent('Marked text')
12
+ expect(mark).toHaveClass('mark')
13
+ })
14
+
15
+ it('applies color palette when provided', () => {
16
+ const { container } = render(
17
+ <Mark colorPalette='information'>Marked text</Mark>
18
+ )
19
+ const mark = container.querySelector('mark')
20
+ expect(mark).toHaveClass('color-palette_information')
21
+ })
22
+
23
+ it('applies custom color palette', () => {
24
+ const { container } = render(
25
+ <Mark colorPalette='success'>Marked text</Mark>
26
+ )
27
+ const mark = container.querySelector('mark')
28
+ expect(mark).toHaveClass('color-palette_success')
29
+ })
30
+
31
+ it('applies custom className', () => {
32
+ const { container } = render(
33
+ <Mark className='custom-class'>Marked text</Mark>
34
+ )
35
+ const mark = container.querySelector('mark')
36
+ expect(mark).toHaveClass('mark', 'custom-class')
37
+ })
38
+
39
+ it('forwards additional HTML attributes', () => {
40
+ const { container } = render(
41
+ <Mark data-testid='mark' title='tooltip'>
42
+ Marked text
43
+ </Mark>
44
+ )
45
+ const mark = container.querySelector('mark')
46
+ expect(mark).toHaveAttribute('data-testid', 'mark')
47
+ expect(mark).toHaveAttribute('title', 'tooltip')
48
+ })
49
+
50
+ it('renders with different color palettes', () => {
51
+ const { rerender, container } = render(
52
+ <Mark colorPalette='accent'>Accent</Mark>
53
+ )
54
+ let mark = container.querySelector('mark')
55
+ expect(mark).toHaveClass('color-palette_accent')
56
+
57
+ rerender(<Mark colorPalette='brand'>Brand</Mark>)
58
+ mark = container.querySelector('mark')
59
+ expect(mark).toHaveClass('color-palette_brand')
60
+
61
+ rerender(<Mark colorPalette='warning'>Warning</Mark>)
62
+ mark = container.querySelector('mark')
63
+ expect(mark).toHaveClass('color-palette_warning')
64
+
65
+ rerender(<Mark colorPalette='danger'>Danger</Mark>)
66
+ mark = container.querySelector('mark')
67
+ expect(mark).toHaveClass('color-palette_danger')
68
+ })
69
+
70
+ it('renders children correctly', () => {
71
+ const { container } = render(
72
+ <Mark>
73
+ This is <strong>important</strong> text
74
+ </Mark>
75
+ )
76
+ const mark = container.querySelector('mark')
77
+ expect(mark).toBeInTheDocument()
78
+ expect(mark?.textContent).toBe('This is important text')
79
+ const strong = mark?.querySelector('strong')
80
+ expect(strong).toHaveTextContent('important')
81
+ })
82
+ })
@@ -0,0 +1,33 @@
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 { mark } from '@oztix/roadie-core/recipes'
9
+
10
+ /**
11
+ * Mark component for highlighting text content
12
+ */
13
+ export interface MarkProps extends HTMLStyledProps<'mark'> {
14
+ /**
15
+ * The color palette to use for the mark
16
+ * @default 'information'
17
+ */
18
+ colorPalette?: ColorPalette
19
+
20
+ /**
21
+ * When true, the component will pass props to its child component
22
+ */
23
+ asChild?: boolean
24
+
25
+ /**
26
+ * The content to mark
27
+ */
28
+ children?: ReactNode
29
+ }
30
+
31
+ export const Mark = styled(ark.mark, mark)
32
+
33
+ Mark.displayName = 'Mark'
@@ -0,0 +1,9 @@
1
+ // Generated file - do not edit directly
2
+ import { createSpotIllustration } from './createSpotIllustration'
3
+ import arrowuprightData from './json/arrow-up-right.json'
4
+
5
+ export const ArrowUpRight = createSpotIllustration(
6
+ 'ArrowUpRight',
7
+ arrowuprightData
8
+ )
9
+ export type ArrowUpRightProps = React.ComponentPropsWithRef<typeof ArrowUpRight>
@@ -0,0 +1,6 @@
1
+ // Generated file - do not edit directly
2
+ import { createSpotIllustration } from './createSpotIllustration'
3
+ import cowboyhatData from './json/cowboy-hat.json'
4
+
5
+ export const CowboyHat = createSpotIllustration('CowboyHat', cowboyhatData)
6
+ export type CowboyHatProps = React.ComponentPropsWithRef<typeof CowboyHat>
@@ -0,0 +1,6 @@
1
+ // Generated file - do not edit directly
2
+ import { createSpotIllustration } from './createSpotIllustration'
3
+ import cursorData from './json/cursor.json'
4
+
5
+ export const Cursor = createSpotIllustration('Cursor', cursorData)
6
+ export type CursorProps = React.ComponentPropsWithRef<typeof Cursor>
@@ -0,0 +1,9 @@
1
+ // Generated file - do not edit directly
2
+ import { createSpotIllustration } from './createSpotIllustration'
3
+ import flowerspiralData from './json/flower-spiral.json'
4
+
5
+ export const FlowerSpiral = createSpotIllustration(
6
+ 'FlowerSpiral',
7
+ flowerspiralData
8
+ )
9
+ export type FlowerSpiralProps = React.ComponentPropsWithRef<typeof FlowerSpiral>
@@ -0,0 +1,6 @@
1
+ // Generated file - do not edit directly
2
+ import { createSpotIllustration } from './createSpotIllustration'
3
+ import footballData from './json/football.json'
4
+
5
+ export const Football = createSpotIllustration('Football', footballData)
6
+ export type FootballProps = React.ComponentPropsWithRef<typeof Football>
@@ -0,0 +1,6 @@
1
+ // Generated file - do not edit directly
2
+ import { createSpotIllustration } from './createSpotIllustration'
3
+ import handData from './json/hand.json'
4
+
5
+ export const Hand = createSpotIllustration('Hand', handData)
6
+ export type HandProps = React.ComponentPropsWithRef<typeof Hand>
@@ -0,0 +1,6 @@
1
+ // Generated file - do not edit directly
2
+ import { createSpotIllustration } from './createSpotIllustration'
3
+ import heartData from './json/heart.json'
4
+
5
+ export const Heart = createSpotIllustration('Heart', heartData)
6
+ export type HeartProps = React.ComponentPropsWithRef<typeof Heart>
@@ -0,0 +1,6 @@
1
+ // Generated file - do not edit directly
2
+ import { createSpotIllustration } from './createSpotIllustration'
3
+ import highfiveData from './json/high-five.json'
4
+
5
+ export const HighFive = createSpotIllustration('HighFive', highfiveData)
6
+ export type HighFiveProps = React.ComponentPropsWithRef<typeof HighFive>
@@ -0,0 +1,6 @@
1
+ // Generated file - do not edit directly
2
+ import { createSpotIllustration } from './createSpotIllustration'
3
+ import mappinData from './json/map-pin.json'
4
+
5
+ export const MapPin = createSpotIllustration('MapPin', mappinData)
6
+ export type MapPinProps = React.ComponentPropsWithRef<typeof MapPin>