Skip to content

Commit ab0323a

Browse files
authored
Making disable text selection in usePress only occur on iOS (#2440)
* making disable text selection in usePress only occur on iOS * whoops need to actually call it * making user-select:none only apply for iOS * fixing useMove tests
1 parent faeb53e commit ab0323a

File tree

3 files changed

+68
-7
lines changed

3 files changed

+68
-7
lines changed

packages/@react-aria/interactions/src/textSelection.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
import {runAfterTransition} from '@react-aria/utils';
13+
import {isIOS, runAfterTransition} from '@react-aria/utils';
1414

1515
// Safari on iOS starts selecting text on long press. The only way to avoid this, it seems,
1616
// is to add user-select: none to the entire page. Adding it to the pressable element prevents
@@ -27,6 +27,12 @@ let state: State = 'default';
2727
let savedUserSelect = '';
2828

2929
export function disableTextSelection() {
30+
// Limit this behavior to iOS only. Android devices don't text select nearby element
31+
// when long pressing on a different element.
32+
if (!isIOS()) {
33+
return;
34+
}
35+
3036
if (state === 'default') {
3137
savedUserSelect = document.documentElement.style.webkitUserSelect;
3238
document.documentElement.style.webkitUserSelect = 'none';
@@ -38,7 +44,9 @@ export function disableTextSelection() {
3844
export function restoreTextSelection() {
3945
// If the state is already default, there's nothing to do.
4046
// If it is restoring, then there's no need to queue a second restore.
41-
if (state !== 'disabled') {
47+
// Limit this behavior to iOS only. Android devices don't text select nearby element
48+
// when long pressing on a different element.
49+
if (state !== 'disabled' || !isIOS()) {
4250
return;
4351
}
4452

packages/@react-aria/interactions/test/useMove.test.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,15 +183,25 @@ describe('useMove', function () {
183183
describe('user-select: none', () => {
184184
let mockUserSelect = 'contain';
185185
let oldUserSelect = document.documentElement.style.webkitUserSelect;
186+
let platformGetter;
187+
188+
beforeAll(() => {
189+
platformGetter = jest.spyOn(window.navigator, 'platform', 'get');
190+
});
191+
192+
afterAll(() => {
193+
jest.restoreAllMocks();
194+
});
186195

187196
beforeEach(() => {
188197
document.documentElement.style.webkitUserSelect = mockUserSelect;
198+
platformGetter.mockReturnValue('iPhone');
189199
});
190200
afterEach(() => {
191201
document.documentElement.style.webkitUserSelect = oldUserSelect;
192202
});
193203

194-
it('adds and removes user-select: none to the body', function () {
204+
it('adds and removes user-select: none to the body (iOS)', function () {
195205
let tree = render(
196206
<Example
197207
onMoveStart={() => {}}
@@ -211,6 +221,26 @@ describe('useMove', function () {
211221
act(() => {jest.advanceTimersByTime(300);});
212222
expect(document.documentElement.style.webkitUserSelect).toBe(mockUserSelect);
213223
});
224+
225+
it('doesn\'t add user-select: none to the body (non-iOS)', function () {
226+
platformGetter.mockReturnValue('Android');
227+
let tree = render(
228+
<Example
229+
onMoveStart={() => {}}
230+
onMove={() => {}}
231+
onMoveEnd={() => {}} />
232+
);
233+
234+
let el = tree.getByTestId(EXAMPLE_ELEMENT_TESTID);
235+
236+
expect(document.documentElement.style.webkitUserSelect).toBe(mockUserSelect);
237+
fireEvent.touchStart(el, {changedTouches: [{identifier: 1, pageX: 1, pageY: 30}]});
238+
expect(document.documentElement.style.webkitUserSelect).toBe(mockUserSelect);
239+
fireEvent.touchMove(el, {changedTouches: [{identifier: 1, pageX: 10, pageY: 25}]});
240+
expect(document.documentElement.style.webkitUserSelect).toBe(mockUserSelect);
241+
fireEvent.touchEnd(el, {changedTouches: [{identifier: 1, pageX: 10, pageY: 25}]});
242+
expect(document.documentElement.style.webkitUserSelect).toBe(mockUserSelect);
243+
});
214244
});
215245

216246
it('doesn\'t bubble to useMove on parent elements', function () {

packages/@react-aria/interactions/test/usePress.test.js

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2194,19 +2194,26 @@ describe('usePress', function () {
21942194
let handler = jest.fn();
21952195
let mockUserSelect = 'contain';
21962196
let oldUserSelect = document.documentElement.style.webkitUserSelect;
2197+
let platformGetter;
2198+
2199+
beforeAll(() => {
2200+
platformGetter = jest.spyOn(window.navigator, 'platform', 'get');
2201+
});
21972202

21982203
afterAll(() => {
21992204
handler.mockClear();
2205+
jest.restoreAllMocks();
22002206
});
22012207

22022208
beforeEach(() => {
22032209
document.documentElement.style.webkitUserSelect = mockUserSelect;
2210+
platformGetter.mockReturnValue('iPhone');
22042211
});
22052212
afterEach(() => {
22062213
document.documentElement.style.webkitUserSelect = oldUserSelect;
22072214
});
22082215

2209-
it('should add user-select: none to html element when press start', function () {
2216+
it('should add user-select: none to html element when press start (iOS)', function () {
22102217
let {getByText} = render(
22112218
<Example
22122219
onPressStart={handler}
@@ -2221,7 +2228,23 @@ describe('usePress', function () {
22212228
expect(document.documentElement.style.webkitUserSelect).toBe('none');
22222229
});
22232230

2224-
it('should remove user-select: none to html element when press end', function () {
2231+
it('should not add user-select: none to html element when press start (non-iOS)', function () {
2232+
platformGetter.mockReturnValue('Android');
2233+
let {getByText} = render(
2234+
<Example
2235+
onPressStart={handler}
2236+
onPressEnd={handler}
2237+
onPressChange={handler}
2238+
onPress={handler}
2239+
onPressUp={handler} />
2240+
);
2241+
2242+
let el = getByText('test');
2243+
fireEvent.touchStart(el, {targetTouches: [{identifier: 1}]});
2244+
expect(document.documentElement.style.webkitUserSelect).toBe(mockUserSelect);
2245+
});
2246+
2247+
it('should remove user-select: none to html element when press end (iOS)', function () {
22252248
let {getByText} = render(
22262249
<Example
22272250
onPressStart={handler}
@@ -2252,7 +2275,7 @@ describe('usePress', function () {
22522275
expect(document.documentElement.style.webkitUserSelect).toBe(mockUserSelect);
22532276
});
22542277

2255-
it('should not remove user-select: none when pressing two different elements quickly', function () {
2278+
it('should not remove user-select: none when pressing two different elements quickly (iOS)', function () {
22562279
let {getAllByText} = render(
22572280
<>
22582281
<Example
@@ -2288,7 +2311,7 @@ describe('usePress', function () {
22882311
expect(document.documentElement.style.webkitUserSelect).toBe(mockUserSelect);
22892312
});
22902313

2291-
it('should remove user-select: none from html element if pressable component unmounts', function () {
2314+
it('should remove user-select: none from html element if pressable component unmounts (iOS)', function () {
22922315
let {getByText, unmount} = render(
22932316
<Example
22942317
onPressStart={handler}

0 commit comments

Comments
 (0)