mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
Some checks failed
ci / Packaged mac smoke (push) Blocked by required conditions
ci / Packaged windows smoke (push) Blocked by required conditions
ci / Detect PR change scopes (push) Failing after 2s
ci / Validate workspace (push) Has been skipped
nix-check / build (push) Failing after 1s
* feat(web): add custom select primitive * fix(web): harden custom select active option state
102 lines
3.2 KiB
TypeScript
102 lines
3.2 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
|
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
import { CustomSelect } from '../../src/components/CustomSelect';
|
|
|
|
afterEach(() => cleanup());
|
|
|
|
describe('CustomSelect', () => {
|
|
it('renders the selected label and chooses an option from the portal menu', () => {
|
|
const onChange = vi.fn();
|
|
render(
|
|
<CustomSelect
|
|
ariaLabel="Model"
|
|
value="gpt-image-2"
|
|
options={[
|
|
{ value: 'gpt-image-2', label: 'GPT Image 2' },
|
|
{ value: 'seedance', label: 'Seedance' },
|
|
]}
|
|
onChange={onChange}
|
|
/>,
|
|
);
|
|
|
|
const trigger = screen.getByRole('combobox', { name: 'Model: GPT Image 2' });
|
|
expect(trigger.getAttribute('aria-expanded')).toBe('false');
|
|
|
|
fireEvent.click(trigger);
|
|
expect(trigger.getAttribute('aria-expanded')).toBe('true');
|
|
|
|
fireEvent.click(screen.getByRole('option', { name: /Seedance/ }));
|
|
expect(onChange).toHaveBeenCalledWith('seedance');
|
|
});
|
|
|
|
it('skips disabled options and supports keyboard selection', () => {
|
|
const onChange = vi.fn();
|
|
render(
|
|
<CustomSelect
|
|
ariaLabel="Provider"
|
|
value="openai"
|
|
options={[
|
|
{ value: 'openai', label: 'OpenAI' },
|
|
{ value: 'disabled', label: 'Disabled', disabled: true },
|
|
{ value: 'custom', label: 'Custom' },
|
|
]}
|
|
onChange={onChange}
|
|
/>,
|
|
);
|
|
|
|
const trigger = screen.getByRole('combobox', { name: 'Provider: OpenAI' });
|
|
fireEvent.keyDown(trigger, { key: 'ArrowDown' });
|
|
fireEvent.keyDown(trigger, { key: 'ArrowDown' });
|
|
expect(trigger.getAttribute('aria-activedescendant')).toBe(
|
|
screen.getByRole('option', { name: /Custom/ }).id,
|
|
);
|
|
expect(trigger.getAttribute('aria-activedescendant')).not.toBe(
|
|
screen.getByRole('option', { name: /Disabled/ }).id,
|
|
);
|
|
|
|
fireEvent.keyDown(trigger, { key: 'Enter' });
|
|
|
|
expect(onChange).toHaveBeenCalledWith('custom');
|
|
expect(onChange).not.toHaveBeenCalledWith('disabled');
|
|
});
|
|
|
|
it('keeps keyboard navigation active state across parent rerenders with fresh options', () => {
|
|
const onChange = vi.fn();
|
|
const options = () => [
|
|
{ value: 'first', label: 'First' },
|
|
{ value: 'second', label: 'Second' },
|
|
{ value: 'third', label: 'Third' },
|
|
];
|
|
const { rerender } = render(
|
|
<CustomSelect
|
|
ariaLabel="Template"
|
|
value="first"
|
|
options={options()}
|
|
onChange={onChange}
|
|
/>,
|
|
);
|
|
|
|
const trigger = screen.getByRole('combobox', { name: 'Template: First' });
|
|
fireEvent.keyDown(trigger, { key: 'ArrowDown' });
|
|
fireEvent.keyDown(trigger, { key: 'ArrowDown' });
|
|
expect(trigger.getAttribute('aria-activedescendant')).toBe(
|
|
screen.getByRole('option', { name: /Second/ }).id,
|
|
);
|
|
|
|
rerender(
|
|
<CustomSelect
|
|
ariaLabel="Template"
|
|
value="first"
|
|
options={options()}
|
|
onChange={onChange}
|
|
/>,
|
|
);
|
|
|
|
const rerenderedTrigger = screen.getByRole('combobox', { name: 'Template: First' });
|
|
expect(rerenderedTrigger.getAttribute('aria-activedescendant')).toBe(
|
|
screen.getByRole('option', { name: /Second/ }).id,
|
|
);
|
|
});
|
|
});
|