Skip to content

chore: drover and drawer moved to ui#4495

Merged
perkinsjr merged 8 commits intomainfrom
move-drawer-to-ui
Dec 12, 2025
Merged

chore: drover and drawer moved to ui#4495
perkinsjr merged 8 commits intomainfrom
move-drawer-to-ui

Conversation

@MichaelUnkey
Copy link
Collaborator

@MichaelUnkey MichaelUnkey commented Dec 11, 2025

What does this PR do?

Fixes # (issue)

If there is not an issue for this, please create one first. This is used to tracking purposes and also helps us understand why this PR exists

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

Usage Locations

Datetime mobile only
image

Filter popover all locations use changes

Ensure filter and datetime functions work as they did before

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Ran make fmt on /go directory
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

@changeset-bot
Copy link

changeset-bot bot commented Dec 11, 2025

⚠️ No Changeset found

Latest commit: aa35fed

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@vercel
Copy link

vercel bot commented Dec 11, 2025

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

Project Deployment Preview Comments Updated (UTC)
dashboard Ready Ready Preview Comment Dec 12, 2025 3:59pm
1 Skipped Deployment
Project Deployment Preview Comments Updated (UTC)
engineering Ignored Ignored Preview Dec 12, 2025 3:59pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

📝 Walkthrough

Walkthrough

Consolidates UI primitives into the internal UI package: adds useIsMobile hook and createContext utility, re-exports Drawer/Drover, moves two dependencies from the dashboard app to internal/ui, and updates many dashboard imports to use @unkey/ui with useIsMobile({ defaultValue: false }) defaults to avoid hydration mismatches.

Changes

Cohort / File(s) Summary
Dashboard imports updated
apps/dashboard/components/logs/checkbox/filter-item.tsx, apps/dashboard/components/logs/checkbox/filters-popover.tsx, apps/dashboard/components/logs/datetime/datetime-popover.tsx, apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx, apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/deployments/components/table/deployments-list.tsx, apps/dashboard/components/ui/sidebar.tsx, apps/dashboard/components/virtual-table/index.tsx
Switched local UI imports to @unkey/ui and updated call sites to use useIsMobile({ defaultValue: false }) where applicable to prevent hydration mismatches.
Removed app hook
apps/dashboard/hooks/use-mobile.tsx
Deleted the local useIsMobile hook implementation (moved into internal/ui).
Dashboard package.json
apps/dashboard/package.json
Removed @radix-ui/react-use-controllable-state and vaul from app dependencies.
Internal UI package.json
internal/ui/package.json
Added @radix-ui/react-use-controllable-state@1.2.2 and vaul@0.9.0 to internal UI dependencies.
Drawer component
internal/ui/src/components/drawer.tsx
Switched imports to relative paths and adjusted the Drawer export construction (Object.assign with properties object as first arg).
Drover component
internal/ui/src/components/drover.tsx
Replaced absolute alias imports with relative paths, updated Popover import usage, and initialized isMobile via useIsMobile({ defaultValue: false }) with a comment about hydration.
New hook: useIsMobile
internal/ui/src/hooks/use-mobile.tsx
Added useIsMobile(options) hook: media-query based detection, listener lifecycle, optional defaultValue to avoid hydration mismatches, and boolean return.
New utility: createContext
internal/ui/src/lib/create-context.tsx
Added generic createContext factory that returns a memoized Provider component and a typed useContext hook with fallback and descriptive error behavior.
Public exports
internal/ui/src/index.ts
Re-exported ./components/drawer, ./components/drover, and ./hooks/use-mobile to expand the internal UI public API surface.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • Pay extra attention to:
    • internal/ui/src/hooks/use-mobile.tsx: breakpoint/media-query logic, initial/default value handling, and cleanup.
    • internal/ui/src/lib/create-context.tsx: generic typings, memoization of context value, and error message correctness.
    • Import path changes in internal/ui/src/components/drover.tsx and drawer.tsx to avoid circular imports and ensure correct relative paths.
    • Dependency relocation between apps/dashboard/package.json and internal/ui/package.json.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'chore: drover and drawer moved to ui' clearly and specifically describes the main refactoring work: moving two UI components (drover and drawer) from local imports to a centralized UI library.
