Skip to content

Conversation

@anmolsinghbhatia
Copy link
Collaborator

@anmolsinghbhatia anmolsinghbhatia commented Dec 2, 2025

Description

This PR includes improvement for sidebar and tab navigation.

Type of Change

  • Bug fix
  • Improvement

Summary by CodeRabbit

  • Bug Fixes

    • Project sidebar now automatically closes when navigating to a project.
  • Refactor

    • Tab navigation responsiveness improved for smoother resizing and more reliable overflow handling.

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

Copilot AI review requested due to automatic review settings December 2, 2025 10:45
@anmolsinghbhatia anmolsinghbhatia self-assigned this Dec 2, 2025
@makeplane
Copy link

makeplane bot commented Dec 2, 2025

Linked to Plane Work Item(s)

This comment was auto-generated by Plane

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 2, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Refactors responsive tab layout to use a ResizeObserver-driven callback ref for container measurement and updates destructuring order in the tab navigation root. Also closes the extended project sidebar when a project list item is clicked.

Changes

Cohort / File(s) Summary
Responsive Tab Layout
apps/web/core/components/navigation/use-responsive-tab-layout.ts, apps/web/core/components/navigation/tab-navigation-root.tsx
Converts containerRef from React.RefObject<HTMLDivElement> to a callback ref (node: HTMLDivElement | null) => void; replaces useEffect measurement with a ResizeObserver and callback ref lifecycle management; adds useCallback import and exposes containerRef in the hook return; adjusts destructuring order in tab-navigation-root.tsx.
Project Sidebar Behaviour
apps/web/core/components/workspace/sidebar/projects-list-item.tsx
Retrieves isExtendedProjectSidebarOpened and toggleExtendedProjectSidebar from theme hook and ensures the extended project sidebar is closed (toggleExtendedProjectSidebar(false)) after item click navigation/accordion handling.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

  • Review use-responsive-tab-layout.ts for correct callback-ref initialization, ResizeObserver setup/disconnect, and ensuring updates occur safely across renders.
  • Confirm tab-navigation-root.tsx destructuring change matches the new return shape.
  • Verify projects-list-item.tsx places toggleExtendedProjectSidebar(false) in the correct post-navigation/accordion branch.

Poem

🐰
I swapped my refs for watching eyes,
ResizeObserver under sunny skies,
When projects open, sidebars hide—
I hop along, delighted, wide-eyed,
Code carrots crunch with every try.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete, missing critical sections like detailed description, test scenarios, and references that are required by the template. Expand the description with detailed explanation of changes, comprehensive test scenarios, and links to related issues (WEB-5556).
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: tab navigation and sidebar improvements across multiple components.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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 chore-navigation-and-sidebar-enhancements

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fee4527 and 7052a1e.

📒 Files selected for processing (2)
  • apps/web/core/components/navigation/use-responsive-tab-layout.ts (4 hunks)
  • apps/web/core/components/workspace/sidebar/projects-list-item.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/core/components/workspace/sidebar/projects-list-item.tsx
🧰 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:

  • apps/web/core/components/navigation/use-responsive-tab-layout.ts
🧠 Learnings (1)
📚 Learning: 2025-10-21T17:22:05.204Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7989
File: apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/[pageId]/page.tsx:45-46
Timestamp: 2025-10-21T17:22:05.204Z
Learning: In the makeplane/plane repository, the refactor from useParams() to params prop is specifically scoped to page.tsx and layout.tsx files in apps/web/app (Next.js App Router pattern). Other components (hooks, regular client components, utilities) should continue using the useParams() hook as that is the correct pattern for non-route components.

Applied to files:

  • apps/web/core/components/navigation/use-responsive-tab-layout.ts
🧬 Code graph analysis (1)
apps/web/core/components/navigation/use-responsive-tab-layout.ts (1)
apps/web/core/components/navigation/tab-navigation-root.tsx (1)
  • TNavigationItem (31-40)
⏰ 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 (2)
apps/web/core/components/navigation/use-responsive-tab-layout.ts (2)

46-71: Well-implemented callback ref with ResizeObserver.

