mirror of
https://github.com/prymitive/karma
synced 2026-05-05 03:16:51 +00:00
feat(ui): paginate silence list
This commit is contained in:
@@ -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 (
|
||||
<React.Fragment>
|
||||
<div className="d-flex justify-content-between mb-3">
|
||||
<div
|
||||
className="d-flex justify-content-between mb-3"
|
||||
data-refresh={settingsStore.fetchConfig.config.interval}
|
||||
>
|
||||
<span className="custom-control custom-switch my-auto flex-grow-0 flex-shrink-0">
|
||||
<input
|
||||
id="silence-show-expired"
|
||||
@@ -184,17 +210,41 @@ const Browser = observer(
|
||||
this.dataSource.silences.length === 0 ? (
|
||||
<Placeholder content="Nothing to show" />
|
||||
) : (
|
||||
<Provider alertStore={alertStore}>
|
||||
{this.dataSource.silences.map(silence => (
|
||||
<ManagedSilence
|
||||
key={`${silence.cluster}/${silence.silence.id}`}
|
||||
cluster={silence.cluster}
|
||||
silence={silence.silence}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
))}
|
||||
</Provider>
|
||||
<React.Fragment>
|
||||
<Provider alertStore={alertStore}>
|
||||
{this.dataSource.silences
|
||||
.slice(
|
||||
(this.pagination.activePage - 1) * this.maxPerPage,
|
||||
this.pagination.activePage * this.maxPerPage
|
||||
)
|
||||
.map(silence => (
|
||||
<ManagedSilence
|
||||
key={`${silence.cluster}/${silence.silence.id}`}
|
||||
cluster={silence.cluster}
|
||||
silence={silence.silence}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
))}
|
||||
</Provider>
|
||||
{this.dataSource.silences.length > this.maxPerPage ? (
|
||||
<div className="mt-3">
|
||||
<Pagination
|
||||
activePage={this.pagination.activePage}
|
||||
itemsCountPerPage={this.maxPerPage}
|
||||
totalItemsCount={this.dataSource.silences.length}
|
||||
pageRangeDisplayed={5}
|
||||
onChange={this.pagination.onPageChange}
|
||||
hideFirstLastPages
|
||||
innerClass="pagination justify-content-center"
|
||||
itemClass="page-item"
|
||||
linkClass="page-link"
|
||||
prevPageText={<FontAwesomeIcon icon={faChevronLeft} />}
|
||||
nextPageText={<FontAwesomeIcon icon={faChevronRight} />}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</React.Fragment>
|
||||
)
|
||||
) : (
|
||||
<Placeholder
|
||||
|
||||
@@ -9,11 +9,13 @@ import { advanceTo, clear } from "jest-date-mock";
|
||||
|
||||
import { MockSilence } from "__mocks__/Alerts";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { Browser } from ".";
|
||||
|
||||
let alertStore;
|
||||
let silenceFormStore;
|
||||
let settingsStore;
|
||||
let cluster;
|
||||
let silence;
|
||||
|
||||
@@ -22,6 +24,7 @@ beforeEach(() => {
|
||||
|
||||
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(
|
||||
<Browser alertStore={alertStore} silenceFormStore={silenceFormStore} />
|
||||
<Browser
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
settingsStore={settingsStore}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -159,6 +179,27 @@ describe("<Browser />", () => {
|
||||
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");
|
||||
|
||||
@@ -105,6 +105,7 @@ const SilenceModalContent = observer(
|
||||
<Browser
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
settingsStore={settingsStore}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user