Description check ✅ Passed The description includes the required checklist, identifies the change type as chore, provides usage/testing context with imagery, and documents testing instructions. However, it lacks a clear high-level summary in the 'What does this PR do?' section and does not reference or create a tracking issue.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch move-drawer-to-ui

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
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: 2

🧹 Nitpick comments (4)
apps/dashboard/components/logs/checkbox/filter-item.tsx (1)

2-2: Using Drover from @unkey/ui is good; consider avoiding deep cn import

Pulling Button, Drover, and KeyboardButton from @unkey/ui matches the shared UI pattern and doesn’t change how this component behaves.

One follow‑up to consider (not blocking here): cn is currently imported from @unkey/ui/src/lib/utils, which reaches into an internal source path of the UI package. If/when cn is exposed via the main @unkey/ui entry (or a dedicated helper export), it would be cleaner to switch to that to keep app code decoupled from internal file layout.

Before changing the cn import, please confirm whether @unkey/ui actually re‑exports it; if not, it may be better to keep the current import until such an export exists.

internal/ui/src/hooks/use-mobile.tsx (1)

1-22: useIsMobile hook is SSR‑safe and correctly manages the media query listener

The hook keeps window usage inside useEffect, initializes from the current viewport, and removes the matchMedia listener on unmount, which is all correct.

If you ever revisit it, a tiny simplification would be to use mql.matches inside onChange and for the initial value:

const mql = window.matchMedia(`(max-width: ${breakpoint - 1}px)`);
const onChange = () => setIsMobile(mql.matches);
setIsMobile(mql.matches);

Functionally it’s equivalent but avoids recomputing via window.innerWidth.

internal/ui/src/lib/create-context.tsx (2)

20-30: Consider using explicit undefined check instead of truthy check.

The type constraint ContextValueType extends object | null allows null as a valid context value. The current truthy check if (context) would treat an explicitly provided null value as missing context.

