multi-select.test.tsx126 lines · main
1import { fireEvent, render, screen } from '@testing-library/react'
2import { useState } from 'react'
3import { describe, expect, it } from 'vitest'
4
5import {
6 MultiSelector,
7 MultiSelectorContent,
8 MultiSelectorInput,
9 MultiSelectorItem,
10 MultiSelectorList,
11 MultiSelectorTrigger,
12} from './index'
13
14// This fixes a "ReferenceError: ResizeObserver is not defined" error in the test
15class ResizeObserver {
16 observe() {}
17 unobserve() {}
18 disconnect() {}
19}
20
21global.ResizeObserver = ResizeObserver
22
23function MultiSelectDemo() {
24 const [selectedValues, setSelectedValues] = useState<string[]>([])
25
26 return (
27 <MultiSelector values={selectedValues} onValuesChange={setSelectedValues}>
28 <MultiSelectorTrigger className="w-72" label="Select fruits" badgeLimit="wrap" />
29 <MultiSelectorContent>
30 <MultiSelectorInput placeholder="Search fruits" showResetIcon />
31 <MultiSelectorList>
32 <MultiSelectorItem value="Apple">Apple</MultiSelectorItem>
33 <MultiSelectorItem value="Banana">Banana</MultiSelectorItem>
34 <MultiSelectorItem value="Cherry">Cherry</MultiSelectorItem>
35 <MultiSelectorItem value="Date">Date</MultiSelectorItem>
36 <MultiSelectorItem value="Elderberrie">Elderberrie</MultiSelectorItem>
37 <MultiSelectorItem value="Fig">Fig</MultiSelectorItem>
38 <MultiSelectorItem value="Grape">Grape</MultiSelectorItem>
39 <MultiSelectorItem value="Kiwi">Kiwi</MultiSelectorItem>
40 <MultiSelectorItem value="Mango" disabled>
41 Mango
42 </MultiSelectorItem>
43 <MultiSelectorItem value="Strawberry">Strawberry</MultiSelectorItem>
44 </MultiSelectorList>
45 </MultiSelectorContent>
46 </MultiSelector>
47 )
48}
49
50describe('multi-select', () => {
51 it('opens the dropdown when the MultiSelectorTrigger is clicked', () => {
52 render(<MultiSelectDemo />)
53
54 const trigger = screen.getByRole('combobox')
55 expect(screen.queryByText('Apple')).not.toBeInTheDocument() // Dropdown should be closed initially
56
57 fireEvent.click(trigger) // Click on the trigger to open
58 expect(screen.getByText('Apple')).toBeInTheDocument() // Apple should be visible in the dropdown
59 })
60
61 it('adds and removes value when toggling MultiSelectorItem', () => {
62 render(<MultiSelectDemo />)
63
64 const trigger = screen.getByRole('combobox')
65 fireEvent.click(trigger) // Open the dropdown
66
67 const appleItem = screen.getByText('Apple')
68 fireEvent.click(appleItem) // Select the "Apple" item
69 expect(trigger).toHaveTextContent('Apple') // "Apple" should appear in the selected values
70
71 fireEvent.click(appleItem) // Select the "Apple" item
72 expect(trigger).not.toHaveTextContent('Apple') // "Apple" should not be selected anymore
73 })
74
75 it('closes the dropdown when clicking outside the component', () => {
76 render(<MultiSelectDemo />)
77
78 const trigger = screen.getByRole('combobox')
79 fireEvent.click(trigger) // Open the dropdown
80 expect(screen.getByText('Apple')).toBeInTheDocument() // Ensure it's open
81
82 fireEvent.click(document.body) // Click outside the component
83
84 setTimeout(() => {
85 expect(screen.queryByText('Apple')).not.toBeInTheDocument() // The dropdown should be closed
86 }, 100)
87 })
88
89 it('removes the last value when hitting Backspace', () => {
90 render(<MultiSelectDemo />)
91
92 const trigger = screen.getByRole('combobox')
93 fireEvent.click(trigger) // Open the dropdown
94
95 const appleItem = screen.getByText('Apple')
96 fireEvent.click(appleItem) // Select the "Apple" item
97 expect(trigger).toHaveTextContent('Apple') // "Apple" should be in the selected values
98
99 fireEvent.keyDown(trigger, { key: 'Backspace' }) // Press Backspace
100 expect(trigger).not.toHaveTextContent('Apple') // "Apple" should be removed
101 })
102
103 it('supports searching and filtering items', () => {
104 render(<MultiSelectDemo />)
105
106 const trigger = screen.getByRole('combobox')
107 fireEvent.click(trigger) // Open the dropdown
108
109 const searchInput = screen.getByPlaceholderText('Search fruits')
110 fireEvent.change(searchInput, { target: { value: 'Ap' } }) // Search for "Ap"
111
112 expect(screen.getByText('Apple')).toBeInTheDocument() // "Apple" should be visible
113 expect(screen.queryByText('Banana')).not.toBeInTheDocument() // "Banana" should not be visible
114 })
115
116 it('prevents selecting disabled items', () => {
117 render(<MultiSelectDemo />)
118
119 const trigger = screen.getByRole('combobox')
120 fireEvent.click(trigger) // Open the dropdown
121
122 const disabledItem = screen.getByText('Mango')
123 fireEvent.click(disabledItem)
124 expect(trigger).not.toHaveTextContent('Mango') // Mango should not be selectable
125 })
126})