mirror of
https://github.com/prymitive/karma
synced 2026-05-09 03:36:44 +00:00
refactor(ui): move common modal code to a dedicated component
This commit is contained in:
@@ -1,16 +1,12 @@
|
||||
import React, { Component } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { observer } from "mobx-react";
|
||||
import { observable, action } from "mobx";
|
||||
|
||||
import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { Configuration } from "./Configuration";
|
||||
import { MountModalBackdrop } from "Components/Animations/MountModal";
|
||||
import { Help } from "./Help";
|
||||
|
||||
const Tab = ({ title, active, onClick }) => (
|
||||
@@ -39,8 +35,7 @@ const MainModalContent = observer(
|
||||
static propTypes = {
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
onHide: PropTypes.func.isRequired,
|
||||
isVisible: PropTypes.bool.isRequired
|
||||
onHide: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
tab = observable(
|
||||
@@ -53,58 +48,40 @@ const MainModalContent = observer(
|
||||
{ setTab: action.bound }
|
||||
);
|
||||
|
||||
componentDidMount() {
|
||||
disableBodyScroll(document.querySelector(".modal"));
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
enableBodyScroll(document.querySelector(".modal"));
|
||||
}
|
||||
|
||||
render() {
|
||||
const { alertStore, settingsStore, onHide, isVisible } = this.props;
|
||||
const { alertStore, settingsStore, onHide } = this.props;
|
||||
|
||||
return ReactDOM.createPortal(
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="modal d-block" role="dialog">
|
||||
<div className="modal-dialog modal-lg" role="document">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header py-2">
|
||||
<nav className="nav nav-pills nav-justified w-100">
|
||||
<Tab
|
||||
title="Configuration"
|
||||
active={this.tab.current === TabNames.Configuration}
|
||||
onClick={() => this.tab.setTab(TabNames.Configuration)}
|
||||
/>
|
||||
<Tab
|
||||
title="Help"
|
||||
active={this.tab.current === TabNames.Help}
|
||||
onClick={() => this.tab.setTab(TabNames.Help)}
|
||||
/>
|
||||
<button type="button" className="close" onClick={onHide}>
|
||||
<span>×</span>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
{this.tab.current === TabNames.Help ? <Help /> : null}
|
||||
{this.tab.current === TabNames.Configuration ? (
|
||||
<Configuration settingsStore={settingsStore} />
|
||||
) : null}
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<span className="text-muted">
|
||||
Version: {alertStore.info.version}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="modal-header py-2">
|
||||
<nav className="nav nav-pills nav-justified w-100">
|
||||
<Tab
|
||||
title="Configuration"
|
||||
active={this.tab.current === TabNames.Configuration}
|
||||
onClick={() => this.tab.setTab(TabNames.Configuration)}
|
||||
/>
|
||||
<Tab
|
||||
title="Help"
|
||||
active={this.tab.current === TabNames.Help}
|
||||
onClick={() => this.tab.setTab(TabNames.Help)}
|
||||
/>
|
||||
<button type="button" className="close" onClick={onHide}>
|
||||
<span>×</span>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
<MountModalBackdrop in={isVisible} unmountOnExit>
|
||||
<div className="modal-backdrop d-block" />
|
||||
</MountModalBackdrop>
|
||||
</React.Fragment>,
|
||||
document.body
|
||||
<div className="modal-body">
|
||||
{this.tab.current === TabNames.Help ? <Help /> : null}
|
||||
{this.tab.current === TabNames.Configuration ? (
|
||||
<Configuration settingsStore={settingsStore} />
|
||||
) : null}
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<span className="text-muted">
|
||||
Version: {alertStore.info.version}
|
||||
</span>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ const FakeModal = () => {
|
||||
alertStore={alertStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={onHide}
|
||||
isVisible={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -46,7 +45,18 @@ const ValidateSetTab = (title, callArg) => {
|
||||
|
||||
describe("<MainModalContent />", () => {
|
||||
it("matches snapshot", () => {
|
||||
const tree = FakeModal();
|
||||
// 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>
|
||||
<MainModalContent
|
||||
alertStore={alertStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={onHide}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
||||
@@ -2,129 +2,121 @@
|
||||
|
||||
exports[`<MainModalContent /> matches snapshot 1`] = `
|
||||
"
|
||||
<div class=\\"modal d-block\\"
|
||||
role=\\"dialog\\"
|
||||
>
|
||||
<div class=\\"modal-dialog modal-lg\\"
|
||||
role=\\"document\\"
|
||||
>
|
||||
<div class=\\"modal-content\\">
|
||||
<div class=\\"modal-header py-2\\">
|
||||
<nav class=\\"nav nav-pills nav-justified w-100\\">
|
||||
<span class=\\"nav-item nav-link cursor-pointer active\\">
|
||||
Configuration
|
||||
</span>
|
||||
<span class=\\"nav-item nav-link cursor-pointer text-primary\\">
|
||||
Help
|
||||
</span>
|
||||
<button type=\\"button\\"
|
||||
class=\\"close\\"
|
||||
>
|
||||
<span>
|
||||
×
|
||||
</span>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
<div class=\\"modal-body\\">
|
||||
<form class=\\"px-3\\">
|
||||
<div class=\\"form-group text-center\\">
|
||||
<label class=\\"mb-4\\">
|
||||
Refresh interval
|
||||
</label>
|
||||
<div aria-disabled=\\"false\\"
|
||||
class=\\"input-range\\"
|
||||
>
|
||||
<span class=\\"input-range__label input-range__label--min\\">
|
||||
<span class=\\"input-range__label-container\\">
|
||||
10s
|
||||
</span>
|
||||
</span>
|
||||
<div class=\\"input-range__track input-range__track--background\\">
|
||||
<div style=\\"left: 0%; width: 18.181818181818183%;\\"
|
||||
class=\\"input-range__track input-range__track--active\\"
|
||||
>
|
||||
</div>
|
||||
<span class=\\"input-range__slider-container\\"
|
||||
style=\\"position: absolute; left: 18.181818181818183%;\\"
|
||||
>
|
||||
<span class=\\"input-range__label input-range__label--value\\">
|
||||
<span class=\\"input-range__label-container\\">
|
||||
30s
|
||||
</span>
|
||||
</span>
|
||||
<div aria-valuemax=\\"120\\"
|
||||
aria-valuemin=\\"10\\"
|
||||
aria-valuenow=\\"30\\"
|
||||
class=\\"input-range__slider\\"
|
||||
draggable=\\"false\\"
|
||||
role=\\"slider\\"
|
||||
tabindex=\\"0\\"
|
||||
>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<span class=\\"input-range__label input-range__label--max\\">
|
||||
<span class=\\"input-range__label-container\\">
|
||||
120s
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"mt-5\\">
|
||||
</div>
|
||||
<div class=\\"form-group text-center\\">
|
||||
<label class=\\"mb-4\\">
|
||||
Default number of alerts to show per group
|
||||
</label>
|
||||
<div aria-disabled=\\"false\\"
|
||||
class=\\"input-range\\"
|
||||
>
|
||||
<span class=\\"input-range__label input-range__label--min\\">
|
||||
<span class=\\"input-range__label-container\\">
|
||||
1
|
||||
</span>
|
||||
</span>
|
||||
<div class=\\"input-range__track input-range__track--background\\">
|
||||
<div style=\\"left: 0%; width: 44.44444444444444%;\\"
|
||||
class=\\"input-range__track input-range__track--active\\"
|
||||
>
|
||||
</div>
|
||||
<span class=\\"input-range__slider-container\\"
|
||||
style=\\"position: absolute; left: 44.44444444444444%;\\"
|
||||
>
|
||||
<span class=\\"input-range__label input-range__label--value\\">
|
||||
<span class=\\"input-range__label-container\\">
|
||||
5
|
||||
</span>
|
||||
</span>
|
||||
<div aria-valuemax=\\"10\\"
|
||||
aria-valuemin=\\"1\\"
|
||||
aria-valuenow=\\"5\\"
|
||||
class=\\"input-range__slider\\"
|
||||
draggable=\\"false\\"
|
||||
role=\\"slider\\"
|
||||
tabindex=\\"0\\"
|
||||
>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<span class=\\"input-range__label input-range__label--max\\">
|
||||
<span class=\\"input-range__label-container\\">
|
||||
10
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class=\\"modal-footer\\">
|
||||
<span class=\\"text-muted\\">
|
||||
Version: unknown
|
||||
<span>
|
||||
<div class=\\"modal-header py-2\\">
|
||||
<nav class=\\"nav nav-pills nav-justified w-100\\">
|
||||
<span class=\\"nav-item nav-link cursor-pointer active\\">
|
||||
Configuration
|
||||
</span>
|
||||
<span class=\\"nav-item nav-link cursor-pointer text-primary\\">
|
||||
Help
|
||||
</span>
|
||||
<button type=\\"button\\"
|
||||
class=\\"close\\"
|
||||
>
|
||||
<span>
|
||||
×
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"modal-body\\">
|
||||
<form class=\\"px-3\\">
|
||||
<div class=\\"form-group text-center\\">
|
||||
<label class=\\"mb-4\\">
|
||||
Refresh interval
|
||||
</label>
|
||||
<div aria-disabled=\\"false\\"
|
||||
class=\\"input-range\\"
|
||||
>
|
||||
<span class=\\"input-range__label input-range__label--min\\">
|
||||
<span class=\\"input-range__label-container\\">
|
||||
10s
|
||||
</span>
|
||||
</span>
|
||||
<div class=\\"input-range__track input-range__track--background\\">
|
||||
<div style=\\"left: 0%; width: 18.181818181818183%;\\"
|
||||
class=\\"input-range__track input-range__track--active\\"
|
||||
>
|
||||
</div>
|
||||
<span class=\\"input-range__slider-container\\"
|
||||
style=\\"position: absolute; left: 18.181818181818183%;\\"
|
||||
>
|
||||
<span class=\\"input-range__label input-range__label--value\\">
|
||||
<span class=\\"input-range__label-container\\">
|
||||
30s
|
||||
</span>
|
||||
</span>
|
||||
<div aria-valuemax=\\"120\\"
|
||||
aria-valuemin=\\"10\\"
|
||||
aria-valuenow=\\"30\\"
|
||||
class=\\"input-range__slider\\"
|
||||
draggable=\\"false\\"
|
||||
role=\\"slider\\"
|
||||
tabindex=\\"0\\"
|
||||
>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<span class=\\"input-range__label input-range__label--max\\">
|
||||
<span class=\\"input-range__label-container\\">
|
||||
120s
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"mt-5\\">
|
||||
</div>
|
||||
<div class=\\"form-group text-center\\">
|
||||
<label class=\\"mb-4\\">
|
||||
Default number of alerts to show per group
|
||||
</label>
|
||||
<div aria-disabled=\\"false\\"
|
||||
class=\\"input-range\\"
|
||||
>
|
||||
<span class=\\"input-range__label input-range__label--min\\">
|
||||
<span class=\\"input-range__label-container\\">
|
||||
1
|
||||
</span>
|
||||
</span>
|
||||
<div class=\\"input-range__track input-range__track--background\\">
|
||||
<div style=\\"left: 0%; width: 44.44444444444444%;\\"
|
||||
class=\\"input-range__track input-range__track--active\\"
|
||||
>
|
||||
</div>
|
||||
<span class=\\"input-range__slider-container\\"
|
||||
style=\\"position: absolute; left: 44.44444444444444%;\\"
|
||||
>
|
||||
<span class=\\"input-range__label input-range__label--value\\">
|
||||
<span class=\\"input-range__label-container\\">
|
||||
5
|
||||
</span>
|
||||
</span>
|
||||
<div aria-valuemax=\\"10\\"
|
||||
aria-valuemin=\\"1\\"
|
||||
aria-valuenow=\\"5\\"
|
||||
class=\\"input-range__slider\\"
|
||||
draggable=\\"false\\"
|
||||
role=\\"slider\\"
|
||||
tabindex=\\"0\\"
|
||||
>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<span class=\\"input-range__label input-range__label--max\\">
|
||||
<span class=\\"input-range__label-container\\">
|
||||
10
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class=\\"modal-footer\\">
|
||||
<span class=\\"text-muted\\">
|
||||
Version: unknown
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -9,8 +9,8 @@ import { faCog } from "@fortawesome/free-solid-svg-icons/faCog";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { MountModal } from "Components/Animations/MountModal";
|
||||
import { TooltipWrapper } from "Components/TooltipWrapper";
|
||||
import { Modal } from "Components/Modal";
|
||||
import { MainModalContent } from "./MainModalContent";
|
||||
|
||||
const MainModal = observer(
|
||||
@@ -33,14 +33,6 @@ const MainModal = observer(
|
||||
{ toggle: action.bound, hide: action.bound }
|
||||
);
|
||||
|
||||
componentDidUpdate() {
|
||||
document.body.classList.toggle("modal-open", this.toggle.show);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.body.classList.remove("modal-open");
|
||||
}
|
||||
|
||||
render() {
|
||||
const { alertStore, settingsStore } = this.props;
|
||||
|
||||
@@ -56,14 +48,14 @@ const MainModal = observer(
|
||||
</span>
|
||||
</TooltipWrapper>
|
||||
</li>
|
||||
<MountModal in={this.toggle.show} unmountOnExit>
|
||||
<Modal isOpen={this.toggle.show}>
|
||||
<MainModalContent
|
||||
alertStore={alertStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={this.toggle.hide}
|
||||
isVisible={this.toggle.show}
|
||||
/>
|
||||
</MountModal>
|
||||
</Modal>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
66
ui/src/Components/Modal/index.js
Normal file
66
ui/src/Components/Modal/index.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import React, { Component } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock";
|
||||
|
||||
import {
|
||||
MountModal,
|
||||
MountModalBackdrop
|
||||
} from "Components/Animations/MountModal";
|
||||
|
||||
const Modal = observer(
|
||||
class Modal extends Component {
|
||||
static propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
children: PropTypes.node.isRequired
|
||||
};
|
||||
|
||||
toggleBodyClass = isOpen => {
|
||||
document.body.classList.toggle("modal-open", isOpen);
|
||||
if (isOpen) {
|
||||
disableBodyScroll(document.querySelector(".modal"));
|
||||
} else {
|
||||
enableBodyScroll(document.querySelector(".modal"));
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const { isOpen } = this.props;
|
||||
this.toggleBodyClass(isOpen);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { isOpen } = this.props;
|
||||
this.toggleBodyClass(isOpen);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.toggleBodyClass(false);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isOpen, children } = this.props;
|
||||
|
||||
return ReactDOM.createPortal(
|
||||
<React.Fragment>
|
||||
<MountModal in={isOpen} unmountOnExit>
|
||||
<div className="modal d-block" role="dialog">
|
||||
<div className="modal-dialog modal-lg" role="document">
|
||||
<div className="modal-content">{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</MountModal>
|
||||
<MountModalBackdrop in={isOpen} unmountOnExit>
|
||||
<div className="modal-backdrop d-block" />
|
||||
</MountModalBackdrop>
|
||||
</React.Fragment>,
|
||||
document.body
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export { Modal };
|
||||
31
ui/src/Components/Modal/index.test.js
Normal file
31
ui/src/Components/Modal/index.test.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import React from "react";
|
||||
|
||||
import { mount } from "enzyme";
|
||||
|
||||
import { Modal } from ".";
|
||||
|
||||
const MountedModal = isOpen => {
|
||||
return mount(
|
||||
<Modal isOpen={isOpen}>
|
||||
<div />
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
describe("<Modal />", () => {
|
||||
it("'modal-open' class is appended to body node when modal is visible", () => {
|
||||
MountedModal(true);
|
||||
expect(document.body.className.split(" ")).toContain("modal-open");
|
||||
});
|
||||
|
||||
it("'modal-open' class is removed from body node after modal is hidden", () => {
|
||||
MountedModal(false);
|
||||
expect(document.body.className.split(" ")).not.toContain("modal-open");
|
||||
});
|
||||
|
||||
it("'modal-open' class is removed from body node after modal is unmounted", () => {
|
||||
const tree = MountedModal(true);
|
||||
tree.unmount();
|
||||
expect(document.body.className.split(" ")).not.toContain("modal-open");
|
||||
});
|
||||
});
|
||||
@@ -1,283 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<NavBar /> matches snapshot with 0 alerts 1`] = `
|
||||
"
|
||||
<div class=\\"container visible\\">
|
||||
<nav class=\\"navbar fixed-top navbar-expand navbar-dark p-1 bg-primary-transparent d-inline-block\\">
|
||||
<div style=\\"position:absolute;width:0;height:0;visibility:hidden;display:none\\">
|
||||
</div>
|
||||
<span class=\\"navbar-brand my-0 mx-2 h1 d-none d-sm-block float-left\\">
|
||||
0
|
||||
<svg aria-hidden=\\"true\\"
|
||||
data-prefix=\\"fas\\"
|
||||
data-icon=\\"circle-notch\\"
|
||||
class=\\"svg-inline--fa fa-circle-notch fa-w-16 fa-lg mx-1 text-muted\\"
|
||||
role=\\"img\\"
|
||||
xmlns=\\"http://www.w3.org/2000/svg\\"
|
||||
viewbox=\\"0 0 512 512\\"
|
||||
style=\\"opacity:0\\"
|
||||
>
|
||||
<path fill=\\"currentColor\\"
|
||||
d=\\"M288 39.056v16.659c0 10.804 7.281 20.159 17.686 23.066C383.204 100.434 440 171.518 440 256c0 101.689-82.295 184-184 184-101.689 0-184-82.295-184-184 0-84.47 56.786-155.564 134.312-177.219C216.719 75.874 224 66.517 224 55.712V39.064c0-15.709-14.834-27.153-30.046-23.234C86.603 43.482 7.394 141.206 8.003 257.332c.72 137.052 111.477 246.956 248.531 246.667C393.255 503.711 504 392.788 504 256c0-115.633-79.14-212.779-186.211-240.236C302.678 11.889 288 23.456 288 39.056z\\"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
<ul class=\\"navbar-nav float-right d-flex flex-row\\">
|
||||
<li class=\\"nav-item\\">
|
||||
<div title=\\"Add new silence\\"
|
||||
class
|
||||
style=\\"display:inline\\"
|
||||
>
|
||||
<span class=\\"nav-link cursor-pointer\\">
|
||||
<svg aria-hidden=\\"true\\"
|
||||
data-prefix=\\"fas\\"
|
||||
data-icon=\\"bell-slash\\"
|
||||
class=\\"svg-inline--fa fa-bell-slash fa-w-20 \\"
|
||||
role=\\"img\\"
|
||||
xmlns=\\"http://www.w3.org/2000/svg\\"
|
||||
viewbox=\\"0 0 640 512\\"
|
||||
>
|
||||
<path fill=\\"currentColor\\"
|
||||
d=\\"M633.82 458.1l-90.62-70.05c.19-1.38.8-2.66.8-4.06.05-7.55-2.61-15.27-8.61-21.71-19.32-20.76-55.47-51.99-55.47-154.29 0-77.7-54.48-139.9-127.94-155.16V32c0-17.67-14.32-32-31.98-32s-31.98 14.33-31.98 32v20.84c-40.33 8.38-74.66 31.07-97.59 62.57L45.47 3.37C38.49-2.05 28.43-.8 23.01 6.18L3.37 31.45C-2.05 38.42-.8 48.47 6.18 53.9l588.35 454.73c6.98 5.43 17.03 4.17 22.46-2.81l19.64-25.27c5.42-6.97 4.17-17.02-2.81-22.45zM157.23 251.54c-8.61 67.96-36.41 93.33-52.62 110.75-6 6.45-8.66 14.16-8.61 21.71.11 16.4 12.98 32 32.1 32h241.92L157.23 251.54zM320 512c35.32 0 63.97-28.65 63.97-64H256.03c0 35.35 28.65 64 63.97 64z\\"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class=\\"nav-item\\">
|
||||
<div title=\\"Settings\\"
|
||||
class
|
||||
style=\\"display:inline\\"
|
||||
>
|
||||
<span class=\\"nav-link cursor-pointer\\">
|
||||
<svg aria-hidden=\\"true\\"
|
||||
data-prefix=\\"fas\\"
|
||||
data-icon=\\"cog\\"
|
||||
class=\\"svg-inline--fa fa-cog fa-w-16 \\"
|
||||
role=\\"img\\"
|
||||
xmlns=\\"http://www.w3.org/2000/svg\\"
|
||||
viewbox=\\"0 0 512 512\\"
|
||||
>
|
||||
<path fill=\\"currentColor\\"
|
||||
d=\\"M444.788 291.1l42.616 24.599c4.867 2.809 7.126 8.618 5.459 13.985-11.07 35.642-29.97 67.842-54.689 94.586a12.016 12.016 0 0 1-14.832 2.254l-42.584-24.595a191.577 191.577 0 0 1-60.759 35.13v49.182a12.01 12.01 0 0 1-9.377 11.718c-34.956 7.85-72.499 8.256-109.219.007-5.49-1.233-9.403-6.096-9.403-11.723v-49.184a191.555 191.555 0 0 1-60.759-35.13l-42.584 24.595a12.016 12.016 0 0 1-14.832-2.254c-24.718-26.744-43.619-58.944-54.689-94.586-1.667-5.366.592-11.175 5.459-13.985L67.212 291.1a193.48 193.48 0 0 1 0-70.199l-42.616-24.599c-4.867-2.809-7.126-8.618-5.459-13.985 11.07-35.642 29.97-67.842 54.689-94.586a12.016 12.016 0 0 1 14.832-2.254l42.584 24.595a191.577 191.577 0 0 1 60.759-35.13V25.759a12.01 12.01 0 0 1 9.377-11.718c34.956-7.85 72.499-8.256 109.219-.007 5.49 1.233 9.403 6.096 9.403 11.723v49.184a191.555 191.555 0 0 1 60.759 35.13l42.584-24.595a12.016 12.016 0 0 1 14.832 2.254c24.718 26.744 43.619 58.944 54.689 94.586 1.667 5.366-.592 11.175-5.459 13.985L444.788 220.9a193.485 193.485 0 0 1 0 70.2zM336 256c0-44.112-35.888-80-80-80s-80 35.888-80 80 35.888 80 80 80 80-35.888 80-80z\\"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<form class=\\"form-inline mw-100\\"
|
||||
data-filters
|
||||
>
|
||||
<div role=\\"combobox\\"
|
||||
aria-haspopup=\\"listbox\\"
|
||||
aria-owns=\\"react-autowhatever-1\\"
|
||||
aria-expanded=\\"false\\"
|
||||
class=\\"autosuggest d-inline-block w-100\\"
|
||||
>
|
||||
<div class=\\"input-group mr-2\\">
|
||||
<div class=\\"input-group-prepend\\">
|
||||
<span class=\\"input-group-text px-2\\">
|
||||
<svg aria-hidden=\\"true\\"
|
||||
data-prefix=\\"fas\\"
|
||||
data-icon=\\"search\\"
|
||||
class=\\"svg-inline--fa fa-search fa-w-16 \\"
|
||||
role=\\"img\\"
|
||||
xmlns=\\"http://www.w3.org/2000/svg\\"
|
||||
viewbox=\\"0 0 512 512\\"
|
||||
>
|
||||
<path fill=\\"currentColor\\"
|
||||
d=\\"M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z\\"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class=\\"form-control p-1 components-filterinput\\">
|
||||
<input type=\\"text\\"
|
||||
class=\\"components-filterinput-wrapper\\"
|
||||
placeholder
|
||||
value
|
||||
autocomplete=\\"off\\"
|
||||
aria-autocomplete=\\"list\\"
|
||||
aria-controls=\\"react-autowhatever-1\\"
|
||||
>
|
||||
</div>
|
||||
<div class=\\"input-group-append\\">
|
||||
<button class=\\"input-group-text rounded-right cursor-pointer components-navbar-history px-2\\"
|
||||
type=\\"button\\"
|
||||
data-toggle=\\"dropdown\\"
|
||||
aria-haspopup=\\"true\\"
|
||||
aria-expanded=\\"true\\"
|
||||
>
|
||||
<svg aria-hidden=\\"true\\"
|
||||
data-prefix=\\"fas\\"
|
||||
data-icon=\\"caret-down\\"
|
||||
class=\\"svg-inline--fa fa-caret-down fa-w-10 \\"
|
||||
role=\\"img\\"
|
||||
xmlns=\\"http://www.w3.org/2000/svg\\"
|
||||
viewbox=\\"0 0 320 512\\"
|
||||
>
|
||||
<path fill=\\"currentColor\\"
|
||||
d=\\"M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z\\"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id=\\"react-autowhatever-1\\"
|
||||
role=\\"listbox\\"
|
||||
class=\\"dropdown\\"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</nav>
|
||||
</div>
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<NavBar /> matches snapshot with 5 alerts 1`] = `
|
||||
"
|
||||
<div class=\\"container visible\\">
|
||||
<nav class=\\"navbar fixed-top navbar-expand navbar-dark p-1 bg-primary-transparent d-inline-block\\">
|
||||
<div style=\\"position:absolute;width:0;height:0;visibility:hidden;display:none\\">
|
||||
</div>
|
||||
<span class=\\"navbar-brand my-0 mx-2 h1 d-none d-sm-block float-left\\">
|
||||
5
|
||||
<svg aria-hidden=\\"true\\"
|
||||
data-prefix=\\"fas\\"
|
||||
data-icon=\\"circle-notch\\"
|
||||
class=\\"svg-inline--fa fa-circle-notch fa-w-16 fa-lg mx-1 text-muted\\"
|
||||
role=\\"img\\"
|
||||
xmlns=\\"http://www.w3.org/2000/svg\\"
|
||||
viewbox=\\"0 0 512 512\\"
|
||||
style=\\"opacity:0\\"
|
||||
>
|
||||
<path fill=\\"currentColor\\"
|
||||
d=\\"M288 39.056v16.659c0 10.804 7.281 20.159 17.686 23.066C383.204 100.434 440 171.518 440 256c0 101.689-82.295 184-184 184-101.689 0-184-82.295-184-184 0-84.47 56.786-155.564 134.312-177.219C216.719 75.874 224 66.517 224 55.712V39.064c0-15.709-14.834-27.153-30.046-23.234C86.603 43.482 7.394 141.206 8.003 257.332c.72 137.052 111.477 246.956 248.531 246.667C393.255 503.711 504 392.788 504 256c0-115.633-79.14-212.779-186.211-240.236C302.678 11.889 288 23.456 288 39.056z\\"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
<ul class=\\"navbar-nav float-right d-flex flex-row\\">
|
||||
<li class=\\"nav-item\\">
|
||||
<div title=\\"Add new silence\\"
|
||||
class
|
||||
style=\\"display:inline\\"
|
||||
>
|
||||
<span class=\\"nav-link cursor-pointer\\">
|
||||
<svg aria-hidden=\\"true\\"
|
||||
data-prefix=\\"fas\\"
|
||||
data-icon=\\"bell-slash\\"
|
||||
class=\\"svg-inline--fa fa-bell-slash fa-w-20 \\"
|
||||
role=\\"img\\"
|
||||
xmlns=\\"http://www.w3.org/2000/svg\\"
|
||||
viewbox=\\"0 0 640 512\\"
|
||||
>
|
||||
<path fill=\\"currentColor\\"
|
||||
d=\\"M633.82 458.1l-90.62-70.05c.19-1.38.8-2.66.8-4.06.05-7.55-2.61-15.27-8.61-21.71-19.32-20.76-55.47-51.99-55.47-154.29 0-77.7-54.48-139.9-127.94-155.16V32c0-17.67-14.32-32-31.98-32s-31.98 14.33-31.98 32v20.84c-40.33 8.38-74.66 31.07-97.59 62.57L45.47 3.37C38.49-2.05 28.43-.8 23.01 6.18L3.37 31.45C-2.05 38.42-.8 48.47 6.18 53.9l588.35 454.73c6.98 5.43 17.03 4.17 22.46-2.81l19.64-25.27c5.42-6.97 4.17-17.02-2.81-22.45zM157.23 251.54c-8.61 67.96-36.41 93.33-52.62 110.75-6 6.45-8.66 14.16-8.61 21.71.11 16.4 12.98 32 32.1 32h241.92L157.23 251.54zM320 512c35.32 0 63.97-28.65 63.97-64H256.03c0 35.35 28.65 64 63.97 64z\\"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class=\\"nav-item\\">
|
||||
<div title=\\"Settings\\"
|
||||
class
|
||||
style=\\"display:inline\\"
|
||||
>
|
||||
<span class=\\"nav-link cursor-pointer\\">
|
||||
<svg aria-hidden=\\"true\\"
|
||||
data-prefix=\\"fas\\"
|
||||
data-icon=\\"cog\\"
|
||||
class=\\"svg-inline--fa fa-cog fa-w-16 \\"
|
||||
role=\\"img\\"
|
||||
xmlns=\\"http://www.w3.org/2000/svg\\"
|
||||
viewbox=\\"0 0 512 512\\"
|
||||
>
|
||||
<path fill=\\"currentColor\\"
|
||||
d=\\"M444.788 291.1l42.616 24.599c4.867 2.809 7.126 8.618 5.459 13.985-11.07 35.642-29.97 67.842-54.689 94.586a12.016 12.016 0 0 1-14.832 2.254l-42.584-24.595a191.577 191.577 0 0 1-60.759 35.13v49.182a12.01 12.01 0 0 1-9.377 11.718c-34.956 7.85-72.499 8.256-109.219.007-5.49-1.233-9.403-6.096-9.403-11.723v-49.184a191.555 191.555 0 0 1-60.759-35.13l-42.584 24.595a12.016 12.016 0 0 1-14.832-2.254c-24.718-26.744-43.619-58.944-54.689-94.586-1.667-5.366.592-11.175 5.459-13.985L67.212 291.1a193.48 193.48 0 0 1 0-70.199l-42.616-24.599c-4.867-2.809-7.126-8.618-5.459-13.985 11.07-35.642 29.97-67.842 54.689-94.586a12.016 12.016 0 0 1 14.832-2.254l42.584 24.595a191.577 191.577 0 0 1 60.759-35.13V25.759a12.01 12.01 0 0 1 9.377-11.718c34.956-7.85 72.499-8.256 109.219-.007 5.49 1.233 9.403 6.096 9.403 11.723v49.184a191.555 191.555 0 0 1 60.759 35.13l42.584-24.595a12.016 12.016 0 0 1 14.832 2.254c24.718 26.744 43.619 58.944 54.689 94.586 1.667 5.366-.592 11.175-5.459 13.985L444.788 220.9a193.485 193.485 0 0 1 0 70.2zM336 256c0-44.112-35.888-80-80-80s-80 35.888-80 80 35.888 80 80 80 80-35.888 80-80z\\"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<form class=\\"form-inline mw-100\\"
|
||||
data-filters
|
||||
>
|
||||
<div role=\\"combobox\\"
|
||||
aria-haspopup=\\"listbox\\"
|
||||
aria-owns=\\"react-autowhatever-1\\"
|
||||
aria-expanded=\\"false\\"
|
||||
class=\\"autosuggest d-inline-block w-100\\"
|
||||
>
|
||||
<div class=\\"input-group mr-2\\">
|
||||
<div class=\\"input-group-prepend\\">
|
||||
<span class=\\"input-group-text px-2\\">
|
||||
<svg aria-hidden=\\"true\\"
|
||||
data-prefix=\\"fas\\"
|
||||
data-icon=\\"search\\"
|
||||
class=\\"svg-inline--fa fa-search fa-w-16 \\"
|
||||
role=\\"img\\"
|
||||
xmlns=\\"http://www.w3.org/2000/svg\\"
|
||||
viewbox=\\"0 0 512 512\\"
|
||||
>
|
||||
<path fill=\\"currentColor\\"
|
||||
d=\\"M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z\\"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class=\\"form-control p-1 components-filterinput\\">
|
||||
<input type=\\"text\\"
|
||||
class=\\"components-filterinput-wrapper\\"
|
||||
placeholder
|
||||
value
|
||||
autocomplete=\\"off\\"
|
||||
aria-autocomplete=\\"list\\"
|
||||
aria-controls=\\"react-autowhatever-1\\"
|
||||
>
|
||||
</div>
|
||||
<div class=\\"input-group-append\\">
|
||||
<button class=\\"input-group-text rounded-right cursor-pointer components-navbar-history px-2\\"
|
||||
type=\\"button\\"
|
||||
data-toggle=\\"dropdown\\"
|
||||
aria-haspopup=\\"true\\"
|
||||
aria-expanded=\\"true\\"
|
||||
>
|
||||
<svg aria-hidden=\\"true\\"
|
||||
data-prefix=\\"fas\\"
|
||||
data-icon=\\"caret-down\\"
|
||||
class=\\"svg-inline--fa fa-caret-down fa-w-10 \\"
|
||||
role=\\"img\\"
|
||||
xmlns=\\"http://www.w3.org/2000/svg\\"
|
||||
viewbox=\\"0 0 320 512\\"
|
||||
>
|
||||
<path fill=\\"currentColor\\"
|
||||
d=\\"M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z\\"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id=\\"react-autowhatever-1\\"
|
||||
role=\\"listbox\\"
|
||||
class=\\"dropdown\\"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</nav>
|
||||
</div>
|
||||
"
|
||||
`;
|
||||
@@ -54,17 +54,6 @@ const ValidateNavClass = (totalFilters, expectedClass) => {
|
||||
};
|
||||
|
||||
describe("<NavBar />", () => {
|
||||
it("matches snapshot with 0 alerts", () => {
|
||||
const tree = RenderNavbar();
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("matches snapshot with 5 alerts", () => {
|
||||
alertStore.info.totalAlerts = 5;
|
||||
const tree = RenderNavbar();
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("navbar-brand shows 15 alerts with totalAlerts=15", () => {
|
||||
alertStore.info.totalAlerts = 15;
|
||||
const tree = RenderNavbar();
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import React, { Component } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore, SilenceFormStage } from "Stores/SilenceFormStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { MountModalBackdrop } from "Components/Animations/MountModal";
|
||||
import { SilenceForm } from "./SilenceForm";
|
||||
import { SilencePreview } from "./SilencePreview";
|
||||
import { SilenceSubmitController } from "./SilenceSubmit/SilenceSubmitController";
|
||||
@@ -23,14 +19,6 @@ const SilenceModalContent = observer(
|
||||
onHide: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
disableBodyScroll(document.querySelector(".modal"));
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
enableBodyScroll(document.querySelector(".modal"));
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
alertStore,
|
||||
@@ -39,59 +27,46 @@ const SilenceModalContent = observer(
|
||||
onHide
|
||||
} = this.props;
|
||||
|
||||
return ReactDOM.createPortal(
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="modal d-block" role="dialog">
|
||||
<div className="modal-dialog modal-lg" role="document">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">
|
||||
{silenceFormStore.data.silenceID === null
|
||||
? silenceFormStore.data.currentStage ===
|
||||
SilenceFormStage.UserInput
|
||||
? "Add new silence"
|
||||
: silenceFormStore.data.currentStage ===
|
||||
SilenceFormStage.Preview
|
||||
? "Preview silenced alerts"
|
||||
: "Silence submitted"
|
||||
: `Editing silence ${silenceFormStore.data.silenceID}`}
|
||||
</h5>
|
||||
<button type="button" className="close" onClick={onHide}>
|
||||
<span className="align-middle">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
{silenceFormStore.data.currentStage ===
|
||||
SilenceFormStage.UserInput ? (
|
||||
<SilenceForm
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
settingsStore={settingsStore}
|
||||
/>
|
||||
) : silenceFormStore.data.currentStage ===
|
||||
SilenceFormStage.Preview ? (
|
||||
<SilencePreview
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
) : (
|
||||
<SilenceSubmitController
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">
|
||||
{silenceFormStore.data.silenceID === null
|
||||
? silenceFormStore.data.currentStage ===
|
||||
SilenceFormStage.UserInput
|
||||
? "Add new silence"
|
||||
: silenceFormStore.data.currentStage ===
|
||||
SilenceFormStage.Preview
|
||||
? "Preview silenced alerts"
|
||||
: "Silence submitted"
|
||||
: `Editing silence ${silenceFormStore.data.silenceID}`}
|
||||
</h5>
|
||||
<button type="button" className="close" onClick={onHide}>
|
||||
<span className="align-middle">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<MountModalBackdrop
|
||||
in={silenceFormStore.toggle.visible}
|
||||
unmountOnExit
|
||||
>
|
||||
<div className="modal-backdrop d-block" />
|
||||
</MountModalBackdrop>
|
||||
</React.Fragment>,
|
||||
document.body
|
||||
<div className="modal-body">
|
||||
{silenceFormStore.data.currentStage ===
|
||||
SilenceFormStage.UserInput ? (
|
||||
<SilenceForm
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
settingsStore={settingsStore}
|
||||
/>
|
||||
) : silenceFormStore.data.currentStage ===
|
||||
SilenceFormStage.Preview ? (
|
||||
<SilencePreview
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
) : (
|
||||
<SilenceSubmitController
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { faBellSlash } from "@fortawesome/free-solid-svg-icons/faBellSlash";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { MountModal } from "Components/Animations/MountModal";
|
||||
import { Modal } from "Components/Modal";
|
||||
import { TooltipWrapper } from "Components/TooltipWrapper";
|
||||
import { SilenceModalContent } from "./SilenceModalContent";
|
||||
|
||||
@@ -35,19 +35,6 @@ const SilenceModal = observer(
|
||||
}
|
||||
};
|
||||
|
||||
componentDidUpdate() {
|
||||
const { silenceFormStore } = this.props;
|
||||
|
||||
document.body.classList.toggle(
|
||||
"modal-open",
|
||||
silenceFormStore.toggle.visible
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.body.classList.remove("modal-open");
|
||||
}
|
||||
|
||||
render() {
|
||||
const { alertStore, silenceFormStore, settingsStore } = this.props;
|
||||
|
||||
@@ -63,14 +50,14 @@ const SilenceModal = observer(
|
||||
</span>
|
||||
</TooltipWrapper>
|
||||
</li>
|
||||
<MountModal in={silenceFormStore.toggle.visible} unmountOnExit>
|
||||
<Modal isOpen={silenceFormStore.toggle.visible}>
|
||||
<SilenceModalContent
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={this.toggleModal}
|
||||
/>
|
||||
</MountModal>
|
||||
</Modal>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user