diff --git a/packages/react/src/Button/ButtonBase.tsx b/packages/react/src/Button/ButtonBase.tsx index 6303ffc5e5c..bc2c02db9cf 100644 --- a/packages/react/src/Button/ButtonBase.tsx +++ b/packages/react/src/Button/ButtonBase.tsx @@ -196,11 +196,11 @@ const ButtonBase = forwardRef( )} - {loading && ( + {loading ? ( {loadingAnnouncement} - )} + ) : null} ) } @@ -228,7 +228,7 @@ const ButtonBase = forwardRef( data-variant={variant} data-label-wrap={labelWrap} data-has-count={count !== undefined ? true : undefined} - aria-describedby={[loadingAnnouncementID, ariaDescribedBy] + aria-describedby={[loading && loadingAnnouncementID, ariaDescribedBy] .filter(descriptionID => Boolean(descriptionID)) .join(' ')} // aria-labelledby is needed because the accessible name becomes unset when the button is in a loading state. diff --git a/packages/react/src/Button/IconButton.features.stories.tsx b/packages/react/src/Button/IconButton.features.stories.tsx index 75050d7c805..8175025b5b4 100644 --- a/packages/react/src/Button/IconButton.features.stories.tsx +++ b/packages/react/src/Button/IconButton.features.stories.tsx @@ -91,7 +91,10 @@ export const KeybindingHintOnDescription = () => ( aria-label="Notifications" description="You have unread notifications" keybindingHint="G+N" + keybindingDescription="G, N" /> ) -export const KeybindingHint = () => +export const KeybindingHint = () => ( + +) diff --git a/packages/react/src/Button/IconButton.tsx b/packages/react/src/Button/IconButton.tsx index 4b86ea1bd48..dbdbf86eb54 100644 --- a/packages/react/src/Button/IconButton.tsx +++ b/packages/react/src/Button/IconButton.tsx @@ -22,6 +22,7 @@ const IconButton = forwardRef( unsafeDisableTooltip = false, keyshortcuts, keybindingHint, + keybindingDescription, className, ...props }, @@ -67,6 +68,7 @@ const IconButton = forwardRef( type={description ? undefined : 'label'} direction={tooltipDirection} keybindingHint={keybindingHint ?? keyshortcuts} + keybindingDescription={keybindingDescription} > // adopted from React.AnchorHTMLAttributes diff --git a/packages/react/src/KeybindingHint/props.ts b/packages/react/src/KeybindingHint/props.ts index 7df8f7ebd21..f3ca3003975 100644 --- a/packages/react/src/KeybindingHint/props.ts +++ b/packages/react/src/KeybindingHint/props.ts @@ -3,6 +3,8 @@ export type KeybindingHintFormat = 'condensed' | 'full' export type KeybindingHintVariant = 'normal' | 'onEmphasis' | 'onPrimary' export interface KeybindingHintProps { + description?: string + /** * The keys involved in this keybinding. These should be the full names of the keys as would * be returned by `KeyboardEvent.key` (e.g. "Control", "Shift", "ArrowUp", "a", etc.). diff --git a/packages/react/src/TooltipV2/Tooltip.tsx b/packages/react/src/TooltipV2/Tooltip.tsx index 2949dc434d5..28cbb8f91e6 100644 --- a/packages/react/src/TooltipV2/Tooltip.tsx +++ b/packages/react/src/TooltipV2/Tooltip.tsx @@ -142,6 +142,7 @@ export type TooltipProps = React.PropsWithChildren< text: string type?: 'label' | 'description' keybindingHint?: KeybindingHintProps['keys'] + keybindingDescription?: KeybindingHintProps['description'] } & SxProp > & React.HTMLAttributes @@ -201,10 +202,21 @@ export const TooltipContext = React.createContext<{tooltipId?: string}>({}) export const Tooltip = React.forwardRef( ( - {direction = 's', text, type = 'description', children, id, className, keybindingHint, ...rest}: TooltipProps, + { + direction = 's', + text, + type = 'description', + children, + id, + className, + keybindingHint, + keybindingDescription, + ...rest + }: TooltipProps, forwardedRef, ) => { const tooltipId = useId(id) + const controlHintId = useId() const child = Children.only(children) const triggerRef = useProvidedRefOrCreate(forwardedRef as React.RefObject) const tooltipElRef = useRef(null) @@ -355,7 +367,10 @@ export const Tooltip = React.forwardRef( React.cloneElement(child as React.ReactElement, { ref: triggerRef, // If it is a type description, we use tooltip to describe the trigger - 'aria-describedby': type === 'description' ? tooltipId : child.props['aria-describedby'], + 'aria-describedby': + type === 'description' + ? [tooltipId, controlHintId].join(' ') + : [child.props['aria-describedby'], controlHintId].filter(Boolean).join(' '), // If it is a label type, we use tooltip to label the trigger 'aria-labelledby': type === 'label' ? tooltipId : child.props['aria-labelledby'], onBlur: (event: React.FocusEvent) => { @@ -400,19 +415,21 @@ export const Tooltip = React.forwardRef( role={type === 'description' ? 'tooltip' : undefined} // stop AT from announcing the tooltip twice: when it is a label type it will be announced with "aria-labelledby",when it is a description type it will be announced with "aria-describedby" aria-hidden={true} - id={tooltipId} // mouse leave and enter on the tooltip itself is needed to keep the tooltip open when the mouse is over the tooltip onMouseEnter={openTooltip} onMouseLeave={closeTooltip} > - {text} - {keybindingHint && ( + {text} + {keybindingHint ? ( ( ) - )} + ) : null} + {keybindingDescription ? ( + . {keybindingDescription} + ) : null}