While the fallback to defaultContext may handle some cases, using context !== undefined would be more precise:

 function useContext(consumerName: string) {
   const context = React.useContext(Context);
-  if (context) {
+  if (context !== undefined) {
     return context;
   }
   if (defaultContext !== undefined) {
     return defaultContext;
   }
   // if a defaultContext wasn't specified, it's a required context.
   throw new Error(`\`${consumerName}\` must be used within \`${rootComponentName}\``);
 }

14-15: Add meaningful explanation to the biome-ignore comment.

The placeholder <explanation> should describe why the lint rule is being suppressed. This helps future maintainers understand the reasoning.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 346ee3a and ae92fcf.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • apps/dashboard/components/logs/checkbox/filter-item.tsx (1 hunks)
  • apps/dashboard/components/logs/checkbox/filters-popover.tsx (1 hunks)
  • apps/dashboard/components/logs/datetime/datetime-popover.tsx (1 hunks)
  • apps/dashboard/package.json (0 hunks)
  • internal/ui/package.json (2 hunks)
  • internal/ui/src/components/drawer.tsx (2 hunks)
  • internal/ui/src/components/drover.tsx (1 hunks)
  • internal/ui/src/hooks/use-mobile.tsx (1 hunks)
  • internal/ui/src/index.ts (1 hunks)
  • internal/ui/src/lib/create-context.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/dashboard/package.json
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 3292
File: apps/dashboard/lib/vault.ts:60-78
Timestamp: 2025-06-02T11:09:05.843Z
Learning: The vault implementation in `apps/dashboard/lib/vault.ts` is a duplicate of the vault package from `api` and should be kept consistent with the original implementation.
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 3292
File: apps/dashboard/lib/vault.ts:80-97
Timestamp: 2025-06-02T11:08:56.397Z
Learning: The vault.ts file in apps/dashboard/lib/vault.ts is a duplicate of the vault package from the `api` directory and should be kept consistent with that original implementation.
Learnt from: p6l-richard
Repo: unkeyed/unkey PR: 2085
File: apps/www/components/ui/button.tsx:39-46
Timestamp: 2024-10-23T16:33:02.143Z
Learning: In `apps/www/components/ui/` directory, components are based on shadcn UI patterns, including their typing conventions.
📚 Learning: 2025-01-30T20:38:00.058Z
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 2825
File: apps/dashboard/app/(app)/logs-v2/components/controls/components/logs-datetime/index.tsx:0-0
Timestamp: 2025-01-30T20:38:00.058Z
Learning: In the logs dashboard, keyboard shortcuts that toggle UI elements (like popovers) should be implemented in the component that owns the state being toggled, not in the presentational wrapper components. For example, the 'T' shortcut for toggling the datetime filter is implemented in DatetimePopover, not in LogsDateTime.

Applied to files:

  • apps/dashboard/components/logs/checkbox/filters-popover.tsx
  • apps/dashboard/components/logs/datetime/datetime-popover.tsx
📚 Learning: 2025-04-24T14:34:30.621Z
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 3115
File: apps/dashboard/components/logs/checkbox/filters-popover.tsx:33-55
Timestamp: 2025-04-24T14:34:30.621Z
Learning: In the ShortcutActivator component within filters-popover.tsx, the purpose is to track keys separately for each filter item, providing a registration mechanism for shortcuts passed to it rather than enforcing specific key combinations like option+shift+key.

Applied to files:

  • apps/dashboard/components/logs/checkbox/filters-popover.tsx
📚 Learning: 2024-12-03T14:07:45.173Z
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 2143
File: apps/dashboard/components/ui/group-button.tsx:21-31
Timestamp: 2024-12-03T14:07:45.173Z
Learning: In the `ButtonGroup` component (`apps/dashboard/components/ui/group-button.tsx`), avoid suggesting the use of `role="group"` in ARIA attributes.

Applied to files:

  • apps/dashboard/components/logs/checkbox/filters-popover.tsx
📚 Learning: 2025-08-25T13:05:22.441Z
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 3834
File: internal/ui/src/components/form/input.tsx:130-131
Timestamp: 2025-08-25T13:05:22.441Z
Learning: The Input component in internal/ui/src/components/form/input.tsx is designed to support interactive right icons (like X buttons for deletion) by enabling pointer events on the right icon container, allowing clicks to be handled by the icon rather than passed through to the input.

Applied to files:

  • apps/dashboard/components/logs/checkbox/filter-item.tsx
📚 Learning: 2024-10-23T16:25:33.113Z
Learnt from: p6l-richard
Repo: unkeyed/unkey PR: 2085
File: apps/www/components/glossary/terms-stepper-mobile.tsx:16-20
Timestamp: 2024-10-23T16:25:33.113Z
Learning: In the `apps/www/components/glossary/terms-stepper-mobile.tsx` file, avoid suggesting to extract the term navigation logic into a custom hook, as the user prefers to keep the component straightforward.

Applied to files:

  • internal/ui/src/components/drover.tsx
📚 Learning: 2025-08-18T10:28:47.391Z
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 3797
File: apps/dashboard/app/(app)/projects/[projectId]/deployments/components/control-cloud/index.tsx:1-4
Timestamp: 2025-08-18T10:28:47.391Z
Learning: In Next.js App Router, components that use React hooks don't need their own "use client" directive if they are rendered within a client component that already has the directive. The client boundary propagates to child components.

Applied to files:

  • internal/ui/src/components/drover.tsx
  • apps/dashboard/components/logs/datetime/datetime-popover.tsx
📚 Learning: 2025-01-22T16:51:59.978Z
Learnt from: MichaelUnkey
Repo: unkeyed/unkey PR: 2810
File: internal/ui/src/components/date-time/components/time-split.tsx:10-14
Timestamp: 2025-01-22T16:51:59.978Z
Learning: The DateTime component in internal/ui/src/components/date-time/components/time-split.tsx already includes sufficient validation through handleChange and handleBlur functions, making additional runtime validation unnecessary.

Applied to files:

  • apps/dashboard/components/logs/datetime/datetime-popover.tsx
⏰ 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). (4)
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Packages / Test
  • GitHub Check: Test Dashboard / Test Dashboard
  • GitHub Check: Build / Build
🔇 Additional comments (5)
internal/ui/src/components/drawer.tsx (1)

9-9: Drawer namespace + relative utils import look consistent

Using a relative import for cn decouples this component from app aliases, and exposing Drawer as a namespace with Root/Trigger/Content/... matches how it’s consumed (<Drawer.Root>, etc.). No behavioral changes from these lines.

