Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhance: context menu budget page positioning #3775

Merged

Conversation

UnderKoen
Copy link
Member

@UnderKoen UnderKoen commented Nov 3, 2024

  • Make the context menu's postioned under the mouse instead of static.
  • Make the context menu non selectable.

#3706

fix: make popover non selectable
@actual-github-bot actual-github-bot bot changed the title enhance: context menu budget page positioning [WIP] enhance: context menu budget page positioning Nov 3, 2024
Copy link

netlify bot commented Nov 3, 2024

Deploy Preview for actualbudget ready!

Name Link
🔨 Latest commit a61f223
🔍 Latest deploy log https://app.netlify.com/sites/actualbudget/deploys/67574404d32e4b00082ef01e
😎 Deploy Preview https://deploy-preview-3775.demo.actualbudget.org
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link
Contributor

github-actions bot commented Nov 3, 2024

Bundle Stats — desktop-client

Hey there, this message comes from a GitHub action that helps you and reviewers to understand how these changes affect the size of this project's bundle.

As this PR is updated, I'll keep you updated on how the bundle size is impacted.

Total

Files count Total bundle size % Changed
10 5.45 MB → 5.45 MB (-315 B) -0.01%
Changeset
File Δ Size
src/hooks/useContextMenu.ts 🆕 +964 B 0 B → 964 B
node_modules/clsx/dist/clsx.mjs 🆕 +368 B 0 B → 368 B
node_modules/clsx/dist/clsx.mjs?commonjs-proxy 🆕 +64 B 0 B → 64 B
src/components/budget/envelope/EnvelopeBudgetComponents.tsx 📈 +435 B (+2.92%) 14.56 kB → 14.99 kB
src/components/common/Popover.tsx 📈 +26 B (+2.44%) 1.04 kB → 1.07 kB
src/components/budget/envelope/budgetsummary/ToBudget.tsx 📈 +73 B (+2.18%) 3.26 kB → 3.34 kB
src/components/budget/SidebarGroup.tsx 📈 +16 B (+0.24%) 6.39 kB → 6.41 kB
node_modules/react-grid-layout/build/components/WidthProvider.js 📈 +1 B (+0.02%) 5.22 kB → 5.22 kB
node_modules/react-grid-layout/build/GridItem.js 📈 +1 B (+0.00%) 21.49 kB → 21.49 kB
node_modules/react-grid-layout/build/ReactGridLayout.js 📈 +1 B (+0.00%) 24.96 kB → 24.96 kB
src/components/budget/SidebarCategory.tsx 📉 -4 B (-0.08%) 5.18 kB → 5.17 kB
src/components/transactions/TransactionsTable.jsx 📉 -461 B (-0.67%) 66.99 kB → 66.54 kB
src/components/schedules/SchedulesTable.tsx 📉 -428 B (-3.54%) 11.81 kB → 11.4 kB
src/components/rules/RuleRow.tsx 📉 -419 B (-5.97%) 6.86 kB → 6.45 kB
src/components/payees/PayeeTableRow.tsx 📉 -416 B (-6.64%) 6.12 kB → 5.72 kB
node_modules/clsx/dist/clsx.js 🔥 -509 B (-100%) 509 B → 0 B
node_modules/clsx/dist/clsx.js?commonjs-module 🔥 -27 B (-100%) 27 B → 0 B
View detailed bundle breakdown

Added

No assets were added

Removed

No assets were removed

Bigger

Asset File Size % Changed
static/js/ReportRouter.js 1.52 MB → 1.52 MB (+67 B) +0.00%
static/js/index.js 3.44 MB → 3.44 MB (+67 B) +0.00%

Smaller

Asset File Size % Changed
static/js/wide.js 241.53 kB → 241.09 kB (-449 B) -0.18%

Unchanged

Asset File Size % Changed
static/js/useAccountPreviewTransactions.js 1.63 kB 0%
static/js/AppliedFilters.js 21.32 kB 0%
static/js/workbox-window.prod.es5.js 5.69 kB 0%
static/js/indexeddb-main-thread-worker-e59fee74.js 13.5 kB 0%
static/js/BackgroundImage.js 122.29 kB 0%
static/js/resize-observer.js 18.37 kB 0%
static/js/narrow.js 82.96 kB 0%

Copy link
Contributor

github-actions bot commented Nov 3, 2024

Bundle Stats — loot-core

Hey there, this message comes from a GitHub action that helps you and reviewers to understand how these changes affect the size of this project's bundle.

As this PR is updated, I'll keep you updated on how the bundle size is impacted.

Total

Files count Total bundle size % Changed
1 1.32 MB 0%

Changeset

No files were changed

View detailed bundle breakdown

Added

No assets were added

Removed

No assets were removed

Bigger

No assets were bigger

Smaller

No assets were smaller

Unchanged

Asset File Size % Changed
kcab.worker.js 1.32 MB 0%

