DetailsList: enabling Shimmer.#4347
Conversation
…he build trying to import directly from the path provided.
|
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 Have commented out the code which shows how we would use the changes to enable use of Shimmer with DetailsList. |
|
Awesome. Might be good if @aditima could take a look at this as well. |
atneik
left a comment
There was a problem hiding this comment.
Looks good! Get an approval from Aditi / Thomas too
| buildColumns, | ||
| SelectionMode, | ||
| } from 'office-ui-fabric-react'; | ||
| import { Toggle } from 'office-ui-fabric-react'; |
There was a problem hiding this comment.
Can we just add Toggle in the above import?
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Whats the point in getting isDataLoaded and items state if we are just going to re-assign them
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
You might need to use @include border-* mixin here and above so that it works as expected in RTL (right-to-left)
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
Nit: typo
Also not sure if we need to mention how it should be used in a particular case.
There was a problem hiding this comment.
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 ? |
There was a problem hiding this comment.
Could do this with a single ternary conditional
|
@ThomasMichon and @aditima FYI |
|
Also @cschleiden |
| checkboxCellClassName?: string; | ||
| rowFieldsAs?: React.StatelessComponent<IDetailsRowFieldsProps> | React.ComponentClass<IDetailsRowFieldsProps>; | ||
| className?: string; | ||
| isShimmer?: boolean; |
There was a problem hiding this comment.
can you just call this shimmer?
<DetailsRow shimmer />
There was a problem hiding this comment.
Changed from isShimmer to shimmer
| } from 'office-ui-fabric-react/lib/HoverCard'; | ||
| import { createListItems } from '@uifabric/example-app-base'; | ||
| import './Shimmer.Example.scss'; | ||
| // import { |
There was a problem hiding this comment.
@cschleiden by undo you mean remove the commented import?
| /** | ||
| * 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. |
|
|
||
| /** | ||
| * 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. |
There was a problem hiding this comment.
I'm not sure why the details list should care about any API calls
There was a problem hiding this comment.
Changed the language as I was thinking more in terms of Items-scope in odsp-common when wrote this explanation.
|
|
||
| if (enableShimmer) { | ||
| this._shimmerInitialItems = new Array(SHIMMER_INITIAL_ITEMS); | ||
| } |
There was a problem hiding this comment.
this is done for every render call?
There was a problem hiding this comment.
Moved it in the constructor. Good catch... It was called on every render )
|
@cschleiden @aditima @ThomasMichon @dzearing @atneik |
… Change some language for enableShimmer description.
|
@dzearing @cschleiden @aditima @ThomasMichon @atneik |
| ref={ this._list } | ||
| role='presentation' | ||
| items={ items } | ||
| items={ enableShimmer && !items.length ? this._shimmerInitialItems : items } |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
@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...
There was a problem hiding this comment.
My suggestion to @Vitalius1 was that to indicate that [] is legitimate, you'll need to set enableShimmer={ false } if you pass items={ [] }.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Should we have a var for 7px? What does it represent?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
@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.
There was a problem hiding this comment.
Wait, but shimmer will still be included in the bundle?
There was a problem hiding this comment.
@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...
There was a problem hiding this comment.
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
There was a problem hiding this comment.
@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, |
There was a problem hiding this comment.
rowProps: IDetailsRowProps
to get type enforcement.
| minimumPixelsForDrag: props.minimumPixelsForDrag | ||
| }) : null; | ||
| this._initialFocusedIndex = props.initialFocusedIndex; | ||
| this._shimmerInitialItems = props.enableShimmer ? new Array(SHIMMER_INITIAL_ITEMS) : []; |
There was a problem hiding this comment.
Do
const SHIMMER_ITEMS = new Array(SHIMMER_INITIAL_ITEMS);at the top of the file. Just re-use the const in the render below.
There was a problem hiding this comment.
Moved it to constant as requested.
There was a problem hiding this comment.
@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 } |
There was a problem hiding this comment.
My suggestion to @Vitalius1 was that to indicate that [] is legitimate, you'll need to set enableShimmer={ false } if you pass items={ [] }.
|
@cschleiden @aditima @ThomasMichon @dzearing @atneik |
Pull request checklist
$ npm run changeDescription of changes
enableShimmerand changes the existing oneonRenderMissingItemto enable the use of the ShimmerComponent by leveraging them.isShimmerin 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.Focus areas to test
Need some feedback on the proposed changes.