The callback ref pattern properly manages the observer lifecycle:

  • Cleans up any existing observer before creating a new one (lines 48-51)
  • Handles node detachment by resetting width and cleaning up (lines 54-57)
  • Sets initial width immediately before ResizeObserver fires (line 60)
  • Correctly observes the node and stores the observer reference (lines 63-70)

The empty dependency array (line 71) is correct since setContainerWidth is stable from useState.


74-82: Cleanup effect properly addresses previous review concern.

This unmount cleanup directly resolves the issue raised in the previous review about potential memory leaks and React warnings from observer callbacks firing after unmount.

The implementation provides comprehensive lifecycle management:

  • Callback ref cleanup (lines 48-51) handles node attachment/detachment
  • This effect ensures cleanup on component unmount
  • Together they prevent observer callbacks from firing on unmounted components

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

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR improves sidebar navigation and tab responsiveness by refactoring the ResizeObserver implementation and adding extended project sidebar auto-close functionality.

  • Refactored useResponsiveTabLayout to use callback ref pattern instead of traditional useRef for better ResizeObserver lifecycle management
  • Added auto-close behavior for the extended project sidebar when navigating to a project
  • Reorganized return values and destructuring for consistency

Reviewed changes

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

File Description
apps/web/core/components/workspace/sidebar/projects-list-item.tsx Adds extended project sidebar auto-close when clicking project items
apps/web/core/components/navigation/use-responsive-tab-layout.ts Refactors ResizeObserver from useEffect to callback ref pattern for immediate measurement
apps/web/core/components/navigation/tab-navigation-root.tsx Updates destructuring order to match refactored hook return value

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

Comment on lines 46 to 71
const containerRef = useCallback((node: HTMLDivElement | null) => {
// Clean up previous observer if it exists
if (resizeObserverRef.current) {
resizeObserverRef.current.disconnect();
resizeObserverRef.current = null;
}

// ResizeObserver to measure container width
useEffect(() => {
if (!container) return;
// If node is null (unmounting), just clean up
if (!node) {
setContainerWidth(0);
return;
}

// Set initial width immediately
setContainerWidth(node.offsetWidth);

// Create and set up new ResizeObserver
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
setContainerWidth(entry.contentRect.width);
}
});

resizeObserver.observe(container);

return () => {
resizeObserver.disconnect();
};
}, [container]);
resizeObserverRef.current = resizeObserver;
resizeObserver.observe(node);
}, []); // Empty deps - callback function remains stable
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

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

The ResizeObserver lacks proper cleanup on component unmount. If the component unmounts while a ResizeObserver callback is pending, it may attempt to call setContainerWidth on an unmounted component, potentially causing a memory leak or React warning.

Add a cleanup effect to ensure the observer is disconnected when the component unmounts:

useEffect(() => {
  return () => {
    if (resizeObserverRef.current) {
      resizeObserverRef.current.disconnect();
      resizeObserverRef.current = null;
    }
  };
}, []);

Copilot uses AI. Check for mistakes.
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 (3)
apps/web/core/components/workspace/sidebar/projects-list-item.tsx (2)

71-75: Consolidate duplicate useAppTheme calls

You’re calling useAppTheme() twice; you can grab all three values in a single call to avoid redundant hook invocations and keep related theme concerns together.

-  const { toggleAnySidebarDropdown } = useAppTheme();
+  const { toggleAnySidebarDropdown, isExtendedProjectSidebarOpened, toggleExtendedProjectSidebar } = useAppTheme();
-  const { isExtendedProjectSidebarOpened, toggleExtendedProjectSidebar } = useAppTheme();

257-267: Confirm whether accordion-only toggles should also close the extended sidebar

handleItemClick now closes the extended project sidebar even when navigationMode === "accordion" and we’re just expanding/collapsing the list (no router.push in that branch). If the intent is to close the extended sidebar only after actual navigation, consider moving this logic into the non-accordion branch:

  const handleItemClick = () => {
    if (projectPreferences.navigationMode === "accordion") {
      setIsProjectListOpen(!isProjectListOpen);
    } else {
      router.push(defaultTabUrl);
+     if (isExtendedProjectSidebarOpened) {
+       toggleExtendedProjectSidebar(false);
+     }
    }
-   // close the extended sidebar if it is open
-   if (isExtendedProjectSidebarOpened) {
-     toggleExtendedProjectSidebar(false);
-   }
  };
