Skip to content

DetailsList: enabling Shimmer.#4347

Merged
ThomasMichon merged 13 commits intomicrosoft:masterfrom
Vitalius1:v-vibr/DetailsList-Shimmer
Apr 2, 2018
Merged

DetailsList: enabling Shimmer.#4347
ThomasMichon merged 13 commits intomicrosoft:masterfrom
Vitalius1:v-vibr/DetailsList-Shimmer

Conversation

@Vitalius1
Copy link
Copy Markdown
Contributor

Pull request checklist

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

Description of changes

  1. Changes DetailsList by giving it a new prop enableShimmer and changes the existing one onRenderMissingItem to enable the use of the ShimmerComponent by leveraging them.
  2. Gives DetailsRow and DetailsRowFields a new prop isShimmer in order to do some logic and enable the styling added to the DetailsRow.scss to recreate the visuals proposed by designers for a basic default shimmer.
  3. Sets up an example in experiments package for Shimmer used within DetilsList

Focus areas to test

Need some feedback on the proposed changes.

@Vitalius1
Copy link
Copy Markdown
Contributor Author

I had to change the example page for the Shimmer using the changes I am proposing because the build was breaking because of example page trying to import the DetailsList using a relative path as oppose to using import {...} from 'office-ui-fabric-react';

Have commented out the code which shows how we would use the changes to enable use of Shimmer with DetailsList.

@atneik atneik requested review from atneik and dzearing March 23, 2018 03:56
@atneik atneik requested a review from ThomasMichon March 23, 2018 19:16
@Jahnp
Copy link
Copy Markdown
Member

Jahnp commented Mar 23, 2018

Awesome. Might be good if @aditima could take a look at this as well.

Copy link
Copy Markdown
Contributor

@atneik atneik 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! Get an approval from Aditi / Thomas too

buildColumns,
SelectionMode,
} from 'office-ui-fabric-react';
import { Toggle } from 'office-ui-fabric-react';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we just add Toggle in the above import?

Copy link
Copy Markdown
Contributor Author

@Vitalius1 Vitalius1 Mar 23, 2018

Choose a reason for hiding this comment

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

This import was used for the example page when I was importing DetailsList using a relative path. When the changes will be merged, this example page will be revised and I will make sure all imports are correct.

item.thumbnail = randomFileType.url;
});
}
let { isDataLoaded, items } = this.state;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Whats the point in getting isDataLoaded and items state if we are just going to re-assign them

Copy link
Copy Markdown
Contributor Author

@Vitalius1 Vitalius1 Mar 23, 2018

Choose a reason for hiding this comment

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

That one is used for simulating asynchronous loading which is triggered by the toggle switch in this particular example. So basically it is used for the toggle switch.


