Skip to content

feat: add popover component#1543

Open
kotAPI wants to merge 3 commits intomainfrom
kotapi/implement-popover-component-features
Open

feat: add popover component#1543
kotAPI wants to merge 3 commits intomainfrom
kotapi/implement-popover-component-features

Conversation

@kotAPI
Copy link
Copy Markdown
Collaborator

@kotAPI kotAPI commented Sep 10, 2025

Summary

  • add Popover component with trigger, content, arrow fragments
  • support focus management, escape dismissal, and optional portal
  • document Popover and add theme styles and tests

Testing

  • npm test src/components/ui/Popover/tests/Popover.test.tsx src/components/ui/Popover/tests/Popover.behavior.test.tsx

Summary by CodeRabbit

  • New Features
    • Added Popover component (Root/Trigger/Content/Arrow), portal support, Escape dismissal, focus restoration; moved color-picker into a Popover and added a compact palette trigger and dark-mode toggle.
  • Style
    • Introduced Popover theming and default styles.
  • Documentation
    • New Popover docs page with example, anatomy, API tables, keyboard shortcuts, navigation entry, and SEO metadata.
  • Tests
    • Added interaction, ref-forwarding, portal, focus, and accessibility tests.
  • Chores
    • Added Storybook stories and examples for Popover.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Sep 10, 2025

📝 Walkthrough

Walkthrough

Adds a new composite Popover UI (Root/Trigger/Content/Arrow) with Floating UI positioning, context, portal support, focus/escape handling, docs (page, anatomy, API tables, examples), Storybook stories, tests, styles, SEO metadata, and a docs navigation entry.

Changes

