feat(ui): show only common labels in the overview modal by default

This commit is contained in:
Łukasz Mierzwa
2019-07-16 19:19:35 +01:00
parent 3d3e81be4b
commit d1e1d7fe2f
2 changed files with 175 additions and 65 deletions

View File

@@ -2,53 +2,97 @@ import React, { Component } from "react";
import PropTypes from "prop-types";
import { observer } from "mobx-react";
import { observable, action } from "mobx";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faChevronUp } from "@fortawesome/free-solid-svg-icons/faChevronUp";
import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
import { AlertStore } from "Stores/AlertStore";
import { TooltipWrapper } from "Components/TooltipWrapper";
import { LabelWithPercent } from "Components/Labels/LabelWithPercent";
const LabelsTable = observer(({ alertStore }) => (
<table
className="table table-borderless top-labels"
style={{ tableLayout: "fixed" }}
>
<tbody className="mw-100">
{alertStore.data.counters.map(nameStats => (
<tr key={nameStats.name}>
<td width="25%" className="text-nowrap mw-100 p-1">
<span className="badge badge-light components-label mx-0 my-1 pl-0 text-left">
<span className="bg-primary text-white mr-1 px-1 components-labelWithPercent-percent">
{nameStats.hits}
</span>
{nameStats.name}
</span>
</td>
<td width="75%" className="mw-100 p-1">
{nameStats.values.slice(0, 9).map((valueStats, i) => (
<LabelWithPercent
key={valueStats.value}
name={nameStats.name}
value={valueStats.value}
hits={valueStats.hits}
percent={valueStats.percent}
offset={valueStats.offset}
isActive={
alertStore.filters.values.filter(
f => f.raw === valueStats.raw
).length > 0
}
/>
))}
{nameStats.values.length > 9 ? (
<div className="components-label badge my-2 text-muted mw-100">
+{nameStats.values.length - 9} more
</div>
) : null}
</td>
</tr>
))}
</tbody>
</table>
));
const TableRows = observer(({ alertStore, nameStats }) =>
nameStats.map(nameStats => (
<tr key={nameStats.name}>
<td width="25%" className="text-nowrap mw-100 p-1">
<span className="badge badge-light components-label mx-0 my-1 pl-0 text-left">
<span className="bg-primary text-white mr-1 px-1 components-labelWithPercent-percent">
{nameStats.hits}
</span>
{nameStats.name}
</span>
</td>
<td width="75%" className="mw-100 p-1">
{nameStats.values.slice(0, 9).map((valueStats, i) => (
<LabelWithPercent
key={valueStats.value}
name={nameStats.name}
value={valueStats.value}
hits={valueStats.hits}
percent={valueStats.percent}
offset={valueStats.offset}
isActive={
alertStore.filters.values.filter(f => f.raw === valueStats.raw)
.length > 0
}
/>
))}
{nameStats.values.length > 9 ? (
<div className="components-label badge my-2 text-muted mw-100">
+{nameStats.values.length - 9} more
</div>
) : null}
</td>
</tr>
))
);
const LabelsTable = observer(
({ alertStore, showAllLabels, toggleAllLabels }) => (
<React.Fragment>
<table
className="table table-borderless top-labels"
style={{ tableLayout: "fixed" }}
>
<tbody className="mw-100">
<TableRows
alertStore={alertStore}
nameStats={alertStore.data.counters.filter(
nameStats => nameStats.hits >= alertStore.info.totalAlerts
)}
></TableRows>
{alertStore.data.counters.filter(
nameStats => nameStats.hits < alertStore.info.totalAlerts
).length > 0 ? (
<tr>
<td colSpan="2" className="px-1 py-0">
<TooltipWrapper
title="Toggle all / only common labels"
delay={[3000, 100]}
>
<FontAwesomeIcon
icon={showAllLabels ? faChevronDown : faChevronUp}
className="cursor-pointer text-muted"
onClick={toggleAllLabels}
/>
</TooltipWrapper>
</td>
</tr>
) : null}
{showAllLabels ? (
<TableRows
alertStore={alertStore}
nameStats={alertStore.data.counters.filter(
nameStats => nameStats.hits < alertStore.info.totalAlerts
)}
></TableRows>
) : null}
</tbody>
</table>
</React.Fragment>
)
);
const NothingToShow = () => (
<div className="jumbotron bg-white">
@@ -65,6 +109,18 @@ const OverviewModalContent = observer(
onHide: PropTypes.func.isRequired
};
allLabels = observable(
{
show: false,
toggle() {
this.show = !this.show;
}
},
{
toggle: action.bound
}
);
render() {
const { alertStore, onHide } = this.props;
@@ -80,7 +136,11 @@ const OverviewModalContent = observer(
{alertStore.data.counters.length === 0 ? (
<NothingToShow />
) : (
<LabelsTable alertStore={alertStore} />
<LabelsTable
alertStore={alertStore}
showAllLabels={this.allLabels.show}
toggleAllLabels={this.allLabels.toggle}
/>
)}
</div>
</React.Fragment>

View File

@@ -21,6 +21,18 @@ afterEach(() => {
jest.restoreAllMocks();
});
const MountedOverviewModalContent = () =>
// we have multiple fragments and enzyme only renders the first one
// in html() and text(), debug() would work but it's noisy
// https://github.com/airbnb/enzyme/issues/1213
mount(
<span>
<Provider alertStore={alertStore}>
<OverviewModalContent alertStore={alertStore} onHide={onHide} />
</Provider>
</span>
);
describe("<OverviewModalContent />", () => {
it("matches snapshot with labels to show", () => {
alertStore.filters.values = [
@@ -63,32 +75,70 @@ describe("<OverviewModalContent />", () => {
}
];
// we have multiple fragments and enzyme only renders the first one
// in html() and text(), debug() would work but it's noisy
// https://github.com/airbnb/enzyme/issues/1213
const tree = mount(
<span>
<Provider alertStore={alertStore}>
<OverviewModalContent alertStore={alertStore} onHide={onHide} />
</Provider>
</span>
);
const tree = MountedOverviewModalContent();
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
});
it("matches snapshot with no labels to show", () => {
alertStore.data.counters = [];
// we have multiple fragments and enzyme only renders the first one
// in html() and text(), debug() would work but it's noisy
// https://github.com/airbnb/enzyme/issues/1213
const tree = mount(
<span>
<Provider alertStore={alertStore}>
<OverviewModalContent alertStore={alertStore} onHide={onHide} />
</Provider>
</span>
);
const tree = MountedOverviewModalContent();
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
});
it("renders all labels after expand button click", () => {
alertStore.info.totalAlerts = 5;
alertStore.data.counters = [
{
name: "foo",
hits: 5,
values: [
{ value: "bar", raw: "foo=bar", hits: 5, percent: 100, offset: 0 }
]
},
{
name: "bar",
hits: 3,
values: [
{
value: "foo",
raw: "bar=foo",
hits: 3,
percent: 100,
offset: 0
}
]
}
];
const tree = MountedOverviewModalContent();
expect(tree.find("span.components-label")).toHaveLength(2);
expect(
tree
.find("span.components-label")
.at(0)
.text()
).toBe("5foo");
expect(
tree
.find("span.components-label")
.at(1)
.text()
).toBe("5foo: bar");
tree.find("svg.cursor-pointer").simulate("click");
expect(tree.find("span.components-label")).toHaveLength(4);
expect(
tree
.find("span.components-label")
.at(2)
.text()
).toBe("3bar");
expect(
tree
.find("span.components-label")
.at(3)
.text()
).toBe("3bar: foo");
});
});