refactor(combo-control): use native Popover instead of bits-ui
The native Popover always renders its content (the vertical slider), so the slider's value label is in the DOM even when closed, and opening is driven by the browser's declarative popovertarget invoker (not simulated by jsdom on click). Update the tests to scope value assertions to the trigger and drive open via showPopover(), matching Popover.svelte.test.ts.
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
within,
|
||||
} from '@testing-library/svelte';
|
||||
import ComboControl from './ComboControl.svelte';
|
||||
|
||||
@@ -16,6 +17,16 @@ function makeControl(value: number, opts: { min?: number; max?: number; step?: n
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The trigger is the button wired to the popover (has popovertarget). The native
|
||||
* Popover always renders its content (the vertical slider, which also displays the
|
||||
* value) in the DOM, so value assertions must be scoped to the trigger to avoid
|
||||
* matching the slider's own value label.
|
||||
*/
|
||||
function getTrigger(): HTMLElement {
|
||||
return document.querySelector('button[popovertarget]') as HTMLElement;
|
||||
}
|
||||
|
||||
describe('ComboControl', () => {
|
||||
describe('Rendering', () => {
|
||||
it('renders decrease and increase buttons', () => {
|
||||
@@ -26,17 +37,17 @@ describe('ComboControl', () => {
|
||||
|
||||
it('renders the current integer value', () => {
|
||||
render(ComboControl, { control: makeControl(42) });
|
||||
expect(screen.getByText('42')).toBeInTheDocument();
|
||||
expect(within(getTrigger()).getByText('42')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('formats decimal value to 1 decimal place when step >= 0.1', () => {
|
||||
render(ComboControl, { control: makeControl(1.5, { step: 0.1 }) });
|
||||
expect(screen.getByText('1.5')).toBeInTheDocument();
|
||||
expect(within(getTrigger()).getByText('1.5')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('formats decimal value to 2 decimal places when step < 0.1', () => {
|
||||
render(ComboControl, { control: makeControl(1.55, { step: 0.01 }) });
|
||||
expect(screen.getByText('1.55')).toBeInTheDocument();
|
||||
expect(within(getTrigger()).getByText('1.55')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders label when label prop is provided', () => {
|
||||
@@ -106,16 +117,32 @@ describe('ComboControl', () => {
|
||||
const control = makeControl(50);
|
||||
render(ComboControl, { control });
|
||||
await fireEvent.click(screen.getByLabelText('Increase'));
|
||||
await waitFor(() => expect(screen.getByText('51')).toBeInTheDocument());
|
||||
await waitFor(() => expect(within(getTrigger()).getByText('51')).toBeInTheDocument());
|
||||
});
|
||||
});
|
||||
|
||||
describe('Popover', () => {
|
||||
it('opens popover with vertical slider on trigger click', async () => {
|
||||
/**
|
||||
* The native Popover always renders its content; opening is driven by the
|
||||
* browser's declarative popovertarget invoker, which jsdom does not simulate
|
||||
* on click (mirrors Popover.svelte.test.ts). So assert the wired-but-closed
|
||||
* state, then drive the open through the API the browser would call.
|
||||
*/
|
||||
it('exposes a popover trigger with the vertical slider as its content', async () => {
|
||||
render(ComboControl, { control: makeControl(50), controlLabel: 'Size control' });
|
||||
expect(screen.queryByRole('slider')).not.toBeInTheDocument();
|
||||
await fireEvent.click(screen.getByText('Size control'));
|
||||
await waitFor(() => expect(screen.getByRole('slider')).toBeInTheDocument());
|
||||
|
||||
const trigger = getTrigger();
|
||||
expect(trigger).toHaveAttribute('aria-expanded', 'false');
|
||||
|
||||
const content = document.getElementById(trigger.getAttribute('popovertarget')!) as HTMLElement;
|
||||
expect(content).toHaveAttribute('data-state', 'closed');
|
||||
// The vertical slider lives inside the popover content. While closed the
|
||||
// content is visibility:hidden, so query including hidden elements.
|
||||
expect(within(content).getByRole('slider', { hidden: true })).toBeInTheDocument();
|
||||
|
||||
content.showPopover();
|
||||
await waitFor(() => expect(content).toHaveAttribute('data-state', 'open'));
|
||||
expect(trigger).toHaveAttribute('aria-expanded', 'true');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user