Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tooltip] Long press select text on iOS #23466

Merged
merged 14 commits into from
Nov 12, 2020
24 changes: 20 additions & 4 deletions packages/material-ui/src/Tooltip/Tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,14 @@ export const styles = (theme) => ({
padding: '4px 8px',
fontSize: theme.typography.pxToRem(11),
maxWidth: 300,
margin: 2,
wordWrap: 'break-word',
fontWeight: theme.typography.fontWeightMedium,
},
/* Styles applied to the tooltip (label wrapper) element if `arrow={true}`. */
tooltipArrow: {
position: 'relative',
margin: '0',
margin: 0,
},
/* Styles applied to the arrow element. */
arrow: {
Expand Down Expand Up @@ -240,14 +241,23 @@ const Tooltip = React.forwardRef(function Tooltip(props, ref) {

const id = useId(idProp);

const prevUserSelect = React.useRef();
const stopTouchInteraction = React.useCallback(() => {
if (prevUserSelect.current !== undefined) {
document.body.style.WebkitUserSelect = prevUserSelect.current;
prevUserSelect.current = undefined;
}
clearTimeout(touchTimer.current);
}, []);

React.useEffect(() => {
return () => {
clearTimeout(closeTimer.current);
clearTimeout(enterTimer.current);
clearTimeout(leaveTimer.current);
clearTimeout(touchTimer.current);
stopTouchInteraction();
};
}, []);
}, [stopTouchInteraction]);

const handleOpen = (event) => {
clearTimeout(hystersisTimer);
Expand Down Expand Up @@ -369,9 +379,15 @@ const Tooltip = React.forwardRef(function Tooltip(props, ref) {
detectTouchStart(event);
clearTimeout(leaveTimer.current);
clearTimeout(closeTimer.current);
clearTimeout(touchTimer.current);
stopTouchInteraction();
event.persist();

prevUserSelect.current = document.body.style.WebkitUserSelect;
// Prevent iOS text selection on long-tap.
document.body.style.WebkitUserSelect = 'none';

touchTimer.current = setTimeout(() => {
oliviertassinari marked this conversation as resolved.
Show resolved Hide resolved
document.body.style.WebkitUserSelect = prevUserSelect.current;
handleEnter(event);
}, enterTouchDelay);
};
Expand Down
70 changes: 67 additions & 3 deletions packages/material-ui/src/Tooltip/Tooltip.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -410,9 +410,7 @@ describe('<Tooltip />', () => {
title="Hello World"
TransitionProps={{ timeout: transitionTimeout }}
>
<button id="testChild" type="submit">
Hello World
</button>
<button type="submit">Hello World</button>
</Tooltip>,
);
act(() => {
Expand Down Expand Up @@ -1083,4 +1081,70 @@ describe('<Tooltip />', () => {
}
});
});

describe('user-select state', () => {
let prevWebkitUserSelect;
beforeEach(() => {
prevWebkitUserSelect = document.body.style.WebkitUserSelect;
});

afterEach(() => {
document.body.style.WebkitUserSelect = prevWebkitUserSelect;
});

it('prevents text-selection during touch-longpress', () => {
const enterTouchDelay = 700;
const enterDelay = 100;
const leaveTouchDelay = 1500;
const transitionTimeout = 10;
const { getByRole } = render(
<Tooltip
enterTouchDelay={enterTouchDelay}
enterDelay={enterDelay}
leaveTouchDelay={leaveTouchDelay}
title="Hello World"
TransitionProps={{ timeout: transitionTimeout }}
>
<button type="submit">Hello World</button>
</Tooltip>,
);
document.body.style.WebkitUserSelect = 'revert';

fireEvent.touchStart(getByRole('button'));

expect(document.body.style.WebkitUserSelect).to.equal('none');

act(() => {
clock.tick(enterTouchDelay + enterDelay);
});
expect(document.body.style.WebkitUserSelect.toLowerCase()).to.equal('revert');
});

it('restores user-select when unmounted during longpress', () => {
const enterTouchDelay = 700;
const enterDelay = 100;
const leaveTouchDelay = 1500;
const transitionTimeout = 10;
const { unmount, getByRole } = render(
<Tooltip
enterTouchDelay={enterTouchDelay}
enterDelay={enterDelay}
leaveTouchDelay={leaveTouchDelay}
title="Hello World"
TransitionProps={{ timeout: transitionTimeout }}
>
<button type="submit">Hello World</button>
</Tooltip>,
);

document.body.style.WebkitUserSelect = 'revert';
// Let updates flush before unmounting
act(() => {
fireEvent.touchStart(getByRole('button'));
});
unmount();

expect(document.body.style.WebkitUserSelect.toLowerCase()).to.equal('revert');
});
});
});