Also applies to: 58-65

apps/dashboard/components/logs/checkbox/filters-popover.tsx (1)

2-2: Importing Drover/KeyboardButton from @unkey/ui aligns with the shared UI surface

Switching these imports to @unkey/ui keeps logs UI on the shared component library without changing how Drover or KeyboardButton are used.

Please confirm that @unkey/ui’s public index indeed re‑exports both Drover and KeyboardButton (e.g., by checking internal/ui/src/index.ts and verifying the built package).

apps/dashboard/components/logs/datetime/datetime-popover.tsx (1)

7-17: Drawer import move to @unkey/ui looks correct; watch mobile behavior

Using Drawer from @unkey/ui keeps this popover in sync with the shared UI package while preserving the existing Drawer.Root/Trigger/Content usage on mobile. This should be a no‑op behaviorally, but it’s worth sanity‑checking the mobile datetime drawer (open/close, overlay, scroll) after the move.

When testing, specifically verify on a small viewport that:

  • The drawer still slides up from the bottom.
  • Background scrolling is correctly locked while open.
  • Closing via outside tap, ESC, and the “Apply” button works as before.
internal/ui/src/index.ts (1)

18-19: Exporting Drawer and Drover from the UI barrel is aligned with app usage

Adding export * from "./components/drawer"; and export * from "./components/drover"; cleanly exposes these primitives to consumers like the dashboard without changing their APIs.

After building the UI package, please confirm that the generated entry file still contains these exports and that import { Drawer, Drover } from "@unkey/ui"; works from another package without relying on TypeScript path aliases.

internal/ui/src/components/drover.tsx (1)

6-10: Relative imports for Drover’s dependencies are appropriate inside @unkey/ui

Pointing Drawer, useIsMobile, createContext, cn, and the Popover primitives to local relative paths avoids circular/self‑imports through @unkey/ui and keeps this component self‑contained. The Root/Trigger/Content/Nested logic remains the same.

Copy link
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: 0

🧹 Nitpick comments (1)
internal/ui/src/lib/create-context.tsx (1)

9-23: Memoization fix correctly depends on actual prop values (previous issue resolved)

Switching from memoizing the spread props object to deriving dependencies from the individual non‑children prop values means useMemo now correctly reuses the context value when those props are referentially stable, addressing the earlier concern where the memo never hit due to object identity changes. The dynamic dependency array is a reasonable tradeoff for a generic helper, as long as callers keep the context props shape stable (no frequent key churn); no changes required from my side.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae92fcf and 49a6597.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx (2 hunks)
  • apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/deployments/components/table/deployments-list.tsx (1 hunks)
  • apps/dashboard/components/logs/datetime/datetime-popover.tsx (2 hunks)
  • apps/dashboard/components/ui/sidebar.tsx (1 hunks)
  • apps/dashboard/components/virtual-table/index.tsx (1 hunks)
  • apps/dashboard/hooks/use-mobile.tsx (1 hunks)
  • apps/dashboard/package.json (0 hunks)
  • internal/ui/src/components/drover.tsx (2 hunks)
  • internal/ui/src/hooks/use-mobile.tsx (1 hunks)
  • internal/ui/src/lib/create-context.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/dashboard/package.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • internal/ui/src/hooks/use-mobile.tsx
  • internal/ui/src/components/drover.tsx
  • apps/dashboard/components/logs/datetime/datetime-popover.tsx
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: p6l-richard
Repo: unkeyed/unkey PR: 2085
File: apps/www/components/ui/button.tsx:39-46
Timestamp: 2024-10-23T16:33:02.143Z
Learning: In `apps/www/components/ui/` directory, components are based on shadcn UI patterns, including their typing conventions.
📚 Learning: 2025-07-25T19:11:00.208Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 3662
File: apps/dashboard/lib/trpc/routers/deployment/getOpenApiDiff.ts:110-147
Timestamp: 2025-07-25T19:11:00.208Z
Learning: In apps/dashboard/lib/trpc/routers/deployment/getOpenApiDiff.ts, the user mcstepp prefers to keep mock data fallbacks in POC/demonstration code for simplicity, even if it wouldn't be production-ready. This aligns with the PR being work-in-progress for demonstration purposes.

