Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 29 additions & 36 deletions packages/raystack/components/select/__tests__/select.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { act, fireEvent, render, screen } from '@testing-library/react';
import { fireEvent, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, expect, it, vi } from 'vitest';
import { Select } from '../select';
Expand All @@ -10,11 +10,7 @@ Object.defineProperty(Element.prototype, 'scrollIntoView', {
writable: true
});

const flushMicrotasks = async () => {
await act(async () => {
await new Promise(r => setTimeout(r, 0));
});
};
const flushMicrotasks = async () => await new Promise(r => setTimeout(r, 0));

const TRIGGER_TEXT = 'Select a fruit';
const FRUIT_OPTIONS = [
Expand Down Expand Up @@ -44,10 +40,10 @@ const BasicSelect = ({ ...props }: SelectRootProps) => {
);
};

const openSelect = async () => {
const openSelect = async (user: ReturnType<typeof userEvent.setup>) => {
const trigger = screen.getByRole('combobox');
fireEvent.click(trigger);
await flushMicrotasks();
await user.click(trigger);
await screen.findByRole('listbox');
};

describe('Select', () => {
Expand All @@ -74,7 +70,8 @@ describe('Select', () => {

it('shows content when trigger is clicked', async () => {
render(<BasicSelect />);
await openSelect();
const user = userEvent.setup();
await openSelect(user);

expect(screen.getByRole('listbox')).toBeInTheDocument();
FRUIT_OPTIONS.forEach(option => {
Expand Down Expand Up @@ -106,12 +103,11 @@ describe('Select', () => {
render(
<BasicSelect defaultValue='apple' onValueChange={handleValueChange} />
);
await openSelect();
const user = userEvent.setup();
await openSelect(user);

const options = screen.getAllByRole('option');
await act(async () => {
await userEvent.click(options[1]);
});
await user.click(options[1]);
await flushMicrotasks();

expect(handleValueChange).toHaveBeenCalledWith('banana');
Expand All @@ -120,16 +116,16 @@ describe('Select', () => {

it('closes content after selection', async () => {
render(<BasicSelect />);
await openSelect();
const user = userEvent.setup();
await openSelect(user);

expect(screen.getByRole('listbox')).toBeInTheDocument();

const options = screen.getAllByRole('option');
await act(async () => {
await userEvent.click(options[1]);
});
await user.click(options[1]);
await flushMicrotasks();

// Base UI keeps the popup in the DOM and toggles visibility.
expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
});
});
Expand All @@ -138,19 +134,16 @@ describe('Select', () => {
it('supports multiple selection', async () => {
const handleValueChange = vi.fn();
render(<BasicSelect multiple onValueChange={handleValueChange} />);
await openSelect();
const user = userEvent.setup();
await openSelect(user);

const options = screen.getAllByRole('option');

await act(async () => {
await userEvent.click(options[1]);
});
await user.click(options[1]);
await flushMicrotasks();
expect(handleValueChange).toHaveBeenCalledWith(['banana']);

await act(async () => {
await userEvent.click(options[4]);
});
await user.click(options[4]);
await flushMicrotasks();
expect(handleValueChange).toHaveBeenCalledWith(['banana', 'pineapple']);

Expand All @@ -162,19 +155,16 @@ describe('Select', () => {
it('allows deselecting items in multiple mode', async () => {
const handleValueChange = vi.fn();
render(<BasicSelect multiple onValueChange={handleValueChange} />);
await openSelect();
const user = userEvent.setup();
await openSelect(user);

const options = screen.getAllByRole('option');

await act(async () => {
await userEvent.click(options[1]);
});
await user.click(options[1]);
await flushMicrotasks();
expect(handleValueChange).toHaveBeenCalledWith(['banana']);

await act(async () => {
await userEvent.click(options[1]);
});
await user.click(options[1]);
await flushMicrotasks();
expect(handleValueChange).toHaveBeenCalledWith([]);
});
Expand Down Expand Up @@ -206,7 +196,7 @@ describe('Select', () => {
it('closes with Escape key', async () => {
const user = userEvent.setup();
render(<BasicSelect />);
await openSelect();
await openSelect(user);

await user.keyboard('{Escape}');

Expand All @@ -219,10 +209,10 @@ describe('Select', () => {
render(
<BasicSelect defaultValue='apple' onValueChange={handleValueChange} />
);
await openSelect();
await openSelect(user);

const options = screen.getAllByRole('option');
await act(() => options[1].focus());
options[1].focus();
await user.keyboard('{Enter}');
await flushMicrotasks();

Expand All @@ -236,8 +226,11 @@ describe('Select', () => {
render(
<BasicSelect defaultValue='apple' onValueChange={handleValueChange} />
);
await openSelect();
await openSelect(user);

// Ensure keyboard events target the listbox.
const options = screen.getAllByRole('option');
options[0]?.focus();
await user.keyboard('{ArrowDown}{ArrowDown}{Enter}');
await flushMicrotasks();

Expand Down
4 changes: 2 additions & 2 deletions packages/raystack/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ export { Label } from './components/label';
export { Link } from './components/link';
export { List } from './components/list';
export { Menu } from './components/menu';
export { Meter } from './components/meter';
export { Menubar } from './components/menubar';
export { Meter } from './components/meter';
export { Navbar } from './components/navbar';
export { Popover } from './components/popover';
export { PreviewCard } from './components/preview-card';
Expand All @@ -72,6 +72,6 @@ export {
ThemeSwitcher,
useTheme
} from './components/theme-provider';
export { ToastContainer, toast } from './components/toast';
export { Toast, toastManager } from './components/toast';
export { Toggle } from './components/toggle';
export { Tooltip } from './components/tooltip';
Loading