diff --git a/packages/module/src/ResponsiveActions/ResponsiveActions.test.tsx b/packages/module/src/ResponsiveActions/ResponsiveActions.test.tsx index f3c29fec..a573e99b 100644 --- a/packages/module/src/ResponsiveActions/ResponsiveActions.test.tsx +++ b/packages/module/src/ResponsiveActions/ResponsiveActions.test.tsx @@ -1,4 +1,4 @@ -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import ResponsiveActions from './ResponsiveActions'; import ResponsiveAction from '../ResponsiveAction'; @@ -56,5 +56,71 @@ describe('ResponsiveActions component', () => { expect(buttons).toHaveLength(2); expect(container).toMatchSnapshot(); }); + + test('ResponsiveActions with all dropdown items disabled should disable kebab', () => { + render( + + Disabled action 1 + Disabled action 2 + ); + + const kebabToggle = screen.getByRole('button', { name: /actions overflow menu/i }); + expect(kebabToggle).toBeDisabled(); + }); + + test('ResponsiveActions with some enabled dropdown items should not disable kebab', () => { + render( + + Disabled action + Enabled action + ); + + const kebabToggle = screen.getByRole('button', { name: /actions overflow menu/i }); + expect(kebabToggle).toBeEnabled(); + }); + + test('ResponsiveActions with enabled pinned item and disabled regular item should disable kebab above breakpoint', () => { + render( + + Enabled pinned action + Disabled regular action + ); + + const kebabToggle = screen.getByRole('button', { name: /actions overflow menu/i }); + expect(kebabToggle).toBeDisabled(); + }); + + test('ResponsiveActions with enabled pinned item and enabled regular item should not disable kebab', () => { + render( + + Enabled pinned action + Enabled regular action + ); + + const kebabToggle = screen.getByRole('button', { name: /actions overflow menu/i }); + expect(kebabToggle).toBeEnabled(); + }); + + test('ResponsiveActions with all dropdown items disabled including pinned should disable kebab', () => { + render( + + Disabled pinned action + Disabled action + ); + + const kebabToggle = screen.getByRole('button', { name: /actions overflow menu/i }); + expect(kebabToggle).toBeDisabled(); + }); + + test('ResponsiveActions with only persistent items should not render kebab', () => { + const { container } = render( + + Persistent action + ); + + // Should not have kebab when only persistent items exist + const kebabToggle = container.querySelector('[data-ouia-component-id="ResponsiveActions-menu-dropdown-toggle"]'); + expect(kebabToggle).toBeNull(); + }); }); }); \ No newline at end of file diff --git a/packages/module/src/ResponsiveActions/ResponsiveActions.tsx b/packages/module/src/ResponsiveActions/ResponsiveActions.tsx index a7e1c5f7..54714c6e 100644 --- a/packages/module/src/ResponsiveActions/ResponsiveActions.tsx +++ b/packages/module/src/ResponsiveActions/ResponsiveActions.tsx @@ -1,8 +1,9 @@ import type { ReactNode, FunctionComponent } from 'react'; -import { Children, isValidElement, useState } from 'react'; +import { Children, isValidElement, useState, useContext } from 'react'; import { Button, Dropdown, DropdownList, MenuToggle, OverflowMenu, OverflowMenuContent, OverflowMenuControl, OverflowMenuDropdownItem, OverflowMenuGroup, OverflowMenuItem, OverflowMenuProps } from '@patternfly/react-core'; import { EllipsisVIcon } from '@patternfly/react-icons'; import { ResponsiveActionProps } from '../ResponsiveAction'; +import { OverflowMenuContext } from '@patternfly/react-core/dist/esm/components/OverflowMenu/OverflowMenuContext'; /** extends OverflowMenuProps */ export interface ResponsiveActionsProps extends Omit { @@ -14,13 +15,62 @@ export interface ResponsiveActionsProps extends Omit = ({ ouiaId = 'ResponsiveActions', breakpoint = 'lg', children, ...props }: ResponsiveActionsProps) => { +const ResponsiveActionsDropdown: FunctionComponent<{ + ouiaId: string; + dropdownItems: ReactNode[]; + pinnedItemsDisabled: boolean[]; + regularItemsDisabled: boolean[]; +}> = ({ ouiaId, dropdownItems, pinnedItemsDisabled, regularItemsDisabled }) => { const [ isOpen, setIsOpen ] = useState(false); + const { isBelowBreakpoint } = useContext(OverflowMenuContext); + + const isKebabDisabled = (() => { + const allPinnedDisabled = pinnedItemsDisabled.length > 0 && pinnedItemsDisabled.every(disabled => disabled); + const allRegularDisabled = regularItemsDisabled.length > 0 && regularItemsDisabled.every(disabled => disabled); + + if (isBelowBreakpoint) { + return (pinnedItemsDisabled.length > 0 || regularItemsDisabled.length > 0) && + (pinnedItemsDisabled.length === 0 || allPinnedDisabled) && + (regularItemsDisabled.length === 0 || allRegularDisabled); + } else { + return allRegularDisabled; + } + })(); + + return ( + setIsOpen(false)} + toggle={(toggleRef) => ( + } + onClick={() => setIsOpen(!isOpen)} + isExpanded={isOpen} + isDisabled={isKebabDisabled} + /> + )} + isOpen={isOpen} + onOpenChange={setIsOpen} + > + + {dropdownItems} + + + ); +}; + +export const ResponsiveActions: FunctionComponent = ({ ouiaId = 'ResponsiveActions', breakpoint = 'lg', children, ...props }: ResponsiveActionsProps) => { // separate persistent, pinned and collapsed actions const persistentActions: ReactNode[] = []; const pinnedActions: ReactNode[] = []; const dropdownItems: ReactNode[] = []; + const pinnedItemsDisabled: boolean[] = []; + const regularItemsDisabled: boolean[] = []; let hasRegularActions = false; Children.forEach(children, (child, index) => { @@ -37,7 +87,6 @@ export const ResponsiveActions: FunctionComponent = ({ o ); } else { - // Track if there are any regular (non-persistent, non-pinned) actions hasRegularActions = true; } @@ -47,6 +96,11 @@ export const ResponsiveActions: FunctionComponent = ({ o {children} ); + if (isPinned) { + pinnedItemsDisabled.push(!!actionProps.isDisabled); + } else { + regularItemsDisabled.push(!!actionProps.isDisabled); + } } } }); @@ -74,27 +128,12 @@ export const ResponsiveActions: FunctionComponent = ({ o ) : null} {dropdownItems.length > 0 && ( - setIsOpen(false)} - toggle={(toggleRef) => ( - } - onClick={() => setIsOpen(!isOpen)} - isExpanded={isOpen} - /> - )} - isOpen={isOpen} - onOpenChange={setIsOpen} - > - - {dropdownItems} - - + )}