Skip to content

Commit a8a0c17

Browse files
committedJul 19, 2023
[fix] Pressable button click regression
Update keyboard handling code to avoid calling preventDefault for 'keydown' events occuring on a native button element. This was causing native 'click' event to be cancelled on buttons. Fix #2560
1 parent 3babcc4 commit a8a0c17

File tree

2 files changed

+46
-9
lines changed

2 files changed

+46
-9
lines changed
 

‎packages/react-native-web/src/exports/Pressable/__tests__/index-test.js

+29
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,35 @@ describe('components/Pressable', () => {
246246
expect(container.firstChild).toMatchSnapshot();
247247
});
248248

249+
test('press interaction as button (keyboard)', () => {
250+
const onPress = jest.fn();
251+
const preventDefault = jest.fn();
252+
const ref = React.createRef();
253+
254+
function TestCase() {
255+
return (
256+
<Pressable
257+
onPress={(e) => {
258+
onPress(e);
259+
}}
260+
ref={ref}
261+
role="button"
262+
/>
263+
);
264+
}
265+
266+
act(() => {
267+
render(<TestCase />);
268+
});
269+
const target = createEventTarget(ref.current);
270+
act(() => {
271+
target.keydown({ key: ' ', preventDefault });
272+
jest.runAllTimers();
273+
});
274+
// Calling preventDefault prevents native 'click' event dispatch
275+
expect(preventDefault).not.toHaveBeenCalled();
276+
});
277+
249278
describe('prop "ref"', () => {
250279
test('value is set', () => {
251280
const ref = jest.fn();

‎packages/react-native-web/src/modules/usePressEvents/PressResponder.js

+17-9
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,15 @@ const Transitions = Object.freeze({
116116
}
117117
});
118118

119+
const getElementRole = (element) => element.getAttribute('role');
120+
121+
const getElementType = (element) => element.tagName.toLowerCase();
122+
119123
const isActiveSignal = (signal) =>
120124
signal === RESPONDER_ACTIVE_PRESS_START ||
121125
signal === RESPONDER_ACTIVE_LONG_PRESS_START;
122126

123-
const isButtonRole = (element) => element.getAttribute('role') === 'button';
127+
const isButtonRole = (element) => getElementRole(element) === 'button';
124128

125129
const isPressStartSignal = (signal) =>
126130
signal === RESPONDER_INACTIVE_PRESS_START ||
@@ -132,10 +136,10 @@ const isTerminalSignal = (signal) =>
132136

133137
const isValidKeyPress = (event) => {
134138
const { key, target } = event;
135-
const role = target.getAttribute('role');
136139
const isSpacebar = key === ' ' || key === 'Spacebar';
137-
138-
return key === 'Enter' || (isSpacebar && role === 'button');
140+
const isButtonish =
141+
getElementType(target) === 'button' || isButtonRole(target);
142+
return key === 'Enter' || (isSpacebar && isButtonish);
139143
};
140144

141145
const DEFAULT_LONG_PRESS_DELAY_MS = 450; // 500 - 50
@@ -307,7 +311,7 @@ export default class PressResponder {
307311
document.removeEventListener('keyup', keyupHandler);
308312

309313
const role = target.getAttribute('role');
310-
const elementType = target.tagName.toLowerCase();
314+
const elementType = getElementType(target);
311315

312316
const isNativeInteractiveElement =
313317
role === 'link' ||
@@ -345,11 +349,15 @@ export default class PressResponder {
345349
// focus is moved to another element during 'keydown'.
346350
document.addEventListener('keyup', keyupHandler);
347351
}
348-
const role = target.getAttribute('role');
349352
const isSpacebarKey = key === ' ' || key === 'Spacebar';
350-
const isButtonRole = role === 'button' || role === 'menuitem';
351-
if (isSpacebarKey && isButtonRole) {
352-
// Prevent spacebar scrolling the window
353+
const role = getElementRole(target);
354+
const isButtonLikeRole = role === 'button' || role === 'menuitem';
355+
if (
356+
isSpacebarKey &&
357+
isButtonLikeRole &&
358+
getElementType(target) !== 'button'
359+
) {
360+
// Prevent spacebar scrolling the window if using non-native button
353361
event.preventDefault();
354362
}
355363
event.stopPropagation();

0 commit comments

Comments
 (0)
Please sign in to comment.