Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ContextMenu } from '@ohif/ui';
import { ContextMenuViewport } from '@ohif/ui-next';

export default {
'ui.contextMenu': ContextMenu,
'ui.contextMenu': ContextMenuViewport,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react';
import { Icons } from '../Icons';

interface ContextMenuViewportProps {
items?: Array<{
label: string;
action: (item: any, props: any) => void;
iconRight?: string;
[key: string]: unknown;
}>;
defaultPosition?: { x: number; y: number };
[key: string]: unknown;
Comment thread
dan-rukas marked this conversation as resolved.
Outdated
}

const ContextMenuViewport = ({ items, ...props }: ContextMenuViewportProps) => {
if (!items) {
return null;
}
Comment thread
dan-rukas marked this conversation as resolved.
Comment on lines +15 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Avoid rendering an empty menu container when items is empty.

Line 16 only checks for falsy values; [] still renders a blank menu shell.

Suggested fix
-  if (!items) {
+  if (!items?.length) {
     return null;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!items) {
return null;
}
if (!items?.length) {
return null;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@platform/ui-next/src/components/ContextMenuViewport/ContextMenuViewport.tsx`
around lines 16 - 18, The ContextMenuViewport rendering guard only checks for
falsy items, so an empty array still creates a blank menu container. Update the
conditional in ContextMenuViewport to also skip rendering when the items
collection is empty, using the existing items check before the menu shell is
returned. Keep the fix localized to ContextMenuViewport so empty menus do not
render at all.


return (
<div
data-cy="context-menu"
className="bg-popover text-popover-foreground animate-in fade-in-0 zoom-in-95 relative z-50 w-48 overflow-hidden rounded-md border border-input p-1 shadow-md"
onContextMenu={e => e.preventDefault()}
>
{items.map((item, index) => (
<div
key={index}
data-cy="context-menu-item"
onClick={() => item.action(item, props)}
className="hover:bg-accent hover:text-accent-foreground flex cursor-default select-none items-center justify-between rounded-sm px-2 py-1.5 text-base outline-none"
Comment thread
dan-rukas marked this conversation as resolved.
>
Comment on lines +26 to +31

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use keyboard-focusable controls for menu actions.

Line 27 renders clickable rows as <div>, so actions are not keyboard-focusable/activatable. Line 31 also uses cursor-default on interactive elements. Use a semantic <button> (or equivalent keyboard handlers) and cursor-pointer.

Suggested fix
-        <div
+        <button
+          type="button"
           key={index}
           data-cy="context-menu-item"
           onClick={() => item.action(item, props)}
-          className="hover:bg-accent hover:text-accent-foreground flex cursor-default select-none items-center justify-between rounded-sm px-2 py-1.5 text-base outline-none"
+          className="hover:bg-accent hover:text-accent-foreground flex w-full cursor-pointer select-none items-center justify-between rounded-sm px-2 py-1.5 text-base outline-none"
         >
           <span>{item.label}</span>
           {item.iconRight && (
             <Icons.ByName
               name={item.iconRight}
               className="inline"
             />
           )}
-        </div>
+        </button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div
key={index}
data-cy="context-menu-item"
onClick={() => item.action(item, props)}
className="hover:bg-accent hover:text-accent-foreground flex cursor-default select-none items-center justify-between rounded-sm px-2 py-1.5 text-base outline-none"
>
<button
type="button"
key={index}
data-cy="context-menu-item"
onClick={() => item.action(item, props)}
className="hover:bg-accent hover:text-accent-foreground flex w-full cursor-pointer select-none items-center justify-between rounded-sm px-2 py-1.5 text-base outline-none"
>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@platform/ui-next/src/components/ContextMenuViewport/ContextMenuViewport.tsx`
around lines 27 - 32, The context menu actions in ContextMenuViewport are
rendered as non-interactive divs, so they cannot be reached or activated with
the keyboard. Update the item rendering in ContextMenuViewport to use a semantic
button-like control for each action, preserve the existing item.action(item,
props) click behavior, and make sure the interactive styling uses cursor-pointer
instead of cursor-default. Ensure the unique item row wrapper and its onClick
handler remain tied to the same ContextMenuViewport mapping logic.

<span>{item.label}</span>
{item.iconRight && (
<Icons.ByName
name={item.iconRight}
className="inline"
/>
)}
</div>
))}
</div>
);
};

export default ContextMenuViewport;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as ContextMenuViewport } from './ContextMenuViewport';
2 changes: 2 additions & 0 deletions platform/ui-next/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ import {
} from './ToolButton';
import { LayoutSelector } from './LayoutSelector';
import { ToolSettings } from './OHIFToolSettings';
import { ContextMenuViewport } from './ContextMenuViewport';
export * from './StudyList';
export { DataRow } from './DataRow';
export { MeasurementTable } from './MeasurementTable';
Expand Down Expand Up @@ -308,6 +309,7 @@ export {
ViewportDialog,
CinePlayer,
LayoutSelector,
ContextMenuViewport,
SmartScrollbar,
useSmartScrollbarLayoutContext,
useSmartScrollbarScrollContext,
Expand Down
Loading