Applied to files:

  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx
  • apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/deployments/components/table/deployments-list.tsx
📚 Learning: 2025-09-23T17:39:59.820Z
Learnt from: perkinsjr
Repo: unkeyed/unkey PR: 4009
File: apps/dashboard/app/(app)/[workspace]/apis/[apiId]/_overview/components/table/components/override-indicator.tsx:88-97
Timestamp: 2025-09-23T17:39:59.820Z
Learning: The useWorkspaceNavigation hook in the Unkey dashboard guarantees that a workspace exists. If no workspace is found, the hook redirects the user to create a new workspace. Users cannot be logged in without a workspace, and new users must create one to continue. Therefore, workspace will never be null when using this hook.

Applied to files:

  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx
📚 Learning: 2025-09-22T18:44:56.279Z
Learnt from: perkinsjr
Repo: unkeyed/unkey PR: 4009
File: apps/dashboard/app/(app)/[workspace]/apis/[apiId]/_overview/components/table/components/log-details/index.tsx:4-5
Timestamp: 2025-09-22T18:44:56.279Z
Learning: In the Unkey dashboard, the workspace hook (useWorkspace) provides security validation by checking database access and user authorization to the workspace, with 10-minute caching for performance. Using URL params (useParams) for workspace slug would bypass this security validation and allow unauthorized access attempts. Always use the workspace hook for workspace-scoped navigation and handle loading states properly rather than switching to URL parameters.

Applied to files:

  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx
📚 Learning: 2025-07-25T19:09:43.284Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 3662
File: apps/dashboard/lib/trpc/routers/deployment/list.ts:11-11
Timestamp: 2025-07-25T19:09:43.284Z
Learning: In apps/dashboard/lib/trpc/routers/deployment/list.ts, the listDeployments procedure intentionally queries the versions table rather than a deployments table. The user mcstepp indicated that renaming the table would require a database migration, which was deferred for the current PR focused on UI features.

Applied to files:

  • apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/deployments/components/table/deployments-list.tsx
📚 Learning: 2025-07-28T20:38:53.244Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 3662
File: apps/dashboard/app/(app)/projects/[projectId]/diff/[...compare]/components/client.tsx:322-341
Timestamp: 2025-07-28T20:38:53.244Z
Learning: In apps/dashboard/app/(app)/projects/[projectId]/diff/[...compare]/components/client.tsx, mcstepp prefers to keep hardcoded endpoint logic in the getDiffType function during POC phases for demonstrating diff functionality, rather than implementing a generic diff algorithm. This follows the pattern of keeping simplified implementations for demonstration purposes.

Applied to files:

  • apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/deployments/components/table/deployments-list.tsx
📚 Learning: 2024-10-23T16:20:19.324Z
Learnt from: p6l-richard
Repo: unkeyed/unkey PR: 2085
File: apps/www/components/glossary/terms-rolodex-desktop.tsx:26-37
Timestamp: 2024-10-23T16:20:19.324Z
Learning: When reviewing React components in this project, avoid suggesting manual memoization with `useMemo` for performance optimizations, as the team prefers to rely on the React compiler to handle such optimizations.

Applied to files:

  • internal/ui/src/lib/create-context.tsx
📚 Learning: 2024-11-08T11:44:42.947Z
Learnt from: unrenamed
Repo: unkeyed/unkey PR: 2652
File: apps/www/components/particles.tsx:88-90
Timestamp: 2024-11-08T11:44:42.947Z
Learning: In React TypeScript components, if a function memoized with `useCallback` is not called elsewhere in the component, and a `useEffect` is used to invoke it, do not suggest removing the `useEffect` as it is necessary to execute the function.

Applied to files:

  • internal/ui/src/lib/create-context.tsx
📚 Learning: 2024-11-13T14:58:01.321Z
Learnt from: unrenamed
Repo: unkeyed/unkey PR: 2660
File: apps/play/app/page-bk.tsx:24-24
Timestamp: 2024-11-13T14:58:01.321Z
Learning: In React, refs created with useRef do not change between renders, so including them in the dependency array of useEffect hooks is unnecessary.

Applied to files:

  • internal/ui/src/lib/create-context.tsx
