refactor(ui): move common modal code to a dedicated component

This commit is contained in:
Łukasz Mierzwa
2018-10-08 17:02:31 +01:00
parent e5883b057e
commit 7adf1bbfd5
10 changed files with 298 additions and 562 deletions

View File

@@ -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>&times;</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>&times;</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>
);
}
}

View File

@@ -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();
});

View File

@@ -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>
"
`;

View File

@@ -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>
);
}

View 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 };

View 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");
});
});

View File

@@ -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>
"
`;

View File

@@ -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();

View File

@@ -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">&times;</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">&times;</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>
);
}
}

View File

@@ -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>
);
}