Files
awx/src/components/PaginatedDataList/PaginatedDataList.jsx
Jake McDermott e72f0bcfd4 update content loading and error handling
unwind error handling

use auth cookie as source of truth, fetch config only when authenticated
2019-06-14 13:00:37 -04:00

193 lines
5.7 KiB
JavaScript

import React, { Fragment } from 'react';
import PropTypes, { arrayOf, shape, string, bool } from 'prop-types';
import { DataList } from '@patternfly/react-core';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { withRouter } from 'react-router-dom';
import styled from 'styled-components';
import ContentEmpty from '../ContentEmpty';
import ContentError from '../ContentError';
import ContentLoading from '../ContentLoading';
import Pagination from '../Pagination';
import DataListToolbar from '../DataListToolbar';
import PaginatedDataListItem from './PaginatedDataListItem';
import {
parseNamespacedQueryString,
updateNamespacedQueryString,
} from '../../util/qs';
import { pluralize, ucFirst } from '../../util/strings';
import { QSConfig } from '../../types';
const EmptyStateControlsWrapper = styled.div`
display: flex;
margin-top: 20px;
margin-right: 20px;
margin-bottom: 20px;
justify-content: flex-end;
& > :not(:first-child) {
margin-left: 20px;
}
`;
class PaginatedDataList extends React.Component {
constructor (props) {
super(props);
this.handleSetPage = this.handleSetPage.bind(this);
this.handleSetPageSize = this.handleSetPageSize.bind(this);
this.handleSort = this.handleSort.bind(this);
}
getSortOrder () {
const { qsConfig, location } = this.props;
const queryParams = parseNamespacedQueryString(qsConfig, location.search);
if (queryParams.order_by && queryParams.order_by.startsWith('-')) {
return [queryParams.order_by.substr(1), 'descending'];
}
return [queryParams.order_by, 'ascending'];
}
handleSetPage (event, pageNumber) {
this.pushHistoryState({ page: pageNumber });
}
handleSetPageSize (event, pageSize) {
this.pushHistoryState({ page_size: pageSize });
}
handleSort (sortedColumnKey, sortOrder) {
this.pushHistoryState({
order_by: sortOrder === 'ascending' ? sortedColumnKey : `-${sortedColumnKey}`,
page: null,
});
}
pushHistoryState (newParams) {
const { history, qsConfig } = this.props;
const { pathname, search } = history.location;
const qs = updateNamespacedQueryString(qsConfig, search, newParams);
history.push(`${pathname}?${qs}`);
}
render () {
const [orderBy, sortOrder] = this.getSortOrder();
const {
contentError,
contentLoading,
emptyStateControls,
items,
itemCount,
qsConfig,
renderItem,
toolbarColumns,
itemName,
itemNamePlural,
showPageSizeOptions,
location,
i18n,
renderToolbar,
} = this.props;
const columns = toolbarColumns.length ? toolbarColumns : [{ name: i18n._(t`Name`), key: 'name', isSortable: true }];
const queryParams = parseNamespacedQueryString(qsConfig, location.search);
const itemDisplayName = ucFirst(pluralize(itemName));
const itemDisplayNamePlural = ucFirst(itemNamePlural || pluralize(itemName));
const dataListLabel = i18n._(t`${itemDisplayName} List`);
const emptyContentMessage = i18n._(t`Please add ${itemDisplayNamePlural} to populate this list `);
const emptyContentTitle = i18n._(t`No ${itemDisplayNamePlural} Found `);
let Content;
if (contentLoading && items.length <= 0) {
Content = (<ContentLoading />);
} else if (contentError) {
Content = (<ContentError />);
} else if (items.length <= 0) {
Content = (<ContentEmpty title={emptyContentTitle} message={emptyContentMessage} />);
} else {
Content = (<DataList aria-label={dataListLabel}>{items.map(renderItem)}</DataList>);
}
if (items.length <= 0) {
return (
<Fragment>
{emptyStateControls && (
<EmptyStateControlsWrapper>
{emptyStateControls}
</EmptyStateControlsWrapper>
)}
{emptyStateControls && (
<div css="border-bottom: 1px solid #d2d2d2" />
)}
{Content}
</Fragment>
);
}
return (
<Fragment>
{renderToolbar({
sortedColumnKey: orderBy,
sortOrder,
columns,
onSearch: () => { },
onSort: this.handleSort,
})}
{Content}
<Pagination
variant="bottom"
itemCount={itemCount}
page={queryParams.page || 1}
perPage={queryParams.page_size}
perPageOptions={showPageSizeOptions ? [
{ title: '5', value: 5 },
{ title: '10', value: 10 },
{ title: '20', value: 20 },
{ title: '50', value: 50 }
] : []}
onSetPage={this.handleSetPage}
onPerPageSelect={this.handleSetPageSize}
/>
</Fragment>
);
}
}
const Item = PropTypes.shape({
id: PropTypes.number.isRequired,
url: PropTypes.string.isRequired,
name: PropTypes.string,
});
PaginatedDataList.propTypes = {
items: PropTypes.arrayOf(Item).isRequired,
itemCount: PropTypes.number.isRequired,
itemName: PropTypes.string,
itemNamePlural: PropTypes.string,
qsConfig: QSConfig.isRequired,
renderItem: PropTypes.func,
toolbarColumns: arrayOf(shape({
name: string.isRequired,
key: string.isRequired,
isSortable: bool,
})),
showPageSizeOptions: PropTypes.bool,
renderToolbar: PropTypes.func,
contentLoading: PropTypes.bool,
contentError: PropTypes.bool,
};
PaginatedDataList.defaultProps = {
contentLoading: false,
contentError: false,
toolbarColumns: [],
itemName: 'item',
itemNamePlural: '',
showPageSizeOptions: true,
renderItem: (item) => (<PaginatedDataListItem key={item.id} item={item} />),
renderToolbar: (props) => (<DataListToolbar {...props} />),
};
export { PaginatedDataList as _PaginatedDataList };
export default withI18n()(withRouter(PaginatedDataList));