mirror of
https://github.com/prymitive/karma
synced 2026-05-15 04:06:41 +00:00
Merge pull request #843 from prymitive/lazy-render
feat(ui): lazy render alert group content
This commit is contained in:
13
ui/package-lock.json
generated
13
ui/package-lock.json
generated
@@ -12452,6 +12452,14 @@
|
||||
"prop-types": "^15.6.2"
|
||||
}
|
||||
},
|
||||
"react-lazily-render": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-lazily-render/-/react-lazily-render-1.2.0.tgz",
|
||||
"integrity": "sha512-7G3w4m187V1VXs2LmF5e++ZPj3BqZ0lSsD63Tnz1L+MqsPCW55qqsqf1cM/uTHAR8gRK0tARosx0o9mJUyBeAg==",
|
||||
"requires": {
|
||||
"scrollparent": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"react-lifecycles-compat": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||
@@ -13471,6 +13479,11 @@
|
||||
"ajv-keywords": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"scrollparent": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/scrollparent/-/scrollparent-2.0.1.tgz",
|
||||
"integrity": "sha1-cV1bnMV3YPsivczDvvtb/gaxoxc="
|
||||
},
|
||||
"scss-tokenizer": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
"react-input-range": "1.3.0",
|
||||
"react-js-pagination": "3.0.2",
|
||||
"react-json-pretty": "2.1.0",
|
||||
"react-lazily-render": "1.2.0",
|
||||
"react-linkify": "0.2.2",
|
||||
"react-masonry-infinite": "1.2.2",
|
||||
"react-moment": "0.9.2",
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<AlertGroupContent /> lazy rendering matches snapshot when invisible 1`] = `
|
||||
"
|
||||
<div class=\\"components-grid-alertgrid-alertgroup p-1 components-animation-fade-appear components-animation-fade-appear-active\\">
|
||||
<div class=\\"card bg-light\\">
|
||||
<h5 class=\\"card-header mb-0 d-flex flex-row px-2 py-1 \\">
|
||||
<span class=\\"flex-shrink-0 flex-grow-0\\">
|
||||
<span class=\\"text-muted cursor-pointer badge pl-0 components-label mr-0 components-grid-alertgroup-099c5ca6d1c92f615b13056b935d0c8dee70f18c\\"
|
||||
data-toggle=\\"dropdown\\"
|
||||
>
|
||||
<svg aria-hidden=\\"true\\"
|
||||
focusable=\\"false\\"
|
||||
data-prefix=\\"fas\\"
|
||||
data-icon=\\"ellipsis-v\\"
|
||||
class=\\"svg-inline--fa fa-ellipsis-v fa-w-6 \\"
|
||||
role=\\"img\\"
|
||||
xmlns=\\"http://www.w3.org/2000/svg\\"
|
||||
viewbox=\\"0 0 192 512\\"
|
||||
>
|
||||
<path fill=\\"currentColor\\"
|
||||
d=\\"M96 184c39.8 0 72 32.2 72 72s-32.2 72-72 72-72-32.2-72-72 32.2-72 72-72zM24 80c0 39.8 32.2 72 72 72s72-32.2 72-72S135.8 8 96 8 24 40.2 24 80zm0 352c0 39.8 32.2 72 72 72s72-32.2 72-72-32.2-72-72-72-72 32.2-72 72z\\"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span class=\\"flex-shrink-1 flex-grow-1\\"
|
||||
style=\\"min-width: 0;\\"
|
||||
>
|
||||
<div class
|
||||
style=\\"display: inline-block; max-width: 100%;\\"
|
||||
data-tooltipped
|
||||
aria-describedby=\\"tippy-tooltip-345\\"
|
||||
data-original-title=\\"Click to only show alerts with this label or Alt+Click to hide them\\"
|
||||
>
|
||||
<span class=\\"components-label badge badge-dark components-label-dark components-label-with-hover\\">
|
||||
<span class=\\"components-label-name\\">
|
||||
alertname:
|
||||
</span>
|
||||
<span class=\\"components-label-value\\">
|
||||
Fake Alert
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class
|
||||
style=\\"display: inline-block; max-width: 100%;\\"
|
||||
data-tooltipped
|
||||
aria-describedby=\\"tippy-tooltip-346\\"
|
||||
data-original-title=\\"Click to only show alerts with this label or Alt+Click to hide them\\"
|
||||
>
|
||||
<span class=\\"components-label badge badge-warning components-label-dark components-label-with-hover\\">
|
||||
<span class=\\"components-label-name\\">
|
||||
groupName:
|
||||
</span>
|
||||
<span class=\\"components-label-value\\">
|
||||
fakeGroup
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
<span class=\\"flex-shrink-0 flex-grow-0 ml-auto pl-1\\">
|
||||
<div class
|
||||
style=\\"display: inline-block; max-width: 100%;\\"
|
||||
data-tooltipped
|
||||
aria-describedby=\\"tippy-tooltip-347\\"
|
||||
data-original-title=\\"Click to only show active alerts or Alt+Click to hide them\\"
|
||||
>
|
||||
<span class=\\"components-label badge badge-danger badge-pill components-label-with-hover\\">
|
||||
1
|
||||
</span>
|
||||
</div>
|
||||
<span class=\\"text-muted cursor-pointer badge px-0 components-label mr-0\\">
|
||||
<div class
|
||||
style=\\"display: inline-block; max-width: 100%;\\"
|
||||
data-tooltipped
|
||||
aria-describedby=\\"tippy-tooltip-348\\"
|
||||
data-original-title=\\"Toggle group details\\"
|
||||
>
|
||||
<svg aria-hidden=\\"true\\"
|
||||
focusable=\\"false\\"
|
||||
data-prefix=\\"fas\\"
|
||||
data-icon=\\"chevron-down\\"
|
||||
class=\\"svg-inline--fa fa-chevron-down fa-w-14 \\"
|
||||
role=\\"img\\"
|
||||
xmlns=\\"http://www.w3.org/2000/svg\\"
|
||||
viewbox=\\"0 0 448 512\\"
|
||||
>
|
||||
<path fill=\\"currentColor\\"
|
||||
d=\\"M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z\\"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
</h5>
|
||||
<div class=\\"card-body bg-white px-2 py-1\\">
|
||||
<ul class=\\"list-group\\">
|
||||
<li class=\\"components-grid-alertgrid-alertgroup-alert list-group-item pl-1 pr-0 py-0 my-1 rounded-0 border-left-1 border-right-0 border-top-0 border-bottom-0 border-light\\">
|
||||
<div class=\\"components-label badge badge-light text-light\\"
|
||||
style=\\"width: 100%; height: 1.3rem;\\"
|
||||
>
|
||||
</div>
|
||||
<div class=\\"components-label badge badge-secondary text-secondary\\"
|
||||
style=\\"width: 70px; height: 1.3rem;\\"
|
||||
>
|
||||
</div>
|
||||
<div class=\\"components-label badge badge-light text-light\\"
|
||||
style=\\"width: 150px; height: 1.3rem;\\"
|
||||
>
|
||||
</div>
|
||||
<div class=\\"components-label badge badge-light text-light\\"
|
||||
style=\\"width: 80px; height: 1.3rem;\\"
|
||||
>
|
||||
</div>
|
||||
<div class=\\"components-label badge badge-light text-light\\"
|
||||
style=\\"width: 70px; height: 1.3rem;\\"
|
||||
>
|
||||
</div>
|
||||
</li>
|
||||
<li class=\\"components-grid-alertgrid-alertgroup-alert list-group-item pl-1 pr-0 py-0 my-1 rounded-0 border-left-1 border-right-0 border-top-0 border-bottom-0 border-light\\">
|
||||
<div class=\\"components-label badge badge-light text-light\\"
|
||||
style=\\"width: 100%; height: 1.3rem;\\"
|
||||
>
|
||||
</div>
|
||||
<div class=\\"components-label badge badge-secondary text-secondary\\"
|
||||
style=\\"width: 70px; height: 1.3rem;\\"
|
||||
>
|
||||
</div>
|
||||
<div class=\\"components-label badge badge-light text-light\\"
|
||||
style=\\"width: 80px; height: 1.3rem;\\"
|
||||
>
|
||||
</div>
|
||||
<div class=\\"components-label badge badge-light text-light\\"
|
||||
style=\\"width: 90px; height: 1.3rem;\\"
|
||||
>
|
||||
</div>
|
||||
<div class=\\"components-label badge badge-light text-light\\"
|
||||
style=\\"width: 120px; height: 1.3rem;\\"
|
||||
>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"
|
||||
`;
|
||||
@@ -6,6 +6,8 @@ import { observable, action, toJS } from "mobx";
|
||||
|
||||
import hash from "object-hash";
|
||||
|
||||
import LazilyRender from "react-lazily-render";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons/faPlus";
|
||||
import { faMinus } from "@fortawesome/free-solid-svg-icons/faMinus";
|
||||
@@ -50,6 +52,87 @@ const AllAlertsAreUsingSameAlertmanagers = alerts => {
|
||||
);
|
||||
};
|
||||
|
||||
const AlertGroupContent = observer(
|
||||
({
|
||||
alertStore,
|
||||
silenceFormStore,
|
||||
group,
|
||||
showAlertmanagers,
|
||||
afterUpdate,
|
||||
renderConfig,
|
||||
showLoadButtons,
|
||||
loadLess,
|
||||
loadMore
|
||||
}) => (
|
||||
<div className="card-body bg-white px-2 py-1">
|
||||
<ul className="list-group">
|
||||
{group.alerts.slice(0, renderConfig.alertsToRender).map(alert => (
|
||||
<Alert
|
||||
key={hash(alert.labels)}
|
||||
group={group}
|
||||
alert={alert}
|
||||
showAlertmanagers={showAlertmanagers}
|
||||
showReceiver={group.alerts.length === 1}
|
||||
afterUpdate={afterUpdate}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
setIsMenuOpen={renderConfig.setIsMenuOpen}
|
||||
/>
|
||||
))}
|
||||
{showLoadButtons ? (
|
||||
<li className="list-group-item border-0 p-0 text-center">
|
||||
<LoadButton
|
||||
icon={faMinus}
|
||||
action={loadLess}
|
||||
tooltip="Show fewer alerts in this group"
|
||||
/>
|
||||
<small className="text-muted mx-2">
|
||||
{Math.min(renderConfig.alertsToRender, group.alerts.length)}
|
||||
{" of "}
|
||||
{group.alerts.length}
|
||||
</small>
|
||||
<LoadButton
|
||||
icon={faPlus}
|
||||
action={loadMore}
|
||||
tooltip="Show more alerts in this group"
|
||||
/>
|
||||
</li>
|
||||
) : null}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
const FakeLabel = ({ width, color }) => (
|
||||
<div
|
||||
className={`components-label badge badge-${color} text-${color}`}
|
||||
style={{ width: width, height: "1.3rem" }}
|
||||
>
|
||||
{" "}
|
||||
</div>
|
||||
);
|
||||
|
||||
const AlertGroupPlaceholder = () => (
|
||||
<div className="card-body bg-white px-2 py-1">
|
||||
<ul className="list-group">
|
||||
<li className="components-grid-alertgrid-alertgroup-alert list-group-item pl-1 pr-0 py-0 my-1 rounded-0 border-left-1 border-right-0 border-top-0 border-bottom-0 border-light">
|
||||
<FakeLabel width="100%" color="light" />
|
||||
<FakeLabel width="70px" color="secondary" />
|
||||
<FakeLabel width="150px" color="light" />
|
||||
<FakeLabel width="80px" color="light" />
|
||||
<FakeLabel width="70px" color="light" />
|
||||
</li>
|
||||
<li className="components-grid-alertgrid-alertgroup-alert list-group-item pl-1 pr-0 py-0 my-1 rounded-0 border-left-1 border-right-0 border-top-0 border-bottom-0 border-light">
|
||||
<FakeLabel width="100%" color="light" />
|
||||
<FakeLabel width="70px" color="secondary" />
|
||||
<FakeLabel width="80px" color="light" />
|
||||
<FakeLabel width="90px" color="light" />
|
||||
<FakeLabel width="120px" color="light" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
|
||||
const AlertGroup = observer(
|
||||
class AlertGroup extends Component {
|
||||
static propTypes = {
|
||||
@@ -224,56 +307,43 @@ const AlertGroup = observer(
|
||||
setIsMenuOpen={this.renderConfig.setIsMenuOpen}
|
||||
/>
|
||||
{this.collapse.value ? null : (
|
||||
<div className="card-body bg-white px-2 py-1">
|
||||
<ul className="list-group">
|
||||
{group.alerts
|
||||
.slice(0, this.renderConfig.alertsToRender)
|
||||
.map(alert => (
|
||||
<Alert
|
||||
key={hash(alert.labels)}
|
||||
group={group}
|
||||
alert={alert}
|
||||
showAlertmanagers={
|
||||
showAlertmanagers && !showAlertmanagersInFooter
|
||||
}
|
||||
showReceiver={group.alerts.length === 1}
|
||||
afterUpdate={afterUpdate}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
setIsMenuOpen={this.renderConfig.setIsMenuOpen}
|
||||
/>
|
||||
))}
|
||||
{group.alerts.length > this.defaultRenderCount ? (
|
||||
<li className="list-group-item border-0 p-0 text-center">
|
||||
<LoadButton
|
||||
icon={faMinus}
|
||||
action={this.loadLess}
|
||||
tooltip="Show fewer alerts in this group"
|
||||
/>
|
||||
<small className="text-muted mx-2">
|
||||
{Math.min(
|
||||
this.renderConfig.alertsToRender,
|
||||
group.alerts.length
|
||||
)}
|
||||
{" of "}
|
||||
{group.alerts.length}
|
||||
</small>
|
||||
<LoadButton
|
||||
icon={faPlus}
|
||||
action={this.loadMore}
|
||||
tooltip="Show more alerts in this group"
|
||||
/>
|
||||
</li>
|
||||
) : null}
|
||||
</ul>
|
||||
</div>
|
||||
<LazilyRender
|
||||
key={group.id}
|
||||
offset={100}
|
||||
onRender={afterUpdate}
|
||||
content={
|
||||
<AlertGroupContent
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
group={group}
|
||||
showAlertmanagers={
|
||||
showAlertmanagers && !showAlertmanagersInFooter
|
||||
}
|
||||
afterUpdate={afterUpdate}
|
||||
renderConfig={this.renderConfig}
|
||||
showLoadButtons={
|
||||
group.alerts.length > this.defaultRenderCount
|
||||
}
|
||||
loadLess={this.loadLess}
|
||||
loadMore={this.loadMore}
|
||||
/>
|
||||
}
|
||||
placeholder={<AlertGroupPlaceholder />}
|
||||
/>
|
||||
)}
|
||||
{this.collapse.value === false && group.alerts.length > 1 ? (
|
||||
<GroupFooter
|
||||
group={group}
|
||||
alertmanagers={footerAlertmanagers}
|
||||
afterUpdate={afterUpdate}
|
||||
silenceFormStore={silenceFormStore}
|
||||
<LazilyRender
|
||||
offset={100}
|
||||
onRender={afterUpdate}
|
||||
content={
|
||||
<GroupFooter
|
||||
group={group}
|
||||
alertmanagers={footerAlertmanagers}
|
||||
afterUpdate={afterUpdate}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
}
|
||||
placeholder={null}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,8 @@ import { mount } from "enzyme";
|
||||
|
||||
import moment from "moment";
|
||||
|
||||
import toDiffableHtml from "diffable-html";
|
||||
|
||||
import { MockAlert, MockAlertGroup } from "__mocks__/Alerts.js";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
@@ -30,6 +32,12 @@ const MockGroup = groupName => {
|
||||
|
||||
let originalInnerWidth;
|
||||
|
||||
const MockedLazyRender = jest.fn(({ content }) => {
|
||||
return content;
|
||||
});
|
||||
|
||||
jest.mock("react-lazily-render", () => props => MockedLazyRender(props));
|
||||
|
||||
beforeAll(() => {
|
||||
originalInnerWidth = global.innerWidth;
|
||||
});
|
||||
@@ -372,3 +380,50 @@ describe("<AlertGroup /> card theme", () => {
|
||||
expect(tree.find("GroupHeader").props().themedCounters).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("<AlertGroupContent /> lazy rendering", () => {
|
||||
// need to add 2x mockImplementationOnce per render since we use it twice
|
||||
// inside each AlertGroupContent
|
||||
const RenderPlaceholder = ({ placeholder }) => {
|
||||
return placeholder;
|
||||
};
|
||||
|
||||
it("renders FilteringLabel when visible", () => {
|
||||
MockAlerts(5);
|
||||
const tree = MountedAlertGroup(jest.fn(), false);
|
||||
expect(tree.find("FilteringLabel").length).toBe(8);
|
||||
});
|
||||
|
||||
it("renders GroupFooter when visible", () => {
|
||||
MockAlerts(5);
|
||||
const tree = MountedAlertGroup(jest.fn(), false);
|
||||
expect(tree.find("GroupFooter").length).toBe(1);
|
||||
});
|
||||
|
||||
it("renders AlertGroupPlaceholder when invisible", () => {
|
||||
MockAlerts(5);
|
||||
MockedLazyRender.mockImplementationOnce(
|
||||
RenderPlaceholder
|
||||
).mockImplementationOnce(RenderPlaceholder);
|
||||
const tree = MountedAlertGroup(jest.fn(), false);
|
||||
expect(tree.find("AlertGroupPlaceholder").length).toBe(1);
|
||||
});
|
||||
|
||||
it("doesn't render GroupFooter when invisible", () => {
|
||||
MockAlerts(5);
|
||||
MockedLazyRender.mockImplementationOnce(
|
||||
RenderPlaceholder
|
||||
).mockImplementationOnce(RenderPlaceholder);
|
||||
const tree = MountedAlertGroup(jest.fn(), false);
|
||||
expect(tree.find("GroupFooter").length).toBe(0);
|
||||
});
|
||||
|
||||
it("matches snapshot when invisible", () => {
|
||||
MockAlerts(5);
|
||||
MockedLazyRender.mockImplementationOnce(
|
||||
RenderPlaceholder
|
||||
).mockImplementationOnce(RenderPlaceholder);
|
||||
const tree = MountedAlertGroup(jest.fn(), false);
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user