diff --git a/packages/react-core/src/components/Dropdown/DropdownItem.tsx b/packages/react-core/src/components/Dropdown/DropdownItem.tsx index f33ca517012..d6cf6e127c1 100644 --- a/packages/react-core/src/components/Dropdown/DropdownItem.tsx +++ b/packages/react-core/src/components/Dropdown/DropdownItem.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { css } from '@patternfly/react-styles'; import { MenuItemProps, MenuItem } from '../Menu'; +import { TooltipProps } from '../Tooltip'; import { useOUIAProps, OUIAProps } from '../../helpers'; /** @@ -17,6 +18,8 @@ export interface DropdownItemProps extends Omit, OUIAProps description?: React.ReactNode; /** Render item as disabled option */ isDisabled?: boolean; + /** Render item as aria-disabled option */ + isAriaDisabled?: boolean; /** Identifies the component in the dropdown onSelect callback */ value?: any; /** Callback for item click */ @@ -25,6 +28,8 @@ export interface DropdownItemProps extends Omit, OUIAProps ouiaId?: number | string; /** Set the value of data-ouia-safe. Only set to true when the component is in a static state, i.e. no animations are occurring. At all other times, this value must be false. */ ouiaSafe?: boolean; + /** Props for adding a tooltip to a menu item */ + tooltipProps?: TooltipProps; } const DropdownItemBase: React.FunctionComponent = ({ @@ -32,11 +37,13 @@ const DropdownItemBase: React.FunctionComponent = ({ className, description, isDisabled, + isAriaDisabled, value, onClick, ouiaId, ouiaSafe, innerRef, + tooltipProps, ...props }: DropdownItemProps) => { const ouiaProps = useOUIAProps(DropdownItem.displayName, ouiaId, ouiaSafe); @@ -45,8 +52,10 @@ const DropdownItemBase: React.FunctionComponent = ({ className={css(className)} description={description} isDisabled={isDisabled} + isAriaDisabled={isAriaDisabled} itemId={value} onClick={onClick} + tooltipProps={tooltipProps} ref={innerRef} {...ouiaProps} {...props} diff --git a/packages/react-core/src/components/Dropdown/examples/Dropdown.md b/packages/react-core/src/components/Dropdown/examples/Dropdown.md index e826d27ee64..f7efe1bff52 100644 --- a/packages/react-core/src/components/Dropdown/examples/Dropdown.md +++ b/packages/react-core/src/components/Dropdown/examples/Dropdown.md @@ -11,7 +11,8 @@ propComponents: 'DropdownList', 'MenuToggle', 'DropdownToggleProps', - 'DropdownPopperProps' + 'DropdownPopperProps', + 'TooltipProps' ] --- diff --git a/packages/react-core/src/components/Dropdown/examples/DropdownBasic.tsx b/packages/react-core/src/components/Dropdown/examples/DropdownBasic.tsx index 7b9b6b4f1b0..e4a7f9dcc78 100644 --- a/packages/react-core/src/components/Dropdown/examples/DropdownBasic.tsx +++ b/packages/react-core/src/components/Dropdown/examples/DropdownBasic.tsx @@ -46,11 +46,14 @@ export const DropdownBasic: React.FunctionComponent = () => { Disabled Link + + Aria-disabled Link + - + Separated Action - ev.preventDefault()}> + ev.preventDefault()}> Separated Link diff --git a/packages/react-core/src/components/Menu/Menu.tsx b/packages/react-core/src/components/Menu/Menu.tsx index e428fed4571..fb6b91c9e00 100644 --- a/packages/react-core/src/components/Menu/Menu.tsx +++ b/packages/react-core/src/components/Menu/Menu.tsx @@ -302,6 +302,8 @@ class MenuBase extends React.Component { (navigableElement?.tagName === 'DIV' && navigableElement.querySelector('input')) || // for MenuSearchInput ((navigableElement.firstChild as Element)?.tagName === 'LABEL' && navigableElement.querySelector('input')) || // for MenuItem checkboxes + ((navigableElement.firstChild as Element)?.tagName === 'DIV' && + navigableElement.querySelector('a, button, input')) || // For aria-disabled element that is rendered inside a div with "display: contents" styling (navigableElement.firstChild as Element) } noHorizontalArrowHandling={ diff --git a/packages/react-core/src/components/Menu/MenuItem.tsx b/packages/react-core/src/components/Menu/MenuItem.tsx index f4c48ba2368..6791897b237 100644 --- a/packages/react-core/src/components/Menu/MenuItem.tsx +++ b/packages/react-core/src/components/Menu/MenuItem.tsx @@ -11,6 +11,7 @@ import CheckIcon from '@patternfly/react-icons/dist/esm/icons/check-icon'; import { Checkbox } from '../Checkbox'; import { MenuContext, MenuItemContext } from './MenuContext'; import { MenuItemAction } from './MenuItemAction'; +import { Tooltip, TooltipProps } from '../Tooltip'; import { canUseDOM } from '../../helpers/util'; import { useIsomorphicLayoutEffect } from '../../helpers/useIsomorphicLayout'; import { GenerateId } from '../../helpers/GenerateId/GenerateId'; @@ -40,6 +41,10 @@ export interface MenuItemProps extends Omit, 'onC component?: React.ElementType | React.ComponentType; /** Render item as disabled option */ isDisabled?: boolean; + /** Render item as aria-disabled option */ + isAriaDisabled?: boolean; + /** Props for adding a tooltip to a menu item */ + tooltipProps?: TooltipProps; /** Render item with icon */ icon?: React.ReactNode; /** Render item with one or more actions */ @@ -94,6 +99,7 @@ const MenuItemBase: React.FunctionComponent = ({ onClick = () => {}, component = 'button', isDisabled = false, + isAriaDisabled = false, isExternalLink = false, isSelected = null, isFocused, @@ -106,6 +112,7 @@ const MenuItemBase: React.FunctionComponent = ({ innerRef, id, 'aria-label': ariaLabel, + tooltipProps, ...props }: MenuItemProps) => { const { @@ -225,10 +232,12 @@ const MenuItemBase: React.FunctionComponent = ({ }; const onItemSelect = (event: any, onSelect: any) => { - // Trigger callback for Menu onSelect - onSelect && onSelect(event, itemId); - // Trigger callback for item onClick - onClick && onClick(event); + if (!isAriaDisabled) { + // Trigger callback for Menu onSelect + onSelect && onSelect(event, itemId); + // Trigger callback for item onClick + onClick && onClick(event); + } }; const _isOnPath = (isOnPath && isOnPath) || (drilldownItemPath && drilldownItemPath.includes(itemId)) || false; let drill: (event: React.KeyboardEvent | React.MouseEvent) => void; @@ -252,16 +261,18 @@ const MenuItemBase: React.FunctionComponent = ({ if (Component === 'a') { additionalProps = { href: to, - 'aria-disabled': isDisabled ? true : null, + 'aria-disabled': isDisabled || isAriaDisabled ? true : null, // prevent invalid 'disabled' attribute on tags disabled: null, target: isExternalLink ? '_blank' : null }; } else if (Component === 'button') { additionalProps = { - type: 'button' + type: 'button', + 'aria-disabled': isAriaDisabled ? true : null }; } + if (isOnPath) { additionalProps['aria-expanded'] = true; } else if (hasFlyout) { @@ -300,25 +311,8 @@ const MenuItemBase: React.FunctionComponent = ({ }; const isSelectMenu = menuRole === 'listbox'; - return ( -
  • + const renderItem = ( + <> {(randomId) => ( = ({ ref={innerRef} {...(!hasCheckbox && { onClick: (event: React.KeyboardEvent | React.MouseEvent) => { - onItemSelect(event, onSelect); - drill && drill(event); - flyoutMenu && handleFlyout(event); + if (!isAriaDisabled) { + onItemSelect(event, onSelect); + drill && drill(event); + flyoutMenu && handleFlyout(event); + } else { + event.preventDefault(); + } } })} {...(hasCheckbox && { htmlFor: randomId })} @@ -355,6 +353,7 @@ const MenuItemBase: React.FunctionComponent = ({ isChecked={isSelected || false} onChange={(event) => onItemSelect(event, onSelect)} isDisabled={isDisabled} + aria-disabled={isAriaDisabled} /> )} @@ -402,6 +401,40 @@ const MenuItemBase: React.FunctionComponent = ({ /> )} + + ); + + return ( +
  • { + if (!isAriaDisabled) { + onMouseOver(); + } + }} + {...(flyoutMenu && !isAriaDisabled && { onKeyDown: handleFlyout })} + ref={ref} + role={!hasCheckbox ? 'none' : 'menuitem'} + {...(hasCheckbox && { 'aria-label': ariaLabel })} + {...props} + > + {tooltipProps ? ( + + {renderItem} + + ) : ( + renderItem + )}
  • ); }; diff --git a/packages/react-core/src/components/Menu/examples/Menu.md b/packages/react-core/src/components/Menu/examples/Menu.md index 26f035f7a47..57c1b9486a5 100644 --- a/packages/react-core/src/components/Menu/examples/Menu.md +++ b/packages/react-core/src/components/Menu/examples/Menu.md @@ -14,7 +14,8 @@ propComponents: 'MenuSearchInput', 'MenuGroup', 'MenuContainer', - 'MenuPopperProps' + 'MenuPopperProps', + 'TooltipProps' ] ouia: true --- diff --git a/packages/react-core/src/components/Menu/examples/MenuBasic.tsx b/packages/react-core/src/components/Menu/examples/MenuBasic.tsx index be87ab6143d..b615617f658 100644 --- a/packages/react-core/src/components/Menu/examples/MenuBasic.tsx +++ b/packages/react-core/src/components/Menu/examples/MenuBasic.tsx @@ -33,6 +33,12 @@ export const MenuBasic: React.FunctionComponent = () => { Disabled link + + Aria-disabled action + + + Aria-disabled link + diff --git a/packages/react-table/src/components/Table/ActionsColumn.tsx b/packages/react-table/src/components/Table/ActionsColumn.tsx index c2004b7f2e9..bf67032d406 100644 --- a/packages/react-table/src/components/Table/ActionsColumn.tsx +++ b/packages/react-table/src/components/Table/ActionsColumn.tsx @@ -114,7 +114,7 @@ const ActionsColumnBase: React.FunctionComponent = ({ {items .filter((item) => !item.isOutsideDropdown) - .map(({ title, itemKey, onClick, tooltip, tooltipProps, isSeparator, ...props }, index) => { + .map(({ title, itemKey, onClick, tooltipProps, isSeparator, ...props }, index) => { if (isSeparator) { return ; } @@ -133,9 +133,9 @@ const ActionsColumnBase: React.FunctionComponent = ({
    ); - if (tooltip) { + if (tooltipProps?.content) { return ( - + {item} ); diff --git a/packages/react-table/src/components/Table/TableTypes.tsx b/packages/react-table/src/components/Table/TableTypes.tsx index 18cb66faebd..0a03be2b382 100644 --- a/packages/react-table/src/components/Table/TableTypes.tsx +++ b/packages/react-table/src/components/Table/TableTypes.tsx @@ -5,6 +5,7 @@ import { DropdownDirection, DropdownPosition } from '@patternfly/react-core/dist import * as React from 'react'; import { CustomActionsToggleProps } from './ActionsColumn'; import { ButtonProps } from '@patternfly/react-core/dist/esm/components/Button'; +import { TooltipProps } from '@patternfly/react-core/dist/esm/components/Tooltip'; export enum TableGridBreakpoint { none = '', @@ -154,10 +155,10 @@ export interface IAction extends Omit, P itemKey?: string; /** Content to display in the actions menu item */ title?: string | React.ReactNode; - /** Tooltip to display when hovered over the item */ - tooltip?: React.ReactNode; - /** Additional props forwarded to the tooltip component */ - tooltipProps?: any; + /** Render item as aria-disabled option */ + isAriaDisabled?: boolean; + /** Props for adding a tooltip to a menu item. This is used to display tooltip when hovered over the item */ + tooltipProps?: TooltipProps; /** Click handler for the actions menu item */ onClick?: (event: React.MouseEvent, rowIndex: number, rowData: IRowData, extraData: IExtraData) => void; /** Flag indicating this action should be placed outside the actions menu, beside the toggle */ diff --git a/packages/react-table/src/components/Table/examples/Table.md b/packages/react-table/src/components/Table/examples/Table.md index 745d166b786..4cb3c7875d5 100644 --- a/packages/react-table/src/components/Table/examples/Table.md +++ b/packages/react-table/src/components/Table/examples/Table.md @@ -17,7 +17,6 @@ propComponents: 'ThSelectType', 'TdTreeRowType', 'ActionsColumn', - 'IActions', 'TdCompoundExpandType', 'TdFavoritesType', 'TdDraggableType',