📚 Learning: 2024-10-23T16:25:33.113Z
Learnt from: p6l-richard
Repo: unkeyed/unkey PR: 2085
File: apps/www/components/glossary/terms-stepper-mobile.tsx:16-20
Timestamp: 2024-10-23T16:25:33.113Z
Learning: In the `apps/www/components/glossary/terms-stepper-mobile.tsx` file, avoid suggesting to extract the term navigation logic into a custom hook, as the user prefers to keep the component straightforward.

Applied to files:

  • apps/dashboard/components/virtual-table/index.tsx
🧬 Code graph analysis (3)
apps/dashboard/components/ui/sidebar.tsx (2)
apps/dashboard/hooks/use-mobile.tsx (1)
  • useIsMobile (8-26)
internal/ui/src/hooks/use-mobile.tsx (1)
  • useIsMobile (8-26)
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx (2)
apps/dashboard/hooks/use-mobile.tsx (1)
  • useIsMobile (8-26)
internal/ui/src/hooks/use-mobile.tsx (1)
  • useIsMobile (8-26)
apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/deployments/components/table/deployments-list.tsx (2)
apps/dashboard/hooks/use-mobile.tsx (1)
  • useIsMobile (8-26)
internal/ui/src/hooks/use-mobile.tsx (1)
  • useIsMobile (8-26)
⏰ 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). (4)
  • GitHub Check: Test Dashboard / Test Dashboard
  • GitHub Check: Test Packages / Test
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Build / Build
🔇 Additional comments (8)
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx (2)

104-104: LGTM - Defensive default for isMobile prop.

The default parameter isMobile = false provides a safe fallback ensuring desktop behavior if the prop is ever omitted.


229-230: LGTM - Hydration-safe mobile detection.

Passing { defaultValue: false } ensures the initial SSR render matches the client hydration by defaulting to desktop view.

apps/dashboard/components/ui/sidebar.tsx (1)

67-68: LGTM - Consistent hydration-safe initialization.

The { defaultValue: false } pattern matches the approach used across other components in this PR, ensuring stable SSR hydration for the sidebar.

apps/dashboard/components/virtual-table/index.tsx (1)

68-69: LGTM - Hydration-safe mobile detection for table height.

Defaulting to desktop ensures SSR/hydration consistency. The table height will update correctly once the effect runs on mobile devices.

apps/dashboard/hooks/use-mobile.tsx (1)

3-25: LGTM - Well-implemented hydration-safe media query hook.

The implementation correctly:

  • Accepts a defaultValue for SSR-safe initial state
  • Guards against server-side execution with the window check
  • Immediately syncs to the actual viewport state via mql.matches after mount
  • Properly cleans up the event listener

This is the standard pattern for avoiding hydration mismatches with media queries in React/Next.js.

apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/deployments/components/table/deployments-list.tsx (1)

49-50: LGTM - Hydration-safe compact view detection.

Correctly uses both the custom breakpoint for the compact view threshold and defaultValue: false to ensure wide view during SSR, preventing hydration mismatches.

internal/ui/src/lib/create-context.tsx (2)

3-8: Generic createContext helper looks solid and aligns with common patterns

The generic factory, type constraints, and useContext error handling mirror well-known Radix-style helpers and give a nice, typed API ([Provider, useContext]). The handling of defaultContext plus the descriptive error message for missing providers looks correct and safe.


25-38: useContext semantics and displayName are clear and ergonomic

The useContext helper correctly distinguishes between “no provider” (error when no defaultContext) and valid null values, and the Provider.displayName improves DevTools debugging. This looks good as-is.

@mcstepp
Copy link
Collaborator

mcstepp commented Dec 11, 2025

wtf is a drover

Copy link
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: 0

♻️ Duplicate comments (1)
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx (1)

11-11: Potential build breaker: confirm @unkey/ui exports useIsMobile from the package entrypoint.

This import will fail at compile time if useIsMobile isn’t re-exported from the public @unkey/ui entry.

#!/bin/bash
set -euo pipefail

# Find likely entrypoints
fd -a '^(index|main)\.(ts|tsx|js|mjs|cjs)$' internal/ui src packages apps 2>/dev/null || true

