Feature/popover #48

Merged
ilia merged 10 commits from feature/popover into main 2026-06-02 18:47:18 +00:00
2 changed files with 70 additions and 0 deletions
Showing only changes of commit ffa897ee54 - Show all commits
@@ -0,0 +1,49 @@
import {
fireEvent,
render,
screen,
} from '@testing-library/svelte';
import Harness from './PopoverHarness.svelte';
/**
* Resolve the popover content element (the [popover] ancestor of the test content).
*/
function getContent(): HTMLElement {
return screen.getByTestId('content').closest('[popover]') as HTMLElement;
}
describe('Popover', () => {
it('renders the trigger with aria wiring, closed by default', () => {
render(Harness);
const trigger = screen.getByRole('button', { name: 'Open' });
expect(trigger).toHaveAttribute('aria-expanded', 'false');
expect(trigger).toHaveAttribute('aria-haspopup', 'dialog');
expect(trigger).toHaveAttribute('popovertarget');
expect(getContent()).toHaveAttribute('data-state', 'closed');
});
it('opens via the popover toggle and syncs aria-expanded + data-state', async () => {
render(Harness);
const trigger = screen.getByRole('button', { name: 'Open' });
// jsdom does not auto-invoke popovertarget; call the API the browser would.
getContent().showPopover();
await Promise.resolve();
expect(getContent()).toHaveAttribute('data-state', 'open');
expect(trigger).toHaveAttribute('aria-expanded', 'true');
});
it('opens when the parent sets open=true (state -> browser)', async () => {
render(Harness, { open: true });
await Promise.resolve();
expect(getContent()).toHaveAttribute('data-state', 'open');
});
it('close() hides the popover and resets aria-expanded', async () => {
render(Harness, { open: true });
await Promise.resolve();
const trigger = screen.getByRole('button', { name: 'Open' });
await fireEvent.click(screen.getByTestId('close'));
expect(getContent()).toHaveAttribute('data-state', 'closed');
expect(trigger).toHaveAttribute('aria-expanded', 'false');
});
});
@@ -0,0 +1,21 @@
<!--
Component: PopoverHarness
Test-only fixture: renders Popover with a button trigger and simple content
exposing the close() callback.
-->
<script lang="ts">
import Popover from './Popover.svelte';
let { open = $bindable(false) }: { open?: boolean } = $props();
</script>
<Popover bind:open>
{#snippet trigger(props)}
<button {...props}>Open</button>
{/snippet}
{#snippet children({ close })}
<div data-testid="content">
<button onclick={close} data-testid="close">Close</button>
</div>
{/snippet}
</Popover>