// 40px to take into account the checkbox of items if present.
.shimmerLeftBorder {
border-left: 40px solid $detailsList-item-default-background-color;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You might need to use @include border-* mixin here and above so that it works as expected in RTL (right-to-left)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Included the mixin and it works like a charm!

/**
* If set to true and we provide an empty array to DetailsList while waiting the API call, it will render 10 shimmer lines.
* When data comes back check if the array of items from the data source is empty.
* If so, we need to provide a false value to this prop to prevent continuos shimmer animation in case you acces an empty folder.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: typo
Also not sure if we need to mention how it should be used in a particular case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Well I just wanted to make sure users know what would happen if the folder they open is empty when the API call is back but the 10 lines of shimmer will infinitely run. What would you suggest?


cellContent = render ? render(item, itemIndex, column) : this._getCellText(item, column);
cellContent = render ?
!isShimmer ?
Copy link
Copy Markdown
Contributor

@atneik atneik Mar 23, 2018

Choose a reason for hiding this comment

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

Could do this with a single ternary conditional

@atneik atneik requested a review from aditima March 23, 2018 20:13
@dzearing
Copy link
Copy Markdown
Member

@ThomasMichon and @aditima FYI

@dzearing
Copy link
Copy Markdown
Member

Also @cschleiden

checkboxCellClassName?: string;
rowFieldsAs?: React.StatelessComponent<IDetailsRowFieldsProps> | React.ComponentClass<IDetailsRowFieldsProps>;
className?: string;
isShimmer?: boolean;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can you just call this shimmer?

<DetailsRow shimmer />

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changed from isShimmer to shimmer

} from 'office-ui-fabric-react/lib/HoverCard';
import { createListItems } from '@uifabric/example-app-base';
import './Shimmer.Example.scss';
// import {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

undo this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@cschleiden by undo you mean remove the commented import?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

yep :)

/**
* If set to true and we provide an empty array to DetailsList while waiting the API call, it will render 10 shimmer lines.
* When data comes back check if the array of items from the data source is empty.
* If so, we need to provide a false value to this prop to prevent continuos shimmer animation in case you acces an empty folder.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

spelling

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks...


/**
* If set to true and we provide an empty array to DetailsList while waiting the API call, it will render 10 shimmer lines.
* When data comes back check if the array of items from the data source is empty.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure why the details list should care about any API calls

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changed the language as I was thinking more in terms of Items-scope in odsp-common when wrote this explanation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

thanks!


if (enableShimmer) {
this._shimmerInitialItems = new Array(SHIMMER_INITIAL_ITEMS);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this is done for every render call?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Moved it in the constructor. Good catch... It was called on every render )

@Vitalius1
Copy link
Copy Markdown
Contributor Author

@cschleiden @aditima @ThomasMichon @dzearing @atneik
When Shimmer Component will be mature enough to be moved from experiments to office-ui-fabric-react package, I was thinking integrating it internally in DetailsList so it could be switched on by simply providing enableShimmer prop without leveraging onRenderMissingItem. Would that raise any concerns of the bundle size?

Vitalie Braga added 2 commits March 26, 2018 17:33
@Vitalius1
Copy link
Copy Markdown
Contributor Author

@dzearing @cschleiden @aditima @ThomasMichon @atneik
Any more changes need to be done? Need this to unblock integration to itemsScope. Will be happy to address any concerns.

ref={ this._list }
role='presentation'
items={ items }
items={ enableShimmer && !items.length ? this._shimmerInitialItems : items }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What happens here when the items array is actually just []? Let's say I make an async call, while it's doing that I want to display the shimmer, but then I just get 0 results and want to display an empty list? Will I have to modify the enableShimmer prop in that case (make it dependent on the loading status) or can we come up with a better interface?

Or, as a cheap fix, don't check for empty length but null?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@cschleiden From my testing with DetailsList it seems that it always needs at least an empty array in order for it to mount. If you provide a null or undefined to items it breaks during the constructor phase and more specific when creating a new Selection on line 123. So checking for null is not an option cause it will never be null. Correct me if I'm wrong...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

My suggestion to @Vitalius1 was that to indicate that [] is legitimate, you'll need to set enableShimmer={ false } if you pass items={ [] }.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If you think about it shimmer should be dependent on loading status. After all it's a placeholder while waiting for data. The enableShimmer right now all is doing is creating an array of 10 null values so that the onRenderMissingItem we provide would render 10 shimmer lines. That number it's an average was discussed with designers as we don't know how many items are coming.

$compactRowVerticalPadding: 6px;
$isPaddedMargin: 24px;
$rowShimmerHorizontalBorder: $rowHorizontalPadding;
$rowShimmerVerticalBorder: ($rowHeight - 7px) / 2;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we have a var for 7px? What does it represent?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This represents the height of the shimmering lines to render. I assume it's logical to be moved to a var.

/>
);
// Rendering Shimmer Animation outside the focus zone
if (shimmer) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is the row component the right place for this? Or should we go one level higher and just not render the row and instead render that shimmer component from the detailslist?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We intend to use DetailsRow in the host application so that we can leverage the DetailsRowFields styling to look as we have columns with shimmer lines not just a single line for the entire width of the row. So we need this in here. Eventually I think the shimmer will be moved inside the detailsList so that we are not exposing DetailsRow to the host.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@cschleiden As of now the Shimmer component can not be imported to DetailsList because it's part of experiments package, which is not a dependency of office-ui-fabric-react.
When the shimmer will be mature enough to be moved along side with other Fabric components I intend to use it internally in DetailsList and even then we should check if the bundle size of the DetailsList is going to be something to worry about.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wait, but shimmer will still be included in the bundle?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@cschleiden Not sure what do you mean? How it will be included if I am not importing it anywhere. As of now it's not included, but in the future we might do that...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You're right my bad. I guess then I don't really understand your approach for including it in detailslist... Why do we have to have that 10 item array there if the rendering is the user's responsibility? I could just do

items={isLoading ? Array(10).fill(null) : realItems}

wherever I use the List? And nothing would have to change

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@cschleiden
I think we took this decision for consistency. User should not decide how many lines of shimmer should be rendered in the beginning. DetailsList will always give them 10 lines as we don't know how many realItems will eventually be rendered. I also will add a fading-out to bottom applying a decreasing opacity in styles based on this number (this will remove the expectancy of the user of how many items to wait even if we have 10 lines of shimmer). Without mergeStyle I can not pass a number the user will give me to .sass, so if I know a hardcoded number I could do a loop inside .sass and apply this opacity.

P.S. this number could change as soon as we can demo this to designers.


return onRenderRow({
const rowProps = {
item: item,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

rowProps: IDetailsRowProps
to get type enforcement.

minimumPixelsForDrag: props.minimumPixelsForDrag
}) : null;
this._initialFocusedIndex = props.initialFocusedIndex;
this._shimmerInitialItems = props.enableShimmer ? new Array(SHIMMER_INITIAL_ITEMS) : [];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do

const SHIMMER_ITEMS = new Array(SHIMMER_INITIAL_ITEMS);

at the top of the file. Just re-use the const in the render below.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Moved it to constant as requested.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@cschleiden @ThomasMichon
I agree with Thomas the the host application should have the control to switch off the enableShimmer because it's the only one who knows about the loading status. And because Shimmer it's a placeholder for while waiting for that status to change it should be flipped on/off by the user.

As of now we are using the existing prop onRenderMissingItem, but later we intend to move it in DetailsList and all the host application will need to do is to on/off the shimmer

ref={ this._list }
role='presentation'
items={ items }
items={ enableShimmer && !items.length ? this._shimmerInitialItems : items }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

My suggestion to @Vitalius1 was that to indicate that [] is legitimate, you'll need to set enableShimmer={ false } if you pass items={ [] }.

@Vitalius1
Copy link
Copy Markdown
Contributor Author

@cschleiden @aditima @ThomasMichon @dzearing @atneik
Could anyone unblock this PR or decide what we do further? It's blocking me to continue with the Shimmer Integration.

@cschleiden cschleiden dismissed their stale review April 2, 2018 21:45

Remove blocking review

@ThomasMichon ThomasMichon merged commit 0771cc2 into microsoft:master Apr 2, 2018
@Vitalius1 Vitalius1 deleted the v-vibr/DetailsList-Shimmer branch April 12, 2018 23:42
@microsoft microsoft locked as resolved and limited conversation to collaborators Aug 31, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants