Merge pull request #208 from keithjgrant/toolbar-render-prop

use a render prop for PaginatedDataList toolbar
This commit is contained in:
Keith Grant 2019-05-20 06:16:48 -07:00 committed by GitHub
commit 6bfbcb35cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 135 additions and 145 deletions

View File

@ -176,11 +176,8 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
>
<Route>
<PaginatedDataList
additionalControls={Array []}
alignToolbarLeft={false}
history={"/history/"}
i18n={"/i18n/"}
isAllSelected={false}
itemCount={2}
itemName="notification"
itemNamePlural=""
@ -216,7 +213,6 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
"url": "",
}
}
onSelectAll={null}
qsConfig={
Object {
"defaultParams": Object {
@ -232,8 +228,8 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
}
}
renderItem={[Function]}
renderToolbar={[Function]}
showPageSizeOptions={true}
showSelectAll={false}
toolbarColumns={
Array [
Object {
@ -257,7 +253,6 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
}
>
<WithI18n
additionalControls={Array []}
columns={
Array [
Object {
@ -279,12 +274,8 @@ exports[`<OrganizationNotifications /> initially renders succesfully 1`] = `
},
]
}
isAllSelected={false}
noLeftMargin={false}
onSearch={[Function]}
onSelectAll={null}
onSort={[Function]}
showSelectAll={false}
sortOrder="ascending"
sortedColumnKey="name"
>

View File

@ -4,6 +4,7 @@ import { withRouter } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import PaginatedDataList from '../PaginatedDataList';
import DataListToolbar from '../DataListToolbar';
import CheckboxListItem from '../ListItem';
import SelectedList from '../SelectedList';
import { getQSConfig, parseNamespacedQueryString } from '../../util/qs';
@ -102,9 +103,7 @@ class SelectResourceStep extends React.Component {
itemCount={count}
itemName={itemName}
qsConfig={this.qsConfig}
toolbarColumns={
columns
}
toolbarColumns={columns}
renderItem={item => (
<CheckboxListItem
isSelected={selectedResourceRows.some(i => i.id === item.id)}
@ -114,7 +113,9 @@ class SelectResourceStep extends React.Component {
onSelect={() => onRowClick(item)}
/>
)}
alignToolbarLeft
renderToolbar={(props) => (
<DataListToolbar {...props} alignToolbarLeft />
)}
showPageSizeOptions={false}
/>
</Fragment>

View File

@ -14,6 +14,7 @@ import { t } from '@lingui/macro';
import { withNetwork } from '../../contexts/Network';
import PaginatedDataList from '../PaginatedDataList';
import DataListToolbar from '../DataListToolbar';
import CheckboxListItem from '../ListItem';
import SelectedList from '../SelectedList';
import { getQSConfig, parseNamespacedQueryString } from '../../util/qs';
@ -176,7 +177,9 @@ class Lookup extends React.Component {
onSelect={() => this.toggleSelected(item)}
/>
)}
alignToolbarLeft
renderToolbar={(props) => (
<DataListToolbar {...props} alignToolbarLeft />
)}
showPageSizeOptions={false}
/>
{lookupSelectedItems.length > 0 && (

View File

@ -1,12 +1,7 @@
import React, { Fragment } from 'react';
import PropTypes, { arrayOf, shape, string, bool, node } from 'prop-types';
import PropTypes, { arrayOf, shape, string, bool } from 'prop-types';
import {
DataList,
DataListItem,
DataListItemRow,
DataListItemCells,
DataListCell,
TextContent,
Title,
EmptyState,
EmptyStateIcon,
@ -15,11 +10,12 @@ import {
import { CubesIcon } from '@patternfly/react-icons';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { withRouter, Link } from 'react-router-dom';
import { withRouter } from 'react-router-dom';
import styled from 'styled-components';
import Pagination from '../Pagination';
import DataListToolbar from '../DataListToolbar';
import PaginatedDataListItem from './PaginatedDataListItem';
import {
parseNamespacedQueryString,
updateNamespacedQueryString,
@ -38,13 +34,6 @@ const EmptyStateControlsWrapper = styled.div`
margin-left: 20px;
}
`;
const ListItemGrid = styled(TextContent)`
display: grid;
grid-template-columns: minmax(70px,max-content) repeat(auto-fit, minmax(60px,max-content));
grid-gap: 10px;
`;
class PaginatedDataList extends React.Component {
constructor (props) {
super(props);
@ -58,12 +47,6 @@ class PaginatedDataList extends React.Component {
this.handleSort = this.handleSort.bind(this);
}
getPageCount () {
const { itemCount, qsConfig, location } = this.props;
const queryParams = parseNamespacedQueryString(qsConfig, location.search);
return Math.ceil(itemCount / queryParams.page_size);
}
getSortOrder () {
const { qsConfig, location } = this.props;
const queryParams = parseNamespacedQueryString(qsConfig, location.search);
@ -95,11 +78,6 @@ class PaginatedDataList extends React.Component {
history.push(`${pathname}?${qs}`);
}
getPluralItemName () {
const { itemName, itemNamePlural } = this.props;
return itemNamePlural || `${itemName}s`;
}
render () {
const {
emptyStateControls,
@ -108,16 +86,12 @@ class PaginatedDataList extends React.Component {
qsConfig,
renderItem,
toolbarColumns,
additionalControls,
itemName,
itemNamePlural,
showSelectAll,
isAllSelected,
onSelectAll,
alignToolbarLeft,
showPageSizeOptions,
location,
i18n
i18n,
renderToolbar,
} = this.props;
const { error } = this.state;
const [orderBy, sortOrder] = this.getSortOrder();
@ -133,78 +107,52 @@ class PaginatedDataList extends React.Component {
)}
</Fragment> // TODO: replace with proper error handling
)}
{items.length === 0
? (
<Fragment>
<EmptyStateControlsWrapper>
{emptyStateControls}
</EmptyStateControlsWrapper>
<div css="border-bottom: 1px solid #d2d2d2" />
<EmptyState>
<EmptyStateIcon icon={CubesIcon} />
<Title size="lg">
{i18n._(t`No ${ucFirst(itemNamePlural || pluralize(itemName))} Found `)}
</Title>
<EmptyStateBody>
{i18n._(t`Please add ${ucFirst(itemNamePlural || pluralize(itemName))} to populate this list `)}
</EmptyStateBody>
</EmptyState>
</Fragment>
)
: (
<Fragment>
<DataListToolbar
sortedColumnKey={orderBy}
sortOrder={sortOrder}
columns={columns}
onSearch={() => { }}
onSort={this.handleSort}
showSelectAll={showSelectAll}
isAllSelected={isAllSelected}
onSelectAll={onSelectAll}
additionalControls={additionalControls}
noLeftMargin={alignToolbarLeft}
/>
<DataList aria-label={i18n._(t`${ucFirst(pluralize(itemName))} List`)}>
{items.map(item => (renderItem ? renderItem(item) : (
<DataListItem
aria-labelledby={`items-list-item-${item.id}`}
key={item.id}
>
<DataListItemRow>
<DataListItemCells dataListCells={[
<DataListCell key="team-name">
<ListItemGrid>
<Link to={{ pathname: item.url }}>
<b id={`items-list-item-${item.id}`}>
{item.name}
</b>
</Link>
</ListItemGrid>
</DataListCell>
]}
/>
</DataListItemRow>
</DataListItem>
)))}
</DataList>
<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>
)}
{items.length === 0 ? (
<Fragment>
<EmptyStateControlsWrapper>
{emptyStateControls}
</EmptyStateControlsWrapper>
<div css="border-bottom: 1px solid #d2d2d2" />
<EmptyState>
<EmptyStateIcon icon={CubesIcon} />
<Title size="lg">
{i18n._(t`No ${ucFirst(itemNamePlural || pluralize(itemName))} Found `)}
</Title>
<EmptyStateBody>
{i18n._(t`Please add ${ucFirst(itemNamePlural || pluralize(itemName))} to populate this list `)}
</EmptyStateBody>
</EmptyState>
</Fragment>
) : (
<Fragment>
{renderToolbar({
sortedColumnKey: orderBy,
sortOrder,
columns,
onSearch: () => { },
onSort: this.handleSort,
})}
<DataList aria-label={i18n._(t`${ucFirst(pluralize(itemName))} List`)}>
{items.map(item => (renderItem ? renderItem(item) : (
<PaginatedDataListItem key={item.id} item={item} />
)))}
</DataList>
<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>
)}
</Fragment>
);
}
@ -228,25 +176,17 @@ PaginatedDataList.propTypes = {
key: string.isRequired,
isSortable: bool,
})),
additionalControls: arrayOf(node),
showSelectAll: PropTypes.bool,
isAllSelected: PropTypes.bool,
onSelectAll: PropTypes.func,
alignToolbarLeft: PropTypes.bool,
showPageSizeOptions: PropTypes.bool,
renderToolbar: PropTypes.func,
};
PaginatedDataList.defaultProps = {
renderItem: null,
toolbarColumns: [],
additionalControls: [],
itemName: 'item',
itemNamePlural: '',
showSelectAll: false,
isAllSelected: false,
onSelectAll: null,
alignToolbarLeft: false,
showPageSizeOptions: true,
renderToolbar: (props) => (<DataListToolbar {...props} />),
};
export { PaginatedDataList as _PaginatedDataList };

View File

@ -0,0 +1,42 @@
import React from 'react';
import { Link } from 'react-router-dom';
import {
DataListItem,
DataListItemRow,
DataListItemCells,
DataListCell,
TextContent,
} from '@patternfly/react-core';
import styled from 'styled-components';
const DetailWrapper = styled(TextContent)`
display: grid;
grid-template-columns:
minmax(70px, max-content)
repeat(auto-fit, minmax(60px, max-content));
grid-gap: 10px;
`;
export default function PaginatedDataListItem ({ item }) {
return (
<DataListItem
aria-labelledby={`items-list-item-${item.id}`}
key={item.id}
>
<DataListItemRow>
<DataListItemCells dataListCells={[
<DataListCell key="team-name">
<DetailWrapper>
<Link to={{ pathname: item.url }}>
<b id={`items-list-item-${item.id}`}>
{item.name}
</b>
</Link>
</DetailWrapper>
</DataListCell>
]}
/>
</DataListItemRow>
</DataListItem>
);
}

View File

@ -1,5 +1,6 @@
import PaginatedDataList from './PaginatedDataList';
export default PaginatedDataList;
export { default as PaginatedDataListItem } from './PaginatedDataListItem';
export { default as ToolbarDeleteButton } from './ToolbarDeleteButton';
export { default as ToolbarAddButton } from './ToolbarAddButton';

View File

@ -3,6 +3,7 @@ import { withRouter } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import PaginatedDataList, { ToolbarAddButton } from '../../../../components/PaginatedDataList';
import DataListToolbar from '../../../../components/DataListToolbar';
import OrganizationAccessItem from '../../components/OrganizationAccessItem';
import DeleteRoleConfirmationModal from '../../components/DeleteRoleConfirmationModal';
import AddResourceRole from '../../../../components/AddRole/AddResourceRole';
@ -172,9 +173,14 @@ class OrganizationAccess extends React.Component {
{ name: i18n._(t`Username`), key: 'username', isSortable: true },
{ name: i18n._(t`Last Name`), key: 'last_name', isSortable: true },
]}
additionalControls={canEdit ? [
<ToolbarAddButton key="add" onClick={this.toggleAddModal} />
] : null}
renderToolbar={(props) => (
<DataListToolbar
{...props}
additionalControls={canEdit ? [
<ToolbarAddButton key="add" onClick={this.toggleAddModal} />
] : null}
/>
)}
renderItem={accessRecord => (
<OrganizationAccessItem
key={accessRecord.id}

View File

@ -13,6 +13,7 @@ import PaginatedDataList, {
ToolbarDeleteButton,
ToolbarAddButton
} from '../../../components/PaginatedDataList';
import DataListToolbar from '../../../components/DataListToolbar';
import OrganizationListItem from '../components/OrganizationListItem';
import { getQSConfig, parseNamespacedQueryString } from '../../../util/qs';
@ -163,20 +164,25 @@ class OrganizationsList extends Component {
{ name: i18n._(t`Modified`), key: 'modified', isSortable: true, isNumeric: true },
{ name: i18n._(t`Created`), key: 'created', isSortable: true, isNumeric: true },
]}
showSelectAll
isAllSelected={isAllSelected}
onSelectAll={this.handleSelectAll}
additionalControls={[
<ToolbarDeleteButton
key="delete"
onDelete={this.handleOrgDelete}
itemsToDelete={selected}
itemName="Organization"
/>,
canAdd
? <ToolbarAddButton key="add" linkTo={`${match.url}/add`} />
: null,
]}
renderToolbar={(props) => (
<DataListToolbar
{...props}
showSelectAll
isAllSelected={isAllSelected}
onSelectAll={this.handleSelectAll}
additionalControls={[
<ToolbarDeleteButton
key="delete"
onDelete={this.handleOrgDelete}
itemsToDelete={selected}
itemName="Organization"
/>,
canAdd
? <ToolbarAddButton key="add" linkTo={`${match.url}/add`} />
: null,
]}
/>
)}
renderItem={(o) => (
<OrganizationListItem
key={o.id}