-
Notifications
You must be signed in to change notification settings - Fork 174
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ui): show modal with list of inhibiting alerts
- Loading branch information
Showing
14 changed files
with
449 additions
and
164 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
ui/src/Components/InhibitedByModal/InhibitedByModalContent.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import React from "react"; | ||
import PropTypes from "prop-types"; | ||
|
||
import { AlertStore } from "Stores/AlertStore"; | ||
import { FormatQuery, QueryOperators, StaticLabels } from "Common/Query"; | ||
import { PaginatedAlertList } from "Components/PaginatedAlertList"; | ||
|
||
const InhibitedByModalContent = ({ alertStore, fingerprints, onHide }) => { | ||
return ( | ||
<React.Fragment> | ||
<div className="modal-header"> | ||
<h5 className="modal-title">Inhibiting alerts</h5> | ||
<button type="button" className="close" onClick={onHide}> | ||
<span className="align-middle">×</span> | ||
</button> | ||
</div> | ||
<div className="modal-body"> | ||
<PaginatedAlertList | ||
alertStore={alertStore} | ||
filters={[ | ||
FormatQuery( | ||
StaticLabels.Fingerprint, | ||
QueryOperators.Regex, | ||
`^(${fingerprints.join("|")})$` | ||
), | ||
]} | ||
/> | ||
</div> | ||
</React.Fragment> | ||
); | ||
}; | ||
InhibitedByModalContent.propTypes = { | ||
alertStore: PropTypes.instanceOf(AlertStore).isRequired, | ||
fingerprints: PropTypes.arrayOf(PropTypes.string).isRequired, | ||
onHide: PropTypes.func.isRequired, | ||
}; | ||
|
||
export { InhibitedByModalContent }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import React, { useState, useCallback } from "react"; | ||
import PropTypes from "prop-types"; | ||
|
||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; | ||
import { faSpinner } from "@fortawesome/free-solid-svg-icons/faSpinner"; | ||
import { faVolumeMute } from "@fortawesome/free-solid-svg-icons/faVolumeMute"; | ||
|
||
import { AlertStore } from "Stores/AlertStore"; | ||
import { TooltipWrapper } from "Components/TooltipWrapper"; | ||
import { Modal } from "Components/Modal"; | ||
|
||
// https://github.com/facebook/react/issues/14603 | ||
const InhibitedByModalContent = React.lazy(() => | ||
import("./InhibitedByModalContent").then((module) => ({ | ||
default: module.InhibitedByModalContent, | ||
})) | ||
); | ||
|
||
const InhibitedByModal = ({ alertStore, fingerprints }) => { | ||
const [isVisible, setIsVisible] = useState(false); | ||
|
||
const toggle = useCallback(() => setIsVisible(!isVisible), [isVisible]); | ||
|
||
return ( | ||
<React.Fragment> | ||
<TooltipWrapper title="This alert is inhibited by other alerts, click to see details"> | ||
<span | ||
className="badge badge-light components-label components-label-with-hover cursor-pointer" | ||
onClick={toggle} | ||
> | ||
<FontAwesomeIcon className="text-success" icon={faVolumeMute} /> | ||
</span> | ||
</TooltipWrapper> | ||
<Modal size="lg" isOpen={isVisible} toggleOpen={toggle}> | ||
<React.Suspense | ||
fallback={ | ||
<h1 className="display-1 text-placeholder p-5 m-auto"> | ||
<FontAwesomeIcon icon={faSpinner} size="lg" spin /> | ||
</h1> | ||
} | ||
> | ||
<InhibitedByModalContent | ||
alertStore={alertStore} | ||
onHide={() => setIsVisible(false)} | ||
isVisible={isVisible} | ||
fingerprints={fingerprints} | ||
/> | ||
</React.Suspense> | ||
</Modal> | ||
</React.Fragment> | ||
); | ||
}; | ||
InhibitedByModal.propTypes = { | ||
alertStore: PropTypes.instanceOf(AlertStore).isRequired, | ||
fingerprints: PropTypes.arrayOf(PropTypes.string).isRequired, | ||
}; | ||
|
||
export { InhibitedByModal }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import React from "react"; | ||
import { act } from "react-dom/test-utils"; | ||
|
||
import { mount } from "enzyme"; | ||
|
||
import { AlertStore } from "Stores/AlertStore"; | ||
import { InhibitedByModal } from "."; | ||
|
||
let alertStore; | ||
|
||
beforeAll(() => { | ||
jest.useFakeTimers(); | ||
}); | ||
|
||
beforeEach(() => { | ||
alertStore = new AlertStore([]); | ||
}); | ||
|
||
afterEach(() => { | ||
document.body.className = ""; | ||
}); | ||
|
||
describe("<InhibitedByModal />", () => { | ||
it("renders a spinner placeholder while modal content is loading", () => { | ||
const tree = mount( | ||
<InhibitedByModal alertStore={alertStore} fingerprints={["foo=bar"]} /> | ||
); | ||
const toggle = tree.find("span.badge.badge-light"); | ||
toggle.simulate("click"); | ||
expect(tree.find("InhibitedByModalContent")).toHaveLength(0); | ||
expect(tree.find(".modal-content").find("svg.fa-spinner")).toHaveLength(1); | ||
}); | ||
|
||
it("renders modal content if fallback is not used", () => { | ||
const tree = mount( | ||
<InhibitedByModal alertStore={alertStore} fingerprints={["foo=bar"]} /> | ||
); | ||
const toggle = tree.find("span.badge.badge-light"); | ||
toggle.simulate("click"); | ||
expect(tree.find(".modal-title").text()).toBe("Inhibiting alerts"); | ||
expect(tree.find(".modal-content").find("svg.fa-spinner")).toHaveLength(0); | ||
}); | ||
|
||
it("hides the modal when toggle() is called twice", () => { | ||
const tree = mount( | ||
<InhibitedByModal alertStore={alertStore} fingerprints={["foo=bar"]} /> | ||
); | ||
const toggle = tree.find("span.badge.badge-light"); | ||
|
||
toggle.simulate("click"); | ||
act(() => jest.runOnlyPendingTimers()); | ||
tree.update(); | ||
expect(tree.find(".modal-title").text()).toBe("Inhibiting alerts"); | ||
|
||
toggle.simulate("click"); | ||
act(() => jest.runOnlyPendingTimers()); | ||
tree.update(); | ||
expect(tree.find(".modal-title")).toHaveLength(0); | ||
}); | ||
|
||
it("hides the modal when button.close is clicked", () => { | ||
const tree = mount( | ||
<InhibitedByModal alertStore={alertStore} fingerprints={["foo=bar"]} /> | ||
); | ||
const toggle = tree.find("span.badge.badge-light"); | ||
|
||
toggle.simulate("click"); | ||
expect(tree.find(".modal-title").text()).toBe("Inhibiting alerts"); | ||
|
||
tree.find("button.close").simulate("click"); | ||
act(() => jest.runOnlyPendingTimers()); | ||
tree.update(); | ||
expect(tree.find("InhibitedByModalContent")).toHaveLength(0); | ||
}); | ||
|
||
it("'modal-open' class is appended to body node when modal is visible", () => { | ||
const tree = mount( | ||
<InhibitedByModal alertStore={alertStore} fingerprints={["foo=bar"]} /> | ||
); | ||
const toggle = tree.find("span.badge.badge-light"); | ||
toggle.simulate("click"); | ||
expect(document.body.className.split(" ")).toContain("modal-open"); | ||
}); | ||
|
||
it("'modal-open' class is removed from body node after modal is hidden", () => { | ||
const tree = mount( | ||
<InhibitedByModal alertStore={alertStore} fingerprints={["foo=bar"]} /> | ||
); | ||
|
||
tree.find("span.badge.badge-light").simulate("click"); | ||
expect(document.body.className.split(" ")).toContain("modal-open"); | ||
|
||
tree.find("span.badge.badge-light").simulate("click"); | ||
act(() => jest.runOnlyPendingTimers()); | ||
expect(document.body.className.split(" ")).not.toContain("modal-open"); | ||
}); | ||
|
||
it("'modal-open' class is removed from body node after modal is unmounted", () => { | ||
const tree = mount( | ||
<InhibitedByModal alertStore={alertStore} fingerprints={["foo=bar"]} /> | ||
); | ||
const toggle = tree.find("span.badge.badge-light"); | ||
toggle.simulate("click"); | ||
tree.unmount(); | ||
expect(document.body.className.split(" ")).not.toContain("modal-open"); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.