@open-mercato/ui 0.6.1-develop.3256.1.fe3dec2464 → 0.6.1-develop.3260.1.dc719cac84
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/.turbo/turbo-build.log +1 -1
- package/dist/backend/CrudForm.js +46 -129
- package/dist/backend/CrudForm.js.map +3 -3
- package/dist/backend/DataTable.js +10 -3
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/EmptyState.js +1 -47
- package/dist/backend/EmptyState.js.map +2 -2
- package/dist/backend/FilterOverlay.js +31 -26
- package/dist/backend/FilterOverlay.js.map +2 -2
- package/dist/backend/FlashMessages.js +23 -25
- package/dist/backend/FlashMessages.js.map +2 -2
- package/dist/backend/JsonBuilder.js +4 -2
- package/dist/backend/JsonBuilder.js.map +2 -2
- package/dist/backend/LogList.js +49 -0
- package/dist/backend/LogList.js.map +7 -0
- package/dist/backend/crud/CollapsibleGroup.js +13 -3
- package/dist/backend/crud/CollapsibleGroup.js.map +2 -2
- package/dist/backend/detail/InlineEditors.js +80 -32
- package/dist/backend/detail/InlineEditors.js.map +2 -2
- package/dist/backend/inputs/ComboboxInput.js +60 -6
- package/dist/backend/inputs/ComboboxInput.js.map +2 -2
- package/dist/backend/inputs/DatePicker.js +6 -142
- package/dist/backend/inputs/DatePicker.js.map +2 -2
- package/dist/backend/inputs/DateTimePicker.js +6 -182
- package/dist/backend/inputs/DateTimePicker.js.map +2 -2
- package/dist/backend/inputs/TagsInput.js +16 -19
- package/dist/backend/inputs/TagsInput.js.map +2 -2
- package/dist/backend/inputs/TimeInput.js +1 -1
- package/dist/backend/inputs/TimeInput.js.map +2 -2
- package/dist/backend/inputs/TimePicker.js +83 -72
- package/dist/backend/inputs/TimePicker.js.map +2 -2
- package/dist/backend/utils/customFieldForms.js +2 -2
- package/dist/backend/utils/customFieldForms.js.map +1 -1
- package/dist/portal/components/PortalEmptyState.js +13 -9
- package/dist/portal/components/PortalEmptyState.js.map +2 -2
- package/dist/primitives/accordion.js +137 -0
- package/dist/primitives/accordion.js.map +7 -0
- package/dist/primitives/alert.js +180 -12
- package/dist/primitives/alert.js.map +2 -2
- package/dist/primitives/calendar.js +122 -32
- package/dist/primitives/calendar.js.map +2 -2
- package/dist/primitives/compact-select.js +47 -0
- package/dist/primitives/compact-select.js.map +7 -0
- package/dist/primitives/counter-input.js +174 -0
- package/dist/primitives/counter-input.js.map +7 -0
- package/dist/primitives/date-picker-helpers.js +79 -0
- package/dist/primitives/date-picker-helpers.js.map +7 -0
- package/dist/primitives/date-picker.js +216 -0
- package/dist/primitives/date-picker.js.map +7 -0
- package/dist/primitives/date-range-picker.js +239 -0
- package/dist/primitives/date-range-picker.js.map +7 -0
- package/dist/primitives/digit-input.js +157 -0
- package/dist/primitives/digit-input.js.map +7 -0
- package/dist/primitives/empty-state.js +93 -0
- package/dist/primitives/empty-state.js.map +7 -0
- package/dist/primitives/inline-input.js +29 -0
- package/dist/primitives/inline-input.js.map +7 -0
- package/dist/primitives/inline-select.js +41 -0
- package/dist/primitives/inline-select.js.map +7 -0
- package/dist/primitives/notification-stack.js +116 -0
- package/dist/primitives/notification-stack.js.map +7 -0
- package/dist/primitives/notification.js +62 -0
- package/dist/primitives/notification.js.map +7 -0
- package/dist/primitives/rich-editor.js +1230 -0
- package/dist/primitives/rich-editor.js.map +7 -0
- package/dist/primitives/select.js +1 -0
- package/dist/primitives/select.js.map +2 -2
- package/dist/primitives/skeleton.js +55 -0
- package/dist/primitives/skeleton.js.map +7 -0
- package/dist/primitives/tag-input.js +186 -0
- package/dist/primitives/tag-input.js.map +7 -0
- package/dist/primitives/tag.js +66 -13
- package/dist/primitives/tag.js.map +2 -2
- package/dist/primitives/time-picker.js +793 -0
- package/dist/primitives/time-picker.js.map +7 -0
- package/package.json +4 -3
- package/src/backend/CrudForm.tsx +61 -113
- package/src/backend/DataTable.tsx +17 -5
- package/src/backend/EmptyState.tsx +13 -63
- package/src/backend/FilterOverlay.tsx +38 -22
- package/src/backend/FlashMessages.tsx +23 -24
- package/src/backend/JsonBuilder.tsx +4 -2
- package/src/backend/LogList.tsx +109 -0
- package/src/backend/__tests__/CrudForm.datetime.test.tsx +12 -7
- package/src/backend/__tests__/FilterOverlay.dateRange.test.tsx +89 -0
- package/src/backend/__tests__/LogList.test.tsx +163 -0
- package/src/backend/crud/CollapsibleGroup.tsx +17 -3
- package/src/backend/detail/InlineEditors.tsx +78 -25
- package/src/backend/detail/__tests__/InlineEditors.test.tsx +81 -1
- package/src/backend/inputs/ComboboxInput.tsx +81 -9
- package/src/backend/inputs/DatePicker.tsx +28 -155
- package/src/backend/inputs/DateTimePicker.tsx +25 -193
- package/src/backend/inputs/TagsInput.tsx +10 -13
- package/src/backend/inputs/TimeInput.tsx +2 -1
- package/src/backend/inputs/TimePicker.tsx +99 -70
- package/src/backend/inputs/__tests__/ComboboxInput.test.tsx +133 -0
- package/src/backend/inputs/__tests__/TimePicker.test.tsx +2 -2
- package/src/backend/utils/__tests__/richTextSanitizer.test.ts +5 -2
- package/src/backend/utils/customFieldForms.ts +2 -2
- package/src/portal/components/PortalEmptyState.tsx +27 -22
- package/src/primitives/__tests__/accordion.test.tsx +197 -0
- package/src/primitives/__tests__/alert.test.tsx +204 -0
- package/src/primitives/__tests__/compact-select.test.tsx +98 -0
- package/src/primitives/__tests__/counter-input.test.tsx +165 -0
- package/src/primitives/__tests__/date-picker.test.tsx +221 -0
- package/src/primitives/__tests__/date-range-picker.test.tsx +173 -0
- package/src/primitives/__tests__/digit-input.test.tsx +168 -0
- package/src/primitives/__tests__/empty-state.test.tsx +154 -0
- package/src/primitives/__tests__/inline-input.test.tsx +100 -0
- package/src/primitives/__tests__/inline-select.test.tsx +103 -0
- package/src/primitives/__tests__/notification-stack.test.tsx +174 -0
- package/src/primitives/__tests__/notification.test.tsx +119 -0
- package/src/primitives/__tests__/rich-editor.test.tsx +411 -0
- package/src/primitives/__tests__/skeleton.test.tsx +76 -0
- package/src/primitives/__tests__/tag-input.test.tsx +198 -0
- package/src/primitives/__tests__/tag.test.tsx +123 -0
- package/src/primitives/__tests__/time-picker.test.tsx +392 -0
- package/src/primitives/accordion.tsx +225 -0
- package/src/primitives/alert.tsx +234 -24
- package/src/primitives/calendar.tsx +146 -38
- package/src/primitives/compact-select.tsx +59 -0
- package/src/primitives/counter-input.tsx +211 -0
- package/src/primitives/date-picker-helpers.ts +96 -0
- package/src/primitives/date-picker.tsx +290 -0
- package/src/primitives/date-range-picker.tsx +281 -0
- package/src/primitives/digit-input.tsx +208 -0
- package/src/primitives/empty-state.tsx +140 -0
- package/src/primitives/inline-input.tsx +55 -0
- package/src/primitives/inline-select.tsx +74 -0
- package/src/primitives/notification-stack.tsx +185 -0
- package/src/primitives/notification.tsx +136 -0
- package/src/primitives/rich-editor.tsx +1578 -0
- package/src/primitives/select.tsx +1 -0
- package/src/primitives/skeleton.tsx +70 -0
- package/src/primitives/tag-input.tsx +223 -0
- package/src/primitives/tag.tsx +69 -10
- package/src/primitives/time-picker.tsx +1059 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as React from 'react'
|
|
6
|
+
import { fireEvent, render, screen } from '@testing-library/react'
|
|
7
|
+
import { TagInput } from '../tag-input'
|
|
8
|
+
|
|
9
|
+
function Harness({
|
|
10
|
+
initial,
|
|
11
|
+
...rest
|
|
12
|
+
}: {
|
|
13
|
+
initial?: string[]
|
|
14
|
+
} & Omit<React.ComponentProps<typeof TagInput>, 'value' | 'onChange'>) {
|
|
15
|
+
const [value, setValue] = React.useState<string[]>(initial ?? [])
|
|
16
|
+
return <TagInput value={value} onChange={setValue} {...rest} />
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getInput(): HTMLInputElement {
|
|
20
|
+
return screen.getByRole('textbox') as HTMLInputElement
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('TagInput primitive', () => {
|
|
24
|
+
it('adds a tag on Enter', () => {
|
|
25
|
+
render(<Harness aria-label="tags" />)
|
|
26
|
+
const input = getInput()
|
|
27
|
+
fireEvent.change(input, { target: { value: 'alpha' } })
|
|
28
|
+
fireEvent.keyDown(input, { key: 'Enter' })
|
|
29
|
+
expect(screen.getByText('alpha')).toBeInTheDocument()
|
|
30
|
+
expect(input.value).toBe('')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('adds a tag when separator (",") is typed', () => {
|
|
34
|
+
render(<Harness aria-label="tags" />)
|
|
35
|
+
const input = getInput()
|
|
36
|
+
fireEvent.change(input, { target: { value: 'beta,' } })
|
|
37
|
+
expect(screen.getByText('beta')).toBeInTheDocument()
|
|
38
|
+
expect(input.value).toBe('')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('splits multiple tags on bulk paste with separator', () => {
|
|
42
|
+
render(<Harness aria-label="tags" />)
|
|
43
|
+
const input = getInput()
|
|
44
|
+
fireEvent.change(input, { target: { value: 'a,b,c,' } })
|
|
45
|
+
expect(screen.getByText('a')).toBeInTheDocument()
|
|
46
|
+
expect(screen.getByText('b')).toBeInTheDocument()
|
|
47
|
+
expect(screen.getByText('c')).toBeInTheDocument()
|
|
48
|
+
expect(input.value).toBe('')
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('removes the last tag on Backspace when input is empty', () => {
|
|
52
|
+
render(<Harness initial={['x', 'y']} aria-label="tags" />)
|
|
53
|
+
const input = getInput()
|
|
54
|
+
expect(screen.getByText('y')).toBeInTheDocument()
|
|
55
|
+
fireEvent.keyDown(input, { key: 'Backspace' })
|
|
56
|
+
expect(screen.queryByText('y')).not.toBeInTheDocument()
|
|
57
|
+
expect(screen.getByText('x')).toBeInTheDocument()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('does not remove a tag on Backspace when input has content', () => {
|
|
61
|
+
render(<Harness initial={['keep']} aria-label="tags" />)
|
|
62
|
+
const input = getInput()
|
|
63
|
+
fireEvent.change(input, { target: { value: 'typing' } })
|
|
64
|
+
fireEvent.keyDown(input, { key: 'Backspace' })
|
|
65
|
+
expect(screen.getByText('keep')).toBeInTheDocument()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('removes a tag when its × button is clicked', () => {
|
|
69
|
+
render(<Harness initial={['removeMe', 'stay']} aria-label="tags" />)
|
|
70
|
+
fireEvent.click(screen.getByRole('button', { name: 'Remove removeMe' }))
|
|
71
|
+
expect(screen.queryByText('removeMe')).not.toBeInTheDocument()
|
|
72
|
+
expect(screen.getByText('stay')).toBeInTheDocument()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('blocks further additions when maxTags is reached', () => {
|
|
76
|
+
render(<Harness initial={['a', 'b']} maxTags={2} aria-label="tags" />)
|
|
77
|
+
const input = getInput()
|
|
78
|
+
expect(input).toBeDisabled()
|
|
79
|
+
fireEvent.change(input, { target: { value: 'c' } })
|
|
80
|
+
fireEvent.keyDown(input, { key: 'Enter' })
|
|
81
|
+
expect(screen.queryByText('c')).not.toBeInTheDocument()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('rejects silently when validate returns false', () => {
|
|
85
|
+
const validate = (tag: string) => tag !== 'bad'
|
|
86
|
+
render(<Harness validate={validate} aria-label="tags" />)
|
|
87
|
+
const input = getInput()
|
|
88
|
+
fireEvent.change(input, { target: { value: 'bad' } })
|
|
89
|
+
fireEvent.keyDown(input, { key: 'Enter' })
|
|
90
|
+
expect(screen.queryByText('bad')).not.toBeInTheDocument()
|
|
91
|
+
expect(screen.queryByRole('alert')).not.toBeInTheDocument()
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('shows error message when validate returns a string', () => {
|
|
95
|
+
const validate = (tag: string) =>
|
|
96
|
+
tag.length < 3 ? 'Tag must be at least 3 characters' : true
|
|
97
|
+
render(<Harness validate={validate} aria-label="tags" />)
|
|
98
|
+
const input = getInput()
|
|
99
|
+
fireEvent.change(input, { target: { value: 'ab' } })
|
|
100
|
+
fireEvent.keyDown(input, { key: 'Enter' })
|
|
101
|
+
expect(screen.queryByText('ab')).not.toBeInTheDocument()
|
|
102
|
+
expect(screen.getByRole('alert')).toHaveTextContent('Tag must be at least 3 characters')
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('clears error after typing again', () => {
|
|
106
|
+
const validate = (tag: string) =>
|
|
107
|
+
tag.length < 3 ? 'Too short' : true
|
|
108
|
+
render(<Harness validate={validate} aria-label="tags" />)
|
|
109
|
+
const input = getInput()
|
|
110
|
+
fireEvent.change(input, { target: { value: 'ab' } })
|
|
111
|
+
fireEvent.keyDown(input, { key: 'Enter' })
|
|
112
|
+
expect(screen.getByRole('alert')).toBeInTheDocument()
|
|
113
|
+
fireEvent.change(input, { target: { value: 'abc' } })
|
|
114
|
+
expect(screen.queryByRole('alert')).not.toBeInTheDocument()
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('skips duplicate tags silently when allowDuplicates is false', () => {
|
|
118
|
+
render(<Harness initial={['dup']} aria-label="tags" />)
|
|
119
|
+
const input = getInput()
|
|
120
|
+
fireEvent.change(input, { target: { value: 'dup' } })
|
|
121
|
+
fireEvent.keyDown(input, { key: 'Enter' })
|
|
122
|
+
const matches = screen.getAllByText('dup')
|
|
123
|
+
expect(matches.length).toBe(1)
|
|
124
|
+
expect(screen.queryByRole('alert')).not.toBeInTheDocument()
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('allows duplicates when allowDuplicates is true', () => {
|
|
128
|
+
render(<Harness initial={['dup']} allowDuplicates aria-label="tags" />)
|
|
129
|
+
const input = getInput()
|
|
130
|
+
fireEvent.change(input, { target: { value: 'dup' } })
|
|
131
|
+
fireEvent.keyDown(input, { key: 'Enter' })
|
|
132
|
+
expect(screen.getAllByText('dup').length).toBe(2)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('supports a custom RegExp separator', () => {
|
|
136
|
+
render(<Harness separator={/[,\s]/} aria-label="tags" />)
|
|
137
|
+
const input = getInput()
|
|
138
|
+
fireEvent.change(input, { target: { value: 'a b,' } })
|
|
139
|
+
expect(screen.getByText('a')).toBeInTheDocument()
|
|
140
|
+
expect(screen.getByText('b')).toBeInTheDocument()
|
|
141
|
+
expect(input.value).toBe('')
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('renders placeholder when empty', () => {
|
|
145
|
+
render(<Harness placeholder="Add tag..." aria-label="tags" />)
|
|
146
|
+
expect(getInput()).toHaveAttribute('placeholder', 'Add tag...')
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('keeps placeholder visible even with existing tags (Figma two-row layout)', () => {
|
|
150
|
+
render(<Harness initial={['one']} placeholder="Add tag..." aria-label="tags" />)
|
|
151
|
+
expect(getInput()).toHaveAttribute('placeholder', 'Add tag...')
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it('renders chips below the input row (two-row layout)', () => {
|
|
155
|
+
const { container } = render(<Harness initial={['a', 'b']} aria-label="tags" />)
|
|
156
|
+
const root = container.querySelector('[data-slot="tag-input"]')
|
|
157
|
+
expect(root).not.toBeNull()
|
|
158
|
+
const inputField = root!.querySelector('[data-slot="tag-input-field"]')
|
|
159
|
+
const chips = root!.querySelector('[data-slot="tag-input-chips"]')
|
|
160
|
+
expect(inputField).not.toBeNull()
|
|
161
|
+
expect(chips).not.toBeNull()
|
|
162
|
+
const inputIndex = Array.from(root!.children).indexOf(inputField!.closest('[data-slot="input-wrapper"]')!)
|
|
163
|
+
const chipsIndex = Array.from(root!.children).indexOf(chips!)
|
|
164
|
+
expect(inputIndex).toBeLessThan(chipsIndex)
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it('renders chips with shape="square" (Figma rectangular tag)', () => {
|
|
168
|
+
const { container } = render(<Harness initial={['x']} aria-label="tags" />)
|
|
169
|
+
const chip = container.querySelector('[data-slot="tag"][data-shape="square"]')
|
|
170
|
+
expect(chip).not.toBeNull()
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('supports lg size matching Figma Medium (40)', () => {
|
|
174
|
+
render(<Harness size="lg" aria-label="tags" />)
|
|
175
|
+
expect(getInput().closest('[data-slot="input-wrapper"]')!.className).toContain('h-10')
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('forwards id and name to underlying input', () => {
|
|
179
|
+
render(<Harness id="tags-field" name="tags[]" aria-label="tags" />)
|
|
180
|
+
const input = getInput()
|
|
181
|
+
expect(input).toHaveAttribute('id', 'tags-field')
|
|
182
|
+
expect(input).toHaveAttribute('name', 'tags[]')
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('disables input and remove buttons when disabled prop is true', () => {
|
|
186
|
+
render(<Harness initial={['locked']} disabled aria-label="tags" />)
|
|
187
|
+
expect(getInput()).toBeDisabled()
|
|
188
|
+
expect(screen.getByRole('button', { name: 'Remove locked' })).toBeDisabled()
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it('works in uncontrolled mode (no value/onChange props)', () => {
|
|
192
|
+
render(<TagInput aria-label="tags" />)
|
|
193
|
+
const input = getInput()
|
|
194
|
+
fireEvent.change(input, { target: { value: 'standalone' } })
|
|
195
|
+
fireEvent.keyDown(input, { key: 'Enter' })
|
|
196
|
+
expect(screen.getByText('standalone')).toBeInTheDocument()
|
|
197
|
+
})
|
|
198
|
+
})
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as React from 'react'
|
|
6
|
+
import { fireEvent, render, screen } from '@testing-library/react'
|
|
7
|
+
import { Tag } from '../tag'
|
|
8
|
+
|
|
9
|
+
describe('Tag primitive', () => {
|
|
10
|
+
it('renders default pill variant when no shape prop is given', () => {
|
|
11
|
+
render(<Tag>Hello</Tag>)
|
|
12
|
+
const root = screen.getByText('Hello').closest('[data-slot="tag"]')
|
|
13
|
+
expect(root).not.toBeNull()
|
|
14
|
+
expect(root!.className).toContain('rounded-full')
|
|
15
|
+
expect(root!.className).toContain('border-border')
|
|
16
|
+
expect(root).toHaveAttribute('data-shape', 'pill')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('renders square shape with rounded-md when shape="square"', () => {
|
|
20
|
+
render(<Tag shape="square">Berlin</Tag>)
|
|
21
|
+
const root = screen.getByText('Berlin').closest('[data-slot="tag"]')
|
|
22
|
+
expect(root!.className).toContain('rounded-md')
|
|
23
|
+
expect(root!.className).not.toContain('rounded-full')
|
|
24
|
+
expect(root).toHaveAttribute('data-shape', 'square')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('applies status colour variants', () => {
|
|
28
|
+
render(<Tag variant="success">OK</Tag>)
|
|
29
|
+
const root = screen.getByText('OK').closest('[data-slot="tag"]')
|
|
30
|
+
expect(root!.className).toContain('text-status-success-text')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('renders a leading dot when dot=true', () => {
|
|
34
|
+
render(
|
|
35
|
+
<Tag variant="error" dot>
|
|
36
|
+
Live
|
|
37
|
+
</Tag>,
|
|
38
|
+
)
|
|
39
|
+
const root = screen.getByText('Live').closest('[data-slot="tag"]')
|
|
40
|
+
const dotSpan = root!.querySelector('[aria-hidden="true"]')
|
|
41
|
+
expect(dotSpan).not.toBeNull()
|
|
42
|
+
expect(dotSpan!.className).toContain('bg-status-error-icon')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('renders no remove button by default', () => {
|
|
46
|
+
render(<Tag>Plain</Tag>)
|
|
47
|
+
expect(screen.queryByRole('button')).toBeNull()
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('renders a remove button when onRemove is provided', () => {
|
|
51
|
+
const onRemove = jest.fn()
|
|
52
|
+
render(<Tag onRemove={onRemove}>Closeable</Tag>)
|
|
53
|
+
const button = screen.getByRole('button', { name: 'Remove' })
|
|
54
|
+
expect(button).toBeInTheDocument()
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('uses custom removeAriaLabel when provided', () => {
|
|
58
|
+
const onRemove = jest.fn()
|
|
59
|
+
render(
|
|
60
|
+
<Tag onRemove={onRemove} removeAriaLabel="Remove Berlin">
|
|
61
|
+
Berlin
|
|
62
|
+
</Tag>,
|
|
63
|
+
)
|
|
64
|
+
expect(screen.getByRole('button', { name: 'Remove Berlin' })).toBeInTheDocument()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('fires onRemove and stops propagation when close button is clicked', () => {
|
|
68
|
+
const onRemove = jest.fn()
|
|
69
|
+
const onClick = jest.fn()
|
|
70
|
+
render(
|
|
71
|
+
<Tag onRemove={onRemove} onClick={onClick}>
|
|
72
|
+
ClickMe
|
|
73
|
+
</Tag>,
|
|
74
|
+
)
|
|
75
|
+
fireEvent.click(screen.getByRole('button', { name: 'Remove' }))
|
|
76
|
+
expect(onRemove).toHaveBeenCalledTimes(1)
|
|
77
|
+
expect(onClick).not.toHaveBeenCalled()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('disables the close button and fades the chip when disabled is true', () => {
|
|
81
|
+
const onRemove = jest.fn()
|
|
82
|
+
render(
|
|
83
|
+
<Tag onRemove={onRemove} disabled>
|
|
84
|
+
Locked
|
|
85
|
+
</Tag>,
|
|
86
|
+
)
|
|
87
|
+
const root = screen.getByText('Locked').closest('[data-slot="tag"]')
|
|
88
|
+
expect(root!.className).toContain('opacity-60')
|
|
89
|
+
expect(root).toHaveAttribute('aria-disabled', 'true')
|
|
90
|
+
const button = screen.getByRole('button', { name: 'Remove' })
|
|
91
|
+
expect(button).toBeDisabled()
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('forwards arbitrary HTMLSpan props (id, data-*, className)', () => {
|
|
95
|
+
render(
|
|
96
|
+
<Tag id="tag-1" data-testid="leading-tag" className="ml-2">
|
|
97
|
+
Forwarded
|
|
98
|
+
</Tag>,
|
|
99
|
+
)
|
|
100
|
+
const root = document.getElementById('tag-1')
|
|
101
|
+
expect(root).not.toBeNull()
|
|
102
|
+
expect(root).toHaveAttribute('data-testid', 'leading-tag')
|
|
103
|
+
expect(root!.className).toContain('ml-2')
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('uses smaller close icon for pill shape and larger for square shape', () => {
|
|
107
|
+
const { rerender, container } = render(
|
|
108
|
+
<Tag shape="pill" onRemove={() => {}}>
|
|
109
|
+
Pill
|
|
110
|
+
</Tag>,
|
|
111
|
+
)
|
|
112
|
+
let svg = container.querySelector('button svg')
|
|
113
|
+
expect(svg!.getAttribute('class')).toContain('size-2.5')
|
|
114
|
+
|
|
115
|
+
rerender(
|
|
116
|
+
<Tag shape="square" onRemove={() => {}}>
|
|
117
|
+
Square
|
|
118
|
+
</Tag>,
|
|
119
|
+
)
|
|
120
|
+
svg = container.querySelector('button svg')
|
|
121
|
+
expect(svg!.getAttribute('class')).toContain('size-3.5')
|
|
122
|
+
})
|
|
123
|
+
})
|