Skip to content

Conversation

@pratapalakshmi
Copy link
Contributor

@pratapalakshmi pratapalakshmi commented Dec 1, 2025

Description

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Feature (non-breaking change which adds functionality)
  • Improvement (change that would cause existing functionality to not work as expected)
  • Code refactoring
  • Performance improvements
  • Documentation update

Screenshots and Media (if applicable)

Test Scenarios

References

Summary by CodeRabbit

  • New Features

    • Added context-based close behavior so option interactions can close dropdowns reliably, including when selecting already-selected items.
  • Bug Fixes

    • Ensured dropdown now consistently closes after an option is selected via mouse interaction.
  • Style

    • Updated dropdown button visuals with improved rounded corners for better consistency.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 1, 2025

Walkthrough

Introduces a DropdownContext and refactors custom select to memoize open/close/toggle callbacks, make options close the dropdown via context and requestAnimationFrame, remove the static Combobox.Options flag, and add a rounded style to custom button variants.

Changes

Cohort / File(s) Summary
Dropdown context & provider
packages/ui/src/dropdowns/custom-select.tsx
Adds DropdownContext and wraps Combobox with a provider to expose a closeDropdown handler; converts openDropdown, closeDropdown, and toggleDropdown to useCallback/memoized functions.
Option interaction & close flow
packages/ui/src/dropdowns/custom-select.tsx
Options read closeDropdown from context and install an onMouseDown handler that calls requestAnimationFrame to close the dropdown after selection; option content rendered inside a div with the dedicated handler; removes static prop on Combobox.Options while keeping portal rendering.
UI adjustments
packages/ui/src/dropdowns/custom-select.tsx
Adds rounded styling to the custom button variant (minor className adjustments).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review context/provider correctness and stable references
  • Verify requestAnimationFrame interaction with Combobox selection lifecycle
  • Confirm removing static doesn't change portal/event behavior in edge cases
  • Check already-selected option flows and focus/blur timing

Poem

🐰 I hopped through code with nimble feet,