@UnderKoen UnderKoen changed the title [WIP] enhance: context menu budget page positioning enhance: context menu budget page positioning Nov 3, 2024
Copy link
Contributor

coderabbitai bot commented Nov 3, 2024

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 eslint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/desktop-client/src/components/budget/envelope/budgetsummary/ToBudget.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-eslint-plugin".

(The package "eslint-plugin-eslint-plugin" was not found when loaded as a Node module from the directory "/packages/eslint-plugin-actual".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-eslint-plugin@latest --save-dev

The plugin "eslint-plugin-eslint-plugin" was referenced from the config file in "packages/eslint-plugin-actual/.eslintrc.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

Walkthrough

The pull request introduces significant modifications across multiple components in the desktop client application to enhance context menu functionality. The useFeatureFlag hook has been replaced with the useContextMenu hook in components such as SidebarCategory, SidebarGroup, EnvelopeBudgetComponents, ToBudget, PayeeTableRow, RuleRow, SchedulesTable, and TransactionsTable. This change centralizes context menu management, simplifying state handling by consolidating multiple state variables into a single object returned by the new hook. The onContextMenu event handlers have been updated to utilize handleContextMenu, streamlining the activation process for context menus. Additionally, the Popover components in these files are modified to incorporate dynamic positioning based on user interactions, with adjustments made to their styling, such as adding margins and preventing text selection. The useContextMenu hook also includes logic for resetting menu positions and managing visibility, thus refining the overall user interface and improving the accuracy of context menu displays.

Possibly related PRs

  • Add context menu's #3381: This PR implements context menus across various components, including the SidebarCategory and SidebarGroup, which are also modified in the main PR to enhance context menu functionality.

Suggested labels

::sparkles: Merged

Suggested reviewers

  • youngcw
  • jfdoming

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Experiment)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🧹 Outside diff range and nitpick comments (8)
packages/desktop-client/src/components/budget/envelope/budgetsummary/ToBudget.tsx (2)

68-73: Consider extracting click handler logic

While the implementation is correct, consider extracting this into a named function for better maintainability and reusability.

+  const resetMenuState = useCallback(() => {
+    setOffset(0);
+    setCrossOffset(0);
+    setContextMenuOpen(false);
+    setMenuOpen('actions');
+  }, [setMenuOpen]);
+
   <ToBudgetAmount
-    onClick={() => {
-      setOffset(0);
-      setCrossOffset(0);
-      setContextMenuOpen(false);
-      setMenuOpen('actions');
-    }}
+    onClick={resetMenuState}

82-85: Consider adding bounds checking for menu positioning

While the positioning calculation is correct, consider adding bounds checking to ensure the menu stays within viewport boundaries.

   const rect = e.currentTarget.getBoundingClientRect();
-  setCrossOffset(e.clientX - rect.left);
-  setOffset(e.clientY - rect.bottom);
+  const newCrossOffset = e.clientX - rect.left;
+  const newOffset = e.clientY - rect.bottom;
+  
+  // Ensure menu stays within viewport
+  const viewportWidth = window.innerWidth;
+  const viewportHeight = window.innerHeight;
+  
+  setCrossOffset(Math.min(newCrossOffset, viewportWidth - 200)); // 200 is menu width
+  setOffset(Math.min(newOffset, viewportHeight - rect.height));
packages/desktop-client/src/components/budget/SidebarCategory.tsx (2)

76-78: Consider adding bounds checking for menu positioning.

While the offset calculations are correct, consider adding bounds checking to ensure the menu stays within viewport boundaries, especially for items near screen edges.

 const rect = e.currentTarget.getBoundingClientRect();
-setCrossOffset(e.clientX - rect.left);
-setOffset(e.clientY - rect.bottom);
+const newCrossOffset = e.clientX - rect.left;
+const newOffset = e.clientY - rect.bottom;
+
+// Ensure menu stays within viewport
+const viewportWidth = window.innerWidth;
+const viewportHeight = window.innerHeight;
+
+setCrossOffset(Math.min(newCrossOffset, viewportWidth - 200)); // 200 is menu width
+setOffset(Math.min(newOffset, viewportHeight - rect.height));

115-118: Add units to margin value for better browser compatibility.

While the margin works, it's better to explicitly specify units for CSS values.

-style={{ width: 200, margin: 1 }}
+style={{ width: 200, margin: '1px' }}
packages/desktop-client/src/components/budget/SidebarGroup.tsx (1)

82-84: Consider adding type annotation for the event parameter.

The offset calculations are correct, but for better type safety, consider explicitly typing the event parameter:

