mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-31 18:02:27 +00:00
Add popover regression coverage
This commit is contained in:
1 parent
d9de74a446
commit
2fb2eea9f0
4 files changed
+658
-259
No files matched your search
@@ -0,0 +1,252 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Popover Scenarios | bitvid</title>
|
||||
<link rel="stylesheet" href="../css/tailwind.generated.css" />
|
||||
</head>
|
||||
<body class="min-h-screen bg-page text-text" data-ds="new">
|
||||
<main class="mx-auto flex max-w-6xl flex-col gap-xl px-lg py-xl">
|
||||
<header class="space-y-sm">
|
||||
<h1 class="text-2xl font-semibold">Popover positioning scenarios</h1>
|
||||
<p class="max-w-3xl text-sm text-muted">
|
||||
Interactive harness that exercises the popover engine near the edges of the
|
||||
viewport, inside modals, and within scroll containers. Playwright scenarios
|
||||
rely on the data attributes surfaced here.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<section class="space-y-md" data-test-section="grid">
|
||||
<div class="space-y-xs">
|
||||
<h2 class="text-xl font-semibold">Grid edge cases</h2>
|
||||
<p class="text-sm text-muted">
|
||||
The rightmost card hugs the viewport edge to verify collision handling.
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid gap-lg sm:grid-cols-2 lg:grid-cols-3">
|
||||
<article class="card space-y-sm p-md">
|
||||
<h3 class="text-lg font-semibold">Top left</h3>
|
||||
<p class="text-sm text-muted">Baseline placement without collisions.</p>
|
||||
<button
|
||||
class="btn-ghost"
|
||||
type="button"
|
||||
data-test-trigger="grid-top-left"
|
||||
aria-label="Open top-left menu"
|
||||
>
|
||||
Menu
|
||||
</button>
|
||||
</article>
|
||||
<article class="card space-y-sm p-md" data-test-card="grid-bottom-right">
|
||||
<h3 class="text-lg font-semibold">Bottom right</h3>
|
||||
<p class="text-sm text-muted">
|
||||
Forces the panel to flip when it would overflow the viewport.
|
||||
</p>
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
class="btn-ghost"
|
||||
type="button"
|
||||
data-test-trigger="grid-bottom-right"
|
||||
aria-label="Open bottom-right menu"
|
||||
>
|
||||
Menu
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
<article class="card space-y-sm p-md">
|
||||
<h3 class="text-lg font-semibold">Center</h3>
|
||||
<p class="text-sm text-muted">Acts as a control for viewport padding.</p>
|
||||
<div class="flex justify-center">
|
||||
<button
|
||||
class="btn-ghost"
|
||||
type="button"
|
||||
data-test-trigger="grid-center"
|
||||
aria-label="Open center menu"
|
||||
>
|
||||
Menu
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="space-y-md" data-test-section="modal">
|
||||
<div class="space-y-xs">
|
||||
<h2 class="text-xl font-semibold">Video modal integration</h2>
|
||||
<p class="text-sm text-muted">
|
||||
Mimics the production video modal so we can verify popovers respect the overlay root.
|
||||
</p>
|
||||
</div>
|
||||
<button class="btn w-40" type="button" data-test-open-modal>
|
||||
Open modal
|
||||
</button>
|
||||
<div
|
||||
class="fixed inset-0 z-overlay-modal hidden items-center justify-center bg-overlay/60 backdrop-blur"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="testModalTitle"
|
||||
data-test-modal
|
||||
>
|
||||
<div class="bv-modal__panel flex max-h-modal-shell flex-col gap-md p-lg">
|
||||
<header class="flex items-center justify-between gap-sm">
|
||||
<div>
|
||||
<p class="text-xs uppercase tracking-wide text-muted">Now playing</p>
|
||||
<h3 id="testModalTitle" class="text-lg font-semibold">
|
||||
Edge-of-viewport sample
|
||||
</h3>
|
||||
</div>
|
||||
<button class="btn-ghost" type="button" data-test-close-modal>
|
||||
Close
|
||||
</button>
|
||||
</header>
|
||||
<div class="space-y-md">
|
||||
<p class="text-sm text-muted">
|
||||
Interact with the more menu to verify popovers inside scroll-locked contexts.
|
||||
</p>
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
class="btn-ghost"
|
||||
type="button"
|
||||
data-test-trigger="modal-menu"
|
||||
aria-label="Open modal menu"
|
||||
>
|
||||
Menu
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="space-y-md" data-test-section="scroll">
|
||||
<div class="space-y-xs">
|
||||
<h2 class="text-xl font-semibold">Scroll container</h2>
|
||||
<p class="text-sm text-muted">
|
||||
Ensures auto-update keeps popovers aligned when the trigger lives in overflow regions.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="h-64 overflow-auto rounded-xl border border-border bg-surface-alt p-md"
|
||||
data-test-scroll-region
|
||||
>
|
||||
<div class="space-y-lg" style="height: 720px">
|
||||
<p class="text-sm text-muted">
|
||||
Scroll to the bottom to find the trigger used in automated tests.
|
||||
</p>
|
||||
<div class="mt-96 flex justify-center">
|
||||
<button
|
||||
class="btn"
|
||||
type="button"
|
||||
data-test-trigger="scroll-bottom"
|
||||
aria-label="Open scroll container menu"
|
||||
>
|
||||
Open menu
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script type="module">
|
||||
import createPopover from "../js/ui/overlay/popoverEngine.js";
|
||||
|
||||
const ACTIVE_LABELS = ["Play", "Save", "Share", "Open in new tab"];
|
||||
|
||||
function buildMenu({ container, trigger, id }) {
|
||||
const panel = container.ownerDocument.createElement("div");
|
||||
panel.className = "popover__panel card w-64 max-w-popover-safe space-y-sm";
|
||||
panel.dataset.testPanel = id;
|
||||
|
||||
const heading = container.ownerDocument.createElement("h4");
|
||||
heading.className = "text-sm font-semibold text-text";
|
||||
heading.textContent = trigger?.getAttribute("aria-label") || "Menu";
|
||||
panel.appendChild(heading);
|
||||
|
||||
const menu = container.ownerDocument.createElement("div");
|
||||
menu.className = "menu space-y-1";
|
||||
|
||||
ACTIVE_LABELS.forEach((label, index) => {
|
||||
const item = container.ownerDocument.createElement("button");
|
||||
item.className = "menu__item justify-between";
|
||||
item.type = "button";
|
||||
item.setAttribute("role", "menuitem");
|
||||
item.textContent = label;
|
||||
item.dataset.index = String(index);
|
||||
menu.appendChild(item);
|
||||
});
|
||||
|
||||
panel.appendChild(menu);
|
||||
container.appendChild(panel);
|
||||
return panel;
|
||||
}
|
||||
|
||||
function attachPopover(trigger, { id }) {
|
||||
const popover = createPopover(
|
||||
trigger,
|
||||
({ container }) => buildMenu({ container, trigger, id }),
|
||||
{ document }
|
||||
);
|
||||
|
||||
trigger.addEventListener("click", async (event) => {
|
||||
event.preventDefault();
|
||||
await popover.open();
|
||||
});
|
||||
|
||||
return popover;
|
||||
}
|
||||
|
||||
const triggers = [
|
||||
{ selector: "[data-test-trigger=grid-top-left]", id: "grid-top-left" },
|
||||
{ selector: "[data-test-trigger=grid-bottom-right]", id: "grid-bottom-right" },
|
||||
{ selector: "[data-test-trigger=grid-center]", id: "grid-center" },
|
||||
{ selector: "[data-test-trigger=modal-menu]", id: "modal-menu" },
|
||||
{ selector: "[data-test-trigger=scroll-bottom]", id: "scroll-bottom" }
|
||||
];
|
||||
|
||||
const popovers = new Map();
|
||||
triggers.forEach(({ selector, id }) => {
|
||||
const trigger = document.querySelector(selector);
|
||||
if (trigger) {
|
||||
popovers.set(id, attachPopover(trigger, { id }));
|
||||
}
|
||||
});
|
||||
|
||||
const modal = document.querySelector("[data-test-modal]");
|
||||
const openModalBtn = document.querySelector("[data-test-open-modal]");
|
||||
const closeModalBtn = document.querySelector("[data-test-close-modal]");
|
||||
|
||||
if (openModalBtn && modal) {
|
||||
openModalBtn.addEventListener("click", () => {
|
||||
modal.classList.remove("hidden");
|
||||
modal.removeAttribute("hidden");
|
||||
document.documentElement.classList.add("modal-open");
|
||||
document.body.classList.add("modal-open");
|
||||
});
|
||||
}
|
||||
|
||||
if (closeModalBtn && modal) {
|
||||
closeModalBtn.addEventListener("click", () => {
|
||||
modal.classList.add("hidden");
|
||||
modal.setAttribute("hidden", "");
|
||||
document.documentElement.classList.remove("modal-open");
|
||||
document.body.classList.remove("modal-open");
|
||||
|
||||
const modalPopover = popovers.get("modal-menu");
|
||||
modalPopover?.close();
|
||||
});
|
||||
}
|
||||
|
||||
window.__popoverTestHarness = {
|
||||
popovers,
|
||||
open(id) {
|
||||
return popovers.get(id)?.open();
|
||||
},
|
||||
close(id) {
|
||||
return popovers.get(id)?.close();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<div id="uiOverlay" data-overlay-root aria-hidden="true"></div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user