Event API: ensure preventDefault works for nested targets (#15633)

This commit is contained in:
Dominic Gannaway
2019-05-13 13:45:48 +01:00
committed by GitHub
parent edfedf3ae9
commit fec74f99da
4 changed files with 46 additions and 5 deletions
@@ -208,6 +208,7 @@ const eventResponderContext: ReactResponderContext = {
childTarget: Element | Document,
parentTarget: Element | Document,
): boolean {
validateResponderContext();
const childFiber = getClosestInstanceFromNode(childTarget);
const parentFiber = getClosestInstanceFromNode(parentTarget);
@@ -345,6 +346,7 @@ const eventResponderContext: ReactResponderContext = {
}
},
getFocusableElementsInScope(): Array<HTMLElement> {
validateResponderContext();
const focusableElements = [];
const eventComponentInstance = ((currentInstance: any): ReactEventComponentInstance);
let node = ((eventComponentInstance.currentFiber: any): Fiber).child;
@@ -383,6 +385,7 @@ const eventResponderContext: ReactResponderContext = {
getEventPointerType(
event: ReactResponderEvent,
): '' | 'mouse' | 'keyboard' | 'pen' | 'touch' {
validateResponderContext();
const nativeEvent: any = event.nativeEvent;
const {type, pointerType} = nativeEvent;
if (pointerType != null) {
@@ -400,6 +403,7 @@ const eventResponderContext: ReactResponderContext = {
return '';
},
getEventCurrentTarget(event: ReactResponderEvent): Element {
validateResponderContext();
const target: any = event.target;
let currentTarget = target;
while (
@@ -412,8 +416,26 @@ const eventResponderContext: ReactResponderContext = {
return currentTarget;
},
getTimeStamp(): number {
validateResponderContext();
return currentTimeStamp;
},
isTargetWithinHostComponent(
target: Element | Document,
elementType: string,
): boolean {
validateResponderContext();
let fiber = getClosestInstanceFromNode(target);
while (fiber !== null) {
if (fiber.stateNode === currentInstance) {
return false;
}
if (fiber.tag === HostComponent && fiber.type === elementType) {
return true;
}
fiber = fiber.return;
}
return false;
},
};
function isTargetWithinEventComponent(target: Element | Document): boolean {
+1 -5
View File
@@ -418,10 +418,6 @@ function dispatchCancel(
}
}
function isAnchorTagElement(eventTarget: EventTarget): boolean {
return (eventTarget: any).nodeName === 'A';
}
function isValidKeyPress(key: string): boolean {
// Accessibility for keyboards. Space and Enter only.
return key === ' ' || key === 'Enter';
@@ -673,7 +669,7 @@ const PressResponder = {
}
case 'click': {
if (isAnchorTagElement(target)) {
if (context.isTargetWithinHostComponent(target, 'a')) {
const {
altKey,
ctrlKey,
@@ -1965,6 +1965,25 @@ describe('Event responder: Press', () => {
expect(preventDefault).toBeCalled();
});
it('prevents native behaviour by default with nested elements', () => {
const onPress = jest.fn();
const preventDefault = jest.fn();
const ref = React.createRef();
const element = (
<Press onPress={onPress}>
<a href="#">
<div ref={ref} />
</a>
</Press>
);
ReactDOM.render(element, container);
ref.current.dispatchEvent(createEvent('pointerdown'));
ref.current.dispatchEvent(createEvent('pointerup'));
ref.current.dispatchEvent(createEvent('click', {preventDefault}));
expect(preventDefault).toBeCalled();
});
it('uses native behaviour for interactions with modifier keys', () => {
const onPress = jest.fn();
const preventDefault = jest.fn();
+4
View File
@@ -194,4 +194,8 @@ export type ReactResponderContext = {
): '' | 'mouse' | 'keyboard' | 'pen' | 'touch',
getEventCurrentTarget(event: ReactResponderEvent): Element,
getTimeStamp: () => number,
isTargetWithinHostComponent: (
target: Element | Document,
elementType: string,
) => boolean,
};