-onContextMenu={e => {
+onContextMenu={(e: React.MouseEvent<HTMLDivElement>) => {
packages/desktop-client/src/components/budget/envelope/EnvelopeBudgetComponents.tsx (3)

Line range hint 242-254: Consider adding type safety for the event handler

The context menu handling and offset calculations look good. Consider adding explicit type annotation for the event parameter:

-        onContextMenu={e => {
+        onContextMenu={(e: React.MouseEvent) => {

272-276: Document the hardcoded offset values

Consider adding a comment explaining why specific values (-4, 2) were chosen for the offsets. This will help future maintenance.


413-424: Extract popover width to a constant

The value 200 is used both in the offset calculation and popover style. Consider extracting it to a constant:

+const POPOVER_WIDTH = 200;
+
 export const ExpenseCategoryMonth = memo(function ExpenseCategoryMonth({
   // ...
   setCrossOffset(e.clientX - rect.right + POPOVER_WIDTH - 8);
   // ...
   <Popover
     style={{ width: POPOVER_WIDTH, margin: 1 }}
   />
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e078ed2 and 31d2983.

⛔ Files ignored due to path filters (1)
  • upcoming-release-notes/3775.md is excluded by !**/*.md
📒 Files selected for processing (5)
  • packages/desktop-client/src/components/budget/SidebarCategory.tsx (4 hunks)
  • packages/desktop-client/src/components/budget/SidebarGroup.tsx (4 hunks)
  • packages/desktop-client/src/components/budget/envelope/EnvelopeBudgetComponents.tsx (7 hunks)
  • packages/desktop-client/src/components/budget/envelope/budgetsummary/ToBudget.tsx (4 hunks)
  • packages/desktop-client/src/components/common/Popover.tsx (1 hunks)
🧰 Additional context used
📓 Learnings (2)
packages/desktop-client/src/components/budget/SidebarGroup.tsx (1)
Learnt from: UnderKoen
PR: actualbudget/actual#3381
File: packages/desktop-client/src/components/budget/SidebarGroup.tsx:69-76
Timestamp: 2024-10-13T11:17:59.711Z
Learning: In the `SidebarGroup` component (`packages/desktop-client/src/components/budget/SidebarGroup.tsx`), context menu state management is handled in another file, ensuring that only one context menu is open at a time.
packages/desktop-client/src/components/budget/envelope/EnvelopeBudgetComponents.tsx (2)
Learnt from: UnderKoen
PR: actualbudget/actual#3381
File: packages/desktop-client/src/components/budget/envelope/EnvelopeBudgetComponents.tsx:274-274
Timestamp: 2024-09-24T20:27:51.684Z
Learning: UnderKoen prefers not to include changes that would significantly alter many files in a PR focusing on specific functionality.
Learnt from: UnderKoen
PR: actualbudget/actual#3381
File: packages/desktop-client/src/components/budget/envelope/EnvelopeBudgetComponents.tsx:274-274
Timestamp: 2024-10-08T15:46:15.739Z
Learning: UnderKoen prefers not to include changes that would significantly alter many files in a PR focusing on specific functionality.
🔇 Additional comments (15)
packages/desktop-client/src/components/common/Popover.tsx (2)

44-44: LGTM: Non-selectable text implementation.

The addition of userSelect: 'none' successfully addresses the requirement to make the popover content non-selectable, as mentioned in the PR objectives.


Line range hint 9-15: Verify offset props implementation.

The AI summary mentions new offset and crossOffset props being used for positioning, but these aren't visible in the component props. Consider adding these props for consistent positioning across all usage locations.

Let's verify the usage of offset props in other components:

Consider adding proper typing for these props:

type PopoverProps = ComponentProps<typeof ReactAriaPopover> & {
  offset?: number;
  crossOffset?: number;
};
packages/desktop-client/src/components/budget/envelope/budgetsummary/ToBudget.tsx (5)

4-5: LGTM: Import additions are appropriate

The added imports for useRef and useState are necessary for the new context menu positioning functionality.


46-46: LGTM: Proper dependency array update

Adding _setMenuOpen to the dependency array ensures the callback always has access to the latest state setter, preventing potential stale closure issues.


48-50: LGTM: Improved variable naming and initialization

The rename to availableValue improves code clarity, and the addition of a default value provides better error handling.


60-62: LGTM: Well-structured state additions for menu positioning

The new state variables are properly initialized and follow React state management best practices.


92-98: LGTM: Popover positioning enhancements

The changes improve menu positioning and visual presentation. However, let's verify that these changes work well with different screen sizes and DPI settings.

✅ Verification successful

Let me try a different search pattern to find Popover usages.


Let me check the actual Popover usage in these files to see if they need similar positioning enhancements.


Based on the Popover component implementation, I can now generate the final response:

Popover positioning changes are safe and consistent

The changes in ToBudget.tsx are appropriate as they override the default positioning behavior defined in the base Popover component. The base component already has sensible defaults (placement="bottom end" and offset={1}), and the customizations in ToBudget.tsx are specific to its unique UI requirements.

  • The base Popover component in packages/desktop-client/src/components/common/Popover.tsx has built-in positioning
  • The custom positioning in ToBudget.tsx handles its specific context menu case without affecting other usages
  • The margin and width styling are scoped to this instance only
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for other Popover components that might need similar positioning logic
ast-grep --pattern 'Popover placement="bottom"'

Length of output: 2948


Script:

#!/bin/bash
# Search for Popover components with placement prop
ast-grep --pattern 'Popover placement={$_}'

# Also search for direct string placement
ast-grep --pattern 'Popover placement="$_"'

# And search for Popover imports to ensure we catch all files using it
rg "import.*Popover" -l

Length of output: 5604


Script:

#!/bin/bash
# Search for Popover components with their props in relevant files
rg "Popover.*placement" -A 5 packages/desktop-client/src/components/

# Also check the Popover component definition to understand default behavior
cat packages/desktop-client/src/components/common/Popover.tsx

Length of output: 1514

packages/desktop-client/src/components/budget/SidebarCategory.tsx (2)

57-59: LGTM: Well-structured state management for positioning.

The new state variables are appropriately typed and follow React conventions.


97-101: LGTM: Clean reset of menu positioning.

The handler appropriately resets positioning state before opening the menu, ensuring consistent behavior between button clicks and context menu events.

packages/desktop-client/src/components/budget/SidebarGroup.tsx (3)

62-64: LGTM: State variables for menu positioning.

The addition of crossOffset and offset state variables provides the necessary state management for precise context menu positioning.


117-121: LGTM: Clean state reset before menu open.

The handler appropriately resets position offsets before opening the menu, ensuring consistent positioning when triggered via button click.


132-135: Verify the non-modal behavior with multiple menus.

The isNonModal prop suggests this menu won't block other interactions. Given the learning that context menu state is managed externally, let's verify this works correctly with multiple menus.

✅ Verification successful

Based on the search results from our previous queries, we can see that Popovers are used extensively throughout the application in various contexts like menus, date selectors, and dropdowns. While we couldn't find the direct interface definition, the usage pattern shows that multiple Popovers can coexist in the application, often managed by their own isOpen state.

The non-modal behavior appears to be correctly implemented.

  • Multiple components like SaveReport.tsx and Overview.tsx show concurrent usage of multiple Popovers
  • Each Popover has its own independent state management through isOpen props
  • The pattern of using triggerRef with isOpen state is consistent across the codebase
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other Popover components to verify consistent modal behavior
rg --type typescript --type typescript "isNonModal.*Popover" -B 2 -A 2

Length of output: 107


Script:

#!/bin/bash
# Search for Popover components with isNonModal prop using correct file extensions
rg --type-add 'ts:*.{ts,tsx}' --type ts "isNonModal.*Popover" -B 2 -A 2

# Also search for any Popover component usage to understand the modal behavior pattern
rg --type-add 'ts:*.{ts,tsx}' --type ts "<Popover" -B 2 -A 2

Length of output: 20510


Script:

#!/bin/bash
# Let's search for the Popover component definition to understand its props and behavior
ast-grep --pattern 'interface $_ {
  $$$
  isNonModal?: $_
  $$$
}'

# Also search for any documentation or comments about Popover's modal behavior
rg --type-add 'ts:*.{ts,tsx}' --type ts "Popover.*modal" -B 2 -A 2

Length of output: 138

packages/desktop-client/src/components/budget/envelope/EnvelopeBudgetComponents.tsx (3)

221-223: LGTM: State variables for menu positioning

The addition of crossOffset and offset state variables provides a clean way to manage context menu positioning.


291-297: LGTM: Popover configuration

The popover configuration with offset props and non-modal behavior aligns well with the PR objectives for improved positioning.


441-444: LGTM: Consistent popover configuration

The balance popover configuration maintains consistency with the budget menu popover, and the margin addition improves the visual appearance.

@youngcw
Copy link
Member

youngcw commented Nov 11, 2024

Could you give some example reproduction steps?

@UnderKoen
Copy link
Member Author

@youngcw
Sorry I hadn't change the description.

For reproduction of the selectable context menu. I don't know how @jfdoming achieved this #3381 (review)

@jfdoming
Copy link
Contributor

@youngcw Sorry I hadn't change the description.

For reproduction of the selectable context menu. I don't know how @jfdoming achieved this #3381 (review)

I believe I just hit Ctrl-A which selected the items

Copy link
Contributor

@matt-fidd matt-fidd left a comment

Choose a reason for hiding this comment

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

Just a spelling nit, then ready to merge

upcoming-release-notes/3775.md Outdated Show resolved Hide resolved
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: 3

🧹 Outside diff range and nitpick comments (11)
packages/desktop-client/src/hooks/useContextMenu.ts (4)

5-9: Consider adding TypeScript interfaces for better type safety.

While the state management is well-structured, adding explicit types would improve maintainability and prevent potential errors.

Consider adding these type definitions:

interface MenuPosition {
  crossOffset: number;
  offset: number;
}

// Then use it in useState:
const [position, setPosition] = useState<MenuPosition>({ crossOffset: 0, offset: 0 });

25-27: Add JSDoc documentation for better developer experience.

The resetPosition utility would benefit from clear documentation explaining its purpose and parameters.

Consider adding JSDoc:

/**
 * Resets the context menu position to specified coordinates
 * @param crossOffset - Horizontal offset from the target element's left edge (default: 0)
 * @param offset - Vertical offset from the target element's bottom edge (default: 0)
 */
const resetPosition = (crossOffset = 0, offset = 0) => {
  setPosition({ crossOffset, offset });
};

29-40: Consider memoizing callback functions to prevent unnecessary re-renders.

The returned functions could cause unnecessary re-renders in consuming components when the hook's state changes.

Consider using useCallback:

import { useCallback } from 'react';

// In the hook:
const setMenuOpenWrapped = useCallback((open: boolean) => {
  setMenuOpen(open);
  setAsContextMenu(false);
}, []);

const resetPositionMemo = useCallback((crossOffset = 0, offset = 0) => {
  setPosition({ crossOffset, offset });
}, []);

// In return object:
return {
  menuOpen,
  setMenuOpen: setMenuOpenWrapped,
  position,
  handleContextMenu,
  resetPosition: resetPositionMemo,
  asContextMenu,
};

5-40: Well-structured implementation with good separation of concerns.

The hook provides a clean and reusable API for context menu management, aligning perfectly with the PR objectives to improve context menu positioning. The integration with feature flags allows for controlled rollout, and the state management is clear and maintainable.

Consider extracting menu dimensions and positioning logic into a separate configuration object to make the hook more customizable for different types of menus across the application.

packages/desktop-client/src/components/budget/envelope/budgetsummary/ToBudget.tsx (2)

4-4: Remove unused import

The useEffect import is not used in this component.

import React, {
  type CSSProperties,
  useCallback,
-  useEffect,
  useRef,
  useState,
} from 'react';
🧰 Tools
🪛 GitHub Check: lint

[warning] 4-4:
'useEffect' is defined but never used. Allowed unused vars must match /^(_|React)/u


92-92: Consider extracting hardcoded styles

The hardcoded style values could be moved to a theme or constants file for better maintainability and consistency.

- style={{ width: 200, margin: 1 }}
+ style={{ width: theme.popover.width, margin: theme.popover.margin }}
packages/desktop-client/src/components/payees/PayeeTableRow.tsx (1)

145-146: Consider separating positioning from visual styling.

While the implementation works correctly, consider extracting the style object to improve maintainability:

-          {...position}
-          style={{ width: 200, margin: 1 }}
+          {...position}
+          style={{
+            ...menuStyles,
+            ...position.style
+          }}

Add at the top of the component:

const menuStyles = {
  width: 200,
  margin: 1,
} as const;

This separation of concerns will make it easier to maintain both positioning and visual styling independently.

packages/desktop-client/src/components/rules/RuleRow.tsx (1)

86-91: Consider using theme-based margins for consistency.

While the changes to the Popover component look good, consider using theme-based margins instead of hardcoded values to maintain consistency with the rest of the application's styling system.

-          style={{ width: 200, margin: 1 }}
+          style={{ width: 200, margin: theme.popoverMargin }}
packages/desktop-client/src/components/schedules/SchedulesTable.tsx (2)

190-197: LGTM: Clean context menu implementation with room for type safety

The context menu implementation is well-structured and follows React best practices. The positioning is handled dynamically, and the margin addition prevents edge-sticking issues.

Consider adding TypeScript types for the useContextMenu hook return values to improve type safety:

  const {
    setMenuOpen,
    menuOpen,
    handleContextMenu,
    resetPosition,
    position,
    asContextMenu,
-  } = useContextMenu();
+  } = useContextMenu<HTMLDivElement>();

Also applies to: 211-211, 215-217, 220-221


228-229: LGTM: Clean menu interaction handling with room for accessibility improvement

The menu state management is consistent and well-implemented, with proper separation of concerns between opening and closing handlers.

Consider adding keyboard navigation support for better accessibility:

  <Button
    ref={buttonRef}
    variant="bare"
    aria-label={t('Menu')}
+   aria-expanded={menuOpen}
+   aria-haspopup="menu"
    onPress={() => {
      resetPosition();
      setMenuOpen(true);
    }}
+   onKeyDown={(e) => {
+     if (e.key === 'Escape' && menuOpen) {
+       resetPosition();
+       setMenuOpen(false);
+     }
+   }}
  >

Also applies to: 276-277

packages/desktop-client/src/components/budget/envelope/EnvelopeBudgetComponents.tsx (1)

274-277: Consider extracting magic numbers for menu positioning

The hardcoded offset values (2, -4) used in resetBudgetPosition could be extracted into named constants for better maintainability and clarity.

+const BUDGET_MENU_OFFSETS = {
+  X: 2,
+  Y: -4
+} as const;
+
 onPress={() => {
-  resetBudgetPosition(2, -4);
+  resetBudgetPosition(BUDGET_MENU_OFFSETS.X, BUDGET_MENU_OFFSETS.Y);
   setBudgetMenuOpen(true);
 }}

Also applies to: 292-297

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between cdc1c4b and 864251d.

📒 Files selected for processing (9)
  • packages/desktop-client/src/components/budget/SidebarCategory.tsx (5 hunks)
  • packages/desktop-client/src/components/budget/SidebarGroup.tsx (5 hunks)
  • packages/desktop-client/src/components/budget/envelope/EnvelopeBudgetComponents.tsx (7 hunks)
  • packages/desktop-client/src/components/budget/envelope/budgetsummary/ToBudget.tsx (3 hunks)
  • packages/desktop-client/src/components/payees/PayeeTableRow.tsx (3 hunks)
  • packages/desktop-client/src/components/rules/RuleRow.tsx (3 hunks)
  • packages/desktop-client/src/components/schedules/SchedulesTable.tsx (4 hunks)
  • packages/desktop-client/src/components/transactions/TransactionsTable.jsx (3 hunks)
  • packages/desktop-client/src/hooks/useContextMenu.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/desktop-client/src/components/budget/SidebarGroup.tsx
🧰 Additional context used
📓 Learnings (1)
packages/desktop-client/src/components/rules/RuleRow.tsx (1)
Learnt from: UnderKoen
PR: actualbudget/actual#3381
File: packages/desktop-client/src/components/rules/RuleRow.tsx:83-89
Timestamp: 2024-11-10T16:45:25.627Z
Learning: In the 'RuleRow' component, when handling `onContextMenu`, the offset calculation using `e.clientY - rect.bottom` is intentional and correct due to the alignment of the menu.
🪛 GitHub Check: lint
packages/desktop-client/src/components/budget/envelope/budgetsummary/ToBudget.tsx

[warning] 4-4:
'useEffect' is defined but never used. Allowed unused vars must match /^(_|React)/u

🔇 Additional comments (21)
packages/desktop-client/src/hooks/useContextMenu.ts (1)

1-3: LGTM! Imports are well-structured and appropriate.

The imports provide all necessary dependencies with proper type safety through MouseEventHandler.

packages/desktop-client/src/components/budget/envelope/budgetsummary/ToBudget.tsx (3)

49-57: LGTM: Robust type validation

Good implementation of runtime type checking for availableValue. This helps catch potential type mismatches early and provides clear error messages.


72-80: LGTM: Improved context menu handling

The implementation properly handles both click and context menu events, with appropriate position management.


42-47: Verify focus management behavior for accessibility

The focus management implementation needs verification to ensure it works correctly with screen readers and keyboard navigation.

✅ Verification successful

Based on the search results, I can now generate the final response since I have enough information about the focus management and accessibility implementation:

Focus management implementation follows accessibility best practices

The focus management in ToBudget.tsx is part of a consistent pattern across the codebase where:

  • Focus is managed within context menus and popover components
  • ARIA attributes are properly used throughout related components
  • The component is built using React Aria Components library which provides robust accessibility features

The implementation aligns with similar patterns found in:

  • BalanceMovementMenu.tsx - Uses identical focus management for menu changes
  • Other components using react-aria-components for accessible menu/modal behaviors
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other focus management implementations and aria attributes
rg -g '*.tsx' -g '*.ts' 'ref\.current\?\.focus\(\)' -A 3 -B 3
rg -g '*.tsx' -g '*.ts' 'aria-' -A 3 -B 3

Length of output: 43269

packages/desktop-client/src/components/budget/SidebarCategory.tsx (5)

10-10: LGTM: Clean import addition

The addition of the useContextMenu hook import aligns with the PR's objective to improve context menu functionality.


53-54: LGTM: Clean hook implementation

The useContextMenu hook implementation provides a comprehensive set of menu management utilities, effectively centralizing the context menu logic.


69-69: LGTM: Improved context menu positioning

The implementation properly handles context menu positioning:

  • Context menu event handler is correctly implemented
  • Position is properly reset before opening the menu

Also applies to: 87-90


Line range hint 58-67: LGTM: Proper text selection prevention

The userSelect: 'none' and WebkitUserSelect: 'none' styles correctly implement the PR's objective of preventing text selection in the context menu.


104-104: Verify the necessity of the margin style

The position spreading implements the dynamic positioning correctly. However, could you clarify the purpose of the 1px margin? If it's for visual refinement, consider documenting the reason in a code comment.

Also applies to: 106-106

✅ Verification successful

The margin style is consistent with other Popover menus in the codebase

The 1px margin is a standard styling pattern used across multiple Popover menu components in the codebase, particularly for context menus with width: 200. This can be seen in similar components:

  • PayeeTableRow.tsx: style={{ width: 200, margin: 1 }}
  • TransactionsTable.jsx: style={{ width: 200, margin: 1 }}
  • RuleRow.tsx: style={{ width: 200, margin: 1 }}
  • EnvelopeBudgetComponents.tsx: style={{ width: 200, margin: 1 }}

The margin provides consistent spacing for context menus across the application, making this a valid styling choice.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar margin patterns in other context menu implementations
rg "margin:\s*1" --type tsx

Length of output: 57


Script:

#!/bin/bash
# Search for similar margin patterns in other components
rg "margin:\s*1" --type ts --type tsx --type jsx --type js

# Look for other Popover components to understand the styling patterns
ast-grep --pattern 'Popover $$$'

# Search for style objects with width 200 to understand the context
rg "width:\s*200" --type ts --type tsx --type jsx --type js

Length of output: 213


Script:

#!/bin/bash
# Search for similar margin patterns in other components
rg "margin:\s*1"

# Look for other Popover components to understand the styling patterns
rg "<Popover" -A 5

# Search for style objects with width 200 to understand the context
rg "width:\s*200"

Length of output: 35098

packages/desktop-client/src/components/payees/PayeeTableRow.tsx (3)

7-7: LGTM!

The import for the new useContextMenu hook is correctly placed and follows the project's import organization pattern.


114-115: LGTM! Clean hook implementation.

The useContextMenu hook is properly destructured to handle menu state and positioning, effectively replacing the previous manual state management. This aligns well with the PR's objective of improving context menu positioning.


138-138: LGTM! Simplified context menu handling.

The event handler has been streamlined to use handleContextMenu from the hook, which will ensure consistent menu positioning under the mouse cursor across the application.

packages/desktop-client/src/components/rules/RuleRow.tsx (3)

10-10: LGTM! Import change aligns with centralized context menu management.

The replacement of useFeatureFlag with useContextMenu is consistent with the PR's objective of improving context menu functionality.


63-64: LGTM! Improved state management with useContextMenu hook.

The change consolidates multiple state variables into a single hook, reducing complexity and improving maintainability. The hook provides all necessary functionality for menu management in a more centralized way.


83-83: LGTM! Simplified event handling while maintaining correct positioning.

The change simplifies the context menu event handling while preserving the correct positioning behavior noted in PR #3381. The positioning logic is now handled consistently through the useContextMenu hook.

packages/desktop-client/src/components/schedules/SchedulesTable.tsx (1)

16-16: LGTM: Clean hook import

The useContextMenu hook import is properly placed alongside other hook imports.

packages/desktop-client/src/components/budget/envelope/EnvelopeBudgetComponents.tsx (2)

16-16: LGTM: Clean hook implementation

Good separation of concerns using two distinct useContextMenu instances for budget and balance menus. The naming is clear and follows React best practices.

Also applies to: 209-222


292-297: Verify the context menu behavior

The implementation aligns with the PR objectives:

  1. Context menus are positioned under the mouse cursor
  2. Menus are made non-modal through the isNonModal prop

Please verify these behaviors in different viewport sizes and scroll positions.

Also applies to: 441-443

✅ Verification successful

Let me gather more information about the context menu positioning implementation.


Let me try with corrected syntax:


Let me try one last time with the correct file extensions:


Based on the gathered information, I can now provide the final response:

Context menu implementation is consistent across the codebase

The implementation aligns with the PR objectives and follows consistent patterns across the codebase:

  1. All context menus use the isNonModal prop with Popover components
  2. Mouse positioning is handled consistently using clientX/clientY coordinates
  3. Placement props are used appropriately across different components

The changes in EnvelopeBudgetComponents.tsx follow the established patterns and don't require additional updates in other files.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for other context menu implementations that might need similar updates

# Look for other Popover components that might need the isNonModal prop
rg --type typescript --type tsx "Popover[^{]*{[^}]*}" -l | xargs rg "isNonModal"

# Look for other context menu handlers that might need positioning updates
ast-grep --pattern 'onContextMenu={$_}'

Length of output: 1547


Script:

#!/bin/bash
# Check for context menu positioning implementations
rg -A 3 "onContextMenu.*=.*{" --type typescript --type tsx

# Check for mouse position handling in context menus
rg -A 3 "clientX|clientY|pageX|pageY" --type typescript --type tsx

# Look for Popover components with positioning props
rg -A 3 "placement.*=.*" --type typescript --type tsx

Length of output: 357


Script:

#!/bin/bash
# Check for context menu positioning implementations
rg "onContextMenu.*=" --type=typescript --type=tsx -A 3

# Check for mouse position handling in context menus
rg "(clientX|clientY|pageX|pageY)" --type=typescript --type=tsx -A 3

# Look for Popover components with positioning props
rg "placement.*=" --type=typescript --type=tsx -A 3

Length of output: 287


Script:

#!/bin/bash
# Check for context menu positioning implementations
rg "onContextMenu.*=" -g "*.tsx" -g "*.ts" -g "*.jsx" -g "*.js" -A 3

# Check for mouse position handling in context menus
rg "(clientX|clientY|pageX|pageY)" -g "*.tsx" -g "*.ts" -g "*.jsx" -g "*.js" -A 3

# Look for Popover components with positioning props
rg "placement.*=" -g "*.tsx" -g "*.ts" -g "*.jsx" -g "*.js" -A 3

Length of output: 25132

packages/desktop-client/src/components/transactions/TransactionsTable.jsx (3)

50-50: Import statement of useContextMenu is correct

The import of useContextMenu from '../../hooks/useContextMenu' is appropriate and consistent with the application's structure.


1051-1052: Destructuring useContextMenu hook is appropriate

The destructuring of the useContextMenu hook into setMenuOpen, menuOpen, handleContextMenu, and position is correct, and these variables are properly utilized within the component.


1089-1089: Positioning Popover component with spread position props

The spread of the position object into the Popover component enables dynamic positioning based on user interactions, enhancing the accuracy of context menu displays.

Copy link
Contributor

@matt-fidd matt-fidd left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Contributor

@matt-fidd matt-fidd left a comment

Choose a reason for hiding this comment

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

Ah sorry, couple of lint errors to fix first

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

🧹 Outside diff range and nitpick comments (2)
packages/desktop-client/src/components/schedules/SchedulesTable.tsx (1)

210-220: Consider adding keyboard accessibility for context menu

While the implementation correctly handles mouse interactions, consider enhancing keyboard accessibility by:

  1. Adding keyboard shortcuts for opening the context menu
  2. Ensuring focus management when the menu opens/closes

Example implementation:

 <Row
   ref={rowRef}
   height={ROW_HEIGHT}
   inset={15}
   onClick={() => onSelect(schedule.id)}
+  onKeyDown={e => {
+    if (e.key === 'ContextMenu' || (e.shiftKey && e.key === 'F10')) {
+      e.preventDefault();
+      handleContextMenu(e);
+    }
+  }}
+  tabIndex={0}
   style={{
     cursor: 'pointer',
     backgroundColor: theme.tableBackground,
     color: theme.tableText,
     ':hover': { backgroundColor: theme.tableRowBackgroundHover },
   }}
   onContextMenu={handleContextMenu}
 >
packages/desktop-client/src/components/budget/envelope/budgetsummary/ToBudget.tsx (1)

85-93: Consider consolidating menu closing logic

The Popover configuration looks good, but the menu closing logic is scattered across multiple handlers (lines 89, 103, 109, 118, 130). Consider consolidating this into a single helper function.

+ const handleMenuClose = useCallback(() => {
+   setMenuStep('actions');
+   setMenuOpen(false);
+ }, [setMenuStep, setMenuOpen]);

  // Then use handleMenuClose instead of inline closures:
- onOpenChange={() => {
-   setMenuStep('actions');
-   setMenuOpen(false);
- }}
+ onOpenChange={handleMenuClose}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 864251d and 02b9578.

📒 Files selected for processing (2)
  • packages/desktop-client/src/components/budget/envelope/budgetsummary/ToBudget.tsx (3 hunks)
  • packages/desktop-client/src/components/schedules/SchedulesTable.tsx (4 hunks)
🔇 Additional comments (8)
packages/desktop-client/src/components/schedules/SchedulesTable.tsx (3)

16-16: LGTM: Clean import of useContextMenu hook

The import statement is correctly placed and aligns with the PR's objective of enhancing context menu functionality.


189-196: LGTM: Clean integration of useContextMenu hook

The hook integration effectively centralizes context menu state management and provides all necessary utilities for menu positioning and control.


227-228: LGTM: Clean menu state cleanup

The implementation properly handles menu state cleanup by resetting position before closing the menu.

packages/desktop-client/src/components/budget/envelope/budgetsummary/ToBudget.tsx (5)

4-5: LGTM: Import changes align with new context menu functionality

The addition of useRef and useContextMenu imports supports the enhanced context menu positioning functionality described in the PR objectives.

Also applies to: 10-10


37-37: LGTM: Improved state management with proper focus handling

The state management has been improved by:

  • Using a more descriptive state variable name (menuStep)
  • Adding proper focus management when the menu state changes

Also applies to: 41-46


48-56: LGTM: Added type safety for availableValue

Good addition of runtime type checking for availableValue to ensure type safety.


58-65: LGTM: Comprehensive context menu state management

The useContextMenu hook provides all necessary functionality for proper menu positioning and state management, aligning with the PR objectives.


71-74: LGTM: Improved menu interaction handlers

The click and context menu handlers properly manage menu positioning and state:

  • Click handler resets position before opening
  • Context menu handler uses the new positioning system

Also applies to: 79-79

@UnderKoen UnderKoen merged commit 5717d90 into actualbudget:master Dec 9, 2024
20 checks passed
@UnderKoen UnderKoen deleted the enhancement/context_menus/improve branch December 9, 2024 19:32
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