From 970752a1530f4de21d4c95f52ca3ac361ca60554 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Fri, 8 May 2026 05:37:58 -0400 Subject: [PATCH] refactor(web): Modal component; admin + discover modals migrated (W2) --- web/src/lib/components/Modal.svelte | 58 +++ web/src/lib/components/Modal.test.ts | 107 ++++++ .../routes/admin/integrations/+page.svelte | 92 ++--- web/src/routes/admin/quarantine/+page.svelte | 179 ++++----- web/src/routes/admin/requests/+page.svelte | 203 +++++------ web/src/routes/admin/users/+page.svelte | 345 ++++++++---------- web/src/routes/discover/+page.svelte | 71 ++-- 7 files changed, 543 insertions(+), 512 deletions(-) create mode 100644 web/src/lib/components/Modal.svelte create mode 100644 web/src/lib/components/Modal.test.ts diff --git a/web/src/lib/components/Modal.svelte b/web/src/lib/components/Modal.svelte new file mode 100644 index 00000000..32257700 --- /dev/null +++ b/web/src/lib/components/Modal.svelte @@ -0,0 +1,58 @@ + + + + +{#if open} + +{/if} diff --git a/web/src/lib/components/Modal.test.ts b/web/src/lib/components/Modal.test.ts new file mode 100644 index 00000000..2e743baa --- /dev/null +++ b/web/src/lib/components/Modal.test.ts @@ -0,0 +1,107 @@ +import { describe, expect, test, vi } from 'vitest'; +import { render, screen, fireEvent } from '@testing-library/svelte'; +import { createRawSnippet } from 'svelte'; +import Modal from './Modal.svelte'; + +// children is a Snippet; createRawSnippet renders raw HTML inside the modal +// panel for assertions on backdrop vs panel click targeting. +const bodySnippet = createRawSnippet(() => ({ + render: () => `

body content

` +})); + +// The component's children-Snippet generic isn't inferable through +// testing-library's render; cast to any to satisfy the prop shape. +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AnyProps = any; + +describe('Modal', () => { + test('renders nothing when open=false', () => { + render(Modal, { + props: { + title: 'Hidden', + open: false, + onClose: vi.fn(), + children: bodySnippet + } as AnyProps + }); + expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); + }); + + test('renders dialog with role/aria attrs when open=true', () => { + render(Modal, { + props: { + title: 'Visible', + open: true, + onClose: vi.fn(), + children: bodySnippet + } as AnyProps + }); + const dialog = screen.getByRole('dialog'); + expect(dialog).toHaveAttribute('aria-modal', 'true'); + expect(dialog).toHaveAttribute('aria-labelledby', 'modal-title'); + }); + + test('title text appears in