Context tucked in cozy and neat.
A gentle frame waits just in time,
Options close with rhythm and rhyme.
Rounded edges gleam — a tiny treat.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description contains only the template structure with no actual implementation details, test scenarios, screenshots, or issue references filled in. Provide detailed description of the changes, select the Type of Change, describe test scenarios performed, and link related issue PAI-963.
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 (1 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely summarizes the main enhancement: adding context-based dropdown management to the CustomSelect component.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@pratapalakshmi pratapalakshmi changed the title feat: enhance CustomSelect component with context for dropdown management [PAI-963] feat: enhance CustomSelect component with context for dropdown management Dec 1, 2025
@makeplane
Copy link

makeplane bot commented Dec 1, 2025

Linked to Plane Work Item(s)

This comment was auto-generated by Plane

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 (2)
packages/ui/src/dropdowns/custom-select.tsx (2)

3-3: Context wiring for dropdown close is solid; consider stricter default.

Using DropdownContext to share closeDropdown into Option keeps the close behavior nicely centralized, and the () => void type keeps call sites simple. If you want to catch incorrect usage of CustomSelect.Option outside of CustomSelect, consider defaulting the context to null (or throwing in the default function) and warning/throwing in development instead of silently no‑oping. This would surface mis-wiring earlier at runtime without affecting the happy path here.

Also applies to: 16-18, 145-148


147-153: Option-level requestAnimationFrame close works; you can avoid redundant closes.

Using requestAnimationFrame in handleMouseDown is a good way to ensure the dropdown closes after Headless UI has processed the click. With the current wiring, a click on a new option will close via both Combobox’s onChange and handleMouseDown, which is harmless but redundant.

If you want to trim that duplication while still closing when re-clicking an already-selected option, you could pass selected into the handler and only force-close in that case, e.g.:

-  const handleMouseDown = useCallback(() => {
-    // Close dropdown for both new and already-selected options.
-    requestAnimationFrame(() => closeDropdown());
-  }, [closeDropdown]);
+  const handleMouseDown = useCallback(
+    (selected: boolean) => {
+      // New selections already close via Combobox.onChange; only force-close
+      // when re-clicking the currently selected option.
+      if (selected) {
+        requestAnimationFrame(() => closeDropdown());
+      }
+    },
+    [closeDropdown]
+  );
...
-      {({ selected }) => (
-        <div
-          onMouseDown={handleMouseDown}
+      {({ selected }) => (
+        <div
+          onMouseDown={() => handleMouseDown(selected)}
           className="flex items-center justify-between gap-2 w-full"
         >

Not required, but slightly tightens the state update path.

Also applies to: 168-171, 174-174

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 123f59e and 070e2f6.

📒 Files selected for processing (1)
  • packages/ui/src/dropdowns/custom-select.tsx (6 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,mts,cts}

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

**/*.{ts,tsx,mts,cts}: Use const type parameters for more precise literal inference in TypeScript 5.0+
Use the satisfies operator to validate types without widening them
Leverage inferred type predicates to reduce the need for explicit is return types in filter/check functions
Use NoInfer<T> utility to block inference for specific type arguments when they should be determined by other arguments
Utilize narrowing in switch(true) blocks for control flow analysis (TypeScript 5.3+)
Rely on narrowing from direct boolean comparisons for type guards
Trust preserved narrowing in closures when variables aren't modified after the check (TypeScript 5.4+)
Use constant indices to narrow object/array properties (TypeScript 5.5+)
Use standard ECMAScript decorators (Stage 3) instead of legacy experimentalDecorators
Use using declarations for explicit resource management with Disposable pattern instead of manual cleanup (TypeScript 5.2+)
Use with { type: "json" } for import attributes; avoid deprecated assert syntax (TypeScript 5.3/5.8+)
Use import type explicitly when importing types to ensure they are erased during compilation, respecting verbatimModuleSyntax flag
Use .ts, .mts, .cts extensions in import type statements (TypeScript 5.2+)
Use import type { Type } from "mod" with { "resolution-mode": "import" } for specific module resolution contexts (TypeScript 5.3+)
Use new iterator methods (map, filter, etc.) if targeting modern environments (TypeScript 5.6+)
Utilize new Set methods like union, intersection, etc., when available (TypeScript 5.5+)
Use Object.groupBy / Map.groupBy standard methods for grouping instead of external libraries (TypeScript 5.4+)
Use Promise.withResolvers() for creating promises with exposed resolve/reject functions (TypeScript 5.7+)
Use copying array methods (toSorted, toSpliced, with) for immutable array operations (TypeScript 5.2+)
Avoid accessing instance fields via super in classes (TypeScript 5....

Files:

  • packages/ui/src/dropdowns/custom-select.tsx
🧠 Learnings (1)
📚 Learning: 2025-10-09T20:42:31.843Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T20:42:31.843Z
Learning: In the makeplane/plane repository, React types are globally available through TypeScript configuration. Type annotations like React.FC, React.ReactNode, etc. can be used without explicitly importing the React namespace. The codebase uses the modern JSX transform, so React imports are not required for JSX or type references.

Applied to files:

  • packages/ui/src/dropdowns/custom-select.tsx
🧬 Code graph analysis (1)
packages/ui/src/dropdowns/custom-select.tsx (3)
packages/ui/src/hooks/use-dropdown-key-down.tsx (1)
  • useDropdownKeyDown (12-31)
packages/ui/src/utils/classname.tsx (1)
  • cn (5-5)
packages/ui/src/dropdowns/helper.tsx (1)
  • ICustomSelectItemProps (93-97)
⏰ 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: Build and lint web apps
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
packages/ui/src/dropdowns/custom-select.tsx (3)

48-53: useCallback memoization for open/close/toggle looks correct.

openDropdown, closeDropdown, and toggleDropdown have the right dependency sets and give stable identities to pass into useDropdownKeyDown, useOutsideClickDetector, and the context provider. This should avoid stale refs while keeping re-renders minimal. No changes needed.

Also applies to: 57-61


63-77: Provider + Combobox + portal/outside-click integration is coherent.

Wrapping Combobox in DropdownContext.Provider and closing via the onChange handler keeps both keyboard and pointer selection flows collapsing the menu consistently, while data-prevent-outside-click on Combobox.Options lets useOutsideClickDetector treat the portalled panel as “inside” even though it’s rendered under document.body. Moving popper refs/styles onto the inner container div is also fine since that’s the actual positioned element.

Also applies to: 116-116, 140-142


83-85: Rounded styling on the custom button aligns the triggers.

Adding rounded to the custom button’s classes brings its shape in line with the default button variant, which should reduce visual inconsistencies between the two entry points.

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 (2)
packages/ui/src/dropdowns/custom-select.tsx (2)

63-141: Two open states (local isOpen + Combobox internal) – please sanity‑check keyboard interactions

With isOpen gating the portal and Headless UI’s own open state controlling whether <Combobox.Options> renders (now that static is removed), the dropdown effectively has two sources of truth. Mouse flows are well covered via onChange + the option’s close handler, but for keyboard‑only interactions (e.g., Arrow keys + Enter/Escape, or re‑selecting the already‑selected option) it’s possible for isOpen and the Combobox’s internal open state to drift out of sync, which could confuse useDropdownKeyDown logic or leave the portal wrapper mounted around a closed options list.

I’d recommend explicitly testing those keyboard scenarios; if you see any inconsistency, consider consolidating to a single open state (deriving isOpen from Combobox’s open or moving all close behavior into Combobox callbacks) instead of maintaining both in parallel.


145-175: Option close-on-interaction is good, but queued requestAnimationFrame can update after unmount

Using DropdownContext plus an onMouseDown handler with requestAnimationFrame is a nice way to ensure the dropdown closes even when the user clicks an already‑selected option, without racing Combobox’s own selection logic. One edge case to be aware of: if the parent onChange causes CustomSelect to unmount (e.g., navigation or conditional rendering) during the same interaction, the queued requestAnimationFrame callback can still fire and call closeDropdown, which would attempt a state update on an unmounted component.

To harden this, you could either:

  • Track an isMounted ref inside CustomSelect and early‑return in closeDropdown when unmounted, or
  • Store the RAF id in a ref inside Option and cancelAnimationFrame it in a cleanup effect.

Separately, you may want DropdownContext to default to null and guard in Option (throw or log) when used outside CustomSelect, so incorrect usage is caught early rather than silently no‑oping.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 070e2f6 and 63a2dd2.

📒 Files selected for processing (1)
  • packages/ui/src/dropdowns/custom-select.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,mts,cts}

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

**/*.{ts,tsx,mts,cts}: Use const type parameters for more precise literal inference in TypeScript 5.0+
Use the satisfies operator to validate types without widening them
Leverage inferred type predicates to reduce the need for explicit is return types in filter/check functions
Use NoInfer<T> utility to block inference for specific type arguments when they should be determined by other arguments
Utilize narrowing in switch(true) blocks for control flow analysis (TypeScript 5.3+)
Rely on narrowing from direct boolean comparisons for type guards
Trust preserved narrowing in closures when variables aren't modified after the check (TypeScript 5.4+)
Use constant indices to narrow object/array properties (TypeScript 5.5+)
Use standard ECMAScript decorators (Stage 3) instead of legacy experimentalDecorators
Use using declarations for explicit resource management with Disposable pattern instead of manual cleanup (TypeScript 5.2+)
Use with { type: "json" } for import attributes; avoid deprecated assert syntax (TypeScript 5.3/5.8+)
Use import type explicitly when importing types to ensure they are erased during compilation, respecting verbatimModuleSyntax flag
Use .ts, .mts, .cts extensions in import type statements (TypeScript 5.2+)
Use import type { Type } from "mod" with { "resolution-mode": "import" } for specific module resolution contexts (TypeScript 5.3+)
Use new iterator methods (map, filter, etc.) if targeting modern environments (TypeScript 5.6+)
Utilize new Set methods like union, intersection, etc., when available (TypeScript 5.5+)
Use Object.groupBy / Map.groupBy standard methods for grouping instead of external libraries (TypeScript 5.4+)
Use Promise.withResolvers() for creating promises with exposed resolve/reject functions (TypeScript 5.7+)
Use copying array methods (toSorted, toSpliced, with) for immutable array operations (TypeScript 5.2+)
Avoid accessing instance fields via super in classes (TypeScript 5....

Files:

  • packages/ui/src/dropdowns/custom-select.tsx
🧠 Learnings (1)
📚 Learning: 2025-10-09T20:42:31.843Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T20:42:31.843Z
Learning: In the makeplane/plane repository, React types are globally available through TypeScript configuration. Type annotations like React.FC, React.ReactNode, etc. can be used without explicitly importing the React namespace. The codebase uses the modern JSX transform, so React imports are not required for JSX or type references.

Applied to files:

  • packages/ui/src/dropdowns/custom-select.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). (2)
  • GitHub Check: Build and lint web apps
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
packages/ui/src/dropdowns/custom-select.tsx (1)

16-56: Context wiring and dropdown state helpers look solid

Using a simple DropdownContext with memoized openDropdown/closeDropdown/toggleDropdown around isOpen and referenceElement keeps visibility and focus management centralized, and the dependency lists look correct for both useDropdownKeyDown and useOutsideClickDetector. I don’t see any correctness issues in this setup.

@sriramveeraghanta sriramveeraghanta merged commit 7caa1bb into makeplane:preview Dec 9, 2025
6 checks passed
@pratapalakshmi pratapalakshmi deleted the chore/enhance/dropdown/functionality branch December 11, 2025 08:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants