Ensure TouchHitTarget element is server side rendered with hit slop (#15385)
* Follow up to 15381 * Add back in hit slop properties * Prettier * Fix lint * move hydration update out of DEV block * Remove pointer-events:auto
This commit is contained in:
parent
2e5d1a8b9e
commit
d38cfd452f
|
@ -97,6 +97,7 @@ let didWarnShadyDOM = false;
|
|||
const DANGEROUSLY_SET_INNER_HTML = 'dangerouslySetInnerHTML';
|
||||
const SUPPRESS_CONTENT_EDITABLE_WARNING = 'suppressContentEditableWarning';
|
||||
const SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
|
||||
const HYDRATE_TOUCH_HIT_TARGET = 'hydrateTouchHitTarget';
|
||||
const AUTOFOCUS = 'autoFocus';
|
||||
const CHILDREN = 'children';
|
||||
const STYLE = 'style';
|
||||
|
@ -1031,6 +1032,8 @@ export function diffHydratedProperties(
|
|||
}
|
||||
ensureListeningTo(rootContainerElement, propKey);
|
||||
}
|
||||
} else if (enableEventAPI && propKey === HYDRATE_TOUCH_HIT_TARGET) {
|
||||
updatePayload = [STYLE, rawProps.style];
|
||||
} else if (
|
||||
__DEV__ &&
|
||||
// Convince Flow we've calculated it (it's DEV-only in this method.)
|
||||
|
|
|
@ -949,11 +949,14 @@ export function getEventTargetChildElement(
|
|||
style: {
|
||||
position: 'absolute',
|
||||
zIndex: -1,
|
||||
pointerEvents: null,
|
||||
bottom: bottom ? `-${bottom}px` : '0px',
|
||||
left: left ? `-${left}px` : '0px',
|
||||
right: right ? `-${right}px` : '0px',
|
||||
top: top ? `-${top}px` : '0px',
|
||||
},
|
||||
hydrateTouchHitTarget: true,
|
||||
suppressHydrationWarning: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1173,15 +1173,24 @@ class ReactDOMServerRenderer {
|
|||
elementType.$$typeof === REACT_EVENT_TARGET_TYPE &&
|
||||
elementType.type === REACT_EVENT_TARGET_TOUCH_HIT
|
||||
) {
|
||||
// We do not render a hit slop element anymore. Instead we rely
|
||||
// on hydration adding in the hit slop element. The previous
|
||||
// logic had a bug where rendering a hit slop at SSR meant that
|
||||
// mouse events incorrectly registered events on the hit slop
|
||||
// even though it designed to be used for touch events only.
|
||||
// The logic that filters out mouse events from the hit slop
|
||||
// is handled in event responder modules, which only get
|
||||
// initialized upon hydration.
|
||||
return '';
|
||||
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;pointer-events:none;z-index:-1;bottom:` +
|
||||
`${bottomString};left:${leftString};right:${rightString};top:${topString}"></div>`
|
||||
);
|
||||
}
|
||||
const nextChildren = toArray(
|
||||
((nextChild: any): ReactElement).props.children,
|
||||
|
|
|
@ -219,6 +219,7 @@ const properties = {};
|
|||
'suppressContentEditableWarning',
|
||||
'suppressHydrationWarning',
|
||||
'style',
|
||||
'hydrateTouchHitTarget',
|
||||
].forEach(name => {
|
||||
properties[name] = new PropertyInfoRecord(
|
||||
name,
|
||||
|
|
|
@ -512,9 +512,46 @@ describe('TouchHitTarget', () => {
|
|||
ReactDOM.render(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe(
|
||||
'<div style="position: relative; z-index: 0;"><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>',
|
||||
'<div style="position: relative; z-index: 0;"><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 style={{position: 'relative', zIndex: 0}}>
|
||||
<TouchHitTarget />
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.innerHTML = '<div style="position:relative;z-index:0"></div>';
|
||||
ReactDOM.hydrate(<Test />, container);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(container.innerHTML).toBe(
|
||||
'<div style="position:relative;z-index:0"></div>',
|
||||
);
|
||||
|
||||
const Test2 = () => (
|
||||
<EventComponent>
|
||||
<div style={{position: 'relative', zIndex: 0}}>
|
||||
<TouchHitTarget top={10} left={10} right={10} bottom={10} />
|
||||
</div>
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
const container2 = document.createElement('div');
|
||||
container2.innerHTML =
|
||||
'<div style="position:relative;z-index:0"><div style="position:absolute;pointer-events:none;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 style="position:relative;z-index:0"><div style="position: absolute; z-index: -1; ' +
|
||||
'bottom: -10px; left: -10px; right: -10px; top: -10px;"></div></div>',
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -565,7 +602,7 @@ describe('TouchHitTarget', () => {
|
|||
expect(output).toBe('<div></div>');
|
||||
});
|
||||
|
||||
it('should render a TouchHitTarget without hit slop values', () => {
|
||||
it('should render a TouchHitTarget with hit slop values', () => {
|
||||
const Test = () => (
|
||||
<EventComponent>
|
||||
<div>
|
||||
|
@ -575,7 +612,10 @@ describe('TouchHitTarget', () => {
|
|||
);
|
||||
|
||||
let output = ReactDOMServer.renderToString(<Test />);
|
||||
expect(output).toBe('<div></div>');
|
||||
expect(output).toBe(
|
||||
'<div><div style="position:absolute;pointer-events:none;z-index:-1;' +
|
||||
'bottom:-10px;left:-10px;right:-10px;top:-10px"></div></div>',
|
||||
);
|
||||
|
||||
const Test2 = () => (
|
||||
<EventComponent>
|
||||
|
@ -586,7 +626,10 @@ describe('TouchHitTarget', () => {
|
|||
);
|
||||
|
||||
output = ReactDOMServer.renderToString(<Test2 />);
|
||||
expect(output).toBe('<div></div>');
|
||||
expect(output).toBe(
|
||||
'<div><div style="position:absolute;pointer-events:none;z-index:-1;' +
|
||||
'bottom:-10px;left:0px;right:0x;top:0px"></div></div>',
|
||||
);
|
||||
|
||||
const Test3 = () => (
|
||||
<EventComponent>
|
||||
|
@ -597,7 +640,10 @@ describe('TouchHitTarget', () => {
|
|||
);
|
||||
|
||||
output = ReactDOMServer.renderToString(<Test3 />);
|
||||
expect(output).toBe('<div></div>');
|
||||
expect(output).toBe(
|
||||
'<div><div style="position:absolute;pointer-events:none;z-index:-1;' +
|
||||
'bottom:-4px;left:-2px;right:-3px;top:-1px"></div></div>',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue