start implementation of pagination for users

This commit is contained in:
Sebastian Sdorra
2018-07-26 15:30:28 +02:00
parent 0d551de147
commit ac32b88256
11 changed files with 508 additions and 32 deletions

View File

@@ -0,0 +1,119 @@
//@flow
import React from "react";
import { translate } from "react-i18next";
import type { PagedCollection } from "../types/Collection";
import { Button } from "./buttons";
type Props = {
collection: PagedCollection,
onPageChange: string => void,
t: string => string
};
class Paginator extends React.Component<Props> {
isLinkUnavailable(linkType: string) {
return !this.props.collection || !this.props.collection._links[linkType];
}
createAction = (linkType: string) => () => {
const { collection, onPageChange } = this.props;
const link = collection._links[linkType].href;
onPageChange(link);
};
renderFirstButton() {
return this.renderPageButton(1, "first");
}
renderPreviousButton() {
return this.renderButton(
"pagination-previous",
"paginator.previous",
"prev"
);
}
renderNextButton() {
return this.renderButton("pagination-next", "paginator.next", "next");
}
renderLastButton() {
const { collection } = this.props;
return this.renderPageButton(collection.pageTotal, "last");
}
renderPageButton(page: number, linkType: string) {
return this.renderButton("pagination-link", page.toString(), linkType);
}
renderButton(className: string, label: string, linkType: string) {
const { t } = this.props;
return (
<Button
className={className}
label={t(label)}
disabled={this.isLinkUnavailable(linkType)}
action={this.createAction(linkType)}
/>
);
}
seperator() {
return <span className="pagination-ellipsis">&hellip;</span>;
}
currentPage(page: number) {
return (
<Button
className="pagination-link is-current"
label={page}
disabled={true}
/>
);
}
pageLinks() {
const { collection } = this.props;
const links = [];
const page = collection.page + 1;
const pageTotal = collection.pageTotal;
if (page > 1) {
links.push(this.renderFirstButton());
}
if (page > 3) {
links.push(this.seperator());
}
if (page > 2) {
links.push(this.renderPageButton(page - 1, "prev"));
}
links.push(this.currentPage(page));
if (page + 1 < pageTotal) {
links.push(this.renderPageButton(page + 1, "next"));
links.push(this.seperator());
}
if (page < pageTotal) {
links.push(this.renderLastButton());
}
return links;
}
render() {
return (
<nav className="pagination is-centered" aria-label="pagination">
{this.renderPreviousButton()}
{this.renderNextButton()}
<ul className="pagination-list">
{this.pageLinks().map((link, index) => {
return <li key={index}>{link}</li>;
})}
</ul>
</nav>
);
}
}
export default translate("commons")(Paginator);

View File

