Skip to content

Commit

Permalink
List and other example updates (#8508)
Browse files Browse the repository at this point in the history
  • Loading branch information
ecraig12345 authored and dzearing committed Apr 1, 2019
1 parent ac9cc96 commit 07fddfc
Show file tree
Hide file tree
Showing 17 changed files with 527 additions and 550 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "office-ui-fabric-react",
"comment": "List and other example updates",
"type": "patch"
}
],
"packageName": "office-ui-fabric-react",
"email": "[email protected]"
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ When considering their place in a layout, contemplate the order in which a user

While buttons can technically be used to navigate a user to another part of the experience, this is not recommended unless that navigation is part of an action or their flow.

Note that both iconProps and menuIconProps take <a href='#/components/icon'>IIconProps</a> to specify name and type.
Note that both `iconProps` and `menuIconProps` take <a href='#/components/icon'>`IIconProps`</a> to specify icon name and type.
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import * as React from 'react';
import { Link } from 'office-ui-fabric-react/lib/Link';
import './Link.Example.scss';
import * as styles from './Link.Example.scss';

export class LinkBasicExample extends React.Component<any, any> {
public render(): JSX.Element {
return (
<div className="docs-LinkExample">
<div className={styles.linkExample}>
<span>When a link has an href, </span>
<Link href="http://dev.office.com/fabric/components/link">it renders as an anchor tag.</Link>
<span> Without an href, </span>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,35 @@
@import '../../../common/common';

// UHF overrides link color and state so we need to re-assign them for the website documentation
:global {
.docs-LinkExample {
.ms-Link {
// Element: ms-Link root component
.root {
color: $ms-color-themePrimary;
margin: 0;
overflow: inherit;
padding: 0;
text-overflow: inherit;
}

// State: The button is not disabled.
.isEnabled {
&:active,
&:hover,
&:active:hover {
color: $ms-color-themeDarker;
}
.linkExample {
:global(.ms-Link) {
// Element: ms-Link root component
.root {
color: $ms-color-themePrimary;
margin: 0;
overflow: inherit;
padding: 0;
text-overflow: inherit;
}

&:focus {
color: $ms-color-themePrimary;
}
// State: The button is not disabled.
.isEnabled {
&:active,
&:hover,
&:active:hover {
color: $ms-color-themeDarker;
}

// State: The button is disabled.
.isDisabled {
color: $ms-color-neutralTertiary;
pointer-events: none;
cursor: default;
&:focus {
color: $ms-color-themePrimary;
}
}

// State: The button is disabled.
.isDisabled {
color: $ms-color-neutralTertiary;
pointer-events: none;
cursor: default;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
List provides a base component for rendering large sets of items. It is agnostic of layout, the tile component used, and selection management. These concerns can be layered separately.

**Performance is important, and DOM content is expensive. Therefore limit what you render.** Unlike a simple for loop that renders all items in a set, a List uses ui virtualization. It only renders a subset of items, and as you scroll around, the subset of rendered content is shifted to what you're looking at. This gives a much better experience for large sets, especially when the per-item components are complex/render intensive/network intensive.
**Performance is important, and DOM content is expensive. Therefore, limit what you render.** List applies this principle by using UI virtualization. Unlike a simple `for` loop that renders all items in a set, a List only renders a subset of items, and as you scroll around, the subset of rendered content is shifted. This gives a much better experience for large sets, especially when the per-item components are complex/render-intensive/network-intensive.

Lists break down the set of items passed in into pages. Only pages within a "materialized window" are actually rendered. As that window changes due to scroll events, pages that fall outside that window are removed, and their layout space is remembered and pushed into spacer elements. This gives the user the experience of browsing massive amounts of content but only using a small number of actual elements. This gives the browser much less layout to resolve, and gives React DOM diffing much less content to worry about.
List breaks down the set of items passed in into pages. Only pages within a "materialized window" are actually rendered. As that window changes due to scroll events, pages that fall outside that window are removed, and their layout space is remembered and pushed into spacer elements. This gives the user the experience of browsing massive amounts of content but only using a small number of actual elements. This gives the browser much less layout to resolve, and gives React DOM diffing much less content to worry about.

Note: although `onRenderCell` is an optional member of `IListProps`, if you do not provide the method, the `List` will still attempt to render the `name` property for each object within the provided `items` array.
Note: if `onRenderCell` is not provided in `IListProps`, the List will attempt to render the `name` property for each object in the `items` array.

## My scrollable content isn't updating on scroll! What should I do?

Add the `data-is-scrollable="true"` attribute to your scrollable element containing the List.

By default, List will use the `BODY` element as the scrollable element. If you contain the List within a scrollable `DIV` using `overflow: auto` or `scroll`, the List needs to listen for scroll events on that element instead. On initialization, the List will traverse up the DOM looking for the first element with the `data-is-scrollable` attribute to know when element to listen to for knowing when to re-evaulate the visible window.
By default, List will use the `<body>` element as the scrollable element. If you contain the List within a scrollable `<div>` using `overflow: auto` or `scroll`, the List needs to listen for scroll events on that element instead. On initialization, the List will traverse up the DOM looking for the first element with the `data-is-scrollable` attribute to know which element to listen to for knowing when to re-evaulate the visible window.

## My List is not re-rendering when I mutate its items! What should I do?

To determine if the List should re-render its contents, the component performs a referential equality check within its `shouldComponentUpdate` method.
This is done to minimize the performance overhead associating with re-rendering the virtualized List pages, as recommended by the [React documentation](https://reactjs.org/docs/optimizing-performance.html#the-power-of-not-mutating-data).
To determine if the List should re-render its contents, the component performs a referential equality check on the `items` array in its `shouldComponentUpdate` method. This is done to minimize the performance overhead associating with re-rendering the virtualized List pages, as recommended by the [React documentation](https://reactjs.org/docs/optimizing-performance.html#the-power-of-not-mutating-data).

As a result of this implementation, the List will not determine it should re-render if the array values are mutated.
To avoid this problem, we recommend re-creating the items array backing the List by using a method such as `Array.prototype.concat` or ES6 spread syntax shown below:
As a result of this implementation, the List will not determine it should re-render if values _within_ the array are mutated. To avoid this problem, we recommend re-creating the `items` array using a method such as `Array.prototype.concat` or ES6 spread syntax shown below:

```tsx
public appendItems(): void {
Expand All @@ -36,4 +34,4 @@ public render(): JSX.Element {
}
```

By re-creating the items array without mutating the values, the List will correctly determine its contents have changed and that it should re-render the new values.
Since the `items` array has been re-created, the List will conclude that its contents have changed and it should re-render the new values.
Original file line number Diff line number Diff line change
@@ -1,48 +1,47 @@
@import '../../../common/common';

:global {
.ms-ListBasicExample-itemCell {
@include focus-border();

min-height: 54px;
padding: 10px;
box-sizing: border-box;
border-bottom: 1px solid $bodyDividerColor;
display: flex;
}
.itemCell {
@include focus-border();

min-height: 54px;
padding: 10px;
box-sizing: border-box;
border-bottom: 1px solid $bodyDividerColor;
display: flex;

.ms-ListBasicExample-itemCell:hover {
&:hover {
background: #EEE;
}
}

.ms-ListBasicExample-itemImage {
flex-shrink: 0;
}

.ms-ListBasicExample-itemContent {
@include margin-left(10px);
overflow: hidden;
flex-grow: 1;
}
.itemImage {
flex-shrink: 0;
}

.ms-ListBasicExample-itemName {
@include ms-font-xl;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.itemContent {
@include margin-left(10px);
overflow: hidden;
flex-grow: 1;
}

.ms-ListBasicExample-itemIndex {
font-size: $ms-font-size-s;
color: $ms-color-neutralTertiary;
margin-bottom: 10px;
}
.itemName {
@include ms-font-xl;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.ms-ListBasicExample-chevron {
align-self: center;
@include margin-left(10px);
color: $ms-color-neutralTertiary;
font-size: $ms-font-size-l;
flex-shrink: 0;
}
.itemIndex {
font-size: $ms-font-size-s;
color: $ms-color-neutralTertiary;
margin-bottom: 10px;
}

.chevron {
align-self: center;
@include margin-left(10px);
color: $ms-color-neutralTertiary;
font-size: $ms-font-size-l;
flex-shrink: 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { TextField } from 'office-ui-fabric-react/lib/TextField';
import { Image, ImageFit } from 'office-ui-fabric-react/lib/Image';
import { Icon } from 'office-ui-fabric-react/lib/Icon';
import { List } from 'office-ui-fabric-react/lib/List';
import './List.Basic.Example.scss';
import * as styles from './List.Basic.Example.scss';

export type IExampleItem = { name: string; thumbnail: string; description: string };

Expand Down Expand Up @@ -54,14 +54,14 @@ export class ListBasicExample extends React.Component<IListBasicExampleProps, IL

private _onRenderCell(item: IExampleItem, index: number | undefined): JSX.Element {
return (
<div className="ms-ListBasicExample-itemCell" data-is-focusable={true}>
<Image className="ms-ListBasicExample-itemImage" src={item.thumbnail} width={50} height={50} imageFit={ImageFit.cover} />
<div className="ms-ListBasicExample-itemContent">
<div className="ms-ListBasicExample-itemName">{item.name}</div>
<div className="ms-ListBasicExample-itemIndex">{`Item ${index}`}</div>
<div className="ms-ListBasicExample-itemDesc">{item.description}</div>
<div className={styles.itemCell} data-is-focusable={true}>
<Image className={styles.itemImage} src={item.thumbnail} width={50} height={50} imageFit={ImageFit.cover} />
<div className={styles.itemContent}>
<div className={styles.itemName}>{item.name}</div>
<div className={styles.itemIndex}>{`Item ${index}`}</div>
<div>{item.description}</div>
</div>
<Icon className="ms-ListBasicExample-chevron" iconName={getRTL() ? 'ChevronLeft' : 'ChevronRight'} />
<Icon className={styles.chevron} iconName={getRTL() ? 'ChevronLeft' : 'ChevronRight'} />
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,33 @@
@import '../../../common/common';

:global {
.ms-ListScrollingExample {
&-container {
overflow: auto;
max-height: 500px;
margin-top: 20px;
}
.container {
overflow: auto;
max-height: 500px;
margin-top: 20px;
}

&-itemCell {
.ms-ListScrollingExample-itemContent {
@include ms-font-m;
@include ms-normalize;
@include ms-clearfix;
position: relative;
display: block;
.itemContent {
@include ms-font-m;
@include ms-normalize;
@include ms-clearfix;
position: relative;
display: block;

border-left: 3px solid $ms-color-themePrimary;
padding-left: 27px; // Reduce padding to allow room for border.
border-left: 3px solid $ms-color-themePrimary;
padding-left: 27px; // Reduce padding to allow room for border.
}

&-odd {
height: 50px;
line-height: 50px;
background: $ms-color-neutralLighter;
}
.itemContentOdd {
height: 50px;
line-height: 50px;
background: $ms-color-neutralLighter;
}

&-even {
height: 25px;
line-height: 25px;
}
}
}
.itemContentEven {
height: 25px;
line-height: 25px;
}

&-selected {
background: lightblue;
}
}
.selected {
background: lightblue;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { DefaultButton } from 'office-ui-fabric-react/lib/Button';
import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
import { List, ScrollToMode } from 'office-ui-fabric-react/lib/List';
import { TextField } from 'office-ui-fabric-react/lib/TextField';
import './List.Scrolling.Example.scss';
import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox';
import * as styles from './List.Scrolling.Example.scss';

export type IExampleItem = { name: string };

Expand Down Expand Up @@ -72,7 +72,7 @@ export class ListScrollingExample extends React.Component<IListScrollingExampleP
onChange={this._onShowItemIndexInViewChanged}
/>
</div>
<div className="ms-ListScrollingExample-container" data-is-scrollable={true}>
<div className={styles.container} data-is-scrollable={true}>
<List ref={this._resolveList} items={items} getPageHeight={this._getPageHeight} onRenderCell={this._onRenderCell} />
</div>
</FocusZone>
Expand Down Expand Up @@ -123,14 +123,8 @@ export class ListScrollingExample extends React.Component<IListScrollingExampleP

private _onRenderCell = (item: IExampleItem, index: number): JSX.Element => {
return (
<div className="ms-ListScrollingExample-itemCell" data-is-focusable={true}>
<div
className={css(
'ms-ListScrollingExample-itemContent',
index % 2 === 0 && 'ms-ListScrollingExample-itemContent-even',
index % 2 === 1 && 'ms-ListScrollingExample-itemContent-odd'
)}
>
<div data-is-focusable={true}>
<div className={css(styles.itemContent, index % 2 === 0 ? styles.itemContentEven : styles.itemContentOdd)}>
{index} &nbsp; {item.name}
</div>
</div>
Expand Down
Loading

0 comments on commit 07fddfc

Please sign in to comment.