Add Popover component - #551
Conversation
A headless wrapper around the native Popover API — the non-modal sibling of Dialog. The `[popover]` element gives top-layer stacking, Esc and (with `popover="auto"`) light-dismiss for free; the component only orchestrates transitions, fanning enter()/leave() out to every Transition/ViewTransition child. It ships no markup and no triggers: opening and closing are wired from HTML with Action (`Popover(#id)->target.toggle()` for a dropdown, mouseenter/mouseleave for a tooltip), and positioning is left entirely to author CSS. `auto` vs `manual` is the author's choice — `manual` keeps the leave transition (dismissal re-added via Action), `auto` trades it for free platform light-dismiss, with the native `toggle` event kept in sync. Adds the component + barrel export, unit tests, and docs (overview, JS API, and dropdown + tooltip example stories). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #551 +/- ##
============================================
+ Coverage 84.84% 84.91% +0.06%
Complexity 145 145
============================================
Files 112 113 +1
Lines 3610 3645 +35
Branches 679 685 +6
============================================
+ Hits 3063 3095 +32
- Misses 477 479 +2
- Partials 70 71 +1
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Export Size@studiometa/ui
Unchanged@studiometa/ui
|
Code ReviewRisk: Low — no blocking issues; safe to merge aside from nits. Adds a new headless Review usage: 265,452 in (214,016 cached) / 10,300 out tokens — $0.3366 (cloudflare-ai-gateway/gpt-5.4, thinking: medium) Reviewed by @weareikko/code-review v0.9.3 for commit 27e3381. |
What
A new headless
Popoverprimitive — the non-modal sibling ofDialog, wrapping the native Popover API.It offloads top-layer stacking, Esc and (with
popover="auto") light-dismiss to the platform, and only orchestrates transitions — fanningenter()/leave()out to everyTransition/ViewTransitionchild, exactly likeDialog.Design
Action, no custom attributes. The component exposesopen()/close()/toggle(); the HTML wires them:data-on:click="Popover(#menu)->target.toggle()"for a dropdown,data-on:mouseenter/data-on:mouseleave/data-on:focus/data-on:blurfor a tooltip,data-on:keydown="event.key === 'Escape' && Popover.close()"for Esc.autovsmanualis the author's choice.manual(used in the examples) lets the component own show/hide so the leave transition always plays, with dismissal re-added viaAction;autotrades that for free platform light-dismiss (an instant close), with the nativetoggleevent kept in sync so thecloseevent still fires.API
popover(the[popover]element),transitionsopen(),close(),toggle()— allPromise<void>open,closeWhat's included
packages/ui/Popover/— component + barrel export, added to the main@studiometa/uiexport.packages/tests/Popover/Popover.spec.ts— 10 tests (open/close/toggle, no-ops, event emission, fanning transitions out, leave-before-hide ordering, and platform light-dismiss sync). Exports snapshot updated.packages/docs/components/Popover/— overview, JS API, and two example stories (dropdown menu + tooltip).Verification
npm run lint— 0 errors, types pass.npm run test— 491 passed.npm run docs:build— succeeds.manualpopover,Transitionchild, anchor positioning) was validated in a browser (Playwright/Chromium): clean click-toggle with no light-dismiss reopen race, Esc + item-click dismissal, and hover/focus tooltip open/close.Follow-up
Once this lands, Combobox/Listbox builds directly on top of
Popover.🤖 Generated with Claude Code