Skip to content

Commit

Permalink
feat(Pagination): User can now enter numbers into the input field wit…
Browse files Browse the repository at this point in the history
…hout highlighting the page numb

Fixes #2344
  • Loading branch information
rebeccaalpert committed Jul 1, 2019
1 parent 67c70a9 commit 6d87751
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 97 deletions.
216 changes: 123 additions & 93 deletions packages/patternfly-4/react-core/src/components/Pagination/Navigation.js
Expand Up @@ -4,6 +4,7 @@ import styles from '@patternfly/react-styles/css/components/Pagination/paginatio
import { css } from '@patternfly/react-styles';
import { AngleLeftIcon, AngleDoubleLeftIcon, AngleRightIcon, AngleDoubleRightIcon } from '@patternfly/react-icons';
import { Button, ButtonVariant } from '../Button';
import { KEY_CODES } from '../../helpers/constants';

const propTypes = {
className: PropTypes.string,
Expand All @@ -15,7 +16,7 @@ const propTypes = {
toFirstPage: PropTypes.string,
currPage: PropTypes.string,
paginationTitle: PropTypes.string,
page: PropTypes.number.isRequired,
page: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
onSetPage: PropTypes.func.isRequired,
onNextClick: PropTypes.func,
onPreviousClick: PropTypes.func,
Expand All @@ -40,99 +41,128 @@ const defaultProps = {
onPageInput: () => undefined
};

const Navigation = ({
page,
lastPage,
pagesTitle,
toLastPage,
toNextPage,
toFirstPage,
toPreviousPage,
currPage,
paginationTitle,
onSetPage,
onNextClick,
onPreviousClick,
onFirstClick,
onLastClick,
onPageInput,
className,
...props
}) => (
<nav className={css(styles.paginationNav, className)} aria-label={paginationTitle} {...props}>
<Button
variant={ButtonVariant.plain}
isDisabled={page === 1}
aria-label={toFirstPage}
data-action="first"
onClick={event => {
onFirstClick(event, 1);
onSetPage(event, 1);
}}
>
<AngleDoubleLeftIcon />
</Button>
<Button
variant={ButtonVariant.plain}
isDisabled={page === 1}
data-action="previous"
onClick={event => {
const newPage = page - 1 >= 1 ? page - 1 : 1;
onPreviousClick(event, newPage);
onSetPage(event, newPage);
}}
aria-label={toPreviousPage}
>
<AngleLeftIcon />
</Button>
<div className={css(styles.paginationNavPageSelect)}>
<input
className={css(styles.formControl)}
aria-label={currPage}
type="number"
min="1"
max={lastPage}
value={page}
onChange={event => {
let inputPage = Number.parseInt(event.target.value, 10);
inputPage = Number.isNaN(inputPage) ? page : inputPage;
inputPage = inputPage > lastPage ? lastPage : inputPage;
inputPage = inputPage < 1 ? 1 : inputPage;
onSetPage(event, Number.isNaN(inputPage) ? page : inputPage);
onPageInput(event, Number.isNaN(inputPage) ? page : inputPage);
class Navigation extends React.Component {
constructor(props) {
super(props);
this.state = { userInputPage: this.props.page };
}

parseInteger(input, lastPage) {
let inputPage = Number.parseInt(input, 10);
if (!Number.isNaN(inputPage)) {
inputPage = inputPage > lastPage ? lastPage : inputPage;
inputPage = inputPage < 1 ? 1 : inputPage;
}
return inputPage;
}

onChange(event, lastPage) {
const inputPage = this.parseInteger(event.target.value, lastPage);
this.setState({userInputPage: Number.isNaN(inputPage) ? event.target.value : inputPage});
}

onKeyDown(event, page, lastPage, onPageInput, onSetPage){
if (event.keyCode === KEY_CODES.ENTER) {
const inputPage = this.parseInteger(event.target.value, lastPage);
onPageInput(event, Number.isNaN(inputPage) ? page : inputPage);
onSetPage(event, Number.isNaN(inputPage) ? page : inputPage);
}
}

render() {
const {
page,
lastPage,
pagesTitle,
toLastPage,
toNextPage,
toFirstPage,
toPreviousPage,
currPage,
paginationTitle,
onSetPage,
onNextClick,
onPreviousClick,
onFirstClick,
onLastClick,
onPageInput,
className,
...props
} = this.props;
const { userInputPage } = this.state;
return <nav className={css(styles.paginationNav, className)} aria-label={paginationTitle} {...props}>
<Button
variant={ButtonVariant.plain}
isDisabled={page === 1}
aria-label={toFirstPage}
data-action="first"
onClick={event => {
onFirstClick(event, 1);
onSetPage(event, 1);
this.setState({userInputPage: 1});
}}
>
<AngleDoubleLeftIcon />
</Button>
<Button
variant={ButtonVariant.plain}
isDisabled={page === 1}
data-action="previous"
onClick={event => {
const newPage = page - 1 >= 1 ? page - 1 : 1;
onPreviousClick(event, newPage);
onSetPage(event, newPage);
this.setState({userInputPage: newPage});
}}
aria-label={toPreviousPage}
>
<AngleLeftIcon />
</Button>
<div className={css(styles.paginationNavPageSelect)}>
<input
className={css(styles.formControl)}
aria-label={currPage}
type="number"
min="1"
max={lastPage}
value={userInputPage}
onKeyDown={event => this.onKeyDown(event, page, lastPage, onPageInput, onSetPage)}
onChange={event => this.onChange(event, lastPage)}
/>
<span aria-hidden="true">
of {lastPage} {pagesTitle}
</span>
</div>
<Button
variant={ButtonVariant.plain}
isDisabled={page === lastPage}
aria-label={toNextPage}
data-action="next"
onClick={event => {
const newPage = page + 1 <= lastPage ? page + 1 : lastPage;
onNextClick(event, newPage);
onSetPage(event, newPage);
this.setState({userInputPage: newPage});
}}
>
<AngleRightIcon />
</Button>
<Button
variant={ButtonVariant.plain}
isDisabled={page === lastPage}
aria-label={toLastPage}
data-action="last"
onClick={event => {
onLastClick(event, lastPage);
onSetPage(event, lastPage);
this.setState({userInputPage: lastPage});
}}
/>
<span aria-hidden="true">
of {lastPage} {pagesTitle}
</span>
</div>
<Button
variant={ButtonVariant.plain}
isDisabled={page === lastPage}
aria-label={toNextPage}
data-action="next"
onClick={event => {
const newPage = page + 1 <= lastPage ? page + 1 : lastPage;
onNextClick(event, newPage);
onSetPage(event, newPage);
}}
>
<AngleRightIcon />
</Button>
<Button
variant={ButtonVariant.plain}
isDisabled={page === lastPage}
aria-label={toLastPage}
data-action="last"
onClick={event => {
onLastClick(event, lastPage);
onSetPage(event, lastPage);
}}
>
<AngleDoubleRightIcon />
</Button>
</nav>
);
>
<AngleDoubleRightIcon />
</Button>
</nav>;
}
}

Navigation.propTypes = propTypes;
Navigation.defaultProps = defaultProps;
Expand Down
Expand Up @@ -111,7 +111,8 @@ describe('API', () => {
wrapper
.find('input')
.first()
.simulate('change', { target: { value: '1' } });
.simulate('change', { target: { value: '1' } })
.simulate('keydown', { keyCode: 13 });
expect(onSetPage.mock.calls).toHaveLength(1);
expect(onSetPage.mock.calls[0][1]).toBe(1);
});
Expand All @@ -121,7 +122,8 @@ describe('API', () => {
wrapper
.find('input')
.first()
.simulate('change', { target: { value: 'a' } });
.simulate('change', { target: { value: 'a' } })
.simulate('keydown', { keyCode: 13 });
expect(onSetPage.mock.calls).toHaveLength(1);
expect(onSetPage.mock.calls[0][1]).toBe(1);
});
Expand All @@ -131,7 +133,8 @@ describe('API', () => {
wrapper
.find('input')
.first()
.simulate('change', { target: { value: '10' } });
.simulate('change', { target: { value: '10' } })
.simulate('keydown', { keyCode: 13 });
expect(onSetPage.mock.calls).toHaveLength(1);
expect(onSetPage.mock.calls[0][1]).toBe(4);
});
Expand All @@ -141,7 +144,8 @@ describe('API', () => {
wrapper
.find('input')
.first()
.simulate('change', { target: { value: '-10' } });
.simulate('change', { target: { value: '-10' } })
.simulate('keydown', { keyCode: 13 });
expect(onSetPage.mock.calls).toHaveLength(1);
expect(onSetPage.mock.calls[0][1]).toBe(1);
});
Expand Down
Expand Up @@ -556,6 +556,7 @@ exports[`component render custom pagination toggle 1`] = `
max={4}
min="1"
onChange={[Function]}
onKeyDown={[Function]}
type="number"
value={1}
/>
Expand Down Expand Up @@ -1086,6 +1087,7 @@ exports[`component render custom perPageOptions 1`] = `
max={4}
min="1"
onChange={[Function]}
onKeyDown={[Function]}
type="number"
value={1}
/>
Expand Down Expand Up @@ -1768,6 +1770,7 @@ exports[`component render custom start end 1`] = `
max={4}
min="1"
onChange={[Function]}
onKeyDown={[Function]}
type="number"
value={1}
/>
Expand Down Expand Up @@ -2171,6 +2174,7 @@ exports[`component render empty per page options 1`] = `
max={4}
min="1"
onChange={[Function]}
onKeyDown={[Function]}
type="number"
value={1}
/>
Expand Down Expand Up @@ -2853,6 +2857,7 @@ exports[`component render last page 1`] = `
max={2}
min="1"
onChange={[Function]}
onKeyDown={[Function]}
type="number"
value={2}
/>
Expand Down Expand Up @@ -3535,6 +3540,7 @@ exports[`component render limited number of pages 1`] = `
max={1}
min="1"
onChange={[Function]}
onKeyDown={[Function]}
type="number"
value={1}
/>
Expand Down Expand Up @@ -4217,6 +4223,7 @@ exports[`component render no items 1`] = `
max={0}
min="1"
onChange={[Function]}
onKeyDown={[Function]}
type="number"
value={1}
/>
Expand Down Expand Up @@ -4894,6 +4901,7 @@ exports[`component render should render correctly bottom 1`] = `
max={2}
min="1"
onChange={[Function]}
onKeyDown={[Function]}
type="number"
value={1}
/>
Expand Down Expand Up @@ -5576,6 +5584,7 @@ exports[`component render should render correctly top 1`] = `
max={2}
min="1"
onChange={[Function]}
onKeyDown={[Function]}
type="number"
value={1}
/>
Expand Down Expand Up @@ -6249,6 +6258,7 @@ exports[`component render titles 1`] = `
max={4}
min="1"
onChange={[Function]}
onKeyDown={[Function]}
type="number"
value={1}
/>
Expand Down Expand Up @@ -6931,6 +6941,7 @@ exports[`component render up drop direction 1`] = `
max={4}
min="1"
onChange={[Function]}
onKeyDown={[Function]}
type="number"
value={1}
/>
Expand Down

0 comments on commit 6d87751

Please sign in to comment.