mirror of
https://github.com/ansible/awx.git
synced 2024-10-31 23:51:09 +03:00
Merge pull request #207 from keithjgrant/126-pf-pagination
Convert to PF Pagination component
This commit is contained in:
commit
5df2b1f346
@ -89,10 +89,35 @@ describe('<PaginatedDataList />', () => {
|
||||
);
|
||||
|
||||
const pagination = wrapper.find('Pagination');
|
||||
pagination.prop('onSetPage')(2, 5);
|
||||
expect(history.location.search).toEqual('?item.page=2&item.page_size=5');
|
||||
pagination.prop('onSetPage')(null, 2);
|
||||
expect(history.location.search).toEqual('?item.page=2');
|
||||
wrapper.update();
|
||||
pagination.prop('onSetPage')(1, 25);
|
||||
expect(history.location.search).toEqual('?item.page=1&item.page_size=25');
|
||||
pagination.prop('onSetPage')(null, 1);
|
||||
expect(history.location.search).toEqual('?item.page=1');
|
||||
});
|
||||
|
||||
test('should navigate to page when Pagination calls onPerPageSelect prop', () => {
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ['/organizations/1/teams'],
|
||||
});
|
||||
const wrapper = mountWithContexts(
|
||||
<PaginatedDataList
|
||||
items={mockData}
|
||||
itemCount={7}
|
||||
queryParams={{
|
||||
page: 1,
|
||||
page_size: 5,
|
||||
order_by: 'name',
|
||||
}}
|
||||
qsConfig={qsConfig}
|
||||
/>, { context: { router: { history } } }
|
||||
);
|
||||
|
||||
const pagination = wrapper.find('Pagination');
|
||||
pagination.prop('onPerPageSelect')(null, 5);
|
||||
expect(history.location.search).toEqual('?item.page_size=5');
|
||||
wrapper.update();
|
||||
pagination.prop('onPerPageSelect')(null, 25);
|
||||
expect(history.location.search).toEqual('?item.page_size=25');
|
||||
});
|
||||
});
|
||||
|
@ -1,349 +0,0 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { I18nProvider } from '@lingui/react';
|
||||
import Pagination from '../../src/components/Pagination';
|
||||
|
||||
describe('<Pagination />', () => {
|
||||
let pagination;
|
||||
|
||||
afterEach(() => {
|
||||
if (pagination) {
|
||||
pagination.unmount();
|
||||
pagination = null;
|
||||
}
|
||||
});
|
||||
|
||||
test('it triggers the expected callbacks on next and last', () => {
|
||||
const next = 'button[aria-label="Next"]';
|
||||
const last = 'button[aria-label="Last"]';
|
||||
|
||||
const onSetPage = jest.fn();
|
||||
|
||||
pagination = mount(
|
||||
<I18nProvider>
|
||||
<Pagination
|
||||
count={21}
|
||||
page={1}
|
||||
pageCount={5}
|
||||
page_size={5}
|
||||
pageSizeOptions={[5, 10, 25, 50]}
|
||||
onSetPage={onSetPage}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
|
||||
pagination.find(next).simulate('click');
|
||||
|
||||
expect(onSetPage).toHaveBeenCalledTimes(1);
|
||||
expect(onSetPage).toBeCalledWith(2, 5);
|
||||
|
||||
pagination.find(last).simulate('click');
|
||||
|
||||
expect(onSetPage).toHaveBeenCalledTimes(2);
|
||||
expect(onSetPage).toBeCalledWith(5, 5);
|
||||
});
|
||||
|
||||
test('it triggers the expected callback on previous and first', () => {
|
||||
const previous = 'button[aria-label="Previous"]';
|
||||
const first = 'button[aria-label="First"]';
|
||||
|
||||
const onSetPage = jest.fn();
|
||||
|
||||
pagination = mount(
|
||||
<I18nProvider>
|
||||
<Pagination
|
||||
count={21}
|
||||
page={5}
|
||||
pageCount={5}
|
||||
page_size={5}
|
||||
pageSizeOptions={[5, 10, 25, 50]}
|
||||
onSetPage={onSetPage}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
|
||||
pagination.find(previous).simulate('click');
|
||||
|
||||
expect(onSetPage).toHaveBeenCalledTimes(1);
|
||||
expect(onSetPage).toBeCalledWith(4, 5);
|
||||
|
||||
pagination.find(first).simulate('click');
|
||||
|
||||
expect(onSetPage).toHaveBeenCalledTimes(2);
|
||||
expect(onSetPage).toBeCalledWith(1, 5);
|
||||
});
|
||||
|
||||
test('previous button does not work on page 1', () => {
|
||||
const previous = 'button[aria-label="First"]';
|
||||
const onSetPage = jest.fn();
|
||||
|
||||
pagination = mount(
|
||||
<I18nProvider>
|
||||
<Pagination
|
||||
count={21}
|
||||
page={1}
|
||||
pageCount={5}
|
||||
page_size={5}
|
||||
pageSizeOptions={[5, 10, 25, 50]}
|
||||
onSetPage={onSetPage}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
pagination.find(previous).simulate('click');
|
||||
expect(onSetPage).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test('changing pageSize works', () => {
|
||||
const pageSizeDropdownToggleSelector = 'DropdownToggle DropdownToggle[className="togglePageSize"]';
|
||||
const pageSizeDropdownItemsSelector = 'DropdownItem button';
|
||||
const onSetPage = jest.fn();
|
||||
|
||||
pagination = mount(
|
||||
<I18nProvider>
|
||||
<Pagination
|
||||
count={21}
|
||||
page={1}
|
||||
pageCount={5}
|
||||
page_size={5}
|
||||
pageSizeOptions={[5, 10, 25, 50]}
|
||||
onSetPage={onSetPage}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
const pageSizeDropdownToggle = pagination.find(pageSizeDropdownToggleSelector);
|
||||
expect(pageSizeDropdownToggle.length).toBe(1);
|
||||
pageSizeDropdownToggle.at(0).simulate('click');
|
||||
|
||||
const pageSizeDropdownItems = pagination.find(pageSizeDropdownItemsSelector);
|
||||
expect(pageSizeDropdownItems.length).toBe(3);
|
||||
pageSizeDropdownItems.at(1).simulate('click');
|
||||
expect(onSetPage).toBeCalledWith(1, 25);
|
||||
});
|
||||
|
||||
test('itemCount displays correctly', () => {
|
||||
const onSetPage = jest.fn();
|
||||
|
||||
pagination = mount(
|
||||
<I18nProvider>
|
||||
<Pagination
|
||||
count={7}
|
||||
page={1}
|
||||
pageCount={2}
|
||||
page_size={5}
|
||||
pageSizeOptions={[5, 10, 25, 50]}
|
||||
onSetPage={onSetPage}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
let itemCount = pagination.find('.awx-pagination__item-count');
|
||||
expect(itemCount.text()).toEqual('Items 1 – 5 of 7');
|
||||
|
||||
pagination = mount(
|
||||
<I18nProvider>
|
||||
<Pagination
|
||||
count={7}
|
||||
page={2}
|
||||
pageCount={2}
|
||||
page_size={5}
|
||||
pageSizeOptions={[5, 10, 25, 50]}
|
||||
onSetPage={onSetPage}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
itemCount = pagination.find('.awx-pagination__item-count');
|
||||
expect(itemCount.text()).toEqual('Items 6 – 7 of 7');
|
||||
});
|
||||
|
||||
test('itemCount matching pageSize displays correctly', () => {
|
||||
const onSetPage = jest.fn();
|
||||
|
||||
pagination = mount(
|
||||
<I18nProvider>
|
||||
<Pagination
|
||||
count={5}
|
||||
page={1}
|
||||
pageCount={1}
|
||||
page_size={5}
|
||||
pageSizeOptions={[5, 10, 25, 50]}
|
||||
onSetPage={onSetPage}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
const itemCount = pagination.find('.awx-pagination__item-count');
|
||||
expect(itemCount.text()).toEqual('Items 1 – 5 of 5');
|
||||
});
|
||||
|
||||
test('itemCount less than pageSize displays correctly', () => {
|
||||
const onSetPage = jest.fn();
|
||||
|
||||
pagination = mount(
|
||||
<I18nProvider>
|
||||
<Pagination
|
||||
count={3}
|
||||
page={1}
|
||||
pageCount={1}
|
||||
page_size={5}
|
||||
pageSizeOptions={[5, 10, 25, 50]}
|
||||
onSetPage={onSetPage}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
const itemCount = pagination.find('.awx-pagination__item-count');
|
||||
expect(itemCount.text()).toEqual('Items 1 – 3 of 3');
|
||||
});
|
||||
|
||||
test('submitting a new page by typing in input works', () => {
|
||||
const textInputSelector = '.awx-pagination__page-input.pf-c-form-control';
|
||||
const submitFormSelector = '.awx-pagination__page-input-form';
|
||||
const onSetPage = jest.fn();
|
||||
|
||||
pagination = mount(
|
||||
<I18nProvider>
|
||||
<Pagination
|
||||
count={21}
|
||||
page={1}
|
||||
pageCount={5}
|
||||
page_size={5}
|
||||
pageSizeOptions={[5, 10, 25, 50]}
|
||||
onSetPage={onSetPage}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
const paginationElem = pagination.find('Pagination');
|
||||
expect(paginationElem.state().value).toBe(1);
|
||||
const textInput = pagination.find(textInputSelector);
|
||||
expect(textInput.length).toBe(1);
|
||||
expect(textInput.at(0).prop('value')).toBe(1);
|
||||
const input = pagination.find('TextInput');
|
||||
expect(input.prop('value')).toBe(1);
|
||||
input.prop('onChange')(2);
|
||||
pagination.update();
|
||||
const submitForm = pagination.find(submitFormSelector);
|
||||
expect(submitForm.length).toBe(1);
|
||||
submitForm.simulate('submit');
|
||||
expect(pagination.find('TextInput').prop('value')).toBe(2);
|
||||
// assert onPageChange was called
|
||||
expect(paginationElem.state().value).toBe(2);
|
||||
});
|
||||
|
||||
test('submitting a new page by typing in input does not work when invalid', () => {
|
||||
const textInputSelector = '.awx-pagination__page-input.pf-c-form-control';
|
||||
const submitFormSelector = '.awx-pagination__page-input-form';
|
||||
const onSetPage = jest.fn();
|
||||
|
||||
pagination = mount(
|
||||
<I18nProvider>
|
||||
<Pagination
|
||||
count={21}
|
||||
page={1}
|
||||
pageCount={5}
|
||||
page_size={5}
|
||||
pageSizeOptions={[5, 10, 25, 50]}
|
||||
onSetPage={onSetPage}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
const paginationElem = pagination.find('Pagination');
|
||||
expect(paginationElem.state().value).toBe(1);
|
||||
const textInput = pagination.find(textInputSelector);
|
||||
expect(textInput.length).toBe(1);
|
||||
expect(textInput.at(0).prop('value')).toBe(1);
|
||||
const input = pagination.find('TextInput');
|
||||
expect(input.prop('value')).toBe(1);
|
||||
input.prop('onChange')('!invalid');
|
||||
pagination.update();
|
||||
const submitForm = pagination.find(submitFormSelector);
|
||||
expect(submitForm.length).toBe(1);
|
||||
submitForm.simulate('submit');
|
||||
expect(pagination.find('TextInput').prop('value')).toBe(1);
|
||||
// assert onPageChange was not called
|
||||
expect(paginationElem.state().value).toBe(1);
|
||||
});
|
||||
|
||||
test('text input page change is not displayed when only 1 page', () => {
|
||||
const onSetPage = jest.fn();
|
||||
const pageNumber = 'input[aria-label="Page Number"]';
|
||||
pagination = mount(
|
||||
<I18nProvider>
|
||||
<Pagination
|
||||
count={4}
|
||||
page={1}
|
||||
pageCount={1}
|
||||
page_size={5}
|
||||
pageSizeOptions={[5, 10, 25, 50]}
|
||||
onSetPage={onSetPage}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
let pageInput = pagination.find(pageNumber);
|
||||
expect(pageInput.length).toBe(0);
|
||||
|
||||
pagination = mount(
|
||||
<I18nProvider>
|
||||
<Pagination
|
||||
count={11}
|
||||
page={1}
|
||||
pageCount={3}
|
||||
page_size={5}
|
||||
pageSizeOptions={[5, 10, 25, 50]}
|
||||
onSetPage={onSetPage}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
pageInput = pagination.find(pageNumber);
|
||||
expect(pageInput.length).toBe(1);
|
||||
});
|
||||
|
||||
test('make sure componentDidUpdate calls onPageChange', () => {
|
||||
const onSetPage = jest.fn();
|
||||
|
||||
pagination = mount(
|
||||
<I18nProvider>
|
||||
<Pagination
|
||||
count={7}
|
||||
page={1}
|
||||
pageCount={2}
|
||||
page_size={5}
|
||||
pageSizeOptions={[5, 10, 25, 50]}
|
||||
onSetPage={onSetPage}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
const paginationElem = pagination.find('Pagination');
|
||||
expect(paginationElem.state().value).toBe(1);
|
||||
pagination.setProps({
|
||||
children: (
|
||||
<Pagination
|
||||
count={7}
|
||||
page={2}
|
||||
pageCount={2}
|
||||
page_size={5}
|
||||
pageSizeOptions={[5, 10, 25, 50]}
|
||||
onSetPage={onSetPage}
|
||||
/>
|
||||
)
|
||||
});
|
||||
paginationElem.update();
|
||||
expect(paginationElem.state().value).toBe(2);
|
||||
});
|
||||
|
||||
test('when showPageSizeOptions is passed as false there should not be a dropdown in the DOM', () => {
|
||||
const pageSizeDropdownSelector = '.awx-pagination__page-size-selection';
|
||||
const onSetPage = jest.fn();
|
||||
|
||||
pagination = mount(
|
||||
<I18nProvider>
|
||||
<Pagination
|
||||
count={21}
|
||||
page={1}
|
||||
pageCount={5}
|
||||
page_size={5}
|
||||
showPageSizeOptions={false}
|
||||
onSetPage={onSetPage}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
const pageSizeDropdown = pagination.find(pageSizeDropdownSelector);
|
||||
expect(pageSizeDropdown.length).toBe(0);
|
||||
});
|
||||
});
|
@ -448,91 +448,6 @@ exports[`<NotificationListItem canToggleNotifications /> initially renders succe
|
||||
</NotificationListItem__DataListCell>
|
||||
</div>
|
||||
</DataListItemCells>
|
||||
<NotificationListItem__Switch
|
||||
aria-label="Toggle notification failure"
|
||||
id="notification-9000-error-toggle"
|
||||
isChecked={false}
|
||||
isDisabled={false}
|
||||
key=".1"
|
||||
label="Failure"
|
||||
onChange={[Function]}
|
||||
rowid="items-list-item-9000"
|
||||
>
|
||||
<StyledComponent
|
||||
aria-label="Toggle notification failure"
|
||||
forwardedComponent={
|
||||
Object {
|
||||
"$$typeof": Symbol(react.forward_ref),
|
||||
"attrs": Array [],
|
||||
"componentStyle": ComponentStyle {
|
||||
"componentId": "NotificationListItem__Switch-j7c411-1",
|
||||
"isStatic": true,
|
||||
"lastClassName": "ceuHGn",
|
||||
"rules": Array [
|
||||
"display:flex;flex-wrap:no-wrap;",
|
||||
],
|
||||
},
|
||||
"displayName": "NotificationListItem__Switch",
|
||||
"foldedComponentIds": Array [],
|
||||
"render": [Function],
|
||||
"styledComponentId": "NotificationListItem__Switch-j7c411-1",
|
||||
"target": [Function],
|
||||
"toString": [Function],
|
||||
"warnTooManyClasses": [Function],
|
||||
"withComponent": [Function],
|
||||
}
|
||||
}
|
||||
forwardedRef={null}
|
||||
id="notification-9000-error-toggle"
|
||||
isChecked={false}
|
||||
isDisabled={false}
|
||||
label="Failure"
|
||||
onChange={[Function]}
|
||||
rowid="items-list-item-9000"
|
||||
>
|
||||
<Switch
|
||||
aria-label="Toggle notification failure"
|
||||
className="NotificationListItem__Switch-j7c411-1 ceuHGn"
|
||||
id="notification-9000-error-toggle"
|
||||
isChecked={false}
|
||||
isDisabled={false}
|
||||
label="Failure"
|
||||
onChange={[Function]}
|
||||
rowid="items-list-item-9000"
|
||||
>
|
||||
<label
|
||||
className="pf-c-switch NotificationListItem__Switch-j7c411-1 ceuHGn"
|
||||
htmlFor="notification-9000-error-toggle"
|
||||
>
|
||||
<input
|
||||
aria-label="Toggle notification failure"
|
||||
checked={false}
|
||||
className="pf-c-switch__input"
|
||||
disabled={false}
|
||||
id="notification-9000-error-toggle"
|
||||
onChange={[Function]}
|
||||
rowid="items-list-item-9000"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
className="pf-c-switch__toggle"
|
||||
/>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pf-c-switch__label pf-m-on"
|
||||
>
|
||||
Failure
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="pf-c-switch__label pf-m-off"
|
||||
>
|
||||
Failure
|
||||
</span>
|
||||
</label>
|
||||
</Switch>
|
||||
</StyledComponent>
|
||||
</NotificationListItem__Switch>
|
||||
</div>
|
||||
</DataListItemRow>
|
||||
</li>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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,37 @@
|
||||
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, DropdownDirection } 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`)
|
||||
}}
|
||||
dropDirection={DropdownDirection.up}
|
||||
{...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';
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user