mirror of
https://github.com/prymitive/karma
synced 2026-05-05 03:16:51 +00:00
feat(ui): paginate long preview lists
This commit is contained in:
@@ -34,6 +34,7 @@
|
||||
"react-highlighter": "0.4.3",
|
||||
"react-idle-timer": "4.0.9",
|
||||
"react-input-range": "1.3.0",
|
||||
"react-js-pagination": "3.0.2",
|
||||
"react-json-pretty": "1.7.9",
|
||||
"react-linkify": "0.2.2",
|
||||
"react-masonry-infinite": "1.2.2",
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<LabelSetList /> matches snapshot with populated list 1`] = `
|
||||
"
|
||||
<ul class=\\"list-group list-group-flush\\">
|
||||
<li class=\\"list-group-item px-0 pt-2 pb-1\\">
|
||||
<span class=\\"components-label text-nowrap text-truncate badge badge-warning mw-100\\">
|
||||
foo: bar
|
||||
</span>
|
||||
</li>
|
||||
<li class=\\"list-group-item px-0 pt-2 pb-1\\">
|
||||
<span class=\\"components-label text-nowrap text-truncate badge badge-warning mw-100\\">
|
||||
job: node_exporter
|
||||
</span>
|
||||
</li>
|
||||
<li class=\\"list-group-item px-0 pt-2 pb-1\\">
|
||||
<span class=\\"components-label text-nowrap text-truncate badge badge-warning mw-100\\">
|
||||
instance: server1
|
||||
</span>
|
||||
</li>
|
||||
<li class=\\"list-group-item px-0 pt-2 pb-1\\">
|
||||
<span class=\\"components-label text-nowrap text-truncate badge badge-warning mw-100\\">
|
||||
cluster: prod
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
"
|
||||
`;
|
||||
@@ -1,8 +1,17 @@
|
||||
import React from "react";
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { observer } from "mobx-react";
|
||||
import { observable, action } from "mobx";
|
||||
|
||||
import hash from "object-hash";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons/faChevronLeft";
|
||||
import { faChevronRight } from "@fortawesome/free-solid-svg-icons/faChevronRight";
|
||||
|
||||
import Pagination from "react-js-pagination";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { StaticLabel } from "Components/Labels/StaticLabel";
|
||||
|
||||
@@ -25,29 +34,78 @@ const GroupListToUniqueLabelsList = groups => {
|
||||
return Object.values(alerts);
|
||||
};
|
||||
|
||||
// used in new silence form preview stage and when deleting silences
|
||||
const LabelSetList = ({ alertStore, labelsList }) =>
|
||||
labelsList.length > 0 ? (
|
||||
<ul className="list-group list-group-flush">
|
||||
{labelsList.map(labels => (
|
||||
<li key={hash(labels)} className="list-group-item px-0 pt-2 pb-1">
|
||||
{Object.entries(labels).map(([name, value]) => (
|
||||
<StaticLabel
|
||||
key={name}
|
||||
alertStore={alertStore}
|
||||
name={name}
|
||||
value={value}
|
||||
/>
|
||||
))}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p className="text-muted text-center">No alerts matched</p>
|
||||
);
|
||||
LabelSetList.propTypes = {
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
labelsList: PropTypes.arrayOf(PropTypes.object).isRequired
|
||||
};
|
||||
const LabelSetList = observer(
|
||||
class LabelSetList extends Component {
|
||||
static propTypes = {
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
labelsList: PropTypes.arrayOf(PropTypes.object).isRequired
|
||||
};
|
||||
|
||||
maxPerPage = 10;
|
||||
|
||||
pagination = observable(
|
||||
{
|
||||
activePage: 1,
|
||||
onPageChange(pageNumber) {
|
||||
this.activePage = pageNumber;
|
||||
}
|
||||
},
|
||||
{
|
||||
onPageChange: action.bound
|
||||
}
|
||||
);
|
||||
|
||||
render() {
|
||||
const { alertStore, labelsList } = this.props;
|
||||
|
||||
return labelsList.length > 0 ? (
|
||||
<React.Fragment>
|
||||
<ul className="list-group list-group-flush">
|
||||
{labelsList
|
||||
.slice(
|
||||
(this.pagination.activePage - 1) * this.maxPerPage,
|
||||
this.pagination.activePage * this.maxPerPage
|
||||
)
|
||||
.map(labels => (
|
||||
<li
|
||||
key={hash(labels)}
|
||||
className="list-group-item px-0 pt-2 pb-1"
|
||||
>
|
||||
{Object.entries(labels).map(([name, value]) => (
|
||||
<StaticLabel
|
||||
key={name}
|
||||
alertStore={alertStore}
|
||||
name={name}
|
||||
value={value}
|
||||
/>
|
||||
))}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{labelsList.length > this.maxPerPage ? (
|
||||
<div className="mt-3">
|
||||
<Pagination
|
||||
activePage={this.pagination.activePage}
|
||||
itemsCountPerPage={this.maxPerPage}
|
||||
totalItemsCount={labelsList.length}
|
||||
pageRangeDisplayed={5}
|
||||
onChange={this.pagination.onPageChange}
|
||||
hideFirstLastPages
|
||||
hideDisabled
|
||||
innerClass="pagination justify-content-center"
|
||||
itemClass="page-item"
|
||||
linkClass="page-link"
|
||||
prevPageText={<FontAwesomeIcon icon={faChevronLeft} />}
|
||||
nextPageText={<FontAwesomeIcon icon={faChevronRight} />}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<p className="text-muted text-center">No alerts matched</p>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export { LabelSetList, GroupListToUniqueLabelsList };
|
||||
|
||||
66
ui/src/Components/LabelSetList/index.test.js
Normal file
66
ui/src/Components/LabelSetList/index.test.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import React from "react";
|
||||
|
||||
import { mount } from "enzyme";
|
||||
|
||||
import toDiffableHtml from "diffable-html";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { LabelSetList } from ".";
|
||||
|
||||
let alertStore;
|
||||
|
||||
beforeEach(() => {
|
||||
alertStore = new AlertStore([]);
|
||||
});
|
||||
|
||||
const MountedLabelSetList = labelsList => {
|
||||
return mount(
|
||||
<LabelSetList alertStore={alertStore} labelsList={labelsList} />
|
||||
);
|
||||
};
|
||||
|
||||
describe("<LabelSetList />", () => {
|
||||
it("renders placeholder on empty list", () => {
|
||||
const tree = MountedLabelSetList([]);
|
||||
expect(tree.text()).toBe("No alerts matched");
|
||||
});
|
||||
|
||||
it("renders labels on populated list", () => {
|
||||
const tree = MountedLabelSetList([{ foo: "bar" }]);
|
||||
expect(tree.text()).not.toBe("No alerts matched");
|
||||
expect(tree.text()).toBe("foo: bar");
|
||||
});
|
||||
|
||||
it("matches snapshot with populated list", () => {
|
||||
const tree = MountedLabelSetList([
|
||||
{ foo: "bar" },
|
||||
{ job: "node_exporter" },
|
||||
{ instance: "server1" },
|
||||
{ cluster: "prod" }
|
||||
]);
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("doesn't render pagination when list has 9 elements", () => {
|
||||
const tree = MountedLabelSetList(
|
||||
Array.from(Array(9), (_, i) => ({ instance: `server${i}` }))
|
||||
);
|
||||
expect(tree.find(".pagination")).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("renders pagination when list has 11 elements", () => {
|
||||
const tree = MountedLabelSetList(
|
||||
Array.from(Array(11), (_, i) => ({ instance: `server${i}` }))
|
||||
);
|
||||
expect(tree.find(".pagination")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("clicking on pagination changes displayed elements", () => {
|
||||
const tree = MountedLabelSetList(
|
||||
Array.from(Array(21), (_, i) => ({ instance: `server${i + 1}` }))
|
||||
);
|
||||
const pageLink = tree.find(".page-link").at(2);
|
||||
pageLink.simulate("click");
|
||||
expect(tree.text()).toBe("instance: server21");
|
||||
});
|
||||
});
|
||||
19
ui/yarn.lock
19
ui/yarn.lock
@@ -7875,6 +7875,11 @@ p-try@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1"
|
||||
integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==
|
||||
|
||||
paginator@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/paginator/-/paginator-1.0.0.tgz#7565702af9ab9616dca61fc22c70eba2a4357265"
|
||||
integrity sha1-dWVwKvmrlhbcph/CLHDroqQ1cmU=
|
||||
|
||||
pako@~1.0.5:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"
|
||||
@@ -8838,7 +8843,7 @@ prompts@^0.1.9:
|
||||
kleur "^2.0.1"
|
||||
sisteransi "^0.1.1"
|
||||
|
||||
prop-types@15.6.2, prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2:
|
||||
prop-types@15.6.2, "prop-types@15.x.x - 16.x.x", prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2:
|
||||
version "15.6.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
|
||||
integrity sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==
|
||||
@@ -9149,6 +9154,16 @@ react-is@^16.5.2:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.5.2.tgz#e2a7b7c3f5d48062eb769fcb123505eb928722e3"
|
||||
integrity sha512-hSl7E6l25GTjNEZATqZIuWOgSnpXb3kD0DVCujmg46K5zLxsbiKaaT6VO9slkSBDPZfYs30lwfJwbOFOnoEnKQ==
|
||||
|
||||
react-js-pagination@3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-js-pagination/-/react-js-pagination-3.0.2.tgz#003fead038c1be3eaeaf2895905b63daadb1824f"
|
||||
integrity sha512-gsXaQVis1YDKhbeZC7VT9dsSWyZ8GZNEX06dy2HLl36LohY2Vt/iWJ5k6HAb0YOKW0mklCY7wjxJIfJeeP295g==
|
||||
dependencies:
|
||||
classnames "^2.2.5"
|
||||
paginator "^1.0.0"
|
||||
prop-types "15.x.x - 16.x.x"
|
||||
react "15.x.x - 16.x.x"
|
||||
|
||||
react-json-pretty@1.7.9:
|
||||
version "1.7.9"
|
||||
resolved "https://registry.yarnpkg.com/react-json-pretty/-/react-json-pretty-1.7.9.tgz#d153c52579fda9e245c81fceb3da22fdf1d93c1e"
|
||||
@@ -9321,7 +9336,7 @@ react-transition-group@2.5.0, react-transition-group@^2.2.1:
|
||||
prop-types "^15.6.2"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
|
||||
react@16.5.2:
|
||||
"react@15.x.x - 16.x.x", react@16.5.2:
|
||||
version "16.5.2"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.5.2.tgz#19f6b444ed139baa45609eee6dc3d318b3895d42"
|
||||
integrity sha512-FDCSVd3DjVTmbEAjUNX6FgfAmQ+ypJfHUsqUJOYNCBUp1h8lqmtC+0mXJ+JjsWx4KAVTkk1vKd1hLQPvEviSuw==
|
||||
|
||||
Reference in New Issue
Block a user