Skip to content

Commit

Permalink
[Tooltip] Meet dismissable WCAG criterion (#22376)
Browse files Browse the repository at this point in the history
  • Loading branch information
eps1lon authored Aug 27, 2020
1 parent a7216e5 commit 90bffd2
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 17 deletions.
4 changes: 2 additions & 2 deletions packages/material-ui/src/Tooltip/Tooltip.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,13 @@ export interface TooltipProps extends StandardProps<React.HTMLAttributes<HTMLDiv
*
* @param {object} event The event source of the callback.
*/
onClose?: (event: React.ChangeEvent<{}>) => void;
onClose?: (event: React.SyntheticEvent | Event) => void;
/**
* Callback fired when the component requests to be open.
*
* @param {object} event The event source of the callback.
*/
onOpen?: (event: React.ChangeEvent<{}>) => void;
onOpen?: (event: React.SyntheticEvent) => void;
/**
* If `true`, the tooltip is shown.
*/
Expand Down
58 changes: 43 additions & 15 deletions packages/material-ui/src/Tooltip/Tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import withStyles from '../styles/withStyles';
import capitalize from '../utils/capitalize';
import Grow from '../Grow';
import Popper from '../Popper';
import useEventCallback from '../utils/useEventCallback';
import useForkRef from '../utils/useForkRef';
import useId from '../utils/useId';
import useIsFocusVisible from '../utils/useIsFocusVisible';
Expand Down Expand Up @@ -326,22 +327,27 @@ const Tooltip = React.forwardRef(function Tooltip(props, ref) {
}
};

const handleClose = (event) => {
clearTimeout(hystersisTimer);
hystersisTimer = setTimeout(() => {
hystersisOpen = false;
}, 800 + leaveDelay);
setOpenState(false);

if (onClose) {
onClose(event);
}
const handleClose = useEventCallback(
/**
* @param {React.SyntheticEvent | Event} event
*/
(event) => {
clearTimeout(hystersisTimer);
hystersisTimer = setTimeout(() => {
hystersisOpen = false;
}, 800 + leaveDelay);
setOpenState(false);

if (onClose) {
onClose(event);
}

clearTimeout(closeTimer.current);
closeTimer.current = setTimeout(() => {
ignoreNonTouchEvents.current = false;
}, theme.transitions.duration.shortest);
};
clearTimeout(closeTimer.current);
closeTimer.current = setTimeout(() => {
ignoreNonTouchEvents.current = false;
}, theme.transitions.duration.shortest);
},
);

const handleLeave = (forward = true) => (event) => {
const childrenProps = children.props;
Expand Down Expand Up @@ -402,6 +408,28 @@ const Tooltip = React.forwardRef(function Tooltip(props, ref) {
}, leaveTouchDelay);
};

React.useEffect(() => {
if (!open) {
return undefined;
}

/**
* @param {KeyboardEvent} nativeEvent
*/
function handleKeyDown(nativeEvent) {
// IE 11, Edge (prior to using Bink?) use 'Esc'
if (nativeEvent.key === 'Escape' || nativeEvent.key === 'Esc') {
handleClose(nativeEvent);
}
}

document.addEventListener('keydown', handleKeyDown);

return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [handleClose, open]);

const handleUseRef = useForkRef(setChildNode, ref);
const handleFocusRef = useForkRef(focusVisibleRef, handleUseRef);
const handleRef = useForkRef(children.ref, handleFocusRef);
Expand Down
26 changes: 26 additions & 0 deletions packages/material-ui/src/Tooltip/Tooltip.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
act,
createClientRender,
fireEvent,
screen,
} from 'test/utils';
import { camelCase } from 'lodash/string';
import Tooltip, { testReset } from './Tooltip';
Expand Down Expand Up @@ -235,6 +236,31 @@ describe('<Tooltip />', () => {
clock.runAll();
});

it('is dismissable by pressing Escape', () => {
const transitionTimeout = 0;
render(
<Tooltip enterDelay={0} TransitionProps={{ timeout: transitionTimeout }} title="Movie quote">
<button autoFocus>Hello, Dave!</button>
</Tooltip>,
);

expect(screen.getByRole('tooltip')).not.toBeInaccessible();

act(() => {
fireEvent.keyDown(
// We don't care about the target. Any Escape should dismiss the tooltip
// eslint-disable-next-line material-ui/disallow-active-element-as-key-event-target
document.activeElement,
{ key: 'Escape' },
);
});
act(() => {
clock.tick(transitionTimeout);
});

expect(screen.queryByRole('tooltip')).to.equal(null);
});

describe('touch screen', () => {
it('should not respond to quick events', () => {
const { getByRole, queryByRole } = render(
Expand Down

0 comments on commit 90bffd2

Please sign in to comment.