echo "---- search for re-export of useIsMobile ----"
rg -n --hidden -S 'export\s*\{\s*[^}]*\buseIsMobile\b[^}]*\}' internal/ui src packages 2>/dev/null || true
rg -n --hidden -S 'export\s+\*\s+from\s+["'\''][^"'\'']*use-mobile[^"'\'']*["'\'']' internal/ui src packages 2>/dev/null || true

echo "---- search for hook definition (sanity) ----"
rg -n --hidden -S 'function\s+useIsMobile\b|export\s+function\s+useIsMobile\b' internal/ui src packages 2>/dev/null || true
🧹 Nitpick comments (1)
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx (1)

99-107: Make NavbarContentProps.isMobile optional (or drop the default) to keep types honest.

isMobile = false implies the prop can be omitted, but the interface currently requires it.

 interface NavbarContentProps {
   apiId: string;
   keyspaceId?: string;
   keyId?: string;
   activePage?: {
     href: string;
     text: string;
   };
   workspace: Workspace;
-  isMobile: boolean;
+  isMobile?: boolean;
   layoutData: ApiLayoutData;
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 49a6597 and dbd7d77.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx (3 hunks)
  • apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/deployments/components/table/deployments-list.tsx (2 hunks)
  • apps/dashboard/components/logs/datetime/datetime-popover.tsx (2 hunks)
  • apps/dashboard/components/ui/sidebar.tsx (2 hunks)
  • apps/dashboard/components/virtual-table/index.tsx (2 hunks)
  • apps/dashboard/hooks/use-mobile.tsx (0 hunks)
  • apps/dashboard/package.json (0 hunks)
  • internal/ui/src/components/drover.tsx (2 hunks)
  • internal/ui/src/index.ts (2 hunks)
💤 Files with no reviewable changes (2)
  • apps/dashboard/package.json
  • apps/dashboard/hooks/use-mobile.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/dashboard/components/ui/sidebar.tsx
  • apps/dashboard/components/logs/datetime/datetime-popover.tsx
  • apps/dashboard/components/virtual-table/index.tsx
  • apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/deployments/components/table/deployments-list.tsx
  • internal/ui/src/index.ts
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-07-25T19:11:00.208Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 3662
File: apps/dashboard/lib/trpc/routers/deployment/getOpenApiDiff.ts:110-147
Timestamp: 2025-07-25T19:11:00.208Z
Learning: In apps/dashboard/lib/trpc/routers/deployment/getOpenApiDiff.ts, the user mcstepp prefers to keep mock data fallbacks in POC/demonstration code for simplicity, even if it wouldn't be production-ready. This aligns with the PR being work-in-progress for demonstration purposes.

Applied to files:

  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx
📚 Learning: 2024-10-23T16:25:33.113Z
Learnt from: p6l-richard
Repo: unkeyed/unkey PR: 2085
File: apps/www/components/glossary/terms-stepper-mobile.tsx:16-20
Timestamp: 2024-10-23T16:25:33.113Z
Learning: In the `apps/www/components/glossary/terms-stepper-mobile.tsx` file, avoid suggesting to extract the term navigation logic into a custom hook, as the user prefers to keep the component straightforward.

Applied to files:

  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx
  • internal/ui/src/components/drover.tsx
📚 Learning: 2025-06-24T13:29:10.129Z
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 3401
File: apps/dashboard/app/(app)/logs/filters.query-params.ts:10-0
Timestamp: 2025-06-24T13:29:10.129Z
Learning: The `buildQueryParams` function in `apps/dashboard/app/(app)/logs/filters.query-params.ts` calls `useFilters()` hook inside it, but this is valid because the function is only called from within other React hooks, maintaining the Rules of Hooks compliance.

Applied to files:

  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx
📚 Learning: 2025-09-23T17:39:59.820Z
Learnt from: perkinsjr
Repo: unkeyed/unkey PR: 4009
File: apps/dashboard/app/(app)/[workspace]/apis/[apiId]/_overview/components/table/components/override-indicator.tsx:88-97
Timestamp: 2025-09-23T17:39:59.820Z
Learning: The useWorkspaceNavigation hook in the Unkey dashboard guarantees that a workspace exists. If no workspace is found, the hook redirects the user to create a new workspace. Users cannot be logged in without a workspace, and new users must create one to continue. Therefore, workspace will never be null when using this hook.

Applied to files:

  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx
📚 Learning: 2025-09-23T17:40:44.944Z
Learnt from: perkinsjr
Repo: unkeyed/unkey PR: 4009
File: apps/dashboard/app/(app)/[workspace]/authorization/roles/navigation.tsx:26-40
Timestamp: 2025-09-23T17:40:44.944Z
Learning: In the Unkey dashboard authorization section navigation components, the maintainer prefers to wrap entire navbars in Suspense rather than scoping Suspense to individual components, even if it blocks the whole navigation during loading.

Applied to files:

  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx
📚 Learning: 2025-09-22T18:44:56.279Z
Learnt from: perkinsjr
Repo: unkeyed/unkey PR: 4009
File: apps/dashboard/app/(app)/[workspace]/apis/[apiId]/_overview/components/table/components/log-details/index.tsx:4-5
Timestamp: 2025-09-22T18:44:56.279Z
Learning: In the Unkey dashboard, the workspace hook (useWorkspace) provides security validation by checking database access and user authorization to the workspace, with 10-minute caching for performance. Using URL params (useParams) for workspace slug would bypass this security validation and allow unauthorized access attempts. Always use the workspace hook for workspace-scoped navigation and handle loading states properly rather than switching to URL parameters.

Applied to files:

  • apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx
📚 Learning: 2025-07-28T20:38:53.244Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 3662
File: apps/dashboard/app/(app)/projects/[projectId]/diff/[...compare]/components/client.tsx:322-341
Timestamp: 2025-07-28T20:38:53.244Z
Learning: In apps/dashboard/app/(app)/projects/[projectId]/diff/[...compare]/components/client.tsx, mcstepp prefers to keep hardcoded endpoint logic in the getDiffType function during POC phases for demonstrating diff functionality, rather than implementing a generic diff algorithm. This follows the pattern of keeping simplified implementations for demonstration purposes.

Applied to files:

  • internal/ui/src/components/drover.tsx
📚 Learning: 2025-08-18T10:28:47.391Z
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 3797
File: apps/dashboard/app/(app)/projects/[projectId]/deployments/components/control-cloud/index.tsx:1-4
Timestamp: 2025-08-18T10:28:47.391Z
Learning: In Next.js App Router, components that use React hooks don't need their own "use client" directive if they are rendered within a client component that already has the directive. The client boundary propagates to child components.

Applied to files:

  • internal/ui/src/components/drover.tsx
🧬 Code graph analysis (2)
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx (1)
internal/ui/src/hooks/use-mobile.tsx (1)
  • useIsMobile (8-26)
internal/ui/src/components/drover.tsx (1)
internal/ui/src/hooks/use-mobile.tsx (1)
  • useIsMobile (8-26)
⏰ 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). (5)
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Packages / Test
  • GitHub Check: Test Dashboard / Test Dashboard
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx (1)

229-231: Hydration mismatch mitigation looks good; verify no “mobile flash” regression on real devices.

useIsMobile({ defaultValue: false }) is a reasonable SSR-safe default; please sanity-check on an actual mobile viewport that the breadcrumbs/actions don’t visibly flicker in a problematic way on first paint.

internal/ui/src/components/drover.tsx (2)

6-10: Import move looks fine; double-check Popover API parity after switching to ./dialog/popover.
This refactor is safe if Popover/PopoverTrigger/PopoverContent from ./dialog/popover preserve the same behavioral expectations (portals, focus/blur interactions, and prop names) as before.


38-53: Hydration-safe defaultValue: false is good; verify the Popover→Drawer swap doesn’t glitch while open=true on mobile.
Because isMobile can flip to true after mount, RootComponent can change component type while the drover is open; please sanity-check focus trapping, dismissal, and “open state” continuity on mobile (and when resizing across the breakpoint).

Copy link
Collaborator

@mcstepp mcstepp left a comment

Choose a reason for hiding this comment

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

👍

@perkinsjr perkinsjr merged commit e4b8d34 into main Dec 12, 2025
21 checks passed
@perkinsjr perkinsjr deleted the move-drawer-to-ui branch December 12, 2025 16:00
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.

3 participants