apps/web/core/components/navigation/use-responsive-tab-layout.ts (1)

4-10: ResizeObserver-based containerRef looks correct; consider minor typing/compat tweaks

The new containerRef callback + resizeObserverRef pattern correctly:

  • Disconnects any existing observer before attaching a new one.
  • Resets containerWidth on null ref.
  • Initializes width immediately and reacts to subsequent ResizeObserver entries.

Two optional refinements you might consider:

  1. Use the built-in ref callback type for clarity
-export type TResponsiveTabLayout = {
+export type TResponsiveTabLayout = {
   visibleItems: TNavigationItem[];
   overflowItems: TNavigationItem[];
   hasOverflow: boolean;
   itemRefs: React.MutableRefObject<(HTMLDivElement | null)[]>;
-  containerRef: (node: HTMLDivElement | null) => void;
+  containerRef: React.RefCallback<HTMLDivElement>;
 };
  1. Guard for environments without ResizeObserver (if you support them / SSR usage is possible)
-  const containerRef = useCallback((node: HTMLDivElement | null) => {
+  const containerRef = useCallback((node: HTMLDivElement | null) => {
+    if (typeof ResizeObserver === "undefined") {
+      if (node) setContainerWidth(node.offsetWidth);
+      else setContainerWidth(0);
+      return;
+    }

These are not blockers; the current implementation is sound for modern browsers and client-only usage.

Also applies to: 33-71

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec478a8 and fee4527.

📒 Files selected for processing (3)
  • apps/web/core/components/navigation/tab-navigation-root.tsx (1 hunks)
  • apps/web/core/components/navigation/use-responsive-tab-layout.ts (4 hunks)
  • apps/web/core/components/workspace/sidebar/projects-list-item.tsx (2 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:

  • apps/web/core/components/navigation/tab-navigation-root.tsx
  • apps/web/core/components/navigation/use-responsive-tab-layout.ts
  • apps/web/core/components/workspace/sidebar/projects-list-item.tsx
🧠 Learnings (1)
📚 Learning: 2025-10-21T17:22:05.204Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7989
File: apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/[pageId]/page.tsx:45-46
Timestamp: 2025-10-21T17:22:05.204Z
Learning: In the makeplane/plane repository, the refactor from useParams() to params prop is specifically scoped to page.tsx and layout.tsx files in apps/web/app (Next.js App Router pattern). Other components (hooks, regular client components, utilities) should continue using the useParams() hook as that is the correct pattern for non-route components.

Applied to files:

  • apps/web/core/components/navigation/use-responsive-tab-layout.ts
🧬 Code graph analysis (2)
apps/web/core/components/navigation/tab-navigation-root.tsx (1)
apps/web/core/components/navigation/use-responsive-tab-layout.ts (1)
  • useResponsiveTabLayout (28-152)
apps/web/core/components/navigation/use-responsive-tab-layout.ts (1)
apps/web/core/components/navigation/tab-navigation-root.tsx (1)
  • TNavigationItem (31-40)
⏰ 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: CodeQL analysis (javascript-typescript)
  • GitHub Check: Agent
  • GitHub Check: Build and lint web apps
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
apps/web/core/components/navigation/tab-navigation-root.tsx (1)

117-123: Destructuring matches updated hook API and is correctly wired

The destructuring order and usage of containerRef and itemRefs align with the updated useResponsiveTabLayout return shape and are used correctly on the container and items.

@pushya22 pushya22 merged commit e650b19 into preview Dec 2, 2025
6 checks passed
@pushya22 pushya22 deleted the chore-navigation-and-sidebar-enhancements branch December 2, 2025 12:35
ClarenceChen0627 pushed a commit to ClarenceChen0627/plane that referenced this pull request Dec 5, 2025
ClarenceChen0627 pushed a commit to ClarenceChen0627/plane that referenced this pull request Dec 5, 2025
ClarenceChen0627 pushed a commit to ClarenceChen0627/plane that referenced this pull request Dec 5, 2025
@anmolsinghbhatia anmolsinghbhatia restored the chore-navigation-and-sidebar-enhancements branch December 29, 2025 14:47
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