Skip to content

Ensure TouchHitTarget element is server side rendered with hit slop #15385

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/react-dom/src/client/ReactDOMComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,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';
Expand Down Expand Up @@ -1028,6 +1029,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.)
Expand Down
3 changes: 3 additions & 0 deletions packages/react-dom/src/client/ReactDOMHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -948,11 +948,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,
},
};
}
Expand Down
27 changes: 18 additions & 9 deletions packages/react-dom/src/server/ReactPartialRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions packages/react-dom/src/shared/DOMProperty.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ const properties = {};
'suppressContentEditableWarning',
'suppressHydrationWarning',
'style',
'hydrateTouchHitTarget',
].forEach(name => {
properties[name] = new PropertyInfoRecord(
name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>',
);
});

Expand Down Expand Up @@ -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>
Expand All @@ -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>
Expand All @@ -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>
Expand All @@ -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>',
);
});
});
});