Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(client): put pagination state in browser URL to handle back button better #554

Merged
merged 1 commit into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 50 additions & 25 deletions judgels-client/src/components/Pagination/Pagination.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Classes } from '@blueprintjs/core';
import classNames from 'classnames';
import { parse, stringify } from 'query-string';
import { PureComponent } from 'react';
import { Component } from 'react';
import ReactPaginate from 'react-paginate';
import { connect } from 'react-redux';
import { push, replace } from 'connected-react-router';
Expand Down Expand Up @@ -44,7 +44,7 @@ function Pagination({ currentPage, pageSize, totalCount, onChangePage }) {
const renderNavigation = () => {
return (
<ReactPaginate
initialPage={currentPage - 1}
forcePage={currentPage - 1}
pageCount={getTotalPages()}
pageRangeDisplayed={3}
marginPagesDisplayed={2}
Expand All @@ -60,6 +60,7 @@ function Pagination({ currentPage, pageSize, totalCount, onChangePage }) {
previousClassName={classNames(Classes.BUTTON, 'pagination__item')}
nextClassName={classNames(Classes.BUTTON, 'pagination__item')}
onPageChange={changePage}
disableInitialCallback
/>
);
};
Expand All @@ -72,41 +73,46 @@ function Pagination({ currentPage, pageSize, totalCount, onChangePage }) {
);
}

class PaginationContainer extends PureComponent {
state = { totalCount: 0 };
class PaginationContainer extends Component {
state = {
currentPage: undefined,
totalCount: 0,
};

render() {
const { location, pageSize } = this.props;
componentDidMount() {
this.refreshPagination();
}

const queries = parse(location.search);
componentDidUpdate(prevProps) {
const queries = parse(this.props.location.search);
const prevQueries = parse(prevProps.location.search);

let currentPage = 1;
const parsedCurrentPage = +queries.page;
if (queries.page && !isNaN(parsedCurrentPage)) {
currentPage = parsedCurrentPage;
if (queries.page !== prevQueries.page) {
this.refreshPagination();
}
}

render() {
const { currentPage, totalCount } = this.state;
if (!currentPage) {
return null;
}

const { pageSize } = this.props;

const props = {
currentPage,
pageSize: pageSize,
totalCount: this.state.totalCount,
pageSize,
totalCount,
onChangePage: this.onChangePage,
};
return <Pagination {...props} />;
}

onChangePage = async nextPage => {
const { location, onAppendRoute, onChangePage } = this.props;

const { location, onPush, onReplace } = this.props;
const queries = parse(location.search);
onAppendRoute(nextPage, queries);
const totalCount = await onChangePage(nextPage);
this.setState({ totalCount });
};
}

const mapDispatchToProps = {
onAppendRoute: (nextPage, queries) => {
let query = '';
if (nextPage > 1) {
query = stringify({ ...queries, page: nextPage });
Expand All @@ -115,10 +121,29 @@ const mapDispatchToProps = {
}

if (!queries.page && nextPage === 1) {
return replace({ search: query });
return onReplace({ search: query });
} else {
return push({ search: query });
return onPush({ search: query });
}
};

refreshPagination = async () => {
const { location, onChangePage } = this.props;
const queries = parse(location.search);

let currentPage = 1;
const parsedCurrentPage = +queries.page;
if (queries.page && !isNaN(parsedCurrentPage)) {
currentPage = parsedCurrentPage;
}
},

const totalCount = await onChangePage(currentPage);
this.setState({ currentPage, totalCount });
};
}

const mapDispatchToProps = {
onPush: push,
onReplace: replace,
};
export default withRouter(connect(undefined, mapDispatchToProps)(PaginationContainer));
28 changes: 7 additions & 21 deletions judgels-client/src/components/Pagination/Pagination.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('Pagination', () => {
let wrapper;
let onChangePage;

const render = pageQuery => {
const render = async pageQuery => {
const props = {
pageSize: 6,
onChangePage,
Expand All @@ -27,6 +27,9 @@ describe('Pagination', () => {
</MemoryRouter>
</Provider>
);

await new Promise(resolve => setImmediate(resolve));
wrapper.update();
};

beforeEach(() => {
Expand All @@ -40,10 +43,7 @@ describe('Pagination', () => {
render('');
});

it('does not show the helper text', async () => {
await new Promise(resolve => setImmediate(resolve));
wrapper.update();

it('does not show the helper text', () => {
expect(wrapper.find('[data-key="pagination-helper-text"]')).toHaveLength(0);
});
});
Expand All @@ -55,10 +55,7 @@ describe('Pagination', () => {
expect(onChangePage).toBeCalledWith(1);
});

it('shows the helper text', async () => {
await new Promise(resolve => setImmediate(resolve));
wrapper.update();

it('shows the helper text', () => {
expect(wrapper.find('[data-key="pagination-helper-text"]').text()).toEqual('Showing 1..6 of 14 results');
});
});
Expand All @@ -70,10 +67,7 @@ describe('Pagination', () => {
expect(onChangePage).toBeCalledWith(3);
});

it('shows the helper text', async () => {
await new Promise(resolve => setImmediate(resolve));
wrapper.update();

it('shows the helper text', () => {
expect(wrapper.find('[data-key="pagination-helper-text"]').text()).toEqual('Showing 13..14 of 14 results');
});
});
Expand All @@ -92,10 +86,6 @@ describe('Pagination', () => {
it('clears the query string', () => {
expect(store.getActions()).toContainEqual(push({ search: '' }));
});

it('navigates to that page', () => {
expect(onChangePage).toBeCalledWith(1);
});
});

describe('when page changes to page > 1', () => {
Expand All @@ -110,10 +100,6 @@ describe('Pagination', () => {
const query = stringify({ page: 3 });
expect(store.getActions()).toContainEqual(push({ search: query }));
});

it('navigates to that page', () => {
expect(onChangePage).toBeCalledWith(3);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ class ContestAnnouncementsPage extends Component {
return (
<Pagination
key={lastRefreshAnnouncementsTime}
currentPage={1}
pageSize={ContestAnnouncementsPage.PAGE_SIZE}
onChangePage={this.onChangePage}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,18 +118,10 @@ class ContestClarificationsPage extends Component {
};

renderPagination = () => {
// updates pagination when clarifications are refreshed
const { lastRefreshClarificationsTime } = this.state;
const key = lastRefreshClarificationsTime || 0;

return (
<Pagination
key={key}
currentPage={1}
pageSize={ContestClarificationsPage.PAGE_SIZE}
onChangePage={this.onChangePage}
/>
);
return <Pagination key={key} pageSize={ContestClarificationsPage.PAGE_SIZE} onChangePage={this.onChangePage} />;
};

toggleAnswerBox = clarification => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ class ContestContestantsPage extends Component {
return (
<Pagination
key={lastRefreshContestantsTime}
currentPage={1}
pageSize={ContestContestantsPage.PAGE_SIZE}
onChangePage={this.onChangePage}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ export class ContestScoreboardPage extends Component {
{this.renderScoreboard()}
<Pagination
key={lastRefreshScoreboardTime}
currentPage={1}
pageSize={ContestScoreboardPage.PAGE_SIZE}
onChangePage={this.onChangePage}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ class ContestSupervisorsPage extends Component {
return (
<Pagination
key={lastRefreshSupervisorsTime}
currentPage={1}
pageSize={ContestSupervisorsPage.PAGE_SIZE}
onChangePage={this.onChangePage}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,7 @@ export class ProblemSubmissionsPage extends Component {
};

renderPagination = () => {
return (
<Pagination
key={1}
currentPage={1}
pageSize={ProblemSubmissionsPage.PAGE_SIZE}
onChangePage={this.onChangePage}
/>
);
return <Pagination pageSize={ProblemSubmissionsPage.PAGE_SIZE} onChangePage={this.onChangePage} />;
};

refreshSubmissions = async page => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ export class ProblemSubmissionsPage extends Component {
return (
<Pagination
key={'' + this.isUserFilterMine()}
currentPage={1}
pageSize={ProblemSubmissionsPage.PAGE_SIZE}
onChangePage={this.onChangePage}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class RatingsPage extends Component {
return (
<Card title="Top ratings">
{this.renderRatings()}
<Pagination currentPage={1} pageSize={RatingsPage.PAGE_SIZE} onChangePage={this.onChangePage} />
<Pagination pageSize={RatingsPage.PAGE_SIZE} onChangePage={this.onChangePage} />
</Card>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ScoresPage extends Component {
return (
<Card title="Top scorers">
{this.renderScores()}
<Pagination currentPage={1} pageSize={ScoresPage.PAGE_SIZE} onChangePage={this.onChangePage} />
<Pagination pageSize={ScoresPage.PAGE_SIZE} onChangePage={this.onChangePage} />
</Card>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { selectMaybeUserJid, selectMaybeUsername } from '../../../modules/sessio
import * as submissionActions from '../modules/submissionActions';

export class SubmissionsPage extends Component {
static PAGE_SIZE = 20;
static PAGE_SIZE = 5;

state = {
response: undefined,
Expand Down Expand Up @@ -77,7 +77,6 @@ export class SubmissionsPage extends Component {
return (
<Pagination
key={'' + this.isUserFilterMine()}
currentPage={1}
pageSize={SubmissionsPage.PAGE_SIZE}
onChangePage={this.onChangePage}
/>
Expand Down