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

Sticky ScrollablePane new props 7.x #9525

Closed

Conversation

tharshada
Copy link
Contributor

@tharshada tharshada commented Jun 20, 2019

Pull request checklist

  • Addresses an existing issue: Fixes #0000
  • Include a change request file using $ npm run change

Description of changes

ScrollablePane has three children: contentContainer, stickyAbove container, stickyBelow container.

<ScrollablePane>
  <Sticky><Breadcrumb {...breadcrumbProps} /></Sticky>
  <DetailsList {...detailsListProps} />
</ScrollablePane>

Initially, all the content is inside contentContainer. Depending on stickyPosition prop, a Sticky component inside ScrollablePane can stickyToTop or stickyToBottom or both.

When a Sticky component is non-sticky, its actual content (<Breadcrumb {...breadcrumbProps} /> in the example above), is inside contentContainer.

When it becomes sticky, a placeHolder of offsetheight & scrollWidth same as the actual content of the component is placed inside contentContainer and the actual content is removed from it & added inside stickyAbove container. The contentContainer is responsible for providing scrollbars to scroll in case of overflow. If the placeholder width is not set, and the actual content was only reason for horizontal overflow, there would be no horizontal scrollbar to scroll and see the overflow content of Sticky component.

When a Sticky component has become sticky, its actual content won't be in sync to contentContainer.scrollLeft on its own. To solve this, on contentContainer scroll event is attached whose handler is responsible for syncing scroll for Sticky components. The other reason for having this handler is, as user scrolls vertically, Sticky components would become sticky or non-sticky. To determine if a Sticky component has become sticky/non-sticky, some calculations are done. When a component becomes non-sticky from sticky, its actual content is removed from stickyAbove container and added back to its original place in DOM.

If there are many Sticky components, they must be added in a particular order in sticky container so that they appear in the same order as in non-sticky state. To ensure this, a sorting is done based on distanceFromTop (calculated for non-sticky div), in Sticky componentDidUpdate if this value gets changed.

The new props are being added with an intention of optimizing performance.

  • experimentalLayoutImprovements?: boolean - If true, enable the following performance optimizations:

    • Don't rearrange/re-sort stickies in sticky container if distanceFromTop is changed. The sorting is done only one time for every Sticky componentDidMount. For the purpose of sorting, order?: number prop has been added to Sticky.
    • Removing actual content from its actual place in DOM, calculating offsetHeight &
      scrollWidth for placeholder and adding actual content to stickyContainer is not done every time Sticky component becomes sticky from non-sticky and vice-versa.
    • The other optimization is done to ensure the offsetHeight & scrollWidth calculations are not done and moving actual content to/from its actual place in DOM from/to stickyContainer is not done every time component becomes sticky/non-sticky. It is achieved by duplicating the actual content at its actual place in DOM and in the stickyContainer.
  • stickyAbove container contains sticky content for all Sticky component(s) for which stickyPosition prop is StickyPosition.Header. It assumes StickyOnScroll behavior for whichstickyPosition prop is StickyPosition.Header

  • stickyBelow container contains sticky content for all Sticky component(s) for which stickyPosition prop is StickyPosition.Footer.
    It assumes StickyAlways behavior for whichstickyPosition prop is StickyPosition.Footer.

  • StickyContainerBehaviorType options:

    • Default: This behavior impacts PLT. It would calculate
      distance from top for nonSticky div even if user interaction has not started. If there is a large DetailsList and the DetailsFooter should be sticky, this calculation helps to ensure the footer is sticky and visible at the bottom without any user interaction (scrolling).

    • StickyOnScroll: Sticky calculations are only started after the first vertical scroll, which means the calculations are skipped in PLT. After first scroll, the behavior is similar to Default. StickyOnScroll is most suitable for headers. If it was used on the DetailsFooter in the previous example, the footer would be visible only after first vertical scroll. Perf optimization assumes this behavior for all Sticky component having 'StickyPosition.Header'.

    • StickyAlways is the least expensive behavior. It ensures the component is always sticky independent of vertical scroll and will always appear at its sticky position. However, StickyAlways is still different from css postion: fixed. Suppose there are multiple components which have sticky behavior StickyAlways and the same sticky position (header/footer). In this case, StickyAlways would not need top or bottom values for these components. StickyAlways is most suitable if sticky position is Header or Footer and there is no content above or below the component respectively. Perf optimization assumes this behavior for all Sticky component having 'StickyPosition.Footer'.

  • Height of horizontal scrollbar & width of vertical scrollbar are needed to correctly position stickyBelow container and stickyAbove container respectively. If scrollbarVisibility={ScrollbarVisibility.always}, height and width calculations for scrollbars are not done for every mutation callback. Scrollbars' height & width are calculated only once per mount phase.

How new props are to be used

 <ScrollablePane 
   scrollbarVisibility={ScrollbarVisibility.always} 
   experimentalLayoutImprovements={true}
>
  <Sticky 
    order={1}
    stickyPosition={StickyPositionType.Header} 
    stickyBackgroundColor={getTheme().palette.white}
  ></Sticky>

  <Sticky 
    order={2}
    stickyPosition={StickyPositionType.Header} 
    stickyBackgroundColor={getTheme().palette.white}
  ></Sticky>

  <Sticky 
    order={1}
    stickyPosition={StickyPositionType.Footer} 
    stickyBackgroundColor={getTheme().palette.white}
  ></Sticky>
</ScrollablePane>

#9060

Microsoft Reviewers: Open in CodeFlow

@msft-github-bot
Copy link
Contributor

msft-github-bot commented Jun 20, 2019

Component Perf Analysis

No significant results to display.

All results

