Skip to content

feat(react): add popover component#653

Merged
mihar-22 merged 9 commits intomainfrom
feat/popover-react
Feb 28, 2026
Merged

feat(react): add popover component#653
mihar-22 merged 9 commits intomainfrom
feat/popover-react

Conversation

@mihar-22
Copy link
Copy Markdown
Member

@mihar-22 mihar-22 commented Feb 27, 2026

Refs #220
Depends on #652

Summary

React compound components for the popover, following the Popover.Root / Popover.Trigger / Popover.Popup pattern.

API Surface

Usage

<Popover.Root side="top" openOnHover>
  <Popover.Trigger>Settings</Popover.Trigger>
  <Popover.Popup>
    <Popover.Arrow />
    {/* content */}
  </Popover.Popup>
</Popover.Root>

Components

Component Element Description
Popover.Root State provider. Accepts all popover props.
Popover.Trigger <button> Toggle button. Receives ARIA attrs and anchor styles.
Popover.Popup <div> Floating content. Positioned via CSS Anchor or JS fallback.
Popover.Arrow <div> Decorative arrow, hidden from assistive tech.

Root Props

Prop Type Default Description
side 'top' | 'bottom' | 'left' | 'right' 'bottom' Placement side
align 'start' | 'center' | 'end' 'center' Cross-axis alignment
modal boolean | 'trap-focus' false Modal behavior
open boolean Controlled open state
defaultOpen boolean false Initial open state
onOpenChange (open, details) => void Called with { reason } on state change
openOnHover boolean false Open on hover
delay number 300 Hover open delay (ms)
closeDelay number 300 Hover close delay (ms)
closeOnEscape boolean true Close on Escape
closeOnOutsideClick boolean true Close on outside click

Part Props

Trigger, Popup, and Arrow accept:

  • render — render prop for custom elements (render={<a />})
  • className — static or state-aware (className={(state) => ...})
  • style — static or state-aware
  • ref — forwarded ref

Context

usePopoverContext() returns { state, popover, core, anchorName, popupId } for advanced use cases.

Implementation details
  • Focus events remapped: onFocusIn/onFocusOut from core → React's onFocus/onBlur
  • useId() output sanitized for valid CSS <dashed-ident> (strips colons)
  • Positioning returns camelCase keys — used directly as CSSProperties, no conversion needed
  • Manual fallback includes RAF-throttled scroll/resize listeners with cleanup

Testing

pnpm -F @videojs/react test

@vercel
Copy link
Copy Markdown

vercel bot commented Feb 27, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
vjs-10-demo-react Ignored Ignored Preview Feb 28, 2026 8:35am

Request Review

@mihar-22 mihar-22 force-pushed the feat/popover-react branch 2 times, most recently from 4c256ce to 04d7132 Compare February 28, 2026 01:54
@mihar-22 mihar-22 force-pushed the feat/popover-html branch 2 times, most recently from ccf6596 to a1c6efa Compare February 28, 2026 02:24
@sampotts sampotts requested a review from Copilot February 28, 2026 03:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new React Popover compound component API (Popover.Root / Popover.Trigger / Popover.Popup / Popover.Arrow) built on top of the existing @videojs/core popover core + DOM handle, providing positioning via CSS Anchor Positioning with a JS measurement fallback.

Changes:

  • Introduces PopoverRoot to wire PopoverCore + createPopover() interaction state into a React context.
  • Adds compound parts (Trigger, Popup, Arrow) that consume context, apply ARIA/data attrs, and integrate positioning/interaction props.
  • Exposes the new Popover namespace from the React package entrypoint.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/react/src/ui/popover/popover-context.tsx Adds context + hook for sharing core/popover/state/ids across parts.
packages/react/src/ui/popover/popover-root.tsx Implements root state provider bridging PopoverCore and createPopover() into React.
packages/react/src/ui/popover/popover-trigger.tsx Implements trigger part, applying trigger ARIA and anchor-name styling.
packages/react/src/ui/popover/popover-popup.tsx Implements popup part with CSS-anchor positioning and JS fallback with scroll/resize tracking.
packages/react/src/ui/popover/popover-arrow.tsx Implements decorative arrow part with state data attrs + aria-hidden.
packages/react/src/ui/popover/index.parts.ts Defines the public compound-part exports (Root/Trigger/Popup/Arrow).
packages/react/src/ui/popover/index.ts Exports the Popover namespace for consumer imports.
packages/react/src/index.ts Re-exports Popover from the package root API.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/react/src/ui/popover/popover-trigger.tsx Outdated
Comment thread packages/react/src/ui/popover/popover-root.tsx Outdated
@mihar-22 mihar-22 force-pushed the feat/popover-react branch 2 times, most recently from d12949b to 7b9790e Compare February 28, 2026 07:14
@mihar-22 mihar-22 force-pushed the feat/popover-html branch 2 times, most recently from 09ea72a to a0a2419 Compare February 28, 2026 07:53
@mihar-22 mihar-22 force-pushed the feat/popover-react branch 2 times, most recently from dad7c4d to 4615176 Compare February 28, 2026 08:22
@mihar-22 mihar-22 force-pushed the feat/popover-html branch 2 times, most recently from f98efea to 80df696 Compare February 28, 2026 08:30
- Defer initial open from connectedCallback to firstUpdated for reliable
  property resolution after upgrade
- Skip getBoundingClientRect/resolveOffsets when CSS Anchor Positioning
  is supported (avoids unnecessary layout measurements)
- Use applyElementProps for trigger cleanup instead of manual
  removeAttribute calls
- Remove stale anchor-name inline style on trigger cleanup
Aligns with the TransitionState.open → active rename in core.
Add React compound component wrappers for the popover UI:
- PopoverRoot: manages state, controlled/uncontrolled open, useId sanitization
- PopoverTrigger: anchor name style, focus event remapping (onFocusIn→onFocus)
- PopoverPopup: CSS Anchor Positioning with JS fallback, scroll/resize tracking
- PopoverArrow: decorative arrow with data-attribute state binding
- Barrel exports via Popover namespace (Popover.Root, Popover.Trigger, etc.)

Positioning functions now return camelCase keys directly compatible with
React's style prop, eliminating the need for toCamelCase/toReactStyle
conversion helpers.
- Re-add useLatestRef (moved from core PR scope)
- Extract useSafeId hook for CSS-safe identifiers from useId()
- Use forwardRef generic params instead of ForwardedRef in function sig
- Rename interaction.open to interaction.active per core rename
Base automatically changed from feat/popover-html to main February 28, 2026 08:39
@mihar-22 mihar-22 merged commit ed43d52 into main Feb 28, 2026
5 checks passed
@mihar-22 mihar-22 deleted the feat/popover-react branch February 28, 2026 08:40
This was referenced Feb 28, 2026
@github-actions github-actions bot mentioned this pull request Mar 10, 2026
This was referenced Apr 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants