Skip to content
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

Fix touch handlers and make them passive #706

Merged
merged 3 commits into from
Nov 16, 2023

Conversation

pradeepnschrodinger
Copy link
Collaborator

@pradeepnschrodinger pradeepnschrodinger commented Nov 10, 2023

Description

Touch support in FDT v2 is wonky.
There's multiple issues here:

problem 1

We're no longer passing down the touchEnabled prop to the cell renderers within FDT.
But the resizer plugin <ResizeCell /> still expects it, making it not work for touch devices.

problem 2

The touch/mouse related event listeners were treated as passive.
This is a problem with React not yet having an API for clients to specify event listener options.

FDT relies on stopping propagation or preventing default behavior of these events in various places, and they did not work as expected.
I'm fixing this by manually registering the handlers via addEventListener with the passive property turned OFF.
See facebook/react#6436

problem 3

Sometimes, the first touch events don't seem to work unless the user does a follow-up click.
One particular case is when the user clicks on the reorder knob; FDT will render a "drag proxy" which replaces the original cell thus making the original touch event (which relies on the original cell to be in the document tree) to not work properly.

The docs explain this nicely: https://developer.mozilla.org/en-US/docs/Web/API/Touch/target

The read-only target property of the Touch interface returns the (EventTarget) on which the touch contact started when it was first placed on the surface, even if the touch point has since moved outside the interactive area of that element or even been removed from the document. Note that if the target element is removed from the document, events will still be targeted at it, and hence won't necessarily bubble up to the window or document anymore. If there is any risk of an element being removed while it is being touched, the best practice is to attach the touch listeners directly to the target.

problem 4

<ReorderCell /> accidentally renders the children twice in some cases because we accidentally passed its' children to its child renderer...
Check this comment for details.

Motivation and Context

Fixes #705

How Has This Been Tested?

Tested with local examples, and also on codesandbox: https://codesandbox.io/s/fixed-data-table-2-touch-resizing-s22nn7
TODO: Test within LiveDesign

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.

@@ -156,6 +156,7 @@ class ColumnGroupsExample extends React.Component {
rowsCount={dataList.getSize()}
width={1000}
height={500}
touchScrollEnabled={true}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated resize-reorder column group example to work on touch devices

@@ -90,6 +90,7 @@ class ReorderExample extends React.Component {
rowsCount={dataList.getSize()}
width={1000}
height={500}
touchScrollEnabled={true}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated basic reorder example to work on touch devices

@@ -794,6 +794,7 @@ class FixedDataTable extends React.Component {
fixedRightColumns={fixedRightColumnGroups}
scrollableColumns={scrollableColumnGroups}
visible={true}
touchEnabled={touchScrollEnabled}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We forgot to pass touchEnabled to the group header...

@@ -205,6 +206,7 @@ class FixedDataTableCell extends React.Component {
);

let cellProps = {
touchEnabled,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This passes touchEnabled to users' cell renderers.

@@ -91,6 +91,7 @@ class FixedDataTableCellDefault extends React.Component {
isGroupHeader,
maxWidth,
minWidth,
touchEnabled,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid passing touchEnabled to the div.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it cause an issue?

@@ -115,11 +115,10 @@ class ReorderCell extends React.PureComponent {
onColumnReorderStart,
onColumnReorderEnd,
reorderStartEvent,
children,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This takes out children outside of props.

Without this change, ReorderCell calls up children(props) which'll in turn result in children being duplicated twice, cause props contains props.children 🤦 .
I saw this live on ColumnGroupsResizeReorderExample.

Comment on lines -174 to -175
onMouseDown={this.onMouseDown}
onTouchStart={this.onTouchStart}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need these to be defined as non-passive so that FDT can default-prevent or stop-propagate on them if required.
So I'm registering these manually by using addEventListener.

Comment on lines -87 to -90
onMouseDown={this.onMouseDown}
onTouchStart={this.props.touchEnabled ? this.onMouseDown : null}
onTouchEnd={this.props.touchEnabled ? this.suppressEvent : null}
onTouchMove={this.props.touchEnabled ? this.suppressEvent : null}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same reasoning as above.
I moved the registration to setupHandlers() and deregistration to cleanupHandlers.

Comment on lines +93 to +102
event.target,
'touchmove',
this._onMouseMove
this._onMouseMove,
{ passive: false }
);
this._eventTouchEndToken = EventListener.listen(
this._domNode,
event.target,
'touchend',
this._onMouseUp
this._onMouseUp,
{ passive: false }
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm registering these at the source element that triggered the touch (i.e, event.target) so we don't abruptly loose the touch handling just because the source element got removed.

TODO: Add a comment explaining this and link to moz docs.

* @return {object} Object with a `remove` method.
*/
listen(target, eventType, callback) {
listen(target, eventType, callback, options = {}) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added options param to let the caller customize the listener.

Copy link
Collaborator

@karry08 karry08 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@pradeepnschrodinger pradeepnschrodinger merged commit 11b82dd into master Nov 16, 2023
6 checks passed
@pradeepnschrodinger pradeepnschrodinger deleted the fix-touch-handlers branch November 16, 2023 07:22
@pradeepnschrodinger
Copy link
Collaborator Author

pradeepnschrodinger commented Nov 16, 2023

Released with v2.0.3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug regression issue that was "fixed" but got reopened later on
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Column resize doesn't properly work on mobile
4 participants