From 26963b4d3baaf4ce576bb607c69502ef576ffd3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Fri, 28 Feb 2020 21:34:10 +0000 Subject: [PATCH] feat(ui): add a pagination component with keyboard shortcuts --- ui/src/Components/Pagination/index.js | 89 ++++++++++++++++++++++ ui/src/Components/Pagination/index.test.js | 51 +++++++++++++ ui/src/Styles/Components/Pagination.scss | 3 + ui/src/Styles/DarkTheme.scss | 1 + ui/src/Styles/LightTheme.scss | 1 + ui/src/__mocks__/KeyPress.js | 6 ++ 6 files changed, 151 insertions(+) create mode 100644 ui/src/Components/Pagination/index.js create mode 100644 ui/src/Components/Pagination/index.test.js create mode 100644 ui/src/Styles/Components/Pagination.scss create mode 100644 ui/src/__mocks__/KeyPress.js diff --git a/ui/src/Components/Pagination/index.js b/ui/src/Components/Pagination/index.js new file mode 100644 index 000000000..c654eedfd --- /dev/null +++ b/ui/src/Components/Pagination/index.js @@ -0,0 +1,89 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; + +import { HotKeys } from "react-hotkeys"; + +import Pagination from "react-js-pagination"; + +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faAngleLeft } from "@fortawesome/free-solid-svg-icons/faAngleLeft"; +import { faAngleRight } from "@fortawesome/free-solid-svg-icons/faAngleRight"; +import { faAngleDoubleLeft } from "@fortawesome/free-solid-svg-icons/faAngleDoubleLeft"; +import { faAngleDoubleRight } from "@fortawesome/free-solid-svg-icons/faAngleDoubleRight"; + +class PageSelect extends Component { + static propTypes = { + totalPages: PropTypes.number.isRequired, + activePage: PropTypes.number.isRequired, + maxPerPage: PropTypes.number.isRequired, + totalItemsCount: PropTypes.number.isRequired, + setPageCallback: PropTypes.func.isRequired + }; + + constructor(props) { + super(props); + this.HotKeysRef = React.createRef(); + } + + onPageUp = () => { + const { setPageCallback, activePage, totalPages } = this.props; + setPageCallback(Math.min(activePage + 1, totalPages)); + }; + + onPageDown = () => { + const { setPageCallback, activePage } = this.props; + setPageCallback(Math.max(activePage - 1, 1)); + }; + + componentDidMount() { + this.HotKeysRef.current.focus(); + } + + render() { + const { + totalItemsCount, + maxPerPage, + activePage, + setPageCallback + } = this.props; + + return ( + + {totalItemsCount > maxPerPage ? ( +
+ } + nextPageText={} + firstPageText={} + lastPageText={} + /> +
+ ) : null} +
+ ); + } +} + +export { PageSelect }; diff --git a/ui/src/Components/Pagination/index.test.js b/ui/src/Components/Pagination/index.test.js new file mode 100644 index 000000000..3c42f9313 --- /dev/null +++ b/ui/src/Components/Pagination/index.test.js @@ -0,0 +1,51 @@ +import React from "react"; + +import { mount } from "enzyme"; + +import { PressKey } from "__mocks__/KeyPress"; +import { PageSelect } from "."; + +describe("", () => { + it("calls setPageCallback on arrow key press", () => { + const setPageCallback = jest.fn(); + + const tree = mount( + + ); + tree.simulate("focus"); + + setPageCallback.mockImplementation(val => + tree.setProps({ activePage: val }) + ); + + PressKey(tree, "ArrowRight", 39); + expect(setPageCallback).toHaveBeenLastCalledWith(2); + + PressKey(tree, "ArrowRight", 39); + expect(setPageCallback).toHaveBeenLastCalledWith(3); + + PressKey(tree, "ArrowRight", 39); + expect(setPageCallback).toHaveBeenLastCalledWith(4); + + PressKey(tree, "ArrowRight", 39); + expect(setPageCallback).toHaveBeenLastCalledWith(4); + + PressKey(tree, "ArrowLeft", 37); + expect(setPageCallback).toHaveBeenLastCalledWith(3); + + PressKey(tree, "ArrowLeft", 37); + expect(setPageCallback).toHaveBeenLastCalledWith(2); + + PressKey(tree, "ArrowLeft", 37); + expect(setPageCallback).toHaveBeenLastCalledWith(1); + + PressKey(tree, "ArrowLeft", 37); + expect(setPageCallback).toHaveBeenLastCalledWith(1); + }); +}); diff --git a/ui/src/Styles/Components/Pagination.scss b/ui/src/Styles/Components/Pagination.scss new file mode 100644 index 000000000..550e68f80 --- /dev/null +++ b/ui/src/Styles/Components/Pagination.scss @@ -0,0 +1,3 @@ +.components-pagination:focus { + outline: none; +} diff --git a/ui/src/Styles/DarkTheme.scss b/ui/src/Styles/DarkTheme.scss index 0f0b67961..066614883 100644 --- a/ui/src/Styles/DarkTheme.scss +++ b/ui/src/Styles/DarkTheme.scss @@ -122,6 +122,7 @@ $color-default: #708090; @import "Styles/Components/MountFade"; @import "Styles/Components/NavBarSlide"; @import "Styles/Components/SilenceModal"; +@import "Styles/Components/Pagination"; .badge, .modal-content { diff --git a/ui/src/Styles/LightTheme.scss b/ui/src/Styles/LightTheme.scss index c276a293e..17db130f2 100644 --- a/ui/src/Styles/LightTheme.scss +++ b/ui/src/Styles/LightTheme.scss @@ -105,6 +105,7 @@ $color-default: #708090; @import "Styles/Components/MountFade"; @import "Styles/Components/NavBarSlide"; @import "Styles/Components/SilenceModal"; +@import "Styles/Components/Pagination"; a { color: $link-color; diff --git a/ui/src/__mocks__/KeyPress.js b/ui/src/__mocks__/KeyPress.js new file mode 100644 index 000000000..c4652b0b1 --- /dev/null +++ b/ui/src/__mocks__/KeyPress.js @@ -0,0 +1,6 @@ +const PressKey = (tree, key, code) => { + tree.simulate("keyDown", { key: key, keyCode: code, which: code }); + tree.simulate("keyUp", { key: key, keyCode: code, which: code }); +}; + +export { PressKey };