Merge pull request #505 from prymitive/truncate-long-silences

feat(ui): truncate silences with long comments
This commit is contained in:
Łukasz Mierzwa
2019-03-08 06:58:47 +00:00
committed by GitHub
7 changed files with 95 additions and 10 deletions

View File

@@ -259,7 +259,15 @@ class SilencedAlert(AlertGenerator):
"{}Z".format((now + datetime.timedelta(
minutes=30)).isoformat()),
"me@example.com",
"Silence '{}''".format(self.name)
"This alert is always silenced and the silence comment is very "
"long to test the UI. Lorem ipsum dolor sit amet, consectetur "
"adipiscing elit, sed do eiusmod tempor incididunt ut labore et"
" dolore magna aliqua. Ut enim ad minim veniam, quis nostrud "
"exercitation ullamco laboris nisi ut aliquip ex ea commodo "
"consequat. Duis aute irure dolor in reprehenderit in voluptate"
" velit esse cillum dolore eu fugiat nulla pariatur. Excepteur "
"sint occaecat cupidatat non proident, sunt in culpa qui "
"officia deserunt mollit anim id est laborum."
)
]
@@ -347,6 +355,34 @@ class InhibitingAlert(AlertGenerator):
]
class SilencedAlertWithJiraLink(AlertGenerator):
name = "Silenced Alert With Jira Link"
comment = "This alert is always silenced and links to a Jira ticket"
def alerts(self):
return [
newAlert(self._labels(instance="server%d" % i, cluster="staging",
severity="critical"),
self._annotations(
dashboard="https://www.atlassian.com/software/jira")
) for i in xrange(1, 9)
]
def silences(self):
now = datetime.datetime.utcnow().replace(microsecond=0)
return [
(
[newMatcher(
"alertname", SilencedAlertWithJiraLink.name, False)],
"{}Z".format(now.isoformat()),
"{}Z".format((now + datetime.timedelta(
minutes=30)).isoformat()),
"me@example.com",
"DEVOPS-123 This text should be a link to the Jira ticket",
)
]
if __name__ == "__main__":
generators = [
AlwaysOnAlert(MAX_INTERVAL),
@@ -360,6 +396,7 @@ if __name__ == "__main__":
LongNameAlerts(MAX_INTERVAL),
InhibitedAlert(MAX_INTERVAL),
InhibitingAlert(MAX_INTERVAL),
SilencedAlertWithJiraLink(MAX_INTERVAL),
]
while True:
for g in generators:

View File

@@ -51,3 +51,6 @@ log:
sentry:
private: https://84a9ef37a6ed4fdb80e9ea2310d1ed26:8c6ee6f0ab02406482ff4b4e824e2c27@sentry.io/1279017
public: https://84a9ef37a6ed4fdb80e9ea2310d1ed26@sentry.io/1279017
jira:
- regex: DEVOPS-[0-9]+
uri: https://jira.example.com

View File

