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

Ability to provide custom scrollbars: #507

Merged
merged 1 commit into from
Mar 2, 2020
Merged

Conversation

rtrg
Copy link
Contributor

@rtrg rtrg commented Dec 10, 2019

Description

This PR provides ability to pass custom scrollbars to FDT.
To disable fdt default scrollbars, need to pass defaultScrollbars prop as false
Can pass custom scrollbars by passing scrollbarX, scrollbarY.
To update custom scrollbars state or trigger fdt scroll, pass onScrollBarsUpdate prop (function) which gets necessary arguments.

How Has This Been Tested?

Made sure all test cases are passing, by updating necessary tests.
Tested manually in examples that scrolling works.

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.

Copy link
Member

@wcjordan wcjordan left a comment

Choose a reason for hiding this comment

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

Thanks for the PR! This looks great, and inline with were we'd like to go. I added a few suggestions, but nothing major / blocking.

I also noticed that the Reorderable Columns example seems broken on this branch and doesn't show the unfrozen columns. Could you take a look and see what the issue might be? Thanks.

@@ -467,6 +493,9 @@ class FixedDataTable extends React.Component {
keyboardPageEnabled: false,
touchScrollEnabled: false,
stopScrollPropagation: false,
defaultScrollbars: true, // For documentation purpose only
Copy link
Member

Choose a reason for hiding this comment

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

Was React complaining about these missing here? If not, I recommend we remove from here, and then update the PropTypes above with a comment like

TODO (jordan) Remove propType of defaultScrollbars without losing documentation (handled in FDTContainer.js)

That way it will be similar to other cases we have.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

intention was to show default values in documentation, I made suggested changes.

@@ -613,6 +642,10 @@ class FixedDataTable extends React.Component {
this._contentHeight = contentHeight;
}

shouldComponentUpdate(nextProps) {
Copy link
Member

Choose a reason for hiding this comment

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

Was this necessary to fix issues, or just a nice perf enhancement here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not specific to this PR, in general good to have.

Copy link
Collaborator

Choose a reason for hiding this comment

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

FixedDataTable has a few objects being passed as props. So I suggest removing the shouldComponentUpdate check here, especially since it's not required by the PR.

If we are absolutely certain that the passed in objects are immutable and that they won't change in the future, then we could have this in.

scrollToY: this._scrollToY
};
if (!shallowEqual(this.previousScrollState, newScrollState)) {
this.props.onScrollBarsUpdate(newScrollState);
Copy link
Member

Choose a reason for hiding this comment

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

This is a lot to report to the callback here. Is all of this information necessary, or could we trim it down?

I'm concerned if we expose too many internal details, we won't be able to modify the API easily in the future. Not an issue if we do need to expose all this, but if there are any ways we could slim the API down, it's worth investigating.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe we have to pass visbileRowsHeight, allRowsHeight to determine scrollbarY height and its thumb height. scrollY to position scrollbarY thumb. scrollbarYOffsetTop to position scrollbarY itself. Similarly for scrollbarX

We can avoid scrollToX and scrollToY as they can be achieved via scrollTo, but I added them as browser native scrollbar has those methods. Please let me know your opinion here if its ok to have only scrollTo

Copy link
Member

Choose a reason for hiding this comment

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

Okay sounds good. I think passing both scrollToX and scrollToY is fine.

I wish we didn't need to pass scrollbarXOffsetTop & scrollbarYOffsetTop... But understandable if there's no other way.

We also might want to consider renaming visibleRowsHeight & allRowsHeight. Maybe to viewportHeight & contentHeight? Likewise for columns / width.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We also might want to consider renaming visibleRowsHeight & allRowsHeight. Maybe to viewportHeight & contentHeight? Likewise for columns / width.

This makes much more sense, renamed these.

@rtrg
Copy link
Contributor Author

rtrg commented Dec 17, 2019

I also noticed that the Reorderable Columns example seems broken on this branch and doesn't show the unfrozen columns. Could you take a look and see what the issue might be? Thanks.

Thanks for pointing out, missed passing scrollbarYWidth prop to row. This is fixed now.

Copy link
Member

@wcjordan wcjordan left a comment

Choose a reason for hiding this comment

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

This looks good from my end. @pradeepnschrodinger & @mycrobe do you have any concerns?

@mycrobe
Copy link
Collaborator

mycrobe commented Jan 7, 2020

no concerns from me

@wcjordan
Copy link
Member

@pradeepnschrodinger do you have any concerns here? If not can we go ahead and merge?

Side note, does it make sense to start an 1.1 branch at target this for that so master can handle any v1.0 bug fixes? Or start a 1.0 branch for bug fixes?

@pradeepnschrodinger
Copy link
Collaborator

@pradeepnschrodinger do you have any concerns here? If not can we go ahead and merge?

This looks very good. I think I'll take a deeper look by end of this week.
Sorry for the big delays.

Side note, does it make sense to start an 1.1 branch at target this for that so master can handle any v1.0 bug fixes? Or start a 1.0 branch for bug fixes?

Yea, let's do a feature branch for custom scrollbars.
Currently master doesn't seem to have a lot of bugs, so let's keep it that way until we have a stable support for custom scrollbars (maybe with some docs and examples to finish off before merging).

This way we will also avoid the case where users complain about any API changes because they had already started using custom scrollbars in an early state through master.

Copy link
Collaborator

@pradeepnschrodinger pradeepnschrodinger left a comment

Choose a reason for hiding this comment

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

Just a few comments.

};

_onVerticalScroll = (/*number*/ scrollPos) => {
if (this.state.scrollToY) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Will this work if user scrolls to value 0 ?
I think it's better to check for undefined instead (if the check is required at all).

(similar for _onHorizontalScroll)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will take care of this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually we are checking this.state.scrollToY and not scrollPos, this should be fine but still took care of comparing with undefined

@@ -613,6 +642,10 @@ class FixedDataTable extends React.Component {
this._contentHeight = contentHeight;
}

shouldComponentUpdate(nextProps) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

FixedDataTable has a few objects being passed as props. So I suggest removing the shouldComponentUpdate check here, especially since it's not required by the PR.

If we are absolutely certain that the passed in objects are immutable and that they won't change in the future, then we could have this in.

</ScrollContainer>
);
}
return fdt;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm a bit confused on how the custom scrollbars get "props" at this level.

When defaultScrollbars is on, we pass this.props to the default scrollbars by

if (this.props.defaultScrollbars) {
      return (
          <ScrollContainer {...this.props}>
            {fdt}
          </ScrollContainer>
      );
    }

Similarly shouldn't we somehow also pass it to the custom scrollbars? Maybe we should avoid passing of props alltogether at this level, and ensure that <FixedDataTable /> passes everything that's required through onScrollBarsUpdate ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

here we are passing props to ScrollContainer to render FDT and also pass touchScrollEnabled and isRTL prop

in case of custom scrollbar people will have access to these props (as they are the one passing them to FDT) and can pass directly to scrollbars if required.

@@ -259,6 +259,8 @@ function setStateFromProps(state, props) {
newState.tableSize.useMaxHeight =
newState.tableSize.height === undefined;

newState.scrollbarXHeight = props.scrollbarXHeight;
Copy link
Collaborator

Choose a reason for hiding this comment

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

could you also add these entries to the getInitialState() function in reducers/index.js?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I keep default values as Scrollbar.SIZE?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

took care of this

@pradeepnschrodinger
Copy link
Collaborator

I'm a bit concerned on how the ScrollContainer component contains the FixedDataTable component, along with the fact that FixedDataTable has to manually update ScrollContainer as a side effect to it's own update.

Whenever user scrolls, a redux action is dispatched and the store is updated.
The updates in the view components goes as follows:
Store -> FixedDataTableContainer -> ScrollContainer -> FixedDataTable

Now this is fine, but the FixedDataTable will again update ScrollContainer's underlying state as a sideeffect through onScrollBarsUpdate. This'll cause yet another rerender:
ScrollContainer -> FixedDataTable

FixedDataTable is quite heavy and an extra render is something we should avoid. Also I'm not sure how fast it's to do these updates through componentDidUpdate.

Instead of all this, can't we ensure a one directional render flow by rendering the scrollbars within FixedDataTable itself?
FixedDataTableContainer -> FixedDataTable -> ScrollBars
FixedDataTable can simply pass all the scroll state details as props in this flow.

@wcjordan
Copy link
Member

@pradeepnschrodinger if we have FDT render the scrollbars itself, will that make it difficult to compose? Different scrollbar components will likely have different APIs and interactions. If we can hoist the composition outside of FDT and require the end user to compose them, then they can handle the integration between the details FDT exposes and the APIs of the scrollbars.

I'm guessing the concerns you've raised around re-rendering are part of why we're looking to add shouldComponentUpdate. Could we take steps to address your concerns with adding shouldComponentUpdate checks?

@rtrg
Copy link
Contributor Author

rtrg commented Jan 27, 2020

Regarding re-render concerns, can we extract out wrapper divfixedDataTableLayout/main (which is responsible for rendering scrollbars) out of FixedDataTable.js and then have ShouldComponentUpdate check in FixedDataTable?

With this, even if scrollbar state changes it doesn't re-render FixedDataTable.js and updates only scrollbars.

@wcjordan
Copy link
Member

How about we include the shouldComponentUpdate check in FixedDataTable, but make it so it always returns true by default, but can be configured to do the comparison if the feature flag is set to use it? Would that work @pradeepnschrodinger ? It seems like it wouldn't break existing table users, but would let us move towards the more optimized approach and get testing of this new scrollbar API.

@wcjordan
Copy link
Member

@rtrg @pradeepnschrodinger anything remaining to be done here before we merge?

@rtrg
Copy link
Contributor Author

rtrg commented Feb 13, 2020

@rtrg @pradeepnschrodinger anything remaining to be done here before we merge?

Took care of minor comments from my side

Copy link
Collaborator

@pradeepnschrodinger pradeepnschrodinger left a comment

Choose a reason for hiding this comment

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

Looks good to me. Apologies for the delays and thanks for follow ups :)