@@ -0,0 +1,227 @@
// @flow
import React from "react";
import type { PagedCollection } from "../types/Collection";
import { mount, shallow } from "enzyme";
import "../tests/enzyme";
import "../tests/i18n";
import Paginator from "./Paginator";
describe("paginator rendering tests", () => {
const dummyLink = {
href: "https://dummy"
};
it("should render all buttons but disabled, without links", () => {
const collection = {
page: 10,
pageTotal: 20,
_links: {}
};
const paginator = shallow(<Paginator collection={collection} />);
const buttons = paginator.find("Button");
expect(buttons.length).toBe(7);
for (let button of buttons) {
expect(button.props.disabled).toBeTruthy();
}
});
it("should render buttons for first page", () => {
const collection = {
page: 0,
pageTotal: 148,
_links: {
first: dummyLink,
next: dummyLink,
last: dummyLink
}
};
const paginator = shallow(<Paginator collection={collection} />);
const buttons = paginator.find("Button");
expect(buttons.length).toBe(5);
// previous button
expect(buttons.get(0).props.disabled).toBeTruthy();
// last button
expect(buttons.get(1).props.disabled).toBeFalsy();
// first button
const firstButton = buttons.get(2).props;
expect(firstButton.disabled).toBeTruthy();
expect(firstButton.label).toBe(1);
// next button
const nextButton = buttons.get(3).props;
expect(nextButton.disabled).toBeFalsy();
expect(nextButton.label).toBe("2");
// last button
const lastButton = buttons.get(4).props;
expect(lastButton.disabled).toBeFalsy();
expect(lastButton.label).toBe("148");
});
it("should render buttons for second page", () => {
const collection = {
page: 1,
pageTotal: 148,
_links: {
first: dummyLink,
prev: dummyLink,
next: dummyLink,
last: dummyLink
}
};
const paginator = shallow(<Paginator collection={collection} />);
const buttons = paginator.find("Button");
expect(buttons.length).toBe(6);
// previous button
expect(buttons.get(0).props.disabled).toBeFalsy();
// last button
expect(buttons.get(1).props.disabled).toBeFalsy();
// first button
const firstButton = buttons.get(2).props;
expect(firstButton.disabled).toBeFalsy();
expect(firstButton.label).toBe("1");
// current button
const currentButton = buttons.get(3).props;
expect(currentButton.disabled).toBeTruthy();
expect(currentButton.label).toBe(2);
// next button
const nextButton = buttons.get(4).props;
expect(nextButton.disabled).toBeFalsy();
expect(nextButton.label).toBe("3");
// last button
const lastButton = buttons.get(5).props;
expect(lastButton.disabled).toBeFalsy();
expect(lastButton.label).toBe("148");
});
it("should render buttons for last page", () => {
const collection = {
page: 147,
pageTotal: 148,
_links: {
first: dummyLink,
prev: dummyLink
}
};
const paginator = shallow(<Paginator collection={collection} />);
const buttons = paginator.find("Button");
expect(buttons.length).toBe(5);
// previous button
expect(buttons.get(0).props.disabled).toBeFalsy();
// last button
expect(buttons.get(1).props.disabled).toBeTruthy();
// first button
const firstButton = buttons.get(2).props;
expect(firstButton.disabled).toBeFalsy();
expect(firstButton.label).toBe("1");
// next button
const nextButton = buttons.get(3).props;
expect(nextButton.disabled).toBeFalsy();
expect(nextButton.label).toBe("147");
// last button
const lastButton = buttons.get(4).props;
expect(lastButton.disabled).toBeTruthy();
expect(lastButton.label).toBe(148);
});
it("should render buttons for penultimate page", () => {
const collection = {
page: 146,
pageTotal: 148,
_links: {
first: dummyLink,
prev: dummyLink,
next: dummyLink,
last: dummyLink
}
};
const paginator = shallow(<Paginator collection={collection} />);
const buttons = paginator.find("Button");
expect(buttons.length).toBe(6);
// previous button
expect(buttons.get(0).props.disabled).toBeFalsy();
// last button
expect(buttons.get(1).props.disabled).toBeFalsy();
// first button
const firstButton = buttons.get(2).props;
expect(firstButton.disabled).toBeFalsy();
expect(firstButton.label).toBe("1");
const currentButton = buttons.get(3).props;
expect(currentButton.disabled).toBeFalsy();
expect(currentButton.label).toBe("146");
// current button
const nextButton = buttons.get(4).props;
expect(nextButton.disabled).toBeTruthy();
expect(nextButton.label).toBe(147);
// last button
const lastButton = buttons.get(5).props;
expect(lastButton.disabled).toBeFalsy();
expect(lastButton.label).toBe("148");
});
it("should render buttons for a page in the middle", () => {
const collection = {
page: 41,
pageTotal: 148,
_links: {
first: dummyLink,
prev: dummyLink,
next: dummyLink,
last: dummyLink
}
};
const paginator = shallow(<Paginator collection={collection} />);
const buttons = paginator.find("Button");
expect(buttons.length).toBe(7);
// previous button
expect(buttons.get(0).props.disabled).toBeFalsy();
// next button
expect(buttons.get(1).props.disabled).toBeFalsy();
// first button
const firstButton = buttons.get(2).props;
expect(firstButton.disabled).toBeFalsy();
expect(firstButton.label).toBe("1");
// previous Button
const previousButton = buttons.get(3).props;
expect(previousButton.disabled).toBeFalsy();
expect(previousButton.label).toBe("41");
// current button
const currentButton = buttons.get(4).props;
expect(currentButton.disabled).toBeTruthy();
expect(currentButton.label).toBe(42);
// next button
const nextButton = buttons.get(5).props;
expect(nextButton.disabled).toBeFalsy();
expect(nextButton.label).toBe("43");
// last button
const lastButton = buttons.get(6).props;
expect(lastButton.disabled).toBeFalsy();
expect(lastButton.label).toBe("148");
});
});

View File

@@ -9,7 +9,8 @@ export type ButtonProps = {
disabled?: boolean,
action?: () => void,
link?: string,
fullWidth?: boolean
fullWidth?: boolean,
className?: string
};
type Props = ButtonProps & {
@@ -17,8 +18,20 @@ type Props = ButtonProps & {
};
class Button extends React.Component<Props> {
static defaultProps = {
type: "default"
};
renderButton = () => {
const { label, loading, disabled, type, action, fullWidth } = this.props;
const {
label,
loading,
disabled,
type,
action,
fullWidth,
className
} = this.props;
const loadingClass = loading ? "is-loading" : "";
const fullWidthClass = fullWidth ? "is-fullwidth" : "";
return (
@@ -29,7 +42,8 @@ class Button extends React.Component<Props> {
"button",
"is-" + type,
loadingClass,
fullWidthClass
fullWidthClass,
className
)}
>
{label}