diff --git a/docs/menus.md b/docs/menus.md index e66c1213..9e4c47b3 100644 --- a/docs/menus.md +++ b/docs/menus.md @@ -29,7 +29,7 @@ dev surface without worrying about duplicates. `createPopover(trigger, render, options)` wires focus management, aria attributes, collision detection, and auto updates. The render callback receives a portal `container` inside the overlay root. Return the panel element so the -engine can toggle `data-popover-state` values while it opens and closes. +engine toggles `data-popover-state` / `data-state` values while it opens and closes. ```js import createPopover from "../js/ui/overlay/popoverEngine.js"; diff --git a/js/ui/overlay/popoverEngine.js b/js/ui/overlay/popoverEngine.js index 494d3961..f3764129 100644 --- a/js/ui/overlay/popoverEngine.js +++ b/js/ui/overlay/popoverEngine.js @@ -756,6 +756,7 @@ export function createPopover(trigger, render, options = {}) { panelElement.hidden = false; panelElement.setAttribute("aria-hidden", "false"); panelElement.dataset.popoverState = "open"; + panelElement.dataset.state = "open"; setExpandedAttribute(anchor, true); bindGlobalHandlers(); @@ -809,6 +810,7 @@ export function createPopover(trigger, render, options = {}) { panel.setAttribute("aria-hidden", "true"); panel.hidden = true; panel.dataset.popoverState = "closed"; + panel.dataset.state = "closed"; updateAriaActiveDescendant(panel, null); if (menuState.items.length) { menuState.items.forEach((item) => { diff --git a/tests/position-floating-panel.test.mjs b/tests/position-floating-panel.test.mjs index 8afe224c..27a9ef72 100644 --- a/tests/position-floating-panel.test.mjs +++ b/tests/position-floating-panel.test.mjs @@ -151,6 +151,7 @@ test("flips placement when bottom placement would collide with viewport", async const panel = documentRef.querySelector('[data-test-panel="collision"]'); assert.ok(panel, "panel should render"); assert.equal(panel.dataset.popoverState, "open"); + assert.equal(panel.dataset.state, "open"); assert.equal(panel.dataset.popoverPlacement, "top-start"); assert.equal(panel.style.position, "fixed"); assert.equal(panel.style.maxWidth, "calc(100vw - var(--space-xl))"); @@ -254,15 +255,18 @@ test("closes previously open popovers without restoring focus", async () => { assert.equal(popoverA.isOpen(), true); assert.equal(triggerA.getAttribute("aria-expanded"), "true"); assert.equal(panelA?.dataset.popoverState, "open"); + assert.equal(panelA?.dataset.state, "open"); await popoverB.open(); assert.equal(popoverB.isOpen(), true); assert.equal(triggerB.getAttribute("aria-expanded"), "true"); assert.equal(panelB?.dataset.popoverState, "open"); + assert.equal(panelB?.dataset.state, "open"); assert.equal(popoverA.isOpen(), false, "first popover should close"); assert.equal(triggerA.getAttribute("aria-expanded"), "false"); assert.equal(panelA?.dataset.popoverState, "closed"); + assert.equal(panelA?.dataset.state, "closed"); assert.notEqual(active.active, triggerA, "focus should not jump back to first trigger"); popoverA.destroy(); diff --git a/tests/ui/popoverEngine.test.mjs b/tests/ui/popoverEngine.test.mjs index e91c7bb7..f6303f50 100644 --- a/tests/ui/popoverEngine.test.mjs +++ b/tests/ui/popoverEngine.test.mjs @@ -124,6 +124,7 @@ test("opens a popover in the overlay root and positions the panel", async () => assert.ok(panel, "panel should be rendered"); assert.equal(trigger.getAttribute("aria-controls"), panel.id); assert.equal(panel.dataset.popoverState, "open"); + assert.equal(panel.dataset.state, "open"); assert.equal(panel.dataset.popoverPlacement, "bottom-start"); assert.equal(panel.style.position, "fixed"); assert.equal(panel.style.left, "100px"); @@ -134,6 +135,7 @@ test("opens a popover in the overlay root and positions the panel", async () => popover.close(); assert.equal(panel.dataset.popoverState, "closed"); + assert.equal(panel.dataset.state, "closed"); assert.equal(trigger.getAttribute("aria-expanded"), "false"); popover.destroy(); @@ -369,6 +371,7 @@ test("escape closes the popover and restores trigger focus", async () => { assert.equal(popover.isOpen(), false); assert.equal(panel.dataset.popoverState, "closed"); + assert.equal(panel.dataset.state, "closed"); assert.equal(panel.getAttribute("aria-activedescendant"), null); assert.equal(documentRef.activeElement, trigger); @@ -496,6 +499,8 @@ test("ensures only one popover is open at a time", async () => { const panelB = documentRef.getElementById("panel-b"); assert.equal(panelA.dataset.popoverState, "closed"); assert.equal(panelB.dataset.popoverState, "open"); + assert.equal(panelA.dataset.state, "closed"); + assert.equal(panelB.dataset.state, "open"); popoverA.destroy(); popoverB.destroy();