Cohort / File(s) Summary
Popover implementation
src/components/ui/Popover/Popover.tsx, src/components/ui/Popover/context/PopoverContext.tsx, src/components/ui/Popover/fragments/PopoverRoot.tsx, src/components/ui/Popover/fragments/PopoverTrigger.tsx, src/components/ui/Popover/fragments/PopoverContent.tsx, src/components/ui/Popover/fragments/PopoverArrow.tsx
Adds composite Popover component with static subcomponents, shared context, Floating UI integration (offset/flip/shift/arrow), controlled/uncontrolled state, portal support, focus management, Escape handling, and exported types.
Documentation & examples
docs/app/docs/components/popover/page.mdx, docs/app/docs/components/popover/seo.ts, docs/app/docs/components/popover/docs/anatomy.tsx, docs/app/docs/components/popover/docs/codeUsage.js, docs/app/docs/components/popover/docs/component_api/*, docs/app/docs/components/popover/docs/examples/popover_example1.tsx
Adds Popover docs page, SEO metadata, anatomy and code usage blocks, API tables for Root/Trigger/Content/Arrow, keyboard shortcuts, and a runnable example.
Stories & sandbox
src/components/ui/Popover/stories/Popover.stories.tsx, src/components/tools/SandboxEditor/SandboxEditor.tsx
Adds Storybook stories demonstrating Popover variants and updates SandboxEditor to use the Popover-based color picker UI (adjusts ColorSelect props/signature).
Tests
src/components/ui/Popover/tests/Popover.behavior.test.tsx, src/components/ui/Popover/tests/Popover.test.tsx
Adds interaction, focus-restoration, portal, outside-click, ref-forwarding, forceMount behavior, and a11y tests validating data-state and accessibility.
Styles & theme
styles/themes/components/popover.scss, styles/themes/default.scss
Adds popover and arrow SCSS and wires the component stylesheet into the default theme.
Docs navigation & changelog
docs/app/docs/docsNavigationSections.tsx, .changeset/curvy-birds-notice.md
Inserts Popover into the docs navigation and adds a changeset describing the release notes.
Package / deps
package.json
Adds a devDependency for motion (dev).

Sequence Diagram(s)

sequenceDiagram
  participant U as User
  participant T as Popover.Trigger
  participant R as Popover.Root (Context)
  participant F as Floating UI
  participant C as Popover.Content
  participant A as Popover.Arrow

  U->>T: Click
  T->>R: toggle open
  R->>F: compute position (offset/flip/shift/arrow)
  F-->>R: floating styles & placement
  R-->>C: provide context (open, data, interactions)
  Note over C: Content renders (optionally portalled)
  C->>A: use arrowRef & placement
  U->>C: Press Escape
  C->>R: request close
  R-->>T: restore focus
  C-->>U: unmount content
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

automerge

Suggested reviewers

  • GoldGroove06

Poem

I nibble code and stitch a sky,
A trigger clicks — the panel flies.
Arrow aims where whispers go,
Escape returns the button's glow.
Hooray — a pop, then off I hop! 🐇✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add popover component' clearly and concisely summarizes the main change—introducing a new Popover component with all its supporting files, documentation, and tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kotapi/implement-popover-component-features

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (29)
docs/app/docs/docsNavigationSections.tsx (1)

134-137: Add “is_new” badge for Popover (optional)

If you want Popover to show as newly added (like other entries), add the flag.

             {
                 title:"Popover",
                 path:"/docs/components/popover"
+                ,is_new:true
             },
docs/app/docs/components/popover/seo.ts (1)

1-8: Type the SEO metadata for safer Next.js integration

Annotate with Metadata so type errors surface at build time.

+import type { Metadata } from 'next';
 import generateSeoMetadata from "@/utils/seo/generateSeoMetadata";

-const popoverMetadata = generateSeoMetadata({
+const popoverMetadata: Metadata = generateSeoMetadata({
     title: 'Popover - Rad UI',
     description: 'A headless React Popover component for floating panels triggered by user interaction.'
 });
 
 export default popoverMetadata;
styles/themes/components/popover.scss (1)

1-10: Use design tokens (with fallbacks) and set a z-index

Align padding/radius with spacing/radius tokens and ensure layering above content.

 .rad-ui-popover {
-    background-color: var(--rad-ui-color-gray-50);
-    padding: 12px;
-    box-shadow: 0 10px 38px -10px #0e121659, 0 10px 20px -15px #0e121633;
-    border-radius: 8px;
+    background-color: var(--rad-ui-color-gray-50);
+    padding: var(--rad-ui-space-3, 12px);
+    box-shadow: 0 10px 38px -10px #0e121659, 0 10px 20px -15px #0e121633;
+    border-radius: var(--rad-ui-radius-2, 8px);
+    z-index: var(--rad-ui-z-index-popover, 50);
 
     .rad-ui-popover-arrow {
         fill: var(--rad-ui-color-gray-50);
     }
 }

Note: Please verify the exact token names (--rad-ui-space-3, --rad-ui-radius-2, --rad-ui-z-index-popover) exist in your design system; keep the fallbacks if uncertain.

docs/app/docs/components/popover/docs/anatomy.tsx (1)

1-15: If rendered live, add a minimal trigger child

In case this snippet is executed (not just shown as code), include a focusable trigger to avoid an empty target.

   return (
     <Popover.Root>
       <Popover.Trigger>
-        {/* Your trigger element */}
+        <button>Open</button>
       </Popover.Trigger>
       <Popover.Content>
         <Popover.Arrow />
         {/* Your popover content */}
       </Popover.Content>
     </Popover.Root>
   );
src/components/ui/Popover/stories/Popover.stories.tsx (1)

6-9: Point Storybook to a concrete component for better docs/controls

Use Popover.Root as the component so Storybook can infer props; the module object Popover has no renderable props.

-const meta: Meta<typeof Popover> = {
-    title: 'Components/Popover',
-    component: Popover
-};
+const meta: Meta<typeof Popover.Root> = {
+    title: 'Components/Popover',
+    component: Popover.Root
+};
src/components/ui/Popover/tests/Popover.test.tsx (4)

1-3: Use userEvent.setup() for reliable async interactions

Initialize a user instance; it avoids flakiness with timers and mirrors current @testing-library/user-event guidance.

