From 6dc2ea985b94acf8c55fc0b7bf178a54d4bc8e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Sat, 26 Oct 2019 19:55:22 +0100 Subject: [PATCH] feat(ui): paginate silence list --- .../Components/SilenceModal/Browser/index.js | 84 +++++++++++++++---- .../SilenceModal/Browser/index.test.js | 43 +++++++++- .../SilenceModal/SilenceModalContent.js | 1 + 3 files changed, 110 insertions(+), 18 deletions(-) diff --git a/ui/src/Components/SilenceModal/Browser/index.js b/ui/src/Components/SilenceModal/Browser/index.js index 6009a1f8c..fc6bcdac8 100644 --- a/ui/src/Components/SilenceModal/Browser/index.js +++ b/ui/src/Components/SilenceModal/Browser/index.js @@ -6,15 +6,19 @@ import { observer, Provider } from "mobx-react"; import { debounce } from "lodash"; +import Pagination from "react-js-pagination"; + import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faSpinner } from "@fortawesome/free-solid-svg-icons/faSpinner"; import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons/faExclamationCircle"; - import { faSortAmountDownAlt } from "@fortawesome/free-solid-svg-icons/faSortAmountDownAlt"; import { faSortAmountUp } from "@fortawesome/free-solid-svg-icons/faSortAmountUp"; +import { faChevronLeft } from "@fortawesome/free-solid-svg-icons/faChevronLeft"; +import { faChevronRight } from "@fortawesome/free-solid-svg-icons/faChevronRight"; import { AlertStore, FormatBackendURI } from "Stores/AlertStore"; import { SilenceFormStore } from "Stores/SilenceFormStore"; +import { Settings } from "Stores/Settings"; import { FetchWithCredentials } from "Common/Fetch"; import { MountFade } from "Components/Animations/MountFade"; import { ManagedSilence } from "Components/ManagedSilence"; @@ -46,7 +50,8 @@ const Browser = observer( class Browser extends Component { static propTypes = { alertStore: PropTypes.instanceOf(AlertStore).isRequired, - silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired + silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired, + settingsStore: PropTypes.instanceOf(Settings).isRequired }; fetchTimer = null; @@ -112,10 +117,28 @@ const Browser = observer( }); }, 500); + maxPerPage = 5; + + pagination = observable( + { + activePage: 1, + onPageChange(pageNumber) { + this.activePage = pageNumber; + } + }, + { + onPageChange: action.bound + } + ); + componentDidMount() { + const { settingsStore } = this.props; + this.onFetch(); - // FIXME use settings refresh interval - this.fetchTimer = setInterval(this.onFetch, 10 * 1000); + this.fetchTimer = setInterval( + this.onFetch, + settingsStore.fetchConfig.config.interval * 1000 + ); } componentWillUnmount() { @@ -124,11 +147,14 @@ const Browser = observer( } render() { - const { alertStore, silenceFormStore } = this.props; + const { alertStore, silenceFormStore, settingsStore } = this.props; return ( -
+
) : ( - - {this.dataSource.silences.map(silence => ( - - ))} - + + + {this.dataSource.silences + .slice( + (this.pagination.activePage - 1) * this.maxPerPage, + this.pagination.activePage * this.maxPerPage + ) + .map(silence => ( + + ))} + + {this.dataSource.silences.length > this.maxPerPage ? ( +
+ } + nextPageText={} + /> +
+ ) : null} +
) ) : ( { alertStore = new AlertStore([]); silenceFormStore = new SilenceFormStore(); + settingsStore = new Settings(); cluster = "am"; silence = MockSilence(); @@ -51,9 +54,26 @@ afterEach(() => { clear(); }); +const MockSilenceList = count => { + let silences = []; + for (var index = 1; index <= count; index++) { + const silence = MockSilence(); + silence.id = `silence${index}`; + silences.push({ + cluster: cluster, + silence: silence + }); + } + return silences; +}; + const MountedBrowser = () => { return mount( - + ); }; @@ -159,6 +179,27 @@ describe("", () => { expect(tree.find("ManagedSilence")).toHaveLength(1); }); + it("renders only first 5 silences", async () => { + fetch.mockResponse(JSON.stringify(MockSilenceList(6))); + const tree = MountedBrowser(); + await expect(tree.instance().dataSource.fetch).resolves.toBeUndefined(); + tree.update(); + expect(tree.find("ManagedSilence")).toHaveLength(5); + }); + + it("renders last silence after page change", async () => { + fetch.mockResponse(JSON.stringify(MockSilenceList(6))); + const tree = MountedBrowser(); + await expect(tree.instance().dataSource.fetch).resolves.toBeUndefined(); + tree.update(); + expect(tree.find("ManagedSilence")).toHaveLength(5); + + const pageLink = tree.find(".page-link").at(3); + pageLink.simulate("click"); + tree.update(); + expect(tree.find("ManagedSilence")).toHaveLength(1); + }); + it("renders error after failed fetch", async () => { jest.spyOn(console, "trace").mockImplementation(() => {}); fetch.mockReject("fake failure"); diff --git a/ui/src/Components/SilenceModal/SilenceModalContent.js b/ui/src/Components/SilenceModal/SilenceModalContent.js index ebc3286c7..41e561eb9 100644 --- a/ui/src/Components/SilenceModal/SilenceModalContent.js +++ b/ui/src/Components/SilenceModal/SilenceModalContent.js @@ -105,6 +105,7 @@ const SilenceModalContent = observer( ) : null}