Scenario Master Ticks PR Ticks Status
BaseButton 828 848
BaseButton (experiments) 1125 1121
DefaultButton 1157 1107
DefaultButton (experiments) 2153 2234
DetailsRow 3684 3666
DetailsRow (fast icons) 3553 3558
DetailsRow without styles 3380 3347
DocumentCardTitle with truncation 34189 34268
MenuButton 1471 1477
MenuButton (experiments) 3957 3935
PrimaryButton 1317 1274
PrimaryButton (experiments) 2162 2203
SplitButton 3155 3107
SplitButton (experiments) 7678 7770
Stack 510 522
Stack with Intrinsic children 1254 1269
Stack with Text children 4686 4933
Text 448 438
Toggle 987 951
Toggle (experiments) 2533 2523
button 60 84

@size-auditor
Copy link

size-auditor bot commented Jun 20, 2019

Bundle test Size (minified) Diff from master
ScrollablePane 66.249 kB ExceedsTolerance     3.828 kB
Sticky 24.391 kB ExceedsTolerance     1.869 kB

ExceedsTolerance  Exceeds Tolerance     ExceedsBaseline  Exceeds Baseline     BelowBaseline  Below Baseline     1 kB = 1000 bytes

@tharshada tharshada mentioned this pull request Jun 20, 2019
2 tasks
@tharshada
Copy link
Contributor Author

@shagra-ms @KevinTCoughlin @leddie24 @ThomasMichon please review

@natalieethell
Copy link
Contributor

@tharshada and I were speaking offline about some snapshot test failures following the addition of a new ScrollablePane example (not yet added to this PR). It looks like the existing ScrollablePane example pages are included in the excludedExampleFiles of ComponentExamples.test.tsx.

https://github.com/OfficeDev/office-ui-fabric-react/blob/e18c895f03ee39ff1a3b964bc9a20ec42f64d53b/packages/office-ui-fabric-react/src/components/ComponentExamples.test.tsx#L80-L81

I think this is because of an issue with ref not being supported by react-test-renderer, as mentioned in more detail here: #6681. ComponentExamples.test is using react-test-rendered to test the example files, which I think is the issue.

@JasonGore it looks like you may have some more context here. If another ScrollablePane example is added, should the example file name also be added to excludedExampleFiles?

@JasonGore
Copy link
Member

That list is there for any tests causing issues.

@natalieethell
Copy link
Contributor

@JasonGore alright, thanks! @tharshada try adding it there and let me know if that helps

@size-auditor
Copy link

size-auditor bot commented Sep 4, 2019

Asset size changes

Project Bundle Baseline Size New Size Difference
office-ui-fabric-react ScrollablePane 59.489 kB 59.508 kB ExceedsBaseline     19 bytes
office-ui-fabric-react Sticky 22.533 kB 22.297 kB BelowBaseline     -236 bytes

ExceedsTolerance Over Tolerance (1024 B) ExceedsBaseline Over Baseline BelowBaseline Below Baseline New New Deleted  Removed 1 kB = 1000 B

Baseline commit: 30384d21f414d50394e8ce1657c9bbc986f93c4c (build)

@tharshada
Copy link
Contributor Author

@betrue-final-final there has been some changes in DetailsHeader component, the screeners for ScrollablePane component 've to be updated. Also, accept the new UI states for ScrollablePane component.

@natalieethell
Copy link
Contributor

@tharshada It looks like there's a merge conflict - want to try merging with the latest master and pushing to your branch?

@tharshada
Copy link
Contributor Author

@tharshada It looks like there's a merge conflict - want to try merging with the latest master and pushing to your branch?

I am working on minimizing the bundle size by making code changes, it 'll take time...

@ecraig12345
Copy link
Member

Now that @tharshada has added a PR description (I further updated the markdown to be easier to read) and all build steps are passing, can someone familiar with the area take another look at this PR? @dzearing @KevinTCoughlin @leddie24 @ThomasMichon

if (
_getStateStickyTopHeight(prevState) !== _getStateStickyTopHeight(state) ||
_getStateStickyBottomHeight(prevState) !== _getStateStickyBottomHeight(state)
) {
this.notifySubscribers();
}

this._async.setTimeout(this._onWindowResize, 0);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

!this._perf() && this._async.setTimeout(this._onWindowResize, 0);

* for no Sticky component, prop 'stickyPosition' is 'StickyPositionType.Both'.
*
* It assumes:
* 1. 'OnScroll' sticky behavior for all Sticky components, props
Copy link
Contributor Author

Choose a reason for hiding this comment

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

modify comment to- 'OnScroll' sticky behavior for all Sticky components, having 'stickyPosition' prop equal to StickyPosition.Header

* It assumes:
* 1. 'OnScroll' sticky behavior for all Sticky components, props
* 'stickyPosition' is 'StickyPositionType.Header',
* 2. 'Always' sticky behavior for all Sticky components, prop
Copy link
Contributor Author

Choose a reason for hiding this comment

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

modify comment same as above


public render(): JSX.Element {
const { items } = this.state;
const stickyBackgroundColor = getTheme().palette.red;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

getTheme().palette.white;

* Returns elem.offsetParent
* @param elem
*/
function _getoffsetParent(elem: HTMLElement): Element | null {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

change to _getOffsetParent

backgroundColor: stickyBackgroundColor
};
}
// debatable
Copy link
Contributor Author

Choose a reason for hiding this comment

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

remove this

@micahgodbolt
Copy link
Member

@tharshada is this PR still active? Can we close it down until you're ready to work on it again?

@tharshada tharshada closed this Dec 5, 2019
@tharshada
Copy link
Contributor Author

@tharshada is this PR still active? Can we close it down until you're ready to work on it again?

done

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

Successfully merging this pull request may close these issues.