-import { render, screen } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
@@
-        await userEvent.click(trigger);
+        const user = userEvent.setup();
+        await user.click(trigger);
@@
-        await userEvent.click(trigger);
+        await user.click(trigger);
@@
-        await userEvent.click(screen.getByText('Trigger'));
+        const user = userEvent.setup();
+        await user.click(screen.getByText('Trigger'));

Also applies to: 19-22, 42-42


2-2: Avoid timing flakes when asserting dismissal

If the Content unmounts asynchronously (animations, portals), prefer waitForElementToBeRemoved.

-import { render, screen } from '@testing-library/react';
+import { render, screen, waitForElementToBeRemoved } from '@testing-library/react';
@@
-        await user.click(trigger);
-        expect(screen.queryByText('Content')).toBeNull();
+        await user.click(trigger);
+        await waitForElementToBeRemoved(() => screen.queryByText('Content'));

Also applies to: 21-23


17-21: Prefer accessible role-based queries

Minor: query by role to better reflect the user-facing accessibility contract.

-        const trigger = screen.getByText('Trigger');
+        const trigger = screen.getByRole('button', { name: /trigger/i });
@@
-        await user.click(screen.getByText('Trigger'));
+        await user.click(screen.getByRole('button', { name: /trigger/i }));

Also applies to: 42-42


25-41: Verify Root ref contract (DOM vs. provider-only)

If Popover.Root is a provider that renders no DOM node (common pattern), asserting an HTMLDivElement here will fail. Either ensure Root forwards a DOM ref by design, or drop this assertion.

-        const rootRef = React.createRef<HTMLDivElement>();
+        // If Root is provider-only, no DOM ref is expected:
+        // const rootRef = React.createRef<HTMLDivElement>();
@@
-            <Popover.Root ref={rootRef}>
+            <Popover.Root>
@@
-        expect(rootRef.current).toBeInstanceOf(HTMLDivElement);
+        // If Root does not render a DOM node, remove this expectation.

Would you like me to align tests with the intended Root contract across the suite?

src/components/ui/Popover/context/PopoverContext.tsx (1)

3-10: Replace any with precise types (Floating UI) when available

If you’re using @floating-ui/react, consider exact types, e.g., ReturnType and FloatingContext, to regain type safety.

I can wire exact typings once I see PopoverRoot/Content implementations. Want me to follow up?

docs/app/docs/components/popover/docs/component_api/trigger.tsx (2)

11-14: Copy fix: article usage

“as child element” → “as a child element”.

-      prop: { name: 'asChild', info_tooltips: 'Render the trigger as child element.' },
+      prop: { name: 'asChild', info_tooltips: 'Render the trigger as a child element.' },

1-16: Optionally enforce the shape with a shared type

If you have a shared API table type, assert with satisfies for drift protection; also consider .ts since there’s no JSX.

-const data = {
+const data = {
   name: 'Trigger',
@@
-};
+} as const;
+
+export default data;

And, if available:

  • import the type (e.g., ApiTableData) and change to:
    const data = { ... } satisfies ApiTableData;

Rename to trigger.ts if consistent with the folder.

docs/app/docs/components/popover/docs/component_api/arrow.tsx (1)

1-10: LGTM; optional: type assertion and extension point

Structure looks consistent. If/when Arrow exposes props (size, offset), append rows here. Optionally add as const / satisfies to catch shape drift.

docs/app/docs/components/popover/docs/component_api/root.tsx (1)

1-31: Type-check the data object; clarify placement type if applicable

Optionally assert with satisfies to keep docs in sync. If placement is a union (e.g., 'top'|'bottom'|...), consider reflecting that in the Type column for clarity.

-const data = {
+const data = {
   name: 'Root',
@@
-};
+} as const;
 
 export default data;
src/components/ui/Popover/tests/Popover.behavior.test.tsx (2)

60-63: Stabilize a11y test by waiting for open content before running axe

Avoid a race by ensuring the popover content is in the DOM first.

-        await user.click(screen.getByText('Trigger'));
+        await user.click(screen.getByText('Trigger'));
+        await screen.findByText('Accessible');
         const results = await axe(container);

