Skip to content

Commit

Permalink
Revert "Revert "Merge pull request #2160 from Shopify/resourcelist-em…
Browse files Browse the repository at this point in the history
…ptystate""

This reverts commit 6ec053f.
  • Loading branch information
chloerice committed Dec 5, 2019
1 parent 6ec053f commit d7ad6d7
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 9 deletions.
48 changes: 47 additions & 1 deletion src/components/ResourceList/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,53 @@ A resource list with simple items and no bulk actions, sorting, or filtering.
</Card>
```

### Resource List with selection and no bulk actions
### Resource list with empty state

Use to explain the purpose of a list of resources when no resources exist yet. This allows a smooth transition from a list in a loading state to a list where zero, one, or many resources exist.

```jsx
function ResourceListWithEmptyStateExample() {
const filters = [
{
key: 'fileType',
label: 'File type',
filter: <TextField label="File type" value="" labelHidden />,
shortcut: true,
},
];

const filterControl = (
<Filters disabled queryValue="" filters={filters} appliedFilters={[]} />
);

const emptyState = (
<EmptyState
heading="Upload a file to get started"
action={{content: 'Upload files'}}
image="https://cdn.shopify.com/s/files/1/2376/3301/products/file-upload-empty-state.png"
>
<p>
You can use the Files section to upload images, videos, and other
documents
</p>
</EmptyState>
);

return (
<Card>
<ResourceList
emptyState={emptyState}
items={[]}
renderItem={() => {}}
filterControl={filterControl}
resourceName={{singular: 'file', plural: 'files'}}
/>
</Card>
);
}
```

### Resource list with selection and no bulk actions

A resource list with simple items and selection.

Expand Down
23 changes: 17 additions & 6 deletions src/components/ResourceList/ResourceList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export interface ResourceListProps {
/** Item data; each item is passed to renderItem */
items: Items;
filterControl?: React.ReactNode;
/** The markup to render when no resources exist yet */
emptyState?: React.ReactNode;
/** Name of the resource, such as customers or products */
resourceName?: {
singular: string;
Expand Down Expand Up @@ -378,7 +380,9 @@ class ResourceList extends React.Component<CombinedProps, State> {
promotedBulkActions,
bulkActions,
filterControl,
emptyState,
loading,
hasMoreItems,
showHeader = false,
sortOptions,
sortValue,
Expand Down Expand Up @@ -471,9 +475,14 @@ class ResourceList extends React.Component<CombinedProps, State> {
<div className={styles['HeaderWrapper-overlay']} />
) : null;

const showEmptyState = filterControl && !this.itemsExist() && !loading;
const showNoResults =
filterControl && !this.itemsExist() && hasMoreItems && !loading;

const showEmptyState =
emptyState !== undefined && !this.itemsExist() && !hasMoreItems;

const headerMarkup = !showEmptyState &&
const headerMarkup = !showNoResults &&
!showEmptyState &&
(showHeader || needsHeader) &&
this.listRef.current && (
<div className={styles.HeaderOuterWrapper}>
Expand Down Expand Up @@ -512,12 +521,14 @@ class ResourceList extends React.Component<CombinedProps, State> {
</div>
);

const emptyStateMarkup = showEmptyState ? (
const noResultsMarkup = showNoResults ? (
<div className={styles.EmptySearchResultWrapper}>
<EmptySearchResult {...this.emptySearchResultText} withIllustration />
</div>
) : null;

const emptyStateMarkup = showEmptyState ? emptyState : null;

const defaultTopPadding = 8;
const topPadding =
loadingPosition > 0 ? loadingPosition : defaultTopPadding;
Expand Down Expand Up @@ -561,9 +572,7 @@ class ResourceList extends React.Component<CombinedProps, State> {
{loadingOverlay}
{items.map(this.renderItem)}
</ul>
) : (
emptyStateMarkup
);
) : null;

const context = {
selectable: this.selectable,
Expand All @@ -581,6 +590,8 @@ class ResourceList extends React.Component<CombinedProps, State> {
{filterControlMarkup}
{headerMarkup}
{listMarkup}
{noResultsMarkup}
{emptyStateMarkup}
{loadingWithoutItemsMarkup}
</div>
</ResourceListContext.Provider>
Expand Down
86 changes: 84 additions & 2 deletions src/components/ResourceList/tests/ResourceList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ResourceItem,
EventListener,
Button,
EmptyState,
} from 'components';
// eslint-disable-next-line no-restricted-imports
import {
Expand Down Expand Up @@ -388,10 +389,11 @@ describe('<ResourceList />', () => {
);
});

it('does not render when items is empty', () => {
it('does not render when items is empty and hasMoreItems is unset', () => {
const resourceList = mountWithAppProvider(
<ResourceList items={[]} renderItem={renderItem} />,
);

expect(findByTestID(resourceList, 'ResourceList-Header').exists()).toBe(
false,
);
Expand Down Expand Up @@ -477,6 +479,7 @@ describe('<ResourceList />', () => {
it('does not render when EmptySearchResult exists', () => {
const resourceList = mountWithAppProvider(
<ResourceList
hasMoreItems
items={[]}
renderItem={renderItem}
filterControl={<div>fake filterControl</div>}
Expand All @@ -489,7 +492,7 @@ describe('<ResourceList />', () => {
});

describe('filterControl', () => {
it('renders when exist', () => {
it('renders when exists', () => {
const resourceList = mountWithAppProvider(
<ResourceList
items={itemsNoID}
Expand All @@ -501,10 +504,89 @@ describe('<ResourceList />', () => {
});
});

describe('emptyState', () => {
it('renders when no items are provided and hasMoreItems is unset', () => {
const emptyState = (
<EmptyState
heading="Upload a file to get started"
action={{content: 'Upload files'}}
image="https://cdn.shopify.com/s/files/1/2376/3301/products/file-upload-empty-state.png"
>
<p>
You can use the Files section to upload images, videos, and other
documents
</p>
</EmptyState>
);

const resourceList = mountWithAppProvider(
<ResourceList
items={[]}
renderItem={renderItem}
emptyState={emptyState}
/>,
);

expect(resourceList.find(EmptyState)).toHaveLength(1);
});

it('does not render when exists but items are provided', () => {
const emptyState = (
<EmptyState
heading="Upload a file to get started"
action={{content: 'Upload files'}}
image="https://cdn.shopify.com/s/files/1/2376/3301/products/file-upload-empty-state.png"
>
<p>
You can use the Files section to upload images, videos, and other
documents
</p>
</EmptyState>
);

const resourceList = mountWithAppProvider(
<ResourceList
items={itemsNoID}
renderItem={renderItem}
emptyState={emptyState}
/>,
);

expect(resourceList.find(EmptyState)).toHaveLength(0);
});

it('does not render when exists but hasMoreItems is true', () => {
const emptyState = (
<EmptyState
heading="Upload a file to get started"
action={{content: 'Upload files'}}
image="https://cdn.shopify.com/s/files/1/2376/3301/products/file-upload-empty-state.png"
>
<p>
You can use the Files section to upload images, videos, and other
documents
</p>
</EmptyState>
);

const resourceList = mountWithAppProvider(
<ResourceList
hasMoreItems
items={[]}
renderItem={renderItem}
emptyState={emptyState}
/>,
);

expect(resourceList.find(EmptyState)).toHaveLength(0);
});
});

describe('emptySearchResult', () => {
it('renders when filterControl exists and items is empty', () => {
const resourceList = mountWithAppProvider(
<ResourceList
hasMoreItems
items={[]}
renderItem={renderItem}
filterControl={<div>fake filterControl</div>}
Expand Down

0 comments on commit d7ad6d7

Please sign in to comment.