mirror of
https://github.com/ansible/awx.git
synced 2026-02-14 01:34:45 -03:30
convert to PF Pagination component
This commit is contained in:
@@ -8,14 +8,6 @@ import CheckboxListItem from '../ListItem';
|
||||
import SelectedList from '../SelectedList';
|
||||
import { getQSConfig, parseNamespacedQueryString } from '../../util/qs';
|
||||
|
||||
const paginationStyling = {
|
||||
paddingLeft: '0',
|
||||
justifyContent: 'flex-end',
|
||||
borderRight: '1px solid #ebebeb',
|
||||
borderBottom: '1px solid #ebebeb',
|
||||
borderTop: '0'
|
||||
};
|
||||
|
||||
class SelectResourceStep extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
@@ -124,7 +116,6 @@ class SelectResourceStep extends React.Component {
|
||||
)}
|
||||
alignToolbarLeft
|
||||
showPageSizeOptions={false}
|
||||
paginationStyling={paginationStyling}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
@@ -18,14 +18,6 @@ import CheckboxListItem from '../ListItem';
|
||||
import SelectedList from '../SelectedList';
|
||||
import { getQSConfig, parseNamespacedQueryString } from '../../util/qs';
|
||||
|
||||
const paginationStyling = {
|
||||
paddingLeft: '0',
|
||||
justifyContent: 'flex-end',
|
||||
borderRight: '1px solid #ebebeb',
|
||||
borderBottom: '1px solid #ebebeb',
|
||||
borderTop: '0'
|
||||
};
|
||||
|
||||
class Lookup extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
@@ -186,7 +178,6 @@ class Lookup extends React.Component {
|
||||
)}
|
||||
alignToolbarLeft
|
||||
showPageSizeOptions={false}
|
||||
paginationStyling={paginationStyling}
|
||||
/>
|
||||
{lookupSelectedItems.length > 0 && (
|
||||
<SelectedList
|
||||
|
||||
@@ -47,6 +47,7 @@ class PaginatedDataList extends React.Component {
|
||||
};
|
||||
|
||||
this.handleSetPage = this.handleSetPage.bind(this);
|
||||
this.handleSetPageSize = this.handleSetPageSize.bind(this);
|
||||
this.handleSort = this.handleSort.bind(this);
|
||||
}
|
||||
|
||||
@@ -65,11 +66,12 @@ class PaginatedDataList extends React.Component {
|
||||
return [queryParams.order_by, 'ascending'];
|
||||
}
|
||||
|
||||
handleSetPage (pageNumber, pageSize) {
|
||||
this.pushHistoryState({
|
||||
page: pageNumber,
|
||||
page_size: pageSize,
|
||||
});
|
||||
handleSetPage (event, pageNumber) {
|
||||
this.pushHistoryState({ page: pageNumber });
|
||||
}
|
||||
|
||||
handleSetPageSize (event, pageSize) {
|
||||
this.pushHistoryState({ page_size: pageSize });
|
||||
}
|
||||
|
||||
handleSort (sortedColumnKey, sortOrder) {
|
||||
@@ -106,7 +108,6 @@ class PaginatedDataList extends React.Component {
|
||||
onSelectAll,
|
||||
alignToolbarLeft,
|
||||
showPageSizeOptions,
|
||||
paginationStyling,
|
||||
location,
|
||||
i18n
|
||||
} = this.props;
|
||||
@@ -118,74 +119,9 @@ class PaginatedDataList extends React.Component {
|
||||
<Fragment>
|
||||
{error && (
|
||||
<Fragment>
|
||||
{error && (
|
||||
<Fragment>
|
||||
<div>{error.message}</div>
|
||||
{error.response && (
|
||||
<div>{error.response.data.detail}</div>
|
||||
)}
|
||||
</Fragment> // TODO: replace with proper error handling
|
||||
)}
|
||||
{items.length === 0 ? (
|
||||
<EmptyState>
|
||||
<EmptyStateIcon icon={CubesIcon} />
|
||||
<Title size="lg">
|
||||
{i18n._(t`No ${ucFirst(itemNamePlural || pluralize(itemName))} Found`)}
|
||||
</Title>
|
||||
<EmptyStateBody>
|
||||
{i18n._(t`Please add ${getArticle(itemName)} ${itemName} to populate this list`)}
|
||||
</EmptyStateBody>
|
||||
</EmptyState>
|
||||
) : (
|
||||
<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">
|
||||
<TextContent style={detailWrapperStyle}>
|
||||
<Link to={{ pathname: item.url }}>
|
||||
<Text
|
||||
id={`items-list-item-${item.id}`}
|
||||
style={detailLabelStyle}
|
||||
>
|
||||
{item.name}
|
||||
</Text>
|
||||
</Link>
|
||||
</TextContent>
|
||||
</DataListCell>
|
||||
]}
|
||||
/>
|
||||
</DataListItemRow>
|
||||
</DataListItem>
|
||||
)))}
|
||||
</DataList>
|
||||
<Pagination
|
||||
count={itemCount}
|
||||
page={queryParams.page || 1}
|
||||
pageCount={this.getPageCount()}
|
||||
page_size={queryParams.page_size}
|
||||
onSetPage={this.handleSetPage}
|
||||
showPageSizeOptions={showPageSizeOptions}
|
||||
style={paginationStyling}
|
||||
/>
|
||||
</Fragment>
|
||||
<div>{error.message}</div>
|
||||
{error.response && (
|
||||
<div>{error.response.data.detail}</div>
|
||||
)}
|
||||
</Fragment> // TODO: replace with proper error handling
|
||||
)}
|
||||
@@ -211,6 +147,7 @@ class PaginatedDataList extends React.Component {
|
||||
isAllSelected={isAllSelected}
|
||||
onSelectAll={onSelectAll}
|
||||
additionalControls={additionalControls}
|
||||
noLeftMargin={alignToolbarLeft}
|
||||
/>
|
||||
<DataList aria-label={i18n._(t`${ucFirst(pluralize(itemName))} List`)}>
|
||||
{items.map(item => (renderItem ? renderItem(item) : (
|
||||
@@ -239,11 +176,18 @@ class PaginatedDataList extends React.Component {
|
||||
)))}
|
||||
</DataList>
|
||||
<Pagination
|
||||
count={itemCount}
|
||||
page={queryParams.page}
|
||||
pageCount={this.getPageCount()}
|
||||
page_size={queryParams.page_size}
|
||||
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>
|
||||
)}
|
||||
@@ -276,7 +220,6 @@ PaginatedDataList.propTypes = {
|
||||
onSelectAll: PropTypes.func,
|
||||
alignToolbarLeft: PropTypes.bool,
|
||||
showPageSizeOptions: PropTypes.bool,
|
||||
paginationStyling: PropTypes.shape(),
|
||||
};
|
||||
|
||||
PaginatedDataList.defaultProps = {
|
||||
@@ -290,7 +233,6 @@ PaginatedDataList.defaultProps = {
|
||||
onSelectAll: null,
|
||||
alignToolbarLeft: false,
|
||||
showPageSizeOptions: true,
|
||||
paginationStyling: null,
|
||||
};
|
||||
|
||||
export { PaginatedDataList as _PaginatedDataList };
|
||||
|
||||
@@ -1,245 +1,36 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import React from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import { Pagination as PFPagination } from '@patternfly/react-core';
|
||||
import { I18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
DropdownDirection,
|
||||
DropdownItem,
|
||||
DropdownToggle,
|
||||
TextInput
|
||||
} from '@patternfly/react-core';
|
||||
|
||||
class Pagination extends Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
const AWXPagination = styled(PFPagination)`
|
||||
${props => (props.perPageOptions && !props.perPageOptions.length && css`
|
||||
.pf-c-options-menu__toggle-button {
|
||||
display: none;
|
||||
}
|
||||
`)}
|
||||
`;
|
||||
|
||||
const { page } = props;
|
||||
this.state = { value: page, isOpen: false };
|
||||
|
||||
this.onPageChange = this.onPageChange.bind(this);
|
||||
this.onSubmit = this.onSubmit.bind(this);
|
||||
this.onFirst = this.onFirst.bind(this);
|
||||
this.onPrevious = this.onPrevious.bind(this);
|
||||
this.onNext = this.onNext.bind(this);
|
||||
this.onLast = this.onLast.bind(this);
|
||||
this.onTogglePageSize = this.onTogglePageSize.bind(this);
|
||||
this.onSelectPageSize = this.onSelectPageSize.bind(this);
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
const { page } = this.props;
|
||||
|
||||
if (prevProps.page !== page) {
|
||||
this.onPageChange(page);
|
||||
}
|
||||
}
|
||||
|
||||
onPageChange (value) {
|
||||
this.setState({ value });
|
||||
}
|
||||
|
||||
onSubmit (event) {
|
||||
const { onSetPage, page, pageCount, page_size } = this.props;
|
||||
const { value } = this.state;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
// eslint-disable-next-line no-bitwise
|
||||
const isPositiveInteger = value >>> 0 === parseFloat(value) && parseInt(value, 10) > 0;
|
||||
const isValid = isPositiveInteger && parseInt(value, 10) <= pageCount;
|
||||
|
||||
if (isValid) {
|
||||
onSetPage(value, page_size);
|
||||
} else {
|
||||
this.setState({ value: page });
|
||||
}
|
||||
}
|
||||
|
||||
onFirst () {
|
||||
const { onSetPage, page_size } = this.props;
|
||||
|
||||
onSetPage(1, page_size);
|
||||
}
|
||||
|
||||
onPrevious () {
|
||||
const { onSetPage, page, page_size } = this.props;
|
||||
const previousPage = page - 1;
|
||||
|
||||
onSetPage(previousPage, page_size);
|
||||
}
|
||||
|
||||
onNext () {
|
||||
const { onSetPage, page, page_size } = this.props;
|
||||
const nextPage = page + 1;
|
||||
|
||||
onSetPage(nextPage, page_size);
|
||||
}
|
||||
|
||||
onLast () {
|
||||
const { onSetPage, pageCount, page_size } = this.props;
|
||||
|
||||
onSetPage(pageCount, page_size);
|
||||
}
|
||||
|
||||
onTogglePageSize (isOpen) {
|
||||
this.setState({ isOpen });
|
||||
}
|
||||
|
||||
onSelectPageSize ({ target }) {
|
||||
const { onSetPage } = this.props;
|
||||
const page = 1;
|
||||
const page_size = parseInt(target.innerText || target.textContent, 10);
|
||||
|
||||
this.setState({ isOpen: false });
|
||||
|
||||
onSetPage(page, page_size);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { up } = DropdownDirection;
|
||||
const {
|
||||
count,
|
||||
page,
|
||||
pageCount,
|
||||
page_size,
|
||||
pageSizeOptions,
|
||||
showPageSizeOptions,
|
||||
style,
|
||||
i18n
|
||||
} = this.props;
|
||||
const { value, isOpen } = this.state;
|
||||
let opts = [];
|
||||
if (pageSizeOptions) {
|
||||
opts = pageSizeOptions.slice().reverse().filter(o => o !== page_size);
|
||||
}
|
||||
const isOnFirst = page === 1;
|
||||
const isOnLast = page === pageCount;
|
||||
|
||||
let itemCount;
|
||||
if (!isOnLast || count === page_size) {
|
||||
itemCount = page_size;
|
||||
} else {
|
||||
itemCount = count % page_size;
|
||||
}
|
||||
const itemMin = ((page - 1) * page_size) + 1;
|
||||
const itemMax = itemMin + itemCount - 1;
|
||||
|
||||
const dropdownItems = opts.map(option => (
|
||||
<DropdownItem
|
||||
key={option}
|
||||
component="button"
|
||||
>
|
||||
{option}
|
||||
</DropdownItem>
|
||||
));
|
||||
|
||||
return (
|
||||
<div className="awx-pagination" style={style}>
|
||||
{showPageSizeOptions && (
|
||||
<div className="awx-pagination__page-size-selection">
|
||||
{i18n._(t`Items Per Page`)}
|
||||
<Dropdown
|
||||
onToggle={this.onTogglePageSize}
|
||||
onSelect={this.onSelectPageSize}
|
||||
direction={up}
|
||||
isOpen={isOpen}
|
||||
toggle={(
|
||||
<DropdownToggle
|
||||
className="togglePageSize"
|
||||
onToggle={this.onTogglePageSize}
|
||||
>
|
||||
{page_size}
|
||||
</DropdownToggle>
|
||||
)}
|
||||
dropdownItems={dropdownItems}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="awx-pagination__counts">
|
||||
<div className="awx-pagination__item-count">
|
||||
{i18n._(t`Items ${itemMin} – ${itemMax} of ${count}`)}
|
||||
</div>
|
||||
{pageCount !== 1 && (
|
||||
<div className="awx-pagination__page-count">
|
||||
<div className="pf-c-input-group pf-m-previous">
|
||||
<Button
|
||||
className="awx-pagination__page-button"
|
||||
variant="tertiary"
|
||||
aria-label={i18n._(t`First`)}
|
||||
isDisabled={isOnFirst}
|
||||
onClick={this.onFirst}
|
||||
>
|
||||
<i className="fas fa-angle-double-left" />
|
||||
</Button>
|
||||
<Button
|
||||
className="awx-pagination__page-button"
|
||||
variant="tertiary"
|
||||
aria-label={i18n._(t`Previous`)}
|
||||
isDisabled={isOnFirst}
|
||||
onClick={this.onPrevious}
|
||||
>
|
||||
<i className="fas fa-angle-left" />
|
||||
</Button>
|
||||
</div>
|
||||
<form
|
||||
className="awx-pagination__page-input-form"
|
||||
onSubmit={this.onSubmit}
|
||||
>
|
||||
{i18n._(t`Page `)}
|
||||
<TextInput
|
||||
className="awx-pagination__page-input"
|
||||
aria-label={i18n._(t`Page Number`)}
|
||||
value={value}
|
||||
type="text"
|
||||
onChange={this.onPageChange}
|
||||
/>
|
||||
{i18n._(t` of ${pageCount}`)}
|
||||
</form>
|
||||
<div className="pf-c-input-group">
|
||||
<Button
|
||||
className="awx-pagination__page-button"
|
||||
variant="tertiary"
|
||||
aria-label={i18n._(t`Next`)}
|
||||
isDisabled={isOnLast}
|
||||
onClick={this.onNext}
|
||||
>
|
||||
<i className="fas fa-angle-right" />
|
||||
</Button>
|
||||
<Button
|
||||
className="awx-pagination__page-button"
|
||||
variant="tertiary"
|
||||
aria-label={i18n._(t`Last`)}
|
||||
isDisabled={isOnLast}
|
||||
onClick={this.onLast}
|
||||
>
|
||||
<i className="fas fa-angle-double-right" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Pagination.propTypes = {
|
||||
count: PropTypes.number,
|
||||
onSetPage: PropTypes.func.isRequired,
|
||||
page: PropTypes.number.isRequired,
|
||||
pageCount: PropTypes.number,
|
||||
pageSizeOptions: PropTypes.arrayOf(PropTypes.number),
|
||||
page_size: PropTypes.number.isRequired,
|
||||
showPageSizeOptions: PropTypes.bool
|
||||
};
|
||||
|
||||
Pagination.defaultProps = {
|
||||
count: null,
|
||||
pageCount: null,
|
||||
pageSizeOptions: [5, 10, 25, 50],
|
||||
showPageSizeOptions: true
|
||||
};
|
||||
|
||||
export default withI18n()(Pagination);
|
||||
export default (props) => (
|
||||
<I18n>
|
||||
{({ i18n }) => (
|
||||
<AWXPagination
|
||||
titles={{
|
||||
items: i18n._(t`items`),
|
||||
pages: i18n._(t`pages`),
|
||||
itemsPerPage: i18n._(t`Items per page`),
|
||||
perPageSuffix: i18n._(t`per page`),
|
||||
toFirstPage: i18n._(t`Go to first page`),
|
||||
toPreviousPage: i18n._(t`Go to previous page`),
|
||||
toLastPage: i18n._(t`Go to last page`),
|
||||
toNextPage: i18n._(t`Go to next page`),
|
||||
optionsToggle: i18n._(t`Select`),
|
||||
currPage: i18n._(t`Current page`),
|
||||
paginationTitle: i18n._(t`Pagination`)
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
)}
|
||||
</I18n>
|
||||
);
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
.awx-pagination {
|
||||
--awx-pagination--BackgroundColor: var(--pf-global--BackgroundColor--light-100);
|
||||
--awx-pagination--BorderColor: #dbdbdb;
|
||||
--awx-pagination--disabled-BackgroundColor: #f2f2f2;
|
||||
--awx-pagination--disabled-Color: #C2C2CA;
|
||||
|
||||
border-top: 1px solid var(--awx-pagination--BorderColor);
|
||||
border-bottom: 1px solid var(--awx-pagination--BorderColor);
|
||||
background-color: var(--awx-pagination--BackgroundColor);
|
||||
height: 55px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
|
||||
--pf-global--target-size--MinHeight: 30px;
|
||||
--pf-global--target-size--MinWidth: 30px;
|
||||
--pf-global--FontSize--md: 14px;
|
||||
|
||||
.awx-pagination__page-size-selection .pf-c-dropdown__toggle {
|
||||
font-weight: bold;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.awx-pagination__counts {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: -20px;
|
||||
}
|
||||
|
||||
.awx-pagination__item-count {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.awx-pagination__page-count {
|
||||
margin-left: -10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.awx-pagination__page-input-form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.awx-pagination__page-input {
|
||||
width: 35px;
|
||||
margin: 0 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.awx-pagination__page-button {
|
||||
width: 55px;
|
||||
height: 55px;
|
||||
}
|
||||
|
||||
.pf-c-input-group .awx-pagination__page-button,
|
||||
.pf-c-input-group .awx-pagination__page-button:after {
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.pf-c-input-group .pf-c-button {
|
||||
border-left: 1px solid var(--awx-pagination--BorderColor);
|
||||
}
|
||||
|
||||
.pf-c-input-group {
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
.pf-c-input-group.pf-m-previous {
|
||||
border-right: 1px solid var(--awx-pagination--BorderColor);
|
||||
}
|
||||
|
||||
.pf-c-input-group .pf-c-button.pf-m-disabled {
|
||||
border: 1px solid var(--awx-pagination--BorderColor);
|
||||
background-color: var(--awx-pagination--disabled-BackgroundColor);
|
||||
color: var(--awx-pagination--disabled-Color);
|
||||
}
|
||||
|
||||
.pf-c-input-group.pf-m-previous .pf-c-button.pf-m-disabled {
|
||||
border-right: 0;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import { t } from '@lingui/macro';
|
||||
|
||||
import '@patternfly/react-core/dist/styles/base.css';
|
||||
import './app.scss';
|
||||
import './components/Pagination/styles.scss';
|
||||
import './components/SelectedList/styles.scss';
|
||||
import './components/AddRole/styles.scss';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user