21-23: Prefer role-based queries over text for the trigger

Use accessible roles to reduce brittleness and improve semantics.

-        const trigger = screen.getByText('Trigger');
+        const trigger = screen.getByRole('button', { name: /trigger/i });
@@
-        const trigger = getByText('Trigger');
+        const trigger = screen.getByRole('button', { name: /trigger/i });
@@
-        await user.click(screen.getByText('Trigger'));
+        await user.click(screen.getByRole('button', { name: /trigger/i }));

Also applies to: 43-45, 61-61

docs/app/docs/components/popover/page.mdx (2)

3-3: Remove unused import

Popover isn’t referenced in this page.

-import Popover from "@radui/ui/Popover";

17-23: Fix wording: “Dismissible”

Use the standard spelling.

   <Documentation.ComponentFeatures
     features={[
       "Accessible and keyboard-friendly",
-      "Dismissable with Escape key",
+      "Dismissible with Escape key",
       "Supports portal rendering"
     ]}
   />
src/components/ui/Popover/fragments/PopoverRoot.tsx (2)

3-4: Memoize provider value to avoid needless subtree re-renders

Context value is recreated every render; memoize it.

-import React, { useRef } from 'react';
+import React, { useRef, useMemo } from 'react';
@@
-    return (
-        <PopoverContext.Provider value={{ open: isOpen, setOpen: setIsOpen, data, interactions, context, arrowRef }}>
+    const contextValue = useMemo(
+        () => ({ open: isOpen, setOpen: setIsOpen, data, interactions, context, arrowRef }),
+        [isOpen, setIsOpen, data, interactions, context]
+    );
+
+    return (
+        <PopoverContext.Provider value={contextValue}>
             <div ref={ref} data-state={isOpen ? 'open' : 'closed'} {...dataAttributes()} {...props}>
                 {children}
             </div>
         </PopoverContext.Provider>
     );

Also applies to: 59-64


8-8: Drop unused data-attribute hook (it’s a no-op here)

You call useCreateDataAttribute with null, creating an empty spread each render. Remove it for simplicity.

-import { useCreateDataAttribute } from '~/core/hooks/createDataAttribute';
@@
-    const dataAttributes = useCreateDataAttribute('popover', null);
@@
-            <div ref={ref} data-state={isOpen ? 'open' : 'closed'} {...dataAttributes()} {...props}>
+            <div ref={ref} data-state={isOpen ? 'open' : 'closed'} {...props}>

Also applies to: 57-57, 61-61

src/components/ui/Popover/fragments/PopoverArrow.tsx (1)

12-12: Copy: align error text with public API name

Say “Popover.Root” (public API) instead of “PopoverRoot component”.

-        throw new Error('PopoverArrow must be used within a PopoverRoot component');
+        throw new Error('PopoverArrow must be used within a Popover.Root component');
src/components/ui/Popover/Popover.tsx (1)

16-19: Silence console in production builds

Gate the warning to dev to avoid noisy prod logs.

-const Popover = React.forwardRef<React.ElementRef<'div'>, React.ComponentPropsWithoutRef<'div'>>((_, __) => {
-    console.warn('Direct usage of Popover is not supported. Use Popover.Root etc.');
-    return null;
-}) as PopoverComponent;
+const Popover = React.forwardRef<React.ElementRef<'div'>, React.ComponentPropsWithoutRef<'div'>>((_, __) => {
+    if (process.env.NODE_ENV !== 'production') {
+        console.warn('Direct usage of Popover is not supported. Use Popover.Root etc.');
+    }
+    return null;
+}) as PopoverComponent;
src/components/ui/Popover/fragments/PopoverTrigger.tsx (2)

16-17: Copy: align error text with public API name

-        throw new Error('PopoverTrigger must be used within a PopoverRoot component');
+        throw new Error('PopoverTrigger must be used within a Popover.Root component');

23-30: A11y: default aria-haspopup for triggers

Expose popover intent to AT. Default to dialog, allow override via props.

         <ButtonPrimitive
             asChild={asChild}
             ref={mergedRef}
             data-state={open ? 'open' : 'closed'}
-            {...getReferenceProps(props)}
+            {...getReferenceProps(props)}
+            aria-haspopup={(props as any)['aria-haspopup'] ?? 'dialog'}
         >
docs/app/docs/components/popover/docs/codeUsage.js (2)

24-29: Remove unused local columns constant

Dead code in docs bundle.

-const columns = [
-    { name: 'Prop', id: 'prop' },
-    { name: 'Type', id: 'type' },
-    { name: 'Default', id: 'default' },
-    { name: 'Description', id: 'description' }
-];

5-7: Harden source loading to avoid docs crash on missing files

Wrap with a small helper and fall back to a comment if read fails.

-import { getSourceCodeFromPath } from '@/utils/parseSourceCode';
+import { getSourceCodeFromPath } from '@/utils/parseSourceCode';
@@
-const example_1_SourceCode = await getSourceCodeFromPath('docs/app/docs/components/popover/docs/examples/popover_example1.tsx');
-const anatomy_SourceCode = await getSourceCodeFromPath('docs/app/docs/components/popover/docs/anatomy.tsx');
+const safeGet = async (p) => {
+    try { return await getSourceCodeFromPath(p); }
+    catch (err) { return `// Source unavailable (${p}): ${String(err)}`; }
+};
+const example_1_SourceCode = await safeGet('docs/app/docs/components/popover/docs/examples/popover_example1.tsx');
+const anatomy_SourceCode = await safeGet('docs/app/docs/components/popover/docs/anatomy.tsx');
src/components/ui/Popover/fragments/PopoverContent.tsx (3)

19-20: Copy: align error text with public API name

-        throw new Error('PopoverContent must be used within a PopoverRoot component');
+        throw new Error('PopoverContent must be used within a Popover.Root component');

45-65: A11y: add role and non-modal hint; keep user override

Non-modal popover content should default to role="dialog" with aria-modal={false}; let consumers override role if needed.

         <Primitive.div
             ref={allRefs}
             tabIndex={-1}
             data-state={open ? 'open' : 'closed'}
             data-side={side}
             data-align={align}
             style={{ ...data.floatingStyles, ...style }}
             {...interactions.getFloatingProps({
                 ...props,
                 onKeyDown: composeEventHandlers(onKeyDown, (e: React.KeyboardEvent) => {
                     if (e.key === 'Escape') {
                         setOpen(false);
                         e.stopPropagation();
                     }
                 })
             })}
+            role={props.role ?? 'dialog'}
+            aria-modal={false}
         >

67-67: Nit: avoid passing null to FloatingPortal root

Coalesce to undefined for default-root behavior.

-    return portalled ? <FloatingPortal root={container}>{element}</FloatingPortal> : element;
+    return portalled ? <FloatingPortal root={container ?? undefined}>{element}</FloatingPortal> : element;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d1d3f15 and 3362706.

📒 Files selected for processing (21)
  • docs/app/docs/components/popover/docs/anatomy.tsx (1 hunks)
  • docs/app/docs/components/popover/docs/codeUsage.js (1 hunks)
  • docs/app/docs/components/popover/docs/component_api/arrow.tsx (1 hunks)
  • docs/app/docs/components/popover/docs/component_api/content.tsx (1 hunks)
  • docs/app/docs/components/popover/docs/component_api/root.tsx (1 hunks)
  • docs/app/docs/components/popover/docs/component_api/trigger.tsx (1 hunks)
  • docs/app/docs/components/popover/docs/examples/popover_example1.tsx (1 hunks)
  • docs/app/docs/components/popover/page.mdx (1 hunks)
  • docs/app/docs/components/popover/seo.ts (1 hunks)
  • docs/app/docs/docsNavigationSections.tsx (1 hunks)
  • src/components/ui/Popover/Popover.tsx (1 hunks)
  • src/components/ui/Popover/context/PopoverContext.tsx (1 hunks)
  • src/components/ui/Popover/fragments/PopoverArrow.tsx (1 hunks)
  • src/components/ui/Popover/fragments/PopoverContent.tsx (1 hunks)
  • src/components/ui/Popover/fragments/PopoverRoot.tsx (1 hunks)
  • src/components/ui/Popover/fragments/PopoverTrigger.tsx (1 hunks)
  • src/components/ui/Popover/stories/Popover.stories.tsx (1 hunks)
  • src/components/ui/Popover/tests/Popover.behavior.test.tsx (1 hunks)
  • src/components/ui/Popover/tests/Popover.test.tsx (1 hunks)
  • styles/themes/components/popover.scss (1 hunks)
  • styles/themes/default.scss (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
docs/**/*.{ts,tsx}

📄 CodeRabbit inference engine (docs/.cursor/rules/posthog-integration.mdc)

In TypeScript, store feature flag names in an enum

Files:

  • docs/app/docs/components/popover/docs/anatomy.tsx
  • docs/app/docs/components/popover/seo.ts
  • docs/app/docs/components/popover/docs/examples/popover_example1.tsx
  • docs/app/docs/docsNavigationSections.tsx
  • docs/app/docs/components/popover/docs/component_api/trigger.tsx
  • docs/app/docs/components/popover/docs/component_api/root.tsx
  • docs/app/docs/components/popover/docs/component_api/arrow.tsx
  • docs/app/docs/components/popover/docs/component_api/content.tsx
docs/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (docs/.cursor/rules/posthog-integration.mdc)

docs/**/*.{ts,tsx,js,jsx}: Write enum/const object members for feature flag names in UPPERCASE_WITH_UNDERSCORE
Gate flag-dependent code with a check that verifies the flag’s values are valid and expected before use
If a custom person/event property is referenced in two or more places, define it in a central enum (TS) or const object (JS) and reuse that reference

Files:

  • docs/app/docs/components/popover/docs/anatomy.tsx
  • docs/app/docs/components/popover/seo.ts
  • docs/app/docs/components/popover/docs/examples/popover_example1.tsx
  • docs/app/docs/docsNavigationSections.tsx
  • docs/app/docs/components/popover/docs/component_api/trigger.tsx
  • docs/app/docs/components/popover/docs/component_api/root.tsx
  • docs/app/docs/components/popover/docs/component_api/arrow.tsx
  • docs/app/docs/components/popover/docs/component_api/content.tsx
  • docs/app/docs/components/popover/docs/codeUsage.js
docs/**/*.{js,jsx}

📄 CodeRabbit inference engine (docs/.cursor/rules/posthog-integration.mdc)

In JavaScript, store feature flag names as strings in a constant object to simulate an enum, and use a consistent naming convention

Files:

  • docs/app/docs/components/popover/docs/codeUsage.js
🧠 Learnings (2)
📚 Learning: 2024-12-12T08:22:59.375Z
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.tsx:0-0
Timestamp: 2024-12-12T08:22:59.375Z
Learning: The `Dropdown.Trigger` component is customizable and needs to be used with `Dropdown.Root`.

Applied to files:

  • docs/app/docs/components/popover/docs/anatomy.tsx
  • src/components/ui/Popover/fragments/PopoverTrigger.tsx
  • src/components/ui/Popover/Popover.tsx
📚 Learning: 2024-12-12T08:34:33.079Z
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.

Applied to files:

  • src/components/ui/Popover/fragments/PopoverTrigger.tsx
🧬 Code graph analysis (9)
docs/app/docs/components/popover/docs/examples/popover_example1.tsx (1)
docs/app/docs/components/popover/docs/anatomy.tsx (1)
  • Popover (3-15)
docs/app/docs/components/popover/docs/component_api/trigger.tsx (1)
docs/components/layout/Documentation/helpers/ComponentHero/ComponentHero.js (1)
  • data (32-32)
src/components/ui/Popover/tests/Popover.behavior.test.tsx (2)
src/test-utils/portal.ts (2)
  • renderWithPortal (4-17)
  • assertFocusReturn (41-43)
test-utils/index.ts (2)
  • keyboard (64-64)
  • axe (56-62)
docs/app/docs/components/popover/docs/component_api/arrow.tsx (1)
docs/components/layout/Documentation/helpers/ComponentHero/ComponentHero.js (1)
  • data (32-32)
src/components/ui/Popover/tests/Popover.test.tsx (1)
docs/app/docs/components/popover/docs/anatomy.tsx (1)
  • Popover (3-15)
docs/app/docs/components/popover/docs/codeUsage.js (1)
docs/utils/parseSourceCode.tsx (1)
  • getSourceCodeFromPath (16-62)
src/components/ui/Popover/fragments/PopoverRoot.tsx (2)
src/core/hooks/useControllableState/index.tsx (1)
  • useControllableState (15-78)
src/core/hooks/createDataAttribute/index.ts (1)
  • useCreateDataAttribute (10-25)
src/components/ui/Popover/stories/Popover.stories.tsx (1)
docs/app/docs/components/popover/docs/anatomy.tsx (1)
  • Popover (3-15)
src/components/ui/Popover/Popover.tsx (1)
docs/app/docs/components/popover/docs/anatomy.tsx (1)
  • Popover (3-15)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: coverage
  • GitHub Check: build
🔇 Additional comments (5)
styles/themes/default.scss (1)

43-43: Importing popover theme tokens — LGTM

The new @use "components/popover"; correctly wires the popover styles into the default theme.

docs/app/docs/components/popover/docs/component_api/content.tsx (1)

1-19: Update description text to use “popover”

 name: 'Content',
- description: 'The popup content.',
+ description: 'The popover content.',

No changes needed for info_tooltips or the portalled prop—they match the schema and runtime implementation.

docs/app/docs/components/popover/docs/examples/popover_example1.tsx (1)

10-14: Use an interactive element for Popover.Trigger child
Popover.Trigger asChild must wrap a semantic interactive element to ensure keyboard operability and correct ARIA. Replace the Card (div) wrapper with a button:

-            <Popover.Trigger asChild>
-                <Card className="bg-gray-50" style={{ display: 'inline-flex', gap: 20 }}>
-                    <Text className="text-gray-950">Open Popover</Text>
-                </Card>
-            </Popover.Trigger>
+            <Popover.Trigger asChild>
+                <button className="bg-gray-50 inline-flex gap-5 px-3 py-2 text-gray-950">
+                    Open Popover
+                </button>
+            </Popover.Trigger>

Remove the now-unused Card and Text imports. Manually verify keyboard focus management, Enter/Space activation, and update or add behavior tests accordingly.

src/components/ui/Popover/tests/Popover.test.tsx (1)

1-1: jest-dom matchers already configured globally
The Jest config’s setupFilesAfterEnv points to src/setupTests.ts, which imports @testing-library/jest-dom, so toBeInTheDocument() is available in all tests.

src/components/ui/Popover/context/PopoverContext.tsx (1)

12-12: Remove null-check audit note for PopoverContext usage
No direct calls to useContext(PopoverContext) were found in the codebase; the usePopoverContext() helper suffices.

Comment thread src/components/ui/Popover/context/PopoverContext.tsx Outdated
Comment thread src/components/ui/Popover/fragments/PopoverArrow.tsx Outdated
Comment thread src/components/ui/Popover/fragments/PopoverRoot.tsx
Comment thread src/components/ui/Popover/fragments/PopoverTrigger.tsx Outdated
Comment thread src/components/ui/Popover/tests/Popover.behavior.test.tsx Outdated
@kotAPI kotAPI removed the codex label Nov 18, 2025
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Feb 21, 2026

🦋 Changeset detected

Latest commit: 3a83a35

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@radui/ui Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Copy Markdown
Contributor

Coverage

This report compares the PR with the base branch. "Δ" shows how the PR affects each metric.

Metric PR Δ
Statements 84.06% -0.10%
Branches 61.76% -0.01%
Functions 69.13% +0.05%
Lines 83.31% -0.15%

Coverage decreased for at least one metric. Please add or update tests to improve coverage.

Run npm run coverage locally for detailed reports and target untested areas to raise these numbers.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
src/components/ui/Popover/stories/Popover.stories.tsx (1)

22-22: Add type="button" to prevent implicit form submission.

Buttons without an explicit type attribute default to type="submit" inside forms, which can cause unexpected behavior.

♻️ Proposed fix
-                    <button className='bg-gray-200 p-2'>Open</button>
+                    <button type="button" className='bg-gray-200 p-2'>Open</button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/Popover/stories/Popover.stories.tsx` at line 22, The button
element used as the Popover trigger (the <button className='bg-gray-200
p-2'>Open</button> in Popover.stories.tsx) lacks an explicit type and can
implicitly act as type="submit" inside forms; update that button to include
type="button" so it won't trigger form submission (modify the JSX for the
Popover trigger button to add type="button").
src/components/tools/SandboxEditor/SandboxEditor.tsx (1)

76-91: Use color instead of idx for the key prop.

Using array index as a key can cause issues with React's reconciliation if the list order changes. Since color (the color name) is unique and stable, it's a better key.

♻️ Proposed fix
-                                            <button
-                                                key={idx}
+                                            <button
+                                                key={color}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/tools/SandboxEditor/SandboxEditor.tsx` around lines 76 - 91,
The map callback in SandboxEditor uses the array index (idx) as the React key
which can break reconciliation; change the key on the button to use the stable
unique color identifier instead (the local variable color / accentColorName) so
replace key={idx} with key={color} (or key={accentColorName}) in the loop that
renders <button> with onClick={() => setColorName(accentColorName)} and
<ColorSelect>.
src/components/ui/Popover/fragments/PopoverArrow.tsx (1)

13-13: Consider adding accessibility attributes for the decorative arrow.

The arrow is purely decorative and should be hidden from assistive technologies.

♻️ Optional improvement
-    return <FloatingArrow ref={mergedRef} context={popover.context} {...rest} className={clsx('rad-ui-popover-arrow', className)} />;
+    return <FloatingArrow ref={mergedRef} context={popover.context} {...rest} className={clsx('rad-ui-popover-arrow', className)} aria-hidden="true" />;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/Popover/fragments/PopoverArrow.tsx` at line 13, The arrow
is decorative and must be hidden from assistive tech; update the JSX for the
FloatingArrow element in PopoverArrow.tsx to include accessibility attributes
(e.g., aria-hidden="true" and role="presentation") so screen readers ignore it,
making sure these explicit attributes are applied after the {...rest} spread on
the FloatingArrow element (refer to the FloatingArrow JSX, mergedRef,
popover.context and className usage) to prevent them from being accidentally
overridden.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/components/tools/SandboxEditor/SandboxEditor.tsx`:
- Around line 76-91: The map callback in SandboxEditor uses the array index
(idx) as the React key which can break reconciliation; change the key on the
button to use the stable unique color identifier instead (the local variable
color / accentColorName) so replace key={idx} with key={color} (or
key={accentColorName}) in the loop that renders <button> with onClick={() =>
setColorName(accentColorName)} and <ColorSelect>.

In `@src/components/ui/Popover/fragments/PopoverArrow.tsx`:
- Line 13: The arrow is decorative and must be hidden from assistive tech;
update the JSX for the FloatingArrow element in PopoverArrow.tsx to include
accessibility attributes (e.g., aria-hidden="true" and role="presentation") so
screen readers ignore it, making sure these explicit attributes are applied
after the {...rest} spread on the FloatingArrow element (refer to the
FloatingArrow JSX, mergedRef, popover.context and className usage) to prevent
them from being accidentally overridden.

In `@src/components/ui/Popover/stories/Popover.stories.tsx`:
- Line 22: The button element used as the Popover trigger (the <button
className='bg-gray-200 p-2'>Open</button> in Popover.stories.tsx) lacks an
explicit type and can implicitly act as type="submit" inside forms; update that
button to include type="button" so it won't trigger form submission (modify the
JSX for the Popover trigger button to add type="button").

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.

1 participant