@@ -47,6 +47,7 @@
"react-select": "2.4.1",
"react-tippy": "1.2.3",
"react-transition-group": "2.6.0",
"react-truncate": "2.4.0",
"whatwg-fetch": "3.0.0"
},
"scripts": {

View File

@@ -15,7 +15,16 @@ exports[`<Silence /> matches snapshot when data is present in alertStore 1`] = `
<div class=\\"card mt-1 border-0 p-1\\">
<div class=\\"card-text mb-0\\">
<span class=\\"text-muted my-1\\">
Fake silence
<span width=\\"0\\">
<span>
</span>
<span>
Fake silence
</span>
<span style=\\"position: fixed; visibility: hidden; top: 0px; left: 0px;\\">
</span>
</span>
<span class=\\"blockquote-footer pt-1\\">
<span class=\\"float-right cursor-pointer\\">
<div class
@@ -71,7 +80,16 @@ exports[`<Silence /> matches snapshot with expaned details 1`] = `
<div class=\\"card mt-1 border-0 p-1\\">
<div class=\\"card-text mb-0\\">
<span class=\\"text-muted my-1\\">
Fake silence
<span width=\\"0\\">
<span>
</span>
<span>
Fake silence
</span>
<span style=\\"position: fixed; visibility: hidden; top: 0px; left: 0px;\\">
</span>
</span>
<span class=\\"blockquote-footer pt-1\\">
<span class=\\"float-right cursor-pointer\\">
<div class

View File

@@ -9,6 +9,8 @@ import hash from "object-hash";
import moment from "moment";
import Moment from "react-moment";
import Truncate from "react-truncate";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons/faExternalLinkAlt";
import { faChevronUp } from "@fortawesome/free-solid-svg-icons/faChevronUp";
@@ -33,19 +35,25 @@ import { DeleteSilence } from "./DeleteSilence";
import "./index.css";
const SilenceComment = ({ silence }) => {
const SilenceComment = ({ silence, collapsed }) => {
const showLines = 2;
if (silence.jiraURL) {
return (
<a href={silence.jiraURL} target="_blank" rel="noopener noreferrer">
<FontAwesomeIcon className="mr-1" icon={faExternalLinkAlt} />
{silence.comment}
<Truncate lines={collapsed ? showLines : false}>
{silence.comment}
</Truncate>
</a>
);
}
return silence.comment;
return (
<Truncate lines={collapsed ? showLines : false}>{silence.comment}</Truncate>
);
};
SilenceComment.propTypes = {
silence: APISilence.isRequired
silence: APISilence.isRequired,
collapsed: PropTypes.bool.isRequired
};
const SilenceExpiryBadgeWithProgress = ({ silence, progress }) => {
@@ -305,7 +313,10 @@ const Silence = inject("alertStore")(
<div className="card mt-1 border-0 p-1">
<div className="card-text mb-0">
<span className="text-muted my-1">
<SilenceComment silence={silence} />
<SilenceComment
silence={silence}
collapsed={this.collapse.value}
/>
<span className="blockquote-footer pt-1">
<span
className="float-right cursor-pointer"

View File

@@ -168,13 +168,23 @@ describe("<Silence />", () => {
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
});
it("renders comment as link when jiraURL is set", () => {
it("renders comment as link when jiraURL is set and silence is collapsed", () => {
alertStore.data.silences.default[silence.id].jiraURL =
"http://jira.example.com";
const tree = MountedSilence(alertmanager).find("Silence");
const link = tree.find("a[href='http://jira.example.com']");
expect(link).toHaveLength(1);
expect(link.text()).toBe("Fake silence");
expect(link.text()).toBe("Fake silence");
});
it("renders comment as link when jiraURL is set and silence is expaned", () => {
alertStore.data.silences.default[silence.id].jiraURL =
"http://jira.example.com";
const tree = MountedSilence(alertmanager).find("Silence");
tree.instance().collapse.toggle();
const link = tree.find("a[href='http://jira.example.com']");
expect(link).toHaveLength(1);
expect(link.text()).toBe("Fake silence…");
});
it("clears progress timer on unmount", () => {

View File

@@ -9508,6 +9508,11 @@ react-transition-group@2.6.0, react-transition-group@^2.2.1:
prop-types "^15.6.2"
react-lifecycles-compat "^3.0.4"
react-truncate@2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/react-truncate/-/react-truncate-2.4.0.tgz#3cf5ff09ab86f93e3c078d359f4de6d75aa60510"
integrity sha512-3QW11/COYwi6iPUaunUhl06DW5NJBJD1WkmxW5YxqqUu6kvP+msB3jfoLg8WRbu57JqgebjVW8Lknw6T5/QZdA==
"react@15.x.x - 16.x.x":
version "16.8.3"
resolved "https://registry.yarnpkg.com/react/-/react-16.8.3.tgz#c6f988a2ce895375de216edcfaedd6b9a76451d9"