mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
Add full TouchHitTarget hit slop (experimental event API) to ReactDOM (#15308)
This commit is contained in:
parent
958b6173fd
commit
4fbbae8afa
|
|
@ -34,7 +34,11 @@ export type ResponderContext = {
|
|||
parentTarget: Element | Document,
|
||||
) => boolean,
|
||||
isTargetWithinEventComponent: (Element | Document) => boolean,
|
||||
isPositionWithinTouchHitTarget: (x: number, y: number) => boolean,
|
||||
isPositionWithinTouchHitTarget: (
|
||||
doc: Document,
|
||||
x: number,
|
||||
y: number,
|
||||
) => boolean,
|
||||
addRootEventTypes: (
|
||||
document: Document,
|
||||
rootEventTypes: Array<ReactEventResponderEventType>,
|
||||
|
|
|
|||
26
packages/react-art/src/ReactARTHostConfig.js
vendored
26
packages/react-art/src/ReactARTHostConfig.js
vendored
|
|
@ -443,15 +443,31 @@ export function handleEventComponent(
|
|||
eventResponder: ReactEventResponder,
|
||||
rootContainerInstance: Container,
|
||||
internalInstanceHandle: Object,
|
||||
) {
|
||||
// TODO: add handleEventComponent implementation
|
||||
): void {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
||||
export function getEventTargetChildElement(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
): null {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
||||
export function handleEventTarget(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
parentInstance: Container,
|
||||
rootContainerInstance: Container,
|
||||
internalInstanceHandle: Object,
|
||||
) {
|
||||
// TODO: add handleEventTarget implementation
|
||||
): boolean {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
||||
export function commitEventTarget(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
instance: Instance,
|
||||
parentInstance: Instance,
|
||||
): void {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
|
|
|||
134
packages/react-dom/src/client/ReactDOMHostConfig.js
vendored
134
packages/react-dom/src/client/ReactDOMHostConfig.js
vendored
|
|
@ -33,7 +33,7 @@ import {
|
|||
isEnabled as ReactBrowserEventEmitterIsEnabled,
|
||||
setEnabled as ReactBrowserEventEmitterSetEnabled,
|
||||
} from '../events/ReactBrowserEventEmitter';
|
||||
import {getChildNamespace} from '../shared/DOMNamespaces';
|
||||
import {Namespaces, getChildNamespace} from '../shared/DOMNamespaces';
|
||||
import {
|
||||
ELEMENT_NODE,
|
||||
TEXT_NODE,
|
||||
|
|
@ -46,6 +46,7 @@ import dangerousStyleValue from '../shared/dangerousStyleValue';
|
|||
import type {DOMContainer} from './ReactDOM';
|
||||
import type {ReactEventResponder} from 'shared/ReactTypes';
|
||||
import {REACT_EVENT_TARGET_TOUCH_HIT} from 'shared/ReactSymbols';
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
|
||||
export type Type = string;
|
||||
export type Props = {
|
||||
|
|
@ -57,6 +58,23 @@ export type Props = {
|
|||
style?: {
|
||||
display?: string,
|
||||
},
|
||||
bottom?: null | number,
|
||||
left?: null | number,
|
||||
right?: null | number,
|
||||
top?: null | number,
|
||||
};
|
||||
export type EventTargetChildElement = {
|
||||
type: string,
|
||||
props: null | {
|
||||
style?: {
|
||||
position?: string,
|
||||
zIndex?: number,
|
||||
bottom?: string,
|
||||
left?: string,
|
||||
right?: string,
|
||||
top?: string,
|
||||
},
|
||||
},
|
||||
};
|
||||
export type Container = Element | Document;
|
||||
export type Instance = Element;
|
||||
|
|
@ -70,7 +88,6 @@ type HostContextDev = {
|
|||
eventData: null | {|
|
||||
isEventComponent?: boolean,
|
||||
isEventTarget?: boolean,
|
||||
eventTargetType?: null | Symbol | number,
|
||||
|},
|
||||
};
|
||||
type HostContextProd = string;
|
||||
|
|
@ -86,6 +103,8 @@ import {
|
|||
} from 'shared/ReactFeatureFlags';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
const {html: HTML_NAMESPACE} = Namespaces;
|
||||
|
||||
// Intentionally not named imports because Rollup would
|
||||
// use dynamic dispatch for CommonJS interop named imports.
|
||||
const {
|
||||
|
|
@ -190,7 +209,6 @@ export function getChildHostContextForEventComponent(
|
|||
const eventData = {
|
||||
isEventComponent: true,
|
||||
isEventTarget: false,
|
||||
eventTargetType: null,
|
||||
};
|
||||
return {namespace, ancestorInfo, eventData};
|
||||
}
|
||||
|
|
@ -204,17 +222,24 @@ export function getChildHostContextForEventTarget(
|
|||
if (__DEV__) {
|
||||
const parentHostContextDev = ((parentHostContext: any): HostContextDev);
|
||||
const {namespace, ancestorInfo} = parentHostContextDev;
|
||||
warning(
|
||||
parentHostContextDev.eventData === null ||
|
||||
!parentHostContextDev.eventData.isEventComponent ||
|
||||
type !== REACT_EVENT_TARGET_TOUCH_HIT,
|
||||
'validateDOMNesting: <TouchHitTarget> cannot not be a direct child of an event component. ' +
|
||||
'Ensure <TouchHitTarget> is a direct child of a DOM element.',
|
||||
);
|
||||
if (type === REACT_EVENT_TARGET_TOUCH_HIT) {
|
||||
warning(
|
||||
parentHostContextDev.eventData === null ||
|
||||
!parentHostContextDev.eventData.isEventComponent,
|
||||
'validateDOMNesting: <TouchHitTarget> cannot not be a direct child of an event component. ' +
|
||||
'Ensure <TouchHitTarget> is a direct child of a DOM element.',
|
||||
);
|
||||
const parentNamespace = parentHostContextDev.namespace;
|
||||
if (parentNamespace !== HTML_NAMESPACE) {
|
||||
throw new Error(
|
||||
'<TouchHitTarget> was used in an unsupported DOM namespace. ' +
|
||||
'Ensure the <TouchHitTarget> is used in an HTML namespace.',
|
||||
);
|
||||
}
|
||||
}
|
||||
const eventData = {
|
||||
isEventComponent: false,
|
||||
isEventTarget: true,
|
||||
eventTargetType: type,
|
||||
};
|
||||
return {namespace, ancestorInfo, eventData};
|
||||
}
|
||||
|
|
@ -249,16 +274,6 @@ export function createInstance(
|
|||
if (__DEV__) {
|
||||
// TODO: take namespace into account when validating.
|
||||
const hostContextDev = ((hostContext: any): HostContextDev);
|
||||
if (enableEventAPI) {
|
||||
const eventData = hostContextDev.eventData;
|
||||
if (eventData !== null) {
|
||||
warning(
|
||||
!eventData.isEventTarget ||
|
||||
eventData.eventTargetType !== REACT_EVENT_TARGET_TOUCH_HIT,
|
||||
'Warning: validateDOMNesting: <TouchHitTarget> must not have any children.',
|
||||
);
|
||||
}
|
||||
}
|
||||
validateDOMNesting(type, null, hostContextDev.ancestorInfo);
|
||||
if (
|
||||
typeof props.children === 'string' ||
|
||||
|
|
@ -365,25 +380,12 @@ export function createTextInstance(
|
|||
if (enableEventAPI) {
|
||||
const eventData = hostContextDev.eventData;
|
||||
if (eventData !== null) {
|
||||
warning(
|
||||
eventData === null ||
|
||||
!eventData.isEventTarget ||
|
||||
eventData.eventTargetType !== REACT_EVENT_TARGET_TOUCH_HIT,
|
||||
'Warning: validateDOMNesting: <TouchHitTarget> must not have any children.',
|
||||
);
|
||||
warning(
|
||||
!eventData.isEventComponent,
|
||||
'validateDOMNesting: React event components cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "%s" in an element.',
|
||||
text,
|
||||
);
|
||||
warning(
|
||||
!eventData.isEventTarget ||
|
||||
eventData.eventTargetType === REACT_EVENT_TARGET_TOUCH_HIT,
|
||||
'validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "%s" in an element.',
|
||||
text,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -899,16 +901,74 @@ export function handleEventComponent(
|
|||
}
|
||||
}
|
||||
|
||||
export function getEventTargetChildElement(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
): null | EventTargetChildElement {
|
||||
if (enableEventAPI) {
|
||||
if (type === REACT_EVENT_TARGET_TOUCH_HIT) {
|
||||
const {bottom, left, right, top} = props;
|
||||
|
||||
if (!bottom && !left && !right && !top) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: 'div',
|
||||
props: {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
zIndex: -1,
|
||||
bottom: bottom ? `-${bottom}px` : '0px',
|
||||
left: left ? `-${left}px` : '0px',
|
||||
right: right ? `-${right}px` : '0px',
|
||||
top: top ? `-${top}px` : '0px',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function handleEventTarget(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
parentInstance: Container,
|
||||
rootContainerInstance: Container,
|
||||
internalInstanceHandle: Object,
|
||||
): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function commitEventTarget(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
instance: Instance,
|
||||
parentInstance: Instance,
|
||||
): void {
|
||||
if (enableEventAPI) {
|
||||
// Touch target hit slop handling
|
||||
if (type === REACT_EVENT_TARGET_TOUCH_HIT) {
|
||||
// TODO
|
||||
if (__DEV__ && canUseDOM) {
|
||||
// This is done at DEV time because getComputedStyle will
|
||||
// typically force a style recalculation and force a layout,
|
||||
// reflow -– both of which are sync are expensive.
|
||||
const computedStyles = window.getComputedStyle(parentInstance);
|
||||
const position = computedStyles.getPropertyValue('position');
|
||||
warning(
|
||||
position !== '' && position !== 'static',
|
||||
'<TouchHitTarget> inserts an empty absolutely positioned <div>. ' +
|
||||
'This requires its parent DOM node to be positioned too, but the ' +
|
||||
'parent DOM node was found to have the style "position" set to ' +
|
||||
'either no value, or a value of "static". Try using a "position" ' +
|
||||
'value of "relative".',
|
||||
);
|
||||
warning(
|
||||
computedStyles.getPropertyValue('zIndex') !== '',
|
||||
'<TouchHitTarget> inserts an empty <div> with "z-index" of "-1". ' +
|
||||
'This requires its parent DOM node to have a "z-index" great than "-1",' +
|
||||
'but the parent DOM node was found to no "z-index" value set.' +
|
||||
' Try using a "z-index" value of "0" or greater.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@ import {
|
|||
PASSIVE_NOT_SUPPORTED,
|
||||
} from 'events/EventSystemFlags';
|
||||
import type {AnyNativeEvent} from 'events/PluginModuleType';
|
||||
import {EventComponent} from 'shared/ReactWorkTags';
|
||||
import {
|
||||
EventComponent,
|
||||
EventTarget as EventTargetWorkTag,
|
||||
} from 'shared/ReactWorkTags';
|
||||
import type {
|
||||
ReactEventResponder,
|
||||
ReactEventResponderEventType,
|
||||
|
|
@ -110,7 +113,31 @@ const eventResponderContext: ResponderContext = {
|
|||
eventsWithStopPropagation.add(eventObject);
|
||||
}
|
||||
},
|
||||
isPositionWithinTouchHitTarget(x: number, y: number): boolean {
|
||||
isPositionWithinTouchHitTarget(doc: Document, x: number, y: number): boolean {
|
||||
// This isn't available in some environments (JSDOM)
|
||||
if (typeof doc.elementFromPoint !== 'function') {
|
||||
return false;
|
||||
}
|
||||
const target = doc.elementFromPoint(x, y);
|
||||
if (target === null) {
|
||||
return false;
|
||||
}
|
||||
const childFiber = getClosestInstanceFromNode(target);
|
||||
if (childFiber === null) {
|
||||
return false;
|
||||
}
|
||||
const parentFiber = childFiber.return;
|
||||
if (parentFiber !== null && parentFiber.tag === EventTargetWorkTag) {
|
||||
const parentNode = ((target.parentNode: any): Element);
|
||||
// TODO find another way to do this without using the
|
||||
// expensive getBoundingClientRect.
|
||||
const {left, top, right, bottom} = parentNode.getBoundingClientRect();
|
||||
// Check if the co-ords intersect with the target element's rect.
|
||||
if (x > left && y > top && x < right && y < bottom) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
isTargetWithinEventComponent(target: Element | Document): boolean {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import {
|
|||
REACT_MEMO_TYPE,
|
||||
REACT_EVENT_COMPONENT_TYPE,
|
||||
REACT_EVENT_TARGET_TYPE,
|
||||
REACT_EVENT_TARGET_TOUCH_HIT,
|
||||
} from 'shared/ReactSymbols';
|
||||
|
||||
import {
|
||||
|
|
@ -1168,6 +1169,29 @@ class ReactDOMServerRenderer {
|
|||
case REACT_EVENT_COMPONENT_TYPE:
|
||||
case REACT_EVENT_TARGET_TYPE: {
|
||||
if (enableEventAPI) {
|
||||
if (
|
||||
elementType.$$typeof === REACT_EVENT_TARGET_TYPE &&
|
||||
elementType.type === REACT_EVENT_TARGET_TOUCH_HIT
|
||||
) {
|
||||
const props = nextElement.props;
|
||||
const bottom = props.bottom || 0;
|
||||
const left = props.left || 0;
|
||||
const right = props.right || 0;
|
||||
const top = props.top || 0;
|
||||
|
||||
if (bottom === 0 && left === 0 && right === 0 && top === 0) {
|
||||
return '';
|
||||
}
|
||||
let topString = top ? `-${top}px` : '0px';
|
||||
let leftString = left ? `-${left}px` : '0px';
|
||||
let rightString = right ? `-${right}px` : '0x';
|
||||
let bottomString = bottom ? `-${bottom}px` : '0px';
|
||||
|
||||
return (
|
||||
`<div style="position:absolute;z-index:-1;bottom:` +
|
||||
`${bottomString};left:${leftString};right:${rightString};top:${topString}"></div>`
|
||||
);
|
||||
}
|
||||
const nextChildren = toArray(
|
||||
((nextChild: any): ReactElement).props.children,
|
||||
);
|
||||
|
|
|
|||
5
packages/react-events/src/Hover.js
vendored
5
packages/react-events/src/Hover.js
vendored
|
|
@ -196,7 +196,7 @@ const HoverResponder = {
|
|||
props: HoverProps,
|
||||
state: HoverState,
|
||||
): void {
|
||||
const {type, nativeEvent} = event;
|
||||
const {type, target, nativeEvent} = event;
|
||||
|
||||
switch (type) {
|
||||
/**
|
||||
|
|
@ -218,6 +218,7 @@ const HoverResponder = {
|
|||
}
|
||||
if (
|
||||
context.isPositionWithinTouchHitTarget(
|
||||
target.ownerDocument,
|
||||
(nativeEvent: any).x,
|
||||
(nativeEvent: any).y,
|
||||
)
|
||||
|
|
@ -244,6 +245,7 @@ const HoverResponder = {
|
|||
if (state.isInHitSlop) {
|
||||
if (
|
||||
!context.isPositionWithinTouchHitTarget(
|
||||
target.ownerDocument,
|
||||
(nativeEvent: any).x,
|
||||
(nativeEvent: any).y,
|
||||
)
|
||||
|
|
@ -254,6 +256,7 @@ const HoverResponder = {
|
|||
} else if (
|
||||
state.isHovered &&
|
||||
context.isPositionWithinTouchHitTarget(
|
||||
target.ownerDocument,
|
||||
(nativeEvent: any).x,
|
||||
(nativeEvent: any).y,
|
||||
)
|
||||
|
|
|
|||
1
packages/react-events/src/Press.js
vendored
1
packages/react-events/src/Press.js
vendored
|
|
@ -259,6 +259,7 @@ const PressResponder = {
|
|||
nativeEvent.button === 2 ||
|
||||
// Ignore pressing on hit slop area with mouse
|
||||
context.isPositionWithinTouchHitTarget(
|
||||
target.ownerDocument,
|
||||
(nativeEvent: any).x,
|
||||
(nativeEvent: any).y,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ let ReactFeatureFlags;
|
|||
let EventComponent;
|
||||
let ReactTestRenderer;
|
||||
let ReactDOM;
|
||||
let ReactDOMServer;
|
||||
let ReactSymbols;
|
||||
let ReactEvents;
|
||||
let TouchHitTarget;
|
||||
|
|
@ -58,6 +59,11 @@ function initReactDOM() {
|
|||
ReactDOM = require('react-dom');
|
||||
}
|
||||
|
||||
function initReactDOMServer() {
|
||||
init();
|
||||
ReactDOMServer = require('react-dom/server');
|
||||
}
|
||||
|
||||
describe('TouchHitTarget', () => {
|
||||
describe('NoopRenderer', () => {
|
||||
beforeEach(() => {
|
||||
|
|
@ -94,9 +100,7 @@ describe('TouchHitTarget', () => {
|
|||
expect(() => {
|
||||
ReactNoop.render(<Test />);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: <TouchHitTarget> must not have any children.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
|
||||
const Test2 = () => (
|
||||
<EventComponent>
|
||||
|
|
@ -109,9 +113,7 @@ describe('TouchHitTarget', () => {
|
|||
expect(() => {
|
||||
ReactNoop.render(<Test2 />);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: <TouchHitTarget> must not have any children.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
|
||||
// Should render without warnings
|
||||
const Test3 = () => (
|
||||
|
|
@ -181,9 +183,7 @@ describe('TouchHitTarget', () => {
|
|||
expect(() => {
|
||||
root.update(<Test />);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: <TouchHitTarget> must not have any children.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
|
||||
const Test2 = () => (
|
||||
<EventComponent>
|
||||
|
|
@ -196,9 +196,7 @@ describe('TouchHitTarget', () => {
|
|||
expect(() => {
|
||||
root.update(<Test2 />);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: <TouchHitTarget> must not have any children.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
|
||||
// Should render without warnings
|
||||
const Test3 = () => (
|
||||
|
|
@ -269,9 +267,7 @@ describe('TouchHitTarget', () => {
|
|||
expect(() => {
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: <TouchHitTarget> must not have any children.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
|
||||
const Test2 = () => (
|
||||
<EventComponent>
|
||||
|
|
@ -284,9 +280,7 @@ describe('TouchHitTarget', () => {
|
|||
expect(() => {
|
||||
ReactDOM.render(<Test2 />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: <TouchHitTarget> must not have any children.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
|
||||
// Should render without warnings
|
||||
const Test3 = () => (
|
||||
|
|
@ -318,5 +312,319 @@ describe('TouchHitTarget', () => {
|
|||
'Ensure <TouchHitTarget> is a direct child of a DOM element.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a conditional TouchHitTarget correctly (false -> true)', () => {
|
||||
let cond = false;
|
||||
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
{cond ? null : (
|
||||
<TouchHitTarget top={10} left={10} right={10} bottom={10} />
|
||||
)}
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe(
|
||||
'<div><div style="position: absolute; z-index: -1; bottom: -10px; ' +
|
||||
'left: -10px; right: -10px; top: -10px;"></div></div>',
|
||||
);
|
||||
|
||||
cond = true;
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe('<div></div>');
|
||||
});
|
||||
|
||||
it('should render a conditional TouchHitTarget correctly (true -> false)', () => {
|
||||
let cond = true;
|
||||
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
{cond ? null : (
|
||||
<TouchHitTarget top={10} left={10} right={10} bottom={10} />
|
||||
)}
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe('<div></div>');
|
||||
|
||||
cond = false;
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe(
|
||||
'<div><div style="position: absolute; z-index: -1; bottom: -10px; ' +
|
||||
'left: -10px; right: -10px; top: -10px;"></div></div>',
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a conditional TouchHitTarget hit slop correctly (false -> true)', () => {
|
||||
let cond = false;
|
||||
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
{cond ? (
|
||||
<TouchHitTarget />
|
||||
) : (
|
||||
<TouchHitTarget top={10} left={10} right={10} bottom={10} />
|
||||
)}
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe(
|
||||
'<div><div style="position: absolute; z-index: -1; bottom: -10px; ' +
|
||||
'left: -10px; right: -10px; top: -10px;"></div></div>',
|
||||
);
|
||||
|
||||
cond = true;
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe('<div></div>');
|
||||
});
|
||||
|
||||
it('should render a conditional TouchHitTarget hit slop correctly (true -> false)', () => {
|
||||
let cond = true;
|
||||
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
<span>Random span 1</span>
|
||||
{cond ? (
|
||||
<TouchHitTarget />
|
||||
) : (
|
||||
<TouchHitTarget top={10} left={10} right={10} bottom={10} />
|
||||
)}
|
||||
<span>Random span 2</span>
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe(
|
||||
'<div><span>Random span 1</span><span>Random span 2</span></div>',
|
||||
);
|
||||
|
||||
cond = false;
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe(
|
||||
'<div><span>Random span 1</span><div style="position: absolute; z-index: -1; bottom: -10px; ' +
|
||||
'left: -10px; right: -10px; top: -10px;"></div><span>Random span 2</span></div>',
|
||||
);
|
||||
});
|
||||
|
||||
it('should update TouchHitTarget hit slop values correctly (false -> true)', () => {
|
||||
let cond = false;
|
||||
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
<span>Random span 1</span>
|
||||
{cond ? (
|
||||
<TouchHitTarget top={10} left={null} right={10} bottom={10} />
|
||||
) : (
|
||||
<TouchHitTarget
|
||||
top={undefined}
|
||||
left={20}
|
||||
right={null}
|
||||
bottom={0}
|
||||
/>
|
||||
)}
|
||||
<span>Random span 2</span>
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe(
|
||||
'<div><span>Random span 1</span><div style="position: absolute; z-index: -1; bottom: 0px; ' +
|
||||
'left: -20px; right: 0px; top: 0px;"></div><span>Random span 2</span></div>',
|
||||
);
|
||||
|
||||
cond = true;
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe(
|
||||
'<div><span>Random span 1</span><div style="position: absolute; z-index: -1; bottom: 0px; ' +
|
||||
'left: -20px; right: 0px; top: 0px;"></div><span>Random span 2</span></div>',
|
||||
);
|
||||
});
|
||||
|
||||
it('should update TouchHitTarget hit slop values correctly (true -> false)', () => {
|
||||
let cond = true;
|
||||
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
<span>Random span 1</span>
|
||||
{cond ? (
|
||||
<TouchHitTarget top={10} left={null} right={10} bottom={10} />
|
||||
) : (
|
||||
<TouchHitTarget
|
||||
top={undefined}
|
||||
left={20}
|
||||
right={null}
|
||||
bottom={0}
|
||||
/>
|
||||
)}
|
||||
<span>Random span 2</span>
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe(
|
||||
'<div><span>Random span 1</span><div style="position: absolute; z-index: -1; bottom: -10px; ' +
|
||||
'left: 0px; right: -10px; top: -10px;"></div><span>Random span 2</span></div>',
|
||||
);
|
||||
|
||||
cond = false;
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe(
|
||||
'<div><span>Random span 1</span><div style="position: absolute; z-index: -1; bottom: -10px; ' +
|
||||
'left: 0px; right: -10px; top: -10px;"></div><span>Random span 2</span></div>',
|
||||
);
|
||||
});
|
||||
|
||||
it('should hydrate TouchHitTarget hit slop elements correcty', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
<TouchHitTarget />
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.innerHTML = '<div></div>';
|
||||
ReactDOM.hydrate(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe('<div></div>');
|
||||
|
||||
const Test2 = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
<TouchHitTarget top={10} left={10} right={10} bottom={10} />
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
const container2 = document.createElement('div');
|
||||
container2.innerHTML =
|
||||
'<div><div style="position:absolute;z-index:-1;bottom:-10px;left:-10px;right:-10px;top:-10px"></div></div>';
|
||||
ReactDOM.hydrate(<Test2 />, container2);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container2.innerHTML).toBe(
|
||||
'<div><div style="position:absolute;z-index:-1;bottom:-10px;left:-10px;right:-10px;top:-10px"></div></div>',
|
||||
);
|
||||
});
|
||||
|
||||
it('should hydrate TouchHitTarget hit slop elements correcty and patch them', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
<TouchHitTarget top={10} left={10} right={10} bottom={10} />
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.innerHTML = '<div></div>';
|
||||
expect(() => {
|
||||
ReactDOM.hydrate(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: Expected server HTML to contain a matching <div> in <div>.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe(
|
||||
'<div><div style="position: absolute; z-index: -1; bottom: -10px; ' +
|
||||
'left: -10px; right: -10px; top: -10px;"></div></div>',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ReactDOMServer', () => {
|
||||
beforeEach(() => {
|
||||
initReactDOMServer();
|
||||
EventComponent = createReactEventComponent();
|
||||
TouchHitTarget = ReactEvents.TouchHitTarget;
|
||||
});
|
||||
|
||||
it('should not warn when a TouchHitTarget is used correctly', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
<TouchHitTarget />
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
const output = ReactDOMServer.renderToString(<Test />);
|
||||
expect(output).toBe('<div></div>');
|
||||
});
|
||||
|
||||
it('should render a TouchHitTarget with hit slop values', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
<TouchHitTarget top={10} left={10} right={10} bottom={10} />
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
let output = ReactDOMServer.renderToString(<Test />);
|
||||
expect(output).toBe(
|
||||
'<div><div style="position:absolute;z-index:-1;bottom:-10px;left:-10px;right:-10px;top:-10px"></div></div>',
|
||||
);
|
||||
|
||||
const Test2 = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
<TouchHitTarget top={null} left={undefined} right={0} bottom={10} />
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
output = ReactDOMServer.renderToString(<Test2 />);
|
||||
expect(output).toBe(
|
||||
'<div><div style="position:absolute;z-index:-1;bottom:-10px;left:0px;right:0x;top:0px"></div></div>',
|
||||
);
|
||||
|
||||
const Test3 = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
<TouchHitTarget top={1} left={2} right={3} bottom={4} />
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
output = ReactDOMServer.renderToString(<Test3 />);
|
||||
expect(output).toBe(
|
||||
'<div><div style="position:absolute;z-index:-1;bottom:-4px;left:-2px;right:-3px;top:-1px"></div></div>',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -438,15 +438,31 @@ export function handleEventComponent(
|
|||
eventResponder: ReactEventResponder,
|
||||
rootContainerInstance: Container,
|
||||
internalInstanceHandle: Object,
|
||||
) {
|
||||
// TODO: add handleEventComponent implementation
|
||||
): void {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
||||
export function getEventTargetChildElement(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
): null {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
||||
export function handleEventTarget(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
parentInstance: Container,
|
||||
rootContainerInstance: Container,
|
||||
internalInstanceHandle: Object,
|
||||
) {
|
||||
// TODO: add handleEventTarget implementation
|
||||
): boolean {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
||||
export function commitEventTarget(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
instance: Instance,
|
||||
parentInstance: Instance,
|
||||
): void {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -498,14 +498,30 @@ export function handleEventComponent(
|
|||
rootContainerInstance: Container,
|
||||
internalInstanceHandle: Object,
|
||||
) {
|
||||
// TODO: add handleEventComponent implementation
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
||||
export function getEventTargetChildElement(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
): null {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
||||
export function handleEventTarget(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
parentInstance: Container,
|
||||
rootContainerInstance: Container,
|
||||
internalInstanceHandle: Object,
|
||||
) {
|
||||
// TODO: add handleEventTarget implementation
|
||||
): boolean {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
||||
export function commitEventTarget(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
instance: Instance,
|
||||
parentInstance: Instance,
|
||||
): void {
|
||||
throw new Error('Not yet implemented.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,12 +33,32 @@ import ReactSharedInternals from 'shared/ReactSharedInternals';
|
|||
import warningWithoutStack from 'shared/warningWithoutStack';
|
||||
import {enableEventAPI} from 'shared/ReactFeatureFlags';
|
||||
|
||||
type EventTargetChildElement = {
|
||||
type: string,
|
||||
props: null | {
|
||||
style?: {
|
||||
position?: string,
|
||||
bottom?: string,
|
||||
left?: string,
|
||||
right?: string,
|
||||
top?: string,
|
||||
},
|
||||
},
|
||||
};
|
||||
type Container = {
|
||||
rootID: string,
|
||||
children: Array<Instance | TextInstance>,
|
||||
pendingChildren: Array<Instance | TextInstance>,
|
||||
};
|
||||
type Props = {prop: any, hidden: boolean, children?: mixed};
|
||||
type Props = {
|
||||
prop: any,
|
||||
hidden: boolean,
|
||||
children?: mixed,
|
||||
bottom?: null | number,
|
||||
left?: null | number,
|
||||
right?: null | number,
|
||||
top?: null | number,
|
||||
};
|
||||
type Instance = {|
|
||||
type: string,
|
||||
id: number,
|
||||
|
|
@ -299,12 +319,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
|
|||
rootContainerInstance: Container,
|
||||
hostContext: HostContext,
|
||||
): Instance {
|
||||
if (__DEV__ && enableEventAPI) {
|
||||
warning(
|
||||
hostContext !== EVENT_TOUCH_HIT_TARGET_CONTEXT,
|
||||
'validateDOMNesting: <TouchHitTarget> must not have any children.',
|
||||
);
|
||||
}
|
||||
if (type === 'errorInCompletePhase') {
|
||||
throw new Error('Error in host config.');
|
||||
}
|
||||
|
|
@ -379,22 +393,12 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
|
|||
internalInstanceHandle: Object,
|
||||
): TextInstance {
|
||||
if (__DEV__ && enableEventAPI) {
|
||||
warning(
|
||||
hostContext !== EVENT_TOUCH_HIT_TARGET_CONTEXT,
|
||||
'validateDOMNesting: <TouchHitTarget> must not have any children.',
|
||||
);
|
||||
warning(
|
||||
hostContext !== EVENT_COMPONENT_CONTEXT,
|
||||
'validateDOMNesting: React event components cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "%s" in an element.',
|
||||
text,
|
||||
);
|
||||
warning(
|
||||
hostContext !== EVENT_TARGET_CONTEXT,
|
||||
'validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "%s" in an element.',
|
||||
text,
|
||||
);
|
||||
}
|
||||
if (hostContext === UPPERCASE_CONTEXT) {
|
||||
text = text.toUpperCase();
|
||||
|
|
@ -431,15 +435,51 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
|
|||
// NO-OP
|
||||
},
|
||||
|
||||
getEventTargetChildElement(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
): null | EventTargetChildElement {
|
||||
if (enableEventAPI) {
|
||||
if (type === REACT_EVENT_TARGET_TOUCH_HIT) {
|
||||
const {bottom, left, right, top} = props;
|
||||
|
||||
if (!bottom && !left && !right && !top) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: 'div',
|
||||
props: {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
zIndex: -1,
|
||||
bottom: bottom ? `-${bottom}px` : '0px',
|
||||
left: left ? `-${left}px` : '0px',
|
||||
right: right ? `-${right}px` : '0px',
|
||||
top: top ? `-${top}px` : '0px',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
handleEventTarget(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
parentInstance: Container,
|
||||
rootContainerInstance: Container,
|
||||
internalInstanceHandle: Object,
|
||||
) {
|
||||
if (type === REACT_EVENT_TARGET_TOUCH_HIT) {
|
||||
// TODO
|
||||
}
|
||||
): boolean {
|
||||
return false;
|
||||
},
|
||||
|
||||
commitEventTarget(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
instance: Instance,
|
||||
parentInstance: Instance,
|
||||
): void {
|
||||
// NO-OP
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ import {
|
|||
registerSuspenseInstanceRetry,
|
||||
} from './ReactFiberHostConfig';
|
||||
import type {SuspenseInstance} from './ReactFiberHostConfig';
|
||||
import {getEventTargetChildElement} from './ReactFiberHostConfig';
|
||||
import {shouldSuspend} from './ReactFiberReconciler';
|
||||
import {
|
||||
pushHostContext,
|
||||
|
|
@ -1988,15 +1989,33 @@ function updateEventComponent(current, workInProgress, renderExpirationTime) {
|
|||
}
|
||||
|
||||
function updateEventTarget(current, workInProgress, renderExpirationTime) {
|
||||
const type = workInProgress.type.type;
|
||||
const nextProps = workInProgress.pendingProps;
|
||||
let nextChildren = nextProps.children;
|
||||
const eventTargetChild = getEventTargetChildElement(type, nextProps);
|
||||
|
||||
reconcileChildren(
|
||||
current,
|
||||
workInProgress,
|
||||
nextChildren,
|
||||
renderExpirationTime,
|
||||
);
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
nextProps.children == null,
|
||||
'Event targets should not have children.',
|
||||
);
|
||||
}
|
||||
if (eventTargetChild !== null) {
|
||||
const child = (workInProgress.child = createFiberFromTypeAndProps(
|
||||
eventTargetChild.type,
|
||||
null,
|
||||
eventTargetChild.props,
|
||||
null,
|
||||
workInProgress.mode,
|
||||
renderExpirationTime,
|
||||
));
|
||||
child.return = workInProgress;
|
||||
|
||||
if (current === null || current.child === null) {
|
||||
child.effectTag = Placement;
|
||||
}
|
||||
} else {
|
||||
reconcileChildren(current, workInProgress, null, renderExpirationTime);
|
||||
}
|
||||
pushHostContextForEventTarget(workInProgress);
|
||||
return workInProgress.child;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import {
|
|||
enableSchedulerTracing,
|
||||
enableProfilerTimer,
|
||||
enableSuspenseServerRenderer,
|
||||
enableEventAPI,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
FunctionComponent,
|
||||
|
|
@ -43,6 +44,7 @@ import {
|
|||
IncompleteClassComponent,
|
||||
MemoComponent,
|
||||
SimpleMemoComponent,
|
||||
EventTarget,
|
||||
} from 'shared/ReactWorkTags';
|
||||
import {
|
||||
invokeGuardedCallback,
|
||||
|
|
@ -90,6 +92,7 @@ import {
|
|||
hideTextInstance,
|
||||
unhideInstance,
|
||||
unhideTextInstance,
|
||||
commitEventTarget,
|
||||
} from './ReactFiberHostConfig';
|
||||
import {
|
||||
captureCommitPhaseError,
|
||||
|
|
@ -299,6 +302,7 @@ function commitBeforeMutationLifeCycles(
|
|||
case HostText:
|
||||
case HostPortal:
|
||||
case IncompleteClassComponent:
|
||||
case EventTarget:
|
||||
// Nothing to do for these component types
|
||||
return;
|
||||
default: {
|
||||
|
|
@ -585,6 +589,7 @@ function commitLifeCycles(
|
|||
}
|
||||
case SuspenseComponent:
|
||||
case IncompleteClassComponent:
|
||||
case EventTarget:
|
||||
break;
|
||||
default: {
|
||||
invariant(
|
||||
|
|
@ -817,7 +822,8 @@ function commitContainer(finishedWork: Fiber) {
|
|||
switch (finishedWork.tag) {
|
||||
case ClassComponent:
|
||||
case HostComponent:
|
||||
case HostText: {
|
||||
case HostText:
|
||||
case EventTarget: {
|
||||
return;
|
||||
}
|
||||
case HostRoot:
|
||||
|
|
@ -955,17 +961,18 @@ function commitPlacement(finishedWork: Fiber): void {
|
|||
let node: Fiber = finishedWork;
|
||||
while (true) {
|
||||
if (node.tag === HostComponent || node.tag === HostText) {
|
||||
const stateNode = node.stateNode;
|
||||
if (before) {
|
||||
if (isContainer) {
|
||||
insertInContainerBefore(parent, node.stateNode, before);
|
||||
insertInContainerBefore(parent, stateNode, before);
|
||||
} else {
|
||||
insertBefore(parent, node.stateNode, before);
|
||||
insertBefore(parent, stateNode, before);
|
||||
}
|
||||
} else {
|
||||
if (isContainer) {
|
||||
appendChildToContainer(parent, node.stateNode);
|
||||
appendChildToContainer(parent, stateNode);
|
||||
} else {
|
||||
appendChild(parent, node.stateNode);
|
||||
appendChild(parent, stateNode);
|
||||
}
|
||||
}
|
||||
} else if (node.tag === HostPortal) {
|
||||
|
|
@ -1195,6 +1202,34 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
|
|||
commitTextUpdate(textInstance, oldText, newText);
|
||||
return;
|
||||
}
|
||||
case EventTarget: {
|
||||
if (enableEventAPI) {
|
||||
const type = finishedWork.type.type;
|
||||
const props = finishedWork.memoizedProps;
|
||||
const instance = finishedWork.stateNode;
|
||||
let parentInstance = null;
|
||||
|
||||
let node = finishedWork.return;
|
||||
// Traverse up the fiber tree until we find the parent host node.
|
||||
while (node !== null) {
|
||||
if (node.tag === HostComponent) {
|
||||
parentInstance = node.stateNode;
|
||||
break;
|
||||
} else if (node.tag === HostRoot) {
|
||||
parentInstance = node.stateNode.containerInfo;
|
||||
break;
|
||||
}
|
||||
node = node.return;
|
||||
}
|
||||
invariant(
|
||||
parentInstance !== null,
|
||||
'This should have a parent host component initialized. This error is likely ' +
|
||||
'caused by a bug in React. Please file an issue.',
|
||||
);
|
||||
commitEventTarget(type, props, instance, parentInstance);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case HostRoot: {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -784,18 +784,15 @@ function completeWork(
|
|||
if (enableEventAPI) {
|
||||
popHostContext(workInProgress);
|
||||
const type = workInProgress.type.type;
|
||||
let node = workInProgress.return;
|
||||
let parentHostInstance = null;
|
||||
// Traverse up the fiber tree till we find a host component fiber
|
||||
while (node !== null) {
|
||||
if (node.tag === HostComponent) {
|
||||
parentHostInstance = node.stateNode;
|
||||
break;
|
||||
}
|
||||
node = node.return;
|
||||
}
|
||||
if (parentHostInstance !== null) {
|
||||
handleEventTarget(type, newProps, parentHostInstance, workInProgress);
|
||||
const rootContainerInstance = getRootHostContainer();
|
||||
const shouldUpdate = handleEventTarget(
|
||||
type,
|
||||
newProps,
|
||||
rootContainerInstance,
|
||||
workInProgress,
|
||||
);
|
||||
if (shouldUpdate) {
|
||||
markUpdate(workInProgress);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -127,9 +127,9 @@ describe('ReactFiberEvents', () => {
|
|||
it('should render a simple event component with a single event target', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<div>Hello world</div>
|
||||
</EventTarget>
|
||||
<div>
|
||||
Hello world<EventTarget />
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -148,10 +148,7 @@ describe('ReactFiberEvents', () => {
|
|||
expect(() => {
|
||||
ReactNoop.render(<Test />);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "Hello world" in an element.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
});
|
||||
|
||||
it('should warn when an event target has a direct text child #2', () => {
|
||||
|
|
@ -167,19 +164,15 @@ describe('ReactFiberEvents', () => {
|
|||
expect(() => {
|
||||
ReactNoop.render(<Test />);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "Hello world" in an element.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
});
|
||||
|
||||
it('should not warn if an event target is not a direct child of an event component', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
<EventTarget>
|
||||
<span>Child 1</span>
|
||||
</EventTarget>
|
||||
<EventTarget />
|
||||
<span>Child 1</span>
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
|
@ -207,9 +200,7 @@ describe('ReactFiberEvents', () => {
|
|||
expect(() => {
|
||||
ReactNoop.render(<Test />);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event targets must not have event components as children.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
});
|
||||
|
||||
it('should handle event components correctly with error boundaries', () => {
|
||||
|
|
@ -219,11 +210,9 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<span>
|
||||
<ErrorComponent />
|
||||
</span>
|
||||
</EventTarget>
|
||||
<span>
|
||||
<ErrorComponent />
|
||||
</span>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -268,11 +257,9 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
const Parent = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<div>
|
||||
<Child />
|
||||
</div>
|
||||
</EventTarget>
|
||||
<div>
|
||||
<Child />
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -321,9 +308,7 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
const Parent = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<Child />
|
||||
</EventTarget>
|
||||
<Child />
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -341,7 +326,7 @@ describe('ReactFiberEvents', () => {
|
|||
});
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
|
||||
'Warning: validateDOMNesting: React event components cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "Text!" in an element.',
|
||||
);
|
||||
});
|
||||
|
|
@ -355,11 +340,7 @@ describe('ReactFiberEvents', () => {
|
|||
_updateCounter = updateCounter;
|
||||
|
||||
if (counter === 1) {
|
||||
return (
|
||||
<EventComponent>
|
||||
<div>Child</div>
|
||||
</EventComponent>
|
||||
);
|
||||
return <EventTarget>123</EventTarget>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -370,18 +351,20 @@ describe('ReactFiberEvents', () => {
|
|||
}
|
||||
|
||||
const Parent = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<div>
|
||||
<EventComponent>
|
||||
<Child />
|
||||
</EventTarget>
|
||||
</EventComponent>
|
||||
</EventComponent>
|
||||
</div>
|
||||
);
|
||||
|
||||
ReactNoop.render(<Parent />);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(ReactNoop).toMatchRenderedOutput(
|
||||
<div>
|
||||
<span>Child - 0</span>
|
||||
<div>
|
||||
<span>Child - 0</span>
|
||||
</div>
|
||||
</div>,
|
||||
);
|
||||
|
||||
|
|
@ -390,9 +373,7 @@ describe('ReactFiberEvents', () => {
|
|||
_updateCounter(counter => counter + 1);
|
||||
});
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event targets must not have event components as children.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
});
|
||||
|
||||
it('should error with a component stack contains the names of the event components and event targets', () => {
|
||||
|
|
@ -404,11 +385,9 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<span>
|
||||
<ErrorComponent />
|
||||
</span>
|
||||
</EventTarget>
|
||||
<span>
|
||||
<ErrorComponent />
|
||||
</span>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -437,7 +416,6 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
expect(componentStackMessage.includes('ErrorComponent')).toBe(true);
|
||||
expect(componentStackMessage.includes('span')).toBe(true);
|
||||
expect(componentStackMessage.includes('TestEventTarget')).toBe(true);
|
||||
expect(componentStackMessage.includes('TestEventComponent')).toBe(true);
|
||||
expect(componentStackMessage.includes('Test')).toBe(true);
|
||||
expect(componentStackMessage.includes('Wrapper')).toBe(true);
|
||||
|
|
@ -498,9 +476,9 @@ describe('ReactFiberEvents', () => {
|
|||
it('should render a simple event component with a single event target', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<div>Hello world</div>
|
||||
</EventTarget>
|
||||
<div>
|
||||
Hello world<EventTarget />
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -511,9 +489,8 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
const Test2 = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<span>I am now a span</span>
|
||||
</EventTarget>
|
||||
<EventTarget />
|
||||
<span>I am now a span</span>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -533,10 +510,7 @@ describe('ReactFiberEvents', () => {
|
|||
expect(() => {
|
||||
root.update(<Test />);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "Hello world" in an element.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
});
|
||||
|
||||
it('should warn when an event target has a direct text child #2', () => {
|
||||
|
|
@ -553,19 +527,15 @@ describe('ReactFiberEvents', () => {
|
|||
expect(() => {
|
||||
root.update(<Test />);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "Hello world" in an element.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
});
|
||||
|
||||
it('should not warn if an event target is not a direct child of an event component', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
<EventTarget>
|
||||
<span>Child 1</span>
|
||||
</EventTarget>
|
||||
<EventTarget />
|
||||
<span>Child 1</span>
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
|
@ -595,9 +565,7 @@ describe('ReactFiberEvents', () => {
|
|||
expect(() => {
|
||||
root.update(<Test />);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event targets must not have event components as children.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
});
|
||||
|
||||
it('should handle event components correctly with error boundaries', () => {
|
||||
|
|
@ -607,11 +575,9 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<span>
|
||||
<ErrorComponent />
|
||||
</span>
|
||||
</EventTarget>
|
||||
<span>
|
||||
<ErrorComponent />
|
||||
</span>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -620,7 +586,7 @@ describe('ReactFiberEvents', () => {
|
|||
error: null,
|
||||
};
|
||||
|
||||
componentDidCatch(error, errStack) {
|
||||
componentDidCatch(error) {
|
||||
this.setState({
|
||||
error,
|
||||
});
|
||||
|
|
@ -657,11 +623,9 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
const Parent = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<div>
|
||||
<Child />
|
||||
</div>
|
||||
</EventTarget>
|
||||
<div>
|
||||
<Child />
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -710,9 +674,7 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
const Parent = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<Child />
|
||||
</EventTarget>
|
||||
<Child />
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -730,7 +692,7 @@ describe('ReactFiberEvents', () => {
|
|||
_updateCounter(counter => counter + 1);
|
||||
});
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
|
||||
'Warning: validateDOMNesting: React event components cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "Text!" in an element.',
|
||||
);
|
||||
});
|
||||
|
|
@ -744,11 +706,7 @@ describe('ReactFiberEvents', () => {
|
|||
_updateCounter = updateCounter;
|
||||
|
||||
if (counter === 1) {
|
||||
return (
|
||||
<EventComponent>
|
||||
<div>Child</div>
|
||||
</EventComponent>
|
||||
);
|
||||
return <EventTarget>123</EventTarget>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -759,11 +717,11 @@ describe('ReactFiberEvents', () => {
|
|||
}
|
||||
|
||||
const Parent = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<div>
|
||||
<EventComponent>
|
||||
<Child />
|
||||
</EventTarget>
|
||||
</EventComponent>
|
||||
</EventComponent>
|
||||
</div>
|
||||
);
|
||||
|
||||
const root = ReactTestRenderer.create(null);
|
||||
|
|
@ -771,7 +729,9 @@ describe('ReactFiberEvents', () => {
|
|||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(root).toMatchRenderedOutput(
|
||||
<div>
|
||||
<span>Child - 0</span>
|
||||
<div>
|
||||
<span>Child - 0</span>
|
||||
</div>
|
||||
</div>,
|
||||
);
|
||||
|
||||
|
|
@ -780,9 +740,7 @@ describe('ReactFiberEvents', () => {
|
|||
_updateCounter(counter => counter + 1);
|
||||
});
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event targets must not have event components as children.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
});
|
||||
|
||||
it('should error with a component stack contains the names of the event components and event targets', () => {
|
||||
|
|
@ -794,11 +752,9 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<span>
|
||||
<ErrorComponent />
|
||||
</span>
|
||||
</EventTarget>
|
||||
<span>
|
||||
<ErrorComponent />
|
||||
</span>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -828,7 +784,6 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
expect(componentStackMessage.includes('ErrorComponent')).toBe(true);
|
||||
expect(componentStackMessage.includes('span')).toBe(true);
|
||||
expect(componentStackMessage.includes('TestEventTarget')).toBe(true);
|
||||
expect(componentStackMessage.includes('TestEventComponent')).toBe(true);
|
||||
expect(componentStackMessage.includes('Test')).toBe(true);
|
||||
expect(componentStackMessage.includes('Wrapper')).toBe(true);
|
||||
|
|
@ -888,9 +843,9 @@ describe('ReactFiberEvents', () => {
|
|||
it('should render a simple event component with a single event target', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<div>Hello world</div>
|
||||
</EventTarget>
|
||||
<div>
|
||||
Hello world<EventTarget />
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -901,9 +856,8 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
const Test2 = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<span>I am now a span</span>
|
||||
</EventTarget>
|
||||
<EventTarget />
|
||||
<span>I am now a span</span>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -923,10 +877,7 @@ describe('ReactFiberEvents', () => {
|
|||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "Hello world" in an element.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
});
|
||||
|
||||
it('should warn when an event target has a direct text child #2', () => {
|
||||
|
|
@ -943,19 +894,15 @@ describe('ReactFiberEvents', () => {
|
|||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "Hello world" in an element.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
});
|
||||
|
||||
it('should not warn if an event target is not a direct child of an event component', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
<EventTarget>
|
||||
<span>Child 1</span>
|
||||
</EventTarget>
|
||||
<EventTarget />
|
||||
<span>Child 1</span>
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
|
@ -981,9 +928,7 @@ describe('ReactFiberEvents', () => {
|
|||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event targets must not have event components as children.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
});
|
||||
|
||||
it('should handle event components correctly with error boundaries', () => {
|
||||
|
|
@ -993,11 +938,9 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<span>
|
||||
<ErrorComponent />
|
||||
</span>
|
||||
</EventTarget>
|
||||
<span>
|
||||
<ErrorComponent />
|
||||
</span>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -1043,11 +986,9 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
const Parent = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<div>
|
||||
<Child />
|
||||
</div>
|
||||
</EventTarget>
|
||||
<div>
|
||||
<Child />
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -1087,9 +1028,7 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
const Parent = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<Child />
|
||||
</EventTarget>
|
||||
<Child />
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -1103,7 +1042,7 @@ describe('ReactFiberEvents', () => {
|
|||
});
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event targets cannot have text DOM nodes as children. ' +
|
||||
'Warning: validateDOMNesting: React event components cannot have text DOM nodes as children. ' +
|
||||
'Wrap the child text "Text!" in an element.',
|
||||
);
|
||||
});
|
||||
|
|
@ -1117,11 +1056,7 @@ describe('ReactFiberEvents', () => {
|
|||
_updateCounter = updateCounter;
|
||||
|
||||
if (counter === 1) {
|
||||
return (
|
||||
<EventComponent>
|
||||
<div>Child</div>
|
||||
</EventComponent>
|
||||
);
|
||||
return <EventTarget>123</EventTarget>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -1132,25 +1067,25 @@ describe('ReactFiberEvents', () => {
|
|||
}
|
||||
|
||||
const Parent = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<div>
|
||||
<EventComponent>
|
||||
<Child />
|
||||
</EventTarget>
|
||||
</EventComponent>
|
||||
</EventComponent>
|
||||
</div>
|
||||
);
|
||||
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<Parent />, container);
|
||||
expect(container.innerHTML).toBe('<div><span>Child - 0</span></div>');
|
||||
expect(container.innerHTML).toBe(
|
||||
'<div><div><span>Child - 0</span></div></div>',
|
||||
);
|
||||
|
||||
expect(() => {
|
||||
ReactTestUtils.act(() => {
|
||||
_updateCounter(counter => counter + 1);
|
||||
});
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toWarnDev(
|
||||
'Warning: validateDOMNesting: React event targets must not have event components as children.',
|
||||
);
|
||||
}).toWarnDev('Warning: Event targets should not have children.');
|
||||
});
|
||||
|
||||
it('should error with a component stack contains the names of the event components and event targets', () => {
|
||||
|
|
@ -1162,11 +1097,9 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<span>
|
||||
<ErrorComponent />
|
||||
</span>
|
||||
</EventTarget>
|
||||
<span>
|
||||
<ErrorComponent />
|
||||
</span>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
@ -1195,7 +1128,6 @@ describe('ReactFiberEvents', () => {
|
|||
|
||||
expect(componentStackMessage.includes('ErrorComponent')).toBe(true);
|
||||
expect(componentStackMessage.includes('span')).toBe(true);
|
||||
expect(componentStackMessage.includes('TestEventTarget')).toBe(true);
|
||||
expect(componentStackMessage.includes('TestEventComponent')).toBe(true);
|
||||
expect(componentStackMessage.includes('Test')).toBe(true);
|
||||
expect(componentStackMessage.includes('Wrapper')).toBe(true);
|
||||
|
|
@ -1222,9 +1154,9 @@ describe('ReactFiberEvents', () => {
|
|||
it('should render a simple event component with a single event target', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<EventTarget>
|
||||
<div>Hello world</div>
|
||||
</EventTarget>
|
||||
<div>
|
||||
Hello world<EventTarget />
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ export const supportsPersistence = $$$hostConfig.supportsPersistence;
|
|||
export const supportsHydration = $$$hostConfig.supportsHydration;
|
||||
export const handleEventComponent = $$$hostConfig.handleEventComponent;
|
||||
export const handleEventTarget = $$$hostConfig.handleEventTarget;
|
||||
export const getEventTargetChildElement =
|
||||
$$$hostConfig.getEventTargetChildElement;
|
||||
|
||||
// -------------------
|
||||
// Mutation
|
||||
|
|
@ -84,6 +86,9 @@ export const hideInstance = $$$hostConfig.hideInstance;
|
|||
export const hideTextInstance = $$$hostConfig.hideTextInstance;
|
||||
export const unhideInstance = $$$hostConfig.unhideInstance;
|
||||
export const unhideTextInstance = $$$hostConfig.unhideTextInstance;
|
||||
export const commitTouchHitTargetUpdate =
|
||||
$$$hostConfig.commitTouchHitTargetUpdate;
|
||||
export const commitEventTarget = $$$hostConfig.commitEventTarget;
|
||||
|
||||
// -------------------
|
||||
// Persistence
|
||||
|
|
|
|||
|
|
@ -14,6 +14,18 @@ import {REACT_EVENT_TARGET_TOUCH_HIT} from 'shared/ReactSymbols';
|
|||
|
||||
import {enableEventAPI} from 'shared/ReactFeatureFlags';
|
||||
|
||||
type EventTargetChildElement = {
|
||||
type: string,
|
||||
props: null | {
|
||||
style?: {
|
||||
position?: string,
|
||||
bottom?: string,
|
||||
left?: string,
|
||||
right?: string,
|
||||
top?: string,
|
||||
},
|
||||
},
|
||||
};
|
||||
export type Type = string;
|
||||
export type Props = Object;
|
||||
export type Container = {|
|
||||
|
|
@ -170,12 +182,6 @@ export function createInstance(
|
|||
hostContext: Object,
|
||||
internalInstanceHandle: Object,
|
||||
): Instance {
|
||||
if (__DEV__ && enableEventAPI) {
|
||||
warning(
|
||||
hostContext !== EVENT_TOUCH_HIT_TARGET_CONTEXT,
|
||||
'validateDOMNesting: <TouchHitTarget> must not have any children.',
|
||||
);
|
||||
}
|
||||
return {
|
||||
type,
|
||||
props,
|
||||
|
|
@ -233,10 +239,6 @@ export function createTextInstance(
|
|||
internalInstanceHandle: Object,
|
||||
): TextInstance {
|
||||
if (__DEV__ && enableEventAPI) {
|
||||
warning(
|
||||
hostContext !== EVENT_TOUCH_HIT_TARGET_CONTEXT,
|
||||
'validateDOMNesting: <TouchHitTarget> must not have any children.',
|
||||
);
|
||||
warning(
|
||||
hostContext !== EVENT_COMPONENT_CONTEXT,
|
||||
'validateDOMNesting: React event components cannot have text DOM nodes as children. ' +
|
||||
|
|
@ -329,17 +331,62 @@ export function handleEventComponent(
|
|||
eventResponder: ReactEventResponder,
|
||||
rootContainerInstance: Container,
|
||||
internalInstanceHandle: Object,
|
||||
) {
|
||||
// TODO: add handleEventComponent implementation
|
||||
): void {
|
||||
// noop
|
||||
}
|
||||
|
||||
export function getEventTargetChildElement(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
): null | EventTargetChildElement {
|
||||
if (enableEventAPI) {
|
||||
if (type === REACT_EVENT_TARGET_TOUCH_HIT) {
|
||||
const {bottom, left, right, top} = props;
|
||||
|
||||
if (!bottom && !left && !right && !top) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: 'div',
|
||||
props: {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
zIndex: -1,
|
||||
bottom: bottom ? `-${bottom}px` : '0px',
|
||||
left: left ? `-${left}px` : '0px',
|
||||
right: right ? `-${right}px` : '0px',
|
||||
top: top ? `-${top}px` : '0px',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function handleEventTarget(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
parentInstance: Container,
|
||||
rootContainerInstance: Container,
|
||||
internalInstanceHandle: Object,
|
||||
) {
|
||||
if (type === REACT_EVENT_TARGET_TOUCH_HIT) {
|
||||
// TODO
|
||||
): boolean {
|
||||
if (enableEventAPI) {
|
||||
if (type === REACT_EVENT_TARGET_TOUCH_HIT) {
|
||||
// In DEV we do a computed style check on the position to ensure
|
||||
// the parent host component is correctly position in the document.
|
||||
if (__DEV__) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function commitEventTarget(
|
||||
type: Symbol | number,
|
||||
props: Props,
|
||||
instance: Instance,
|
||||
parentInstance: Instance,
|
||||
): void {
|
||||
// noop
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,3 +47,5 @@ export const didNotFindHydratableContainerSuspenseInstance = shim;
|
|||
export const didNotFindHydratableInstance = shim;
|
||||
export const didNotFindHydratableTextInstance = shim;
|
||||
export const didNotFindHydratableSuspenseInstance = shim;
|
||||
export const canHydrateTouchHitTargetInstance = shim;
|
||||
export const hydrateTouchHitTargetInstance = shim;
|
||||
|
|
|
|||
|
|
@ -30,3 +30,4 @@ export const finalizeContainerChildren = shim;
|
|||
export const replaceContainerChildren = shim;
|
||||
export const cloneHiddenInstance = shim;
|
||||
export const cloneHiddenTextInstance = shim;
|
||||
export const cloneHiddenTouchHitTargetInstance = shim;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user