@pradeepnschrodinger
Copy link
Collaborator

@wcjordan , as offlined some time ago, should we cut off master and make branch v1.1 ? We'll merge this PR into it, and continue any related work on the v1.1 branch.

@wcjordan
Copy link
Member

Yep. Although now that I think about it, maybe we should cut v1.0 and have 1.1 work go to master? That would reflect the branching model we use for LiveDesign and may be less confusing.

To disable fdt default scrollbars, need to pass defaultScrollbars prop as false
Can pass custom scrollbars by passing scrollbarX, scrollbarY.
To update custom scrollbars state or trigger fdt scroll, pass onScrollBarsUpdate prop (function) which gets necessary arguments.
@wcjordan
Copy link
Member

v1.0 branch has now been cut. Any concerns with me merging this to master?

@pradeepnschrodinger
Copy link
Collaborator

No concerns from my side, thanks.

@wcjordan wcjordan merged commit 534d060 into schrodinger:master Mar 2, 2020
@wcjordan
Copy link
Member

wcjordan commented Mar 2, 2020

Awesome. I've merged!

@rtrg any thoughts on what next steps we should look at? Should we look to add an example using native scroll?

@rtrg
Copy link
Contributor Author

rtrg commented Mar 3, 2020

Awesome. I've merged!

Thanks!

@rtrg any thoughts on what next steps we should look at? Should we look to add an example using native scroll?

Yes, I will raise a PR for that soon.

@wcjordan
Copy link
Member

This is now released w/ v1.1.

@wcjordan
Copy link
Member

wcjordan commented Jun 4, 2020

@rtrg any chance you have any documentation or an example putting this new API to use which we could add to the project?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants