diff --git a/src/Tooltip/index.tsx b/src/Tooltip/index.tsx index 2716ac5caf5..bd92e1317aa 100644 --- a/src/Tooltip/index.tsx +++ b/src/Tooltip/index.tsx @@ -2,7 +2,7 @@ import React from 'react' import {useSSRSafeId} from '@react-aria/ssr' import type {AnchorPosition, AnchorSide, AnchorAlignment} from '@primer/behaviors' import Box from '../Box' -import {useAnchoredPosition} from '../hooks' +import {useAnchoredPosition, useProvidedRefOrCreate} from '../hooks' import {SxProp, merge, BetterSystemStyleObject} from '../sx' type TooltipDirection = 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' @@ -21,7 +21,7 @@ export type TooltipProps = { /** Use aria-describedby or aria-labelledby */ type?: 'description' | 'label' /** Tooltip target */ - children: React.ReactElement + children: React.ReactElement & {ref?: React.RefObject} /** When set to true, tooltip appears without any delay */ noDelay?: boolean /** @deprecated Always set to true now. */ @@ -55,8 +55,16 @@ export const Tooltip: React.FC = ({ sx = {}, ...props }) => { + const tooltipId = useSSRSafeId() + + const childRef = children.ref + const anchorElementRef = useProvidedRefOrCreate(childRef) const tooltipRef = React.useRef(null) - const anchorElementRef = React.useRef(null) + + const child = React.cloneElement(children, { + ref: anchorElementRef, + [type === 'description' ? 'aria-describedby' : 'aria-labelledby']: tooltipId + }) const {position} = useAnchoredPosition({ side: directionToPosition[direction].side, @@ -66,13 +74,6 @@ export const Tooltip: React.FC = ({ anchorElementRef }) - const tooltipId = useSSRSafeId() - - const child = React.cloneElement(children, { - ref: anchorElementRef, - [type === 'description' ? 'aria-describedby' : 'aria-labelledby']: tooltipId - }) - const tooltipText = text || props['aria-label'] return ( diff --git a/src/__tests__/ActionMenu.test.tsx b/src/__tests__/ActionMenu.test.tsx index 7aa30c560ec..29fee6e3066 100644 --- a/src/__tests__/ActionMenu.test.tsx +++ b/src/__tests__/ActionMenu.test.tsx @@ -3,10 +3,11 @@ import 'babel-polyfill' import {axe, toHaveNoViolations} from 'jest-axe' import React from 'react' import theme from '../theme' -import {ActionMenu, ActionList, BaseStyles, ThemeProvider, SSRProvider} from '..' +import {ActionMenu, ActionList, BaseStyles, ThemeProvider, SSRProvider, IconButton} from '..' import {behavesAsComponent, checkExports, checkStoriesForAxeViolations} from '../utils/testing' import {SingleSelection, MixedSelection} from '../stories/ActionMenu/examples.stories' import '@testing-library/jest-dom' +import {TriangleDownIcon} from '@primer/octicons-react' expect.extend(toHaveNoViolations) function Example(): JSX.Element { @@ -136,6 +137,32 @@ describe('ActionMenu', () => { cleanup() }) + it('should open Menu on MenuAnchor click with IconButton', async () => { + const component = HTMLRender( + + + + + + + + + + New file + Copy link + + + + + + + ) + const button = component.getByLabelText('Toggle Menu') + fireEvent.click(button) + expect(component.getByRole('menu')).toBeInTheDocument() + cleanup() + }) + it('should have no axe violations', async () => { const {container} = HTMLRender() const results = await axe(container)