mirror of
https://github.com/prymitive/karma
synced 2026-05-07 03:26:52 +00:00
feat(ui): theme switching
This add "proper" dark mode using darkly bootstrap theme
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
import { configure, getStorybook, setAddon } from "@storybook/react";
|
||||
import React from "react";
|
||||
import {
|
||||
configure,
|
||||
getStorybook,
|
||||
setAddon,
|
||||
addDecorator
|
||||
} from "@storybook/react";
|
||||
|
||||
import createPercyAddon from "@percy-io/percy-storybook";
|
||||
|
||||
@@ -10,6 +16,12 @@ setAddon(percyAddon);
|
||||
// mock date so the silence form always shows same preview
|
||||
advanceTo(new Date(Date.UTC(2018, 7, 14, 17, 36, 40)));
|
||||
|
||||
addDecorator(story => {
|
||||
document.body.classList.add("theme-light");
|
||||
document.body.style = "";
|
||||
return story();
|
||||
});
|
||||
|
||||
const req = require.context("../src/Components", true, /\.stories\.(js|tsx)$/);
|
||||
|
||||
function loadStories() {
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
// bundled font assets, so we don't need to talk to Google Fonts API
|
||||
@import "src/Fonts.scss";
|
||||
|
||||
@import "Theme.scss";
|
||||
|
||||
@import "~bootstrap/scss/bootstrap";
|
||||
@import "~bootswatch/dist/flatly/bootswatch";
|
||||
|
||||
// negative margin used for silence expiry badges with progress
|
||||
.nmb-05 {
|
||||
margin-bottom: -($spacer * 0.125) !important;
|
||||
}
|
||||
|
||||
// this is used for navbar, to make it transparent
|
||||
.bg-primary-transparent {
|
||||
background-color: rgba($primary, 0.95);
|
||||
}
|
||||
// version for modals
|
||||
.bg-primary-transparent-80 {
|
||||
background-color: rgba($dark, 0.8);
|
||||
}
|
||||
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
.cursor-text {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.mw-1p {
|
||||
min-width: 1%;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
import { shallow } from "enzyme";
|
||||
import { shallow, mount } from "enzyme";
|
||||
|
||||
import { NewUnappliedFilter } from "Stores/AlertStore";
|
||||
import { App } from "./App";
|
||||
@@ -18,12 +18,15 @@ beforeEach(() => {
|
||||
// createing App instance will push current filters into window.location
|
||||
// ensure it's wiped after each test
|
||||
window.history.pushState({}, "App", "/");
|
||||
|
||||
document.body.className = "";
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
localStorage.setItem("savedFilters", "");
|
||||
jest.restoreAllMocks();
|
||||
window.history.pushState({}, "App", "/");
|
||||
document.body.className = "";
|
||||
});
|
||||
|
||||
describe("<App />", () => {
|
||||
@@ -155,7 +158,19 @@ describe("<App />", () => {
|
||||
window.onpopstate(event);
|
||||
});
|
||||
|
||||
it("appends 'dark-theme' class to #root if dark mode is enabled", () => {
|
||||
it("appends correct theme class to #root if dark mode is disabled", () => {
|
||||
const tree = shallow(
|
||||
<App
|
||||
defaultFilters={["foo=bar"]}
|
||||
uiDefaults={Object.assign({}, uiDefaults, { DarkMode: false })}
|
||||
/>
|
||||
);
|
||||
tree.instance().componentWillUnmount();
|
||||
|
||||
expect(document.body.className.split(" ")).toContain("theme-light");
|
||||
});
|
||||
|
||||
it("appends 'theme-dark' class to #root if dark mode is enabled", () => {
|
||||
const tree = shallow(
|
||||
<App
|
||||
defaultFilters={["foo=bar"]}
|
||||
@@ -164,6 +179,22 @@ describe("<App />", () => {
|
||||
);
|
||||
tree.instance().componentWillUnmount();
|
||||
|
||||
expect(document.body.className.split(" ")).toContain("dark-theme");
|
||||
expect(document.body.className.split(" ")).toContain("theme-dark");
|
||||
});
|
||||
|
||||
it("toggling settingsStore.themeConfig.config.darkTheme modifies the theme", () => {
|
||||
const tree = mount(
|
||||
<App
|
||||
defaultFilters={["foo=bar"]}
|
||||
uiDefaults={Object.assign({}, uiDefaults, { DarkMode: false })}
|
||||
/>
|
||||
);
|
||||
tree.update();
|
||||
expect(document.body.className.split(" ")).toContain("theme-light");
|
||||
|
||||
tree.instance().settingsStore.themeConfig.config.darkTheme = true;
|
||||
tree.update();
|
||||
expect(document.body.className.split(" ")).toContain("theme-dark");
|
||||
tree.instance().componentWillUnmount();
|
||||
});
|
||||
});
|
||||
|
||||
155
ui/src/App.tsx
155
ui/src/App.tsx
@@ -1,5 +1,7 @@
|
||||
import React, { Component } from "react";
|
||||
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
import { AlertStore, DecodeLocationSearch } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
@@ -7,10 +9,12 @@ import { NavBar } from "Components/NavBar";
|
||||
import { Grid } from "Components/Grid";
|
||||
import { Fetcher } from "Components/Fetcher";
|
||||
import { FaviconBadge } from "Components/FaviconBadge";
|
||||
import { ReactSelectColors, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { Theme, ThemeContext } from "Components/Theme";
|
||||
import { ErrorBoundary } from "./ErrorBoundary";
|
||||
|
||||
import "./App.scss";
|
||||
import "./DarkTheme.scss";
|
||||
import "Styles/ResetCSS.scss";
|
||||
import "Styles/App.scss";
|
||||
|
||||
interface UIDefaults {
|
||||
Refresh: number;
|
||||
@@ -27,81 +31,98 @@ interface AppProps {
|
||||
uiDefaults: UIDefaults;
|
||||
}
|
||||
|
||||
class App extends Component<AppProps, {}> {
|
||||
alertStore: AlertStore;
|
||||
silenceFormStore: SilenceFormStore;
|
||||
settingsStore: Settings;
|
||||
filters: Array<string> = [];
|
||||
const App = observer(
|
||||
class App extends Component<AppProps, {}> {
|
||||
alertStore: AlertStore;
|
||||
silenceFormStore: SilenceFormStore;
|
||||
settingsStore: Settings;
|
||||
filters: Array<string> = [];
|
||||
|
||||
constructor(props: AppProps) {
|
||||
super(props);
|
||||
constructor(props: AppProps) {
|
||||
super(props);
|
||||
|
||||
const { defaultFilters, uiDefaults } = this.props;
|
||||
const { defaultFilters, uiDefaults } = this.props;
|
||||
|
||||
this.silenceFormStore = new SilenceFormStore();
|
||||
this.settingsStore = new Settings(uiDefaults);
|
||||
this.silenceFormStore = new SilenceFormStore();
|
||||
this.settingsStore = new Settings(uiDefaults);
|
||||
|
||||
let filters;
|
||||
this.state = { darkTheme: false };
|
||||
|
||||
// parse and decode request query args
|
||||
const p = DecodeLocationSearch(window.location.search);
|
||||
let filters;
|
||||
|
||||
// p.defaultsUsed means that karma URI didn't have ?q=foo query args
|
||||
if (p.defaultsUsed) {
|
||||
// no ?q=foo set, use defaults saved by the user or from backend config
|
||||
if (this.settingsStore.savedFilters.config.present) {
|
||||
filters = this.settingsStore.savedFilters.config.filters;
|
||||
// parse and decode request query args
|
||||
const p = DecodeLocationSearch(window.location.search);
|
||||
|
||||
// p.defaultsUsed means that karma URI didn't have ?q=foo query args
|
||||
if (p.defaultsUsed) {
|
||||
// no ?q=foo set, use defaults saved by the user or from backend config
|
||||
if (this.settingsStore.savedFilters.config.present) {
|
||||
filters = this.settingsStore.savedFilters.config.filters;
|
||||
} else {
|
||||
filters = defaultFilters;
|
||||
}
|
||||
} else {
|
||||
filters = defaultFilters;
|
||||
// user passed ?q=foo, use it as initial filters
|
||||
filters = p.params.q;
|
||||
}
|
||||
} else {
|
||||
// user passed ?q=foo, use it as initial filters
|
||||
filters = p.params.q;
|
||||
|
||||
this.alertStore = new AlertStore(filters);
|
||||
}
|
||||
|
||||
this.alertStore = new AlertStore(filters);
|
||||
onPopState = (event: PopStateEvent) => {
|
||||
event.preventDefault();
|
||||
const p = DecodeLocationSearch(window.location.search);
|
||||
this.alertStore.filters.setWithoutLocation(p.params.q);
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
window.onpopstate = this.onPopState;
|
||||
|
||||
document.body.classList.toggle(
|
||||
"theme-dark",
|
||||
this.settingsStore.themeConfig.config.darkTheme
|
||||
);
|
||||
document.body.classList.toggle(
|
||||
"theme-light",
|
||||
!this.settingsStore.themeConfig.config.darkTheme
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.onpopstate = () => {};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<Theme settingsStore={this.settingsStore} />
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
reactSelectStyles: this.settingsStore.themeConfig.config.darkTheme
|
||||
? ReactSelectStyles(ReactSelectColors.Dark)
|
||||
: ReactSelectStyles(ReactSelectColors.Light)
|
||||
}}
|
||||
>
|
||||
<NavBar
|
||||
alertStore={this.alertStore}
|
||||
settingsStore={this.settingsStore}
|
||||
silenceFormStore={this.silenceFormStore}
|
||||
/>
|
||||
<Grid
|
||||
alertStore={this.alertStore}
|
||||
settingsStore={this.settingsStore}
|
||||
silenceFormStore={this.silenceFormStore}
|
||||
/>
|
||||
</ThemeContext.Provider>
|
||||
<FaviconBadge alertStore={this.alertStore} />
|
||||
<Fetcher
|
||||
alertStore={this.alertStore}
|
||||
settingsStore={this.settingsStore}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onPopState = (event: PopStateEvent) => {
|
||||
event.preventDefault();
|
||||
const p = DecodeLocationSearch(window.location.search);
|
||||
this.alertStore.filters.setWithoutLocation(p.params.q);
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
window.onpopstate = this.onPopState;
|
||||
|
||||
document.body.classList.toggle(
|
||||
"dark-theme",
|
||||
this.settingsStore.themeConfig.config.darkTheme
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.onpopstate = () => {};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<FaviconBadge alertStore={this.alertStore} />
|
||||
<NavBar
|
||||
alertStore={this.alertStore}
|
||||
settingsStore={this.settingsStore}
|
||||
silenceFormStore={this.silenceFormStore}
|
||||
/>
|
||||
<Grid
|
||||
alertStore={this.alertStore}
|
||||
settingsStore={this.settingsStore}
|
||||
silenceFormStore={this.silenceFormStore}
|
||||
/>
|
||||
<Fetcher
|
||||
alertStore={this.alertStore}
|
||||
settingsStore={this.settingsStore}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export { App };
|
||||
|
||||
@@ -6,8 +6,6 @@ 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 "./index.scss";
|
||||
|
||||
const Trigger = ({ text, isOpen }) => (
|
||||
<div className="d-flex flex-row justify-content-between">
|
||||
<div>{text}</div>
|
||||
@@ -28,7 +26,7 @@ const Accordion = ({ text, content, extraProps }) => (
|
||||
className="card"
|
||||
openedClassName="card"
|
||||
triggerClassName="card-header cursor-pointer border-bottom-0"
|
||||
triggerOpenedClassName="card-header cursor-pointer bg-light"
|
||||
triggerOpenedClassName="card-header cursor-pointer"
|
||||
contentOuterClassName="collapse show"
|
||||
contentInnerClassName="card-body my-2"
|
||||
{...extraProps}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
.accordion > .Collapsible.card {
|
||||
overflow: unset;
|
||||
}
|
||||
@@ -2,6 +2,8 @@ import React from "react";
|
||||
|
||||
import { mount } from "enzyme";
|
||||
|
||||
import toDiffableHtml from "diffable-html";
|
||||
|
||||
import { advanceTo, clear } from "jest-date-mock";
|
||||
|
||||
import { MockAlertGroup, MockAlert } from "__mocks__/Alerts.js";
|
||||
@@ -85,7 +87,7 @@ describe("<AlertAck />", () => {
|
||||
|
||||
it("uses faCheck icon when idle", () => {
|
||||
const tree = MountedAlertAck();
|
||||
expect(tree.html()).toMatch(/fa-check/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/fa-check/);
|
||||
});
|
||||
|
||||
it("uses faExclamationCircle after failed fetch", async () => {
|
||||
@@ -96,7 +98,7 @@ describe("<AlertAck />", () => {
|
||||
await expect(
|
||||
tree.instance().submitState.silencesByCluster["default"].fetch
|
||||
).resolves.toBeUndefined();
|
||||
expect(tree.html()).toMatch(/fa-exclamation-circle/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/fa-exclamation-circle/);
|
||||
});
|
||||
|
||||
it("[v1] uses faCheckCircle after successful fetch", async () => {
|
||||
@@ -109,7 +111,7 @@ describe("<AlertAck />", () => {
|
||||
await expect(
|
||||
tree.instance().submitState.silencesByCluster["default"].fetch
|
||||
).resolves.toBeUndefined();
|
||||
expect(tree.html()).toMatch(/fa-check-circle/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/fa-check-circle/);
|
||||
});
|
||||
|
||||
it("[v2] uses faCheckCircle after successful fetch", async () => {
|
||||
@@ -121,7 +123,7 @@ describe("<AlertAck />", () => {
|
||||
await expect(
|
||||
tree.instance().submitState.silencesByCluster["default"].fetch
|
||||
).resolves.toBeUndefined();
|
||||
expect(tree.html()).toMatch(/fa-check-circle/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/fa-check-circle/);
|
||||
});
|
||||
|
||||
it("sends a request on click", () => {
|
||||
|
||||
@@ -3,8 +3,6 @@ import PropTypes from "prop-types";
|
||||
|
||||
import { CSSTransition } from "react-transition-group";
|
||||
|
||||
import "./index.css";
|
||||
|
||||
const DropdownSlide = ({ children, duration, ...props }) => (
|
||||
<CSSTransition
|
||||
classNames="components-animation-slide"
|
||||
|
||||
@@ -3,8 +3,6 @@ import PropTypes from "prop-types";
|
||||
|
||||
import { CSSTransition } from "react-transition-group";
|
||||
|
||||
import "./index.css";
|
||||
|
||||
const MountFade = ({ children, duration, ...props }) => (
|
||||
<CSSTransition
|
||||
classNames="components-animation-fade"
|
||||
|
||||
@@ -3,8 +3,6 @@ import PropTypes from "prop-types";
|
||||
|
||||
import { CSSTransition } from "react-transition-group";
|
||||
|
||||
import "./index.scss";
|
||||
|
||||
const MountModal = ({ children, duration, ...props }) => (
|
||||
<CSSTransition
|
||||
classNames="components-animation-modal"
|
||||
|
||||
@@ -3,8 +3,6 @@ import PropTypes from "prop-types";
|
||||
|
||||
import { CSSTransition } from "react-transition-group";
|
||||
|
||||
import "./index.css";
|
||||
|
||||
const NavBarSlide = ({ children, duration, ...props }) => (
|
||||
<CSSTransition
|
||||
classNames="components-animation-navbar"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
exports[`<Alert /> matches snapshot when inhibited 1`] = `
|
||||
"
|
||||
<li class=\\"components-grid-alertgrid-alertgroup-alert list-group-item pl-1 pr-0 py-0 my-1 rounded-0 border-left-1 border-right-0 border-top-0 border-bottom-0 border-danger\\">
|
||||
<li class=\\"components-grid-alertgrid-alertgroup-alert list-group-item bg-transparent pl-1 pr-0 py-0 my-1 rounded-0 border-left-1 border-right-0 border-top-0 border-bottom-0 border-danger\\">
|
||||
<div>
|
||||
<div class
|
||||
style=\\"display: inline-block; max-width: 100%;\\"
|
||||
@@ -162,7 +162,7 @@ exports[`<Alert /> matches snapshot when inhibited 1`] = `
|
||||
|
||||
exports[`<Alert /> matches snapshot with showAlertmanagers=false showReceiver=false 1`] = `
|
||||
"
|
||||
<li class=\\"components-grid-alertgrid-alertgroup-alert list-group-item pl-1 pr-0 py-0 my-1 rounded-0 border-left-1 border-right-0 border-top-0 border-bottom-0 border-danger\\">
|
||||
<li class=\\"components-grid-alertgrid-alertgroup-alert list-group-item bg-transparent pl-1 pr-0 py-0 my-1 rounded-0 border-left-1 border-right-0 border-top-0 border-bottom-0 border-danger\\">
|
||||
<div>
|
||||
<div class
|
||||
style=\\"display: inline-block; max-width: 100%;\\"
|
||||
|
||||
@@ -17,8 +17,6 @@ import { RenderNonLinkAnnotation, RenderLinkAnnotation } from "../Annotation";
|
||||
import { AlertMenu } from "./AlertMenu";
|
||||
import { RenderSilence } from "../Silences";
|
||||
|
||||
import "./index.scss";
|
||||
|
||||
const Alert = observer(
|
||||
class Alert extends Component {
|
||||
static propTypes = {
|
||||
@@ -46,7 +44,7 @@ const Alert = observer(
|
||||
|
||||
let classNames = [
|
||||
"components-grid-alertgrid-alertgroup-alert",
|
||||
"list-group-item",
|
||||
"list-group-item bg-transparent",
|
||||
"pl-1 pr-0 py-0",
|
||||
"my-1",
|
||||
"rounded-0",
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
@import "src/Theme.scss";
|
||||
|
||||
.components-grid-alertgrid-alertgroup-alert {
|
||||
&:hover {
|
||||
background-color: $gray-100;
|
||||
}
|
||||
|
||||
&.list-group-item {
|
||||
border-width: 3px;
|
||||
line-height: 1rem;
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,6 @@ import { faSearchMinus } from "@fortawesome/free-solid-svg-icons/faSearchMinus";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { TooltipWrapper } from "Components/TooltipWrapper";
|
||||
|
||||
import "./index.css";
|
||||
|
||||
const RenderNonLinkAnnotation = observer(
|
||||
class RenderNonLinkAnnotation extends Component {
|
||||
static propTypes = {
|
||||
|
||||
@@ -79,7 +79,7 @@ describe("<RenderNonLinkAnnotation />", () => {
|
||||
|
||||
it("contains value when visible=true", () => {
|
||||
const tree = ShallowNonLinkAnnotation(true);
|
||||
expect(tree.html()).toMatch(/some long text/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/some long text/);
|
||||
});
|
||||
|
||||
it("matches snapshot when visible=false", () => {
|
||||
@@ -89,7 +89,7 @@ describe("<RenderNonLinkAnnotation />", () => {
|
||||
|
||||
it("doesn't contain value when visible=false", () => {
|
||||
const tree = ShallowNonLinkAnnotation(false);
|
||||
expect(tree.html()).not.toMatch(/some long text/);
|
||||
expect(toDiffableHtml(tree.html())).not.toMatch(/some long text/);
|
||||
});
|
||||
|
||||
it("links inside annotation are rendered as a.href", () => {
|
||||
@@ -100,19 +100,19 @@ describe("<RenderNonLinkAnnotation />", () => {
|
||||
|
||||
it("clicking on - icon hides the value", () => {
|
||||
const tree = MountedNonLinkAnnotation(true);
|
||||
expect(tree.html()).toMatch(/fa-search-minus/);
|
||||
expect(tree.html()).toMatch(/some long text/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/fa-search-minus/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/some long text/);
|
||||
tree.find(".fa-search-minus").simulate("click");
|
||||
expect(tree.html()).toMatch(/fa-search-plus/);
|
||||
expect(tree.html()).not.toMatch(/some long text/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/fa-search-plus/);
|
||||
expect(toDiffableHtml(tree.html())).not.toMatch(/some long text/);
|
||||
});
|
||||
|
||||
it("clicking on + icon shows the value", () => {
|
||||
const tree = MountedNonLinkAnnotation(false);
|
||||
expect(tree.html()).toMatch(/fa-search-plus/);
|
||||
expect(tree.html()).not.toMatch(/some long text/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/fa-search-plus/);
|
||||
expect(toDiffableHtml(tree.html())).not.toMatch(/some long text/);
|
||||
tree.find(".components-grid-annotation").simulate("click");
|
||||
expect(tree.html()).toMatch(/fa-search-minus/);
|
||||
expect(tree.html()).toMatch(/some long text/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/fa-search-minus/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/some long text/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
exports[`<GroupFooter /> matches snapshot 1`] = `
|
||||
"
|
||||
<div class=\\"card-footer bg-card-footer-default px-2 py-1\\">
|
||||
<div class=\\"card-footer components-grid-alertgrid-alertgroup-footer px-2 py-1\\">
|
||||
<div class=\\"mb-1\\">
|
||||
<div class
|
||||
style=\\"display: inline-block; max-width: 100%;\\"
|
||||
@@ -147,7 +147,7 @@ exports[`<GroupFooter /> matches snapshot 1`] = `
|
||||
|
||||
exports[`<GroupFooter /> mathes snapshot when silence is rendered 1`] = `
|
||||
"
|
||||
<div class=\\"card-footer bg-card-footer-default px-2 py-1\\">
|
||||
<div class=\\"card-footer components-grid-alertgrid-alertgroup-footer px-2 py-1\\">
|
||||
<div class=\\"mb-1\\">
|
||||
<div class
|
||||
style=\\"display: inline-block; max-width: 100%;\\"
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
.components-grid-alertgrid-alertgroup-shared-silence {
|
||||
border-width: 3px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.components-grid-alertgrid-alertgroup-shared-silence > .card {
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
/* this is default card background color, can't access it as scss variable */
|
||||
.card-footer.bg-card-footer-default {
|
||||
background-color: rgb(247, 247, 247);
|
||||
}
|
||||
@@ -11,8 +11,6 @@ import { FilteringLabel } from "Components/Labels/FilteringLabel";
|
||||
import { RenderNonLinkAnnotation, RenderLinkAnnotation } from "../Annotation";
|
||||
import { RenderSilence } from "../Silences";
|
||||
|
||||
import "./index.css";
|
||||
|
||||
const GroupFooter = observer(
|
||||
class GroupFooter extends Component {
|
||||
static propTypes = {
|
||||
@@ -33,7 +31,7 @@ const GroupFooter = observer(
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div className="card-footer bg-card-footer-default px-2 py-1">
|
||||
<div className="card-footer components-grid-alertgrid-alertgroup-footer px-2 py-1">
|
||||
<div className="mb-1">
|
||||
{group.shared.annotations
|
||||
.filter(a => a.isLink === false)
|
||||
|
||||
@@ -25,11 +25,7 @@ import { GroupFooter } from "./GroupFooter";
|
||||
const LoadButton = ({ icon, action, tooltip }) => {
|
||||
return (
|
||||
<TooltipWrapper title={tooltip}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm py-0 bg-white"
|
||||
onClick={action}
|
||||
>
|
||||
<button type="button" className="btn btn-sm py-0" onClick={action}>
|
||||
<FontAwesomeIcon className="text-muted" icon={icon} />
|
||||
</button>
|
||||
</TooltipWrapper>
|
||||
@@ -224,7 +220,7 @@ const AlertGroup = observer(
|
||||
setIsMenuOpen={this.renderConfig.setIsMenuOpen}
|
||||
/>
|
||||
{this.collapse.value ? null : (
|
||||
<div className="card-body px-2 py-1 bg-white components-grid-alertgrid-card">
|
||||
<div className="card-body px-2 py-1 components-grid-alertgrid-card">
|
||||
<ul className="list-group">
|
||||
{group.alerts
|
||||
.slice(0, this.renderConfig.alertsToRender)
|
||||
@@ -244,7 +240,7 @@ const AlertGroup = observer(
|
||||
/>
|
||||
))}
|
||||
{group.alerts.length > this.defaultRenderCount ? (
|
||||
<li className="list-group-item border-0 p-0 text-center">
|
||||
<li className="list-group-item border-0 p-0 text-center bg-transparent">
|
||||
<LoadButton
|
||||
icon={faMinus}
|
||||
action={this.loadLess}
|
||||
|
||||
@@ -21,8 +21,6 @@ import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { AlertGroup } from "./AlertGroup";
|
||||
import { GridSizesConfig, GetGridElementWidth } from "./GridSize";
|
||||
|
||||
import "./index.css";
|
||||
|
||||
const AlertGrid = observer(
|
||||
class AlertGrid extends Component {
|
||||
static propTypes = {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
exports[`<EmptyGrid /> matches snapshot 1`] = `
|
||||
"
|
||||
<h1 class=\\"display-1 text-secondary screen-center\\">
|
||||
<h1 class=\\"display-1 text-placeholder screen-center\\">
|
||||
<svg aria-hidden=\\"true\\"
|
||||
focusable=\\"false\\"
|
||||
data-prefix=\\"fas\\"
|
||||
|
||||
@@ -8,7 +8,7 @@ import { MountFade } from "Components/Animations/MountFade";
|
||||
import "./index.scss";
|
||||
|
||||
const EmptyGrid = () => (
|
||||
<h1 className="display-1 text-secondary screen-center">
|
||||
<h1 className="display-1 text-placeholder screen-center">
|
||||
<MountFade in={true}>
|
||||
<FontAwesomeIcon icon={faMugHot} style={{ fontSize: "14rem" }} />
|
||||
</MountFade>
|
||||
|
||||
@@ -10,7 +10,7 @@ import { FatalError } from "./FatalError";
|
||||
import { UpgradeNeeded } from "./UpgradeNeeded";
|
||||
import { Grid } from ".";
|
||||
|
||||
import "Percy.scss";
|
||||
import "Styles/Percy.scss";
|
||||
|
||||
storiesOf("Grid", module)
|
||||
.add("FatalError", () => {
|
||||
|
||||
@@ -10,8 +10,6 @@ import {
|
||||
} from "Common/Colors";
|
||||
import { QueryOperators, FormatQuery, StaticLabels } from "Common/Query";
|
||||
|
||||
import "./index.scss";
|
||||
|
||||
const isBackgroundDark = brightness => brightness <= 125;
|
||||
|
||||
// base class for shared code, not used directly
|
||||
|
||||
@@ -15,8 +15,6 @@ import { QueryOperators } from "Common/Query";
|
||||
import { TooltipWrapper } from "Components/TooltipWrapper";
|
||||
import { BaseLabel } from "Components/Labels/BaseLabel";
|
||||
|
||||
import "./index.scss";
|
||||
|
||||
const FilterInputLabel = observer(
|
||||
class FilterInputLabel extends BaseLabel {
|
||||
static propTypes = {
|
||||
@@ -98,7 +96,7 @@ const FilterInputLabel = observer(
|
||||
value={filter.raw}
|
||||
propName="raw"
|
||||
change={this.onChange}
|
||||
classEditing="py-0 border-0 bg-light text-black rounded"
|
||||
classEditing="py-0 border-0 editing rounded"
|
||||
afterStart={alertStore.status.pause}
|
||||
afterFinish={alertStore.status.resume}
|
||||
/>
|
||||
|
||||
@@ -7,8 +7,6 @@ import { QueryOperators } from "Common/Query";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { BaseLabel } from "Components/Labels/BaseLabel";
|
||||
|
||||
import "./index.css";
|
||||
|
||||
const HistoryLabel = observer(
|
||||
class HistoryLabel extends BaseLabel {
|
||||
static propTypes = {
|
||||
|
||||
@@ -10,8 +10,6 @@ import { AlertStore } from "Stores/AlertStore";
|
||||
import { QueryOperators, FormatQuery } from "Common/Query";
|
||||
import { BaseLabel } from "Components/Labels/BaseLabel";
|
||||
|
||||
import "./index.scss";
|
||||
|
||||
const LabelWithPercent = observer(
|
||||
class LabelWithPercent extends BaseLabel {
|
||||
static propTypes = {
|
||||
|
||||
@@ -90,16 +90,16 @@ describe("<LabelWithPercent />", () => {
|
||||
|
||||
it("uses bg-danger when percent is >66", () => {
|
||||
const tree = MountedLabelWithPercent("foo", "bar", 25, 67, 0, false);
|
||||
expect(tree.html()).toMatch(/progress-bar bg-danger/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/progress-bar bg-danger/);
|
||||
});
|
||||
|
||||
it("uses bg-warning when percent is >33", () => {
|
||||
const tree = MountedLabelWithPercent("foo", "bar", 25, 66, 0, false);
|
||||
expect(tree.html()).toMatch(/progress-bar bg-warning/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/progress-bar bg-warning/);
|
||||
});
|
||||
|
||||
it("uses bg-success when percent is <=33", () => {
|
||||
const tree = MountedLabelWithPercent("foo", "bar", 25, 33, 0, false);
|
||||
expect(tree.html()).toMatch(/progress-bar bg-success/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/progress-bar bg-success/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,13 +7,14 @@ import { observer } from "mobx-react";
|
||||
import Select from "react-select";
|
||||
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
|
||||
const AlertGroupCollapseConfiguration = observer(
|
||||
class AlertGroupCollapseConfiguration extends Component {
|
||||
static propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired
|
||||
};
|
||||
static contextType = ThemeContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -56,7 +57,7 @@ const AlertGroupCollapseConfiguration = observer(
|
||||
return (
|
||||
<div className="form-group mb-0">
|
||||
<Select
|
||||
styles={ReactSelectStyles}
|
||||
styles={this.context.reactSelectStyles}
|
||||
classNamePrefix="react-select"
|
||||
instanceId="configuration-collapse"
|
||||
defaultValue={this.valueToOption(
|
||||
|
||||
@@ -5,6 +5,8 @@ import { mount } from "enzyme";
|
||||
import toDiffableHtml from "diffable-html";
|
||||
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { ReactSelectColors, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { AlertGroupCollapseConfiguration } from "./AlertGroupCollapseConfiguration";
|
||||
|
||||
let settingsStore;
|
||||
@@ -14,7 +16,13 @@ beforeEach(() => {
|
||||
|
||||
const FakeConfiguration = () => {
|
||||
return mount(
|
||||
<AlertGroupCollapseConfiguration settingsStore={settingsStore} />
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Light)
|
||||
}}
|
||||
>
|
||||
<AlertGroupCollapseConfiguration settingsStore={settingsStore} />
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -8,8 +8,6 @@ import InputRange from "react-input-range";
|
||||
|
||||
import { Settings } from "Stores/Settings";
|
||||
|
||||
import "./InputRange.scss";
|
||||
|
||||
const AlertGroupConfiguration = observer(
|
||||
class AlertGroupConfiguration extends Component {
|
||||
static propTypes = {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { observer } from "mobx-react";
|
||||
import Select from "react-select";
|
||||
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { SortLabelName } from "./SortLabelName";
|
||||
|
||||
const AlertGroupSortConfiguration = observer(
|
||||
@@ -15,6 +15,7 @@ const AlertGroupSortConfiguration = observer(
|
||||
static propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired
|
||||
};
|
||||
static contextType = ThemeContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -67,7 +68,7 @@ const AlertGroupSortConfiguration = observer(
|
||||
<div className="d-flex flex-fill flex-lg-row flex-column justify-content-between">
|
||||
<div className="flex-shrink-0 flex-grow-1 flex-basis-auto">
|
||||
<Select
|
||||
styles={ReactSelectStyles}
|
||||
styles={this.context.reactSelectStyles}
|
||||
classNamePrefix="react-select"
|
||||
instanceId="configuration-sort-order"
|
||||
defaultValue={this.valueToOption(
|
||||
|
||||
@@ -5,6 +5,8 @@ import { mount } from "enzyme";
|
||||
import toDiffableHtml from "diffable-html";
|
||||
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { ReactSelectColors, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { AlertGroupSortConfiguration } from "./AlertGroupSortConfiguration";
|
||||
|
||||
let settingsStore;
|
||||
@@ -18,7 +20,15 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
const FakeConfiguration = () => {
|
||||
return mount(<AlertGroupSortConfiguration settingsStore={settingsStore} />);
|
||||
return mount(
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Light)
|
||||
}}
|
||||
>
|
||||
<AlertGroupSortConfiguration settingsStore={settingsStore} />
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const ExpandSortLabelSuggestions = async () => {
|
||||
|
||||
@@ -10,8 +10,6 @@ import InputRange from "react-input-range";
|
||||
|
||||
import { Settings } from "Stores/Settings";
|
||||
|
||||
import "./InputRange.scss";
|
||||
|
||||
const AlertGroupWidthConfiguration = observer(
|
||||
class AlertGroupWidthConfiguration extends Component {
|
||||
static propTypes = {
|
||||
|
||||
@@ -8,8 +8,6 @@ import InputRange from "react-input-range";
|
||||
|
||||
import { Settings } from "Stores/Settings";
|
||||
|
||||
import "./InputRange.scss";
|
||||
|
||||
const FetchConfiguration = observer(
|
||||
class FetchConfiguration extends Component {
|
||||
static propTypes = {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { StaticLabels } from "Common/Query";
|
||||
import { FetchGet } from "Common/Fetch";
|
||||
import { FormatBackendURI } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
|
||||
const valueToOption = v => ({ label: v, value: v });
|
||||
|
||||
@@ -19,6 +19,7 @@ const SortLabelName = observer(
|
||||
static propTypes = {
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired
|
||||
};
|
||||
static contextType = ThemeContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -71,7 +72,7 @@ const SortLabelName = observer(
|
||||
|
||||
return (
|
||||
<Creatable
|
||||
styles={ReactSelectStyles}
|
||||
styles={this.context.reactSelectStyles}
|
||||
classNamePrefix="react-select"
|
||||
instanceId="configuration-sort-label"
|
||||
defaultValue={valueToOption(
|
||||
|
||||
@@ -17,9 +17,13 @@ const ThemeConfiguration = observer(
|
||||
settingsStore.themeConfig.config.darkTheme = event.target.checked;
|
||||
|
||||
document.body.classList.toggle(
|
||||
"dark-theme",
|
||||
"theme-dark",
|
||||
settingsStore.themeConfig.config.darkTheme
|
||||
);
|
||||
document.body.classList.toggle(
|
||||
"theme-light",
|
||||
!settingsStore.themeConfig.config.darkTheme
|
||||
);
|
||||
});
|
||||
|
||||
render() {
|
||||
|
||||
@@ -4,9 +4,9 @@ exports[`<AlertGroupCollapseConfiguration /> matches snapshot with default value
|
||||
"
|
||||
<div class=\\"form-group mb-0\\">
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<div class=\\"react-select__control css-au8pbo-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-pb81dw\\">
|
||||
<div class=\\"react-select__single-value css-1uccc91-singleValue\\">
|
||||
<div class=\\"react-select__control css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-97xgis\\">
|
||||
<div class=\\"react-select__single-value css-1wh03ml-singleValue\\">
|
||||
Collapse on mobile
|
||||
</div>
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
|
||||
@@ -6,9 +6,9 @@ exports[`<AlertGroupSortConfiguration /> matches snapshot with default values 1`
|
||||
<div class=\\"d-flex flex-fill flex-lg-row flex-column justify-content-between\\">
|
||||
<div class=\\"flex-shrink-0 flex-grow-1 flex-basis-auto\\">
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<div class=\\"react-select__control css-au8pbo-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-pb81dw\\">
|
||||
<div class=\\"react-select__single-value css-1uccc91-singleValue\\">
|
||||
<div class=\\"react-select__control css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-97xgis\\">
|
||||
<div class=\\"react-select__single-value css-1wh03ml-singleValue\\">
|
||||
Use defaults from karma config file
|
||||
</div>
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
|
||||
@@ -4,7 +4,7 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
"
|
||||
<form class=\\"px-3 accordion\\">
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Refresh interval
|
||||
@@ -28,7 +28,7 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible__contentOuter collapse show\\"
|
||||
style=\\"height:auto;-webkit-transition:none;-ms-transition:none;transition:none;overflow:visible\\"
|
||||
style=\\"height: auto; transition: none; overflow: visible;\\"
|
||||
>
|
||||
<div class=\\"Collapsible__contentInner card-body my-2\\">
|
||||
<div class=\\"form-group mb-0 text-center\\">
|
||||
@@ -41,12 +41,12 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
<div class=\\"input-range__track input-range__track--background\\">
|
||||
<div style=\\"left:0%;width:18.181818181818183%\\"
|
||||
<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%\\"
|
||||
style=\\"position: absolute; left: 18.181818181818183%;\\"
|
||||
>
|
||||
<span class=\\"input-range__label input-range__label--value\\">
|
||||
<span class=\\"input-range__label-container\\">
|
||||
@@ -75,7 +75,7 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Filter bar configuration
|
||||
@@ -99,15 +99,15 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible__contentOuter collapse show\\"
|
||||
style=\\"height:auto;-webkit-transition:none;-ms-transition:none;transition:none;overflow:visible\\"
|
||||
style=\\"height: auto; transition: none; overflow: visible;\\"
|
||||
>
|
||||
<div class=\\"Collapsible__contentInner card-body my-2\\">
|
||||
<div class=\\"form-group mb-0\\">
|
||||
<div class=\\"form-check form-check-inline\\">
|
||||
<span class=\\"custom-control custom-switch\\">
|
||||
<input type=\\"checkbox\\"
|
||||
id=\\"configuration-autohide\\"
|
||||
<input id=\\"configuration-autohide\\"
|
||||
class=\\"custom-control-input\\"
|
||||
type=\\"checkbox\\"
|
||||
value
|
||||
checked
|
||||
>
|
||||
@@ -123,7 +123,7 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Theme
|
||||
@@ -147,15 +147,15 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible__contentOuter collapse show\\"
|
||||
style=\\"height:auto;-webkit-transition:none;-ms-transition:none;transition:none;overflow:visible\\"
|
||||
style=\\"height: auto; transition: none; overflow: visible;\\"
|
||||
>
|
||||
<div class=\\"Collapsible__contentInner card-body my-2\\">
|
||||
<div class=\\"form-group mb-2\\">
|
||||
<div class=\\"form-check form-check-inline\\">
|
||||
<span class=\\"custom-control custom-switch\\">
|
||||
<input type=\\"checkbox\\"
|
||||
id=\\"configuration-colortitlebar\\"
|
||||
<input id=\\"configuration-colortitlebar\\"
|
||||
class=\\"custom-control-input\\"
|
||||
type=\\"checkbox\\"
|
||||
value
|
||||
>
|
||||
<label class=\\"custom-control-label cursor-pointer mr-3\\"
|
||||
@@ -169,9 +169,9 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
<div class=\\"form-group mb-0\\">
|
||||
<div class=\\"form-check form-check-inline\\">
|
||||
<span class=\\"custom-control custom-switch\\">
|
||||
<input type=\\"checkbox\\"
|
||||
id=\\"configuration-theme\\"
|
||||
<input id=\\"configuration-theme\\"
|
||||
class=\\"custom-control-input\\"
|
||||
type=\\"checkbox\\"
|
||||
value
|
||||
>
|
||||
<label class=\\"custom-control-label cursor-pointer mr-3\\"
|
||||
@@ -189,7 +189,7 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Minimal alert group width
|
||||
@@ -213,7 +213,7 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible__contentOuter collapse show\\"
|
||||
style=\\"height:auto;-webkit-transition:none;-ms-transition:none;transition:none;overflow:visible\\"
|
||||
style=\\"height: auto; transition: none; overflow: visible;\\"
|
||||
>
|
||||
<div class=\\"Collapsible__contentInner card-body my-2\\">
|
||||
<div class=\\"form-group mb-0 text-center\\">
|
||||
@@ -226,12 +226,12 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
<div class=\\"input-range__track input-range__track--background\\">
|
||||
<div style=\\"left:0%;width:24%\\"
|
||||
<div style=\\"left: 0%; width: 24%;\\"
|
||||
class=\\"input-range__track input-range__track--active\\"
|
||||
>
|
||||
</div>
|
||||
<span class=\\"input-range__slider-container\\"
|
||||
style=\\"position:absolute;left:24%\\"
|
||||
style=\\"position: absolute; left: 24%;\\"
|
||||
>
|
||||
<span class=\\"input-range__label input-range__label--value\\">
|
||||
<span class=\\"input-range__label-container\\">
|
||||
@@ -260,7 +260,7 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Default number of alerts to show per group
|
||||
@@ -284,7 +284,7 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible__contentOuter collapse show\\"
|
||||
style=\\"height:auto;-webkit-transition:none;-ms-transition:none;transition:none;overflow:visible\\"
|
||||
style=\\"height: auto; transition: none; overflow: visible;\\"
|
||||
>
|
||||
<div class=\\"Collapsible__contentInner card-body my-2\\">
|
||||
<div class=\\"form-group mb-0 text-center\\">
|
||||
@@ -297,12 +297,12 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
<div class=\\"input-range__track input-range__track--background\\">
|
||||
<div style=\\"left:0%;width:44.44444444444444%\\"
|
||||
<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%\\"
|
||||
style=\\"position: absolute; left: 44.44444444444444%;\\"
|
||||
>
|
||||
<span class=\\"input-range__label input-range__label--value\\">
|
||||
<span class=\\"input-range__label-container\\">
|
||||
@@ -331,7 +331,7 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Default alert group display
|
||||
@@ -355,32 +355,32 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible__contentOuter collapse show\\"
|
||||
style=\\"height:auto;-webkit-transition:none;-ms-transition:none;transition:none;overflow:visible\\"
|
||||
style=\\"height: auto; transition: none; overflow: visible;\\"
|
||||
>
|
||||
<div class=\\"Collapsible__contentInner card-body my-2\\">
|
||||
<div class=\\"form-group mb-0\\">
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<div class=\\"react-select__control css-au8pbo-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-pb81dw\\">
|
||||
<div class=\\"react-select__single-value css-1uccc91-singleValue\\">
|
||||
<div class=\\"react-select__control css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-97xgis\\">
|
||||
<div class=\\"react-select__single-value css-1wh03ml-singleValue\\">
|
||||
Collapse on mobile
|
||||
</div>
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
<div class=\\"react-select__input\\"
|
||||
style=\\"display:inline-block\\"
|
||||
style=\\"display: inline-block;\\"
|
||||
>
|
||||
<input type=\\"text\\"
|
||||
autocapitalize=\\"none\\"
|
||||
<input autocapitalize=\\"none\\"
|
||||
autocomplete=\\"off\\"
|
||||
autocorrect=\\"off\\"
|
||||
id=\\"react-select-configuration-collapse-input\\"
|
||||
spellcheck=\\"false\\"
|
||||
tabindex=\\"0\\"
|
||||
value
|
||||
type=\\"text\\"
|
||||
aria-autocomplete=\\"list\\"
|
||||
style=\\"box-sizing:content-box;width:1px;label:input;background:0;border:0;font-size:inherit;opacity:1;outline:0;padding:0;color:inherit\\"
|
||||
style=\\"box-sizing: content-box; width: 2px; border: 0px; font-size: inherit; opacity: 1; outline: 0; padding: 0px;\\"
|
||||
value
|
||||
>
|
||||
<div style=\\"position:absolute;top:0;left:0;visibility:hidden;height:0;overflow:scroll;white-space:pre\\">
|
||||
<div style=\\"position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-size: inherit; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;\\">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -410,7 +410,7 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Grid sort order
|
||||
@@ -434,34 +434,34 @@ exports[`<Configuration /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible__contentOuter collapse show\\"
|
||||
style=\\"height:auto;-webkit-transition:none;-ms-transition:none;transition:none;overflow:visible\\"
|
||||
style=\\"height: auto; transition: none; overflow: visible;\\"
|
||||
>
|
||||
<div class=\\"Collapsible__contentInner card-body my-2\\">
|
||||
<div class=\\"form-group mb-0\\">
|
||||
<div class=\\"d-flex flex-fill flex-lg-row flex-column justify-content-between\\">
|
||||
<div class=\\"flex-shrink-0 flex-grow-1 flex-basis-auto\\">
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<div class=\\"react-select__control css-au8pbo-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-pb81dw\\">
|
||||
<div class=\\"react-select__single-value css-1uccc91-singleValue\\">
|
||||
<div class=\\"react-select__control css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-97xgis\\">
|
||||
<div class=\\"react-select__single-value css-1wh03ml-singleValue\\">
|
||||
Use defaults from karma config file
|
||||
</div>
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
<div class=\\"react-select__input\\"
|
||||
style=\\"display:inline-block\\"
|
||||
style=\\"display: inline-block;\\"
|
||||
>
|
||||
<input type=\\"text\\"
|
||||
autocapitalize=\\"none\\"
|
||||
<input autocapitalize=\\"none\\"
|
||||
autocomplete=\\"off\\"
|
||||
autocorrect=\\"off\\"
|
||||
id=\\"react-select-configuration-sort-order-input\\"
|
||||
spellcheck=\\"false\\"
|
||||
tabindex=\\"0\\"
|
||||
value
|
||||
type=\\"text\\"
|
||||
aria-autocomplete=\\"list\\"
|
||||
style=\\"box-sizing:content-box;width:1px;label:input;background:0;border:0;font-size:inherit;opacity:1;outline:0;padding:0;color:inherit\\"
|
||||
style=\\"box-sizing: content-box; width: 2px; border: 0px; font-size: inherit; opacity: 1; outline: 0; padding: 0px;\\"
|
||||
value
|
||||
>
|
||||
<div style=\\"position:absolute;top:0;left:0;visibility:hidden;height:0;overflow:scroll;white-space:pre\\">
|
||||
<div style=\\"position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-size: inherit; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;\\">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
import React from "react";
|
||||
|
||||
import { shallow } from "enzyme";
|
||||
import { mount } from "enzyme";
|
||||
|
||||
import toDiffableHtml from "diffable-html";
|
||||
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { ReactSelectColors, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { Configuration } from ".";
|
||||
|
||||
describe("<Configuration />", () => {
|
||||
it("matches snapshot", () => {
|
||||
const settingsStore = new Settings();
|
||||
const tree = shallow(
|
||||
<Configuration settingsStore={settingsStore} defaultIsOpen={true} />
|
||||
const tree = mount(
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Light)
|
||||
}}
|
||||
>
|
||||
<Configuration settingsStore={settingsStore} defaultIsOpen={true} />
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -6,6 +6,8 @@ import toDiffableHtml from "diffable-html";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { ReactSelectColors, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { MainModalContent } from "./MainModalContent";
|
||||
|
||||
let alertStore;
|
||||
@@ -22,14 +24,26 @@ afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
const Wrapped = component => (
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Light)
|
||||
}}
|
||||
>
|
||||
{component}
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
|
||||
const FakeModal = () => {
|
||||
return mount(
|
||||
<MainModalContent
|
||||
alertStore={alertStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={onHide}
|
||||
expandAllOptions={true}
|
||||
/>
|
||||
Wrapped(
|
||||
<MainModalContent
|
||||
alertStore={alertStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={onHide}
|
||||
expandAllOptions={true}
|
||||
/>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -51,12 +65,14 @@ describe("<MainModalContent />", () => {
|
||||
// https://github.com/airbnb/enzyme/issues/1213
|
||||
const tree = mount(
|
||||
<span>
|
||||
<MainModalContent
|
||||
alertStore={alertStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={onHide}
|
||||
expandAllOptions={true}
|
||||
/>
|
||||
{Wrapped(
|
||||
<MainModalContent
|
||||
alertStore={alertStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={onHide}
|
||||
expandAllOptions={true}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
|
||||
@@ -4,7 +4,7 @@ exports[`<Help /> matches snapshot 1`] = `
|
||||
"
|
||||
<div class=\\"accordion\\">
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Fiter operators
|
||||
@@ -163,7 +163,7 @@ exports[`<Help /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Filtering using alert labels
|
||||
@@ -339,7 +339,7 @@ exports[`<Help /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Filtering alerts using special filters
|
||||
|
||||
@@ -23,7 +23,7 @@ exports[`<MainModalContent /> matches snapshot 1`] = `
|
||||
<div class=\\"modal-body\\">
|
||||
<form class=\\"px-3 accordion\\">
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Refresh interval
|
||||
@@ -94,7 +94,7 @@ exports[`<MainModalContent /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Filter bar configuration
|
||||
@@ -142,7 +142,7 @@ exports[`<MainModalContent /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Theme
|
||||
@@ -208,7 +208,7 @@ exports[`<MainModalContent /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Minimal alert group width
|
||||
@@ -279,7 +279,7 @@ exports[`<MainModalContent /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Default number of alerts to show per group
|
||||
@@ -350,7 +350,7 @@ exports[`<MainModalContent /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Default alert group display
|
||||
@@ -379,9 +379,9 @@ exports[`<MainModalContent /> matches snapshot 1`] = `
|
||||
<div class=\\"Collapsible__contentInner card-body my-2\\">
|
||||
<div class=\\"form-group mb-0\\">
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<div class=\\"react-select__control css-au8pbo-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-pb81dw\\">
|
||||
<div class=\\"react-select__single-value css-1uccc91-singleValue\\">
|
||||
<div class=\\"react-select__control css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-97xgis\\">
|
||||
<div class=\\"react-select__single-value css-1wh03ml-singleValue\\">
|
||||
Collapse on mobile
|
||||
</div>
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
@@ -429,7 +429,7 @@ exports[`<MainModalContent /> matches snapshot 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"Collapsible card\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer bg-light\\">
|
||||
<div class=\\"Collapsible__trigger is-open card-header cursor-pointer\\">
|
||||
<div class=\\"d-flex flex-row justify-content-between\\">
|
||||
<div>
|
||||
Grid sort order
|
||||
@@ -460,9 +460,9 @@ exports[`<MainModalContent /> matches snapshot 1`] = `
|
||||
<div class=\\"d-flex flex-fill flex-lg-row flex-column justify-content-between\\">
|
||||
<div class=\\"flex-shrink-0 flex-grow-1 flex-basis-auto\\">
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<div class=\\"react-select__control css-au8pbo-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-pb81dw\\">
|
||||
<div class=\\"react-select__single-value css-1uccc91-singleValue\\">
|
||||
<div class=\\"react-select__control css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-97xgis\\">
|
||||
<div class=\\"react-select__single-value css-1wh03ml-singleValue\\">
|
||||
Use defaults from karma config file
|
||||
</div>
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
|
||||
@@ -62,7 +62,7 @@ const MainModal = observer(
|
||||
<Modal isOpen={this.toggle.show} toggleOpen={this.toggle.toggle}>
|
||||
<React.Suspense
|
||||
fallback={
|
||||
<h1 className="display-1 text-secondary p-5 m-auto">
|
||||
<h1 className="display-1 text-placeholder p-5 m-auto">
|
||||
<FontAwesomeIcon icon={faSpinner} size="lg" spin />
|
||||
</h1>
|
||||
}
|
||||
|
||||
@@ -4,9 +4,11 @@ import { storiesOf } from "@storybook/react";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { ReactSelectColors, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { MainModalContent, TabNames } from "./MainModalContent";
|
||||
|
||||
import "Percy.scss";
|
||||
import "Styles/Percy.scss";
|
||||
|
||||
storiesOf("MainModal", module)
|
||||
.addDecorator(storyFn => (
|
||||
@@ -20,13 +22,19 @@ storiesOf("MainModal", module)
|
||||
const alertStore = new AlertStore([]);
|
||||
const settingsStore = new Settings();
|
||||
return (
|
||||
<MainModalContent
|
||||
alertStore={alertStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={() => {}}
|
||||
isVisible={true}
|
||||
expandAllOptions={true}
|
||||
/>
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Light)
|
||||
}}
|
||||
>
|
||||
<MainModalContent
|
||||
alertStore={alertStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={() => {}}
|
||||
isVisible={true}
|
||||
expandAllOptions={true}
|
||||
/>
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
})
|
||||
.add("Help", () => {
|
||||
|
||||
@@ -4,6 +4,8 @@ import { mount } from "enzyme";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { ReactSelectColors, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { MainModal } from ".";
|
||||
|
||||
let alertStore;
|
||||
@@ -20,7 +22,13 @@ beforeEach(() => {
|
||||
|
||||
const MountedMainModal = () => {
|
||||
return mount(
|
||||
<MainModal alertStore={alertStore} settingsStore={settingsStore} />
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Light)
|
||||
}}
|
||||
>
|
||||
<MainModal alertStore={alertStore} settingsStore={settingsStore} />
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -245,7 +245,7 @@ const DeleteSilenceModalContent = observer(
|
||||
<div className="d-flex flex-row-reverse">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline-danger mr-2"
|
||||
className="btn btn-danger mr-2"
|
||||
onClick={this.onDelete}
|
||||
disabled={
|
||||
this.deleteState.fetch !== null &&
|
||||
@@ -299,7 +299,7 @@ const DeleteSilence = observer(
|
||||
return (
|
||||
<React.Fragment>
|
||||
<button
|
||||
className="btn btn-outline-danger btn-sm"
|
||||
className="btn btn-danger btn-sm"
|
||||
onClick={this.toggle.toggle}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
|
||||
@@ -86,7 +86,7 @@ const VerifyResponse = async response => {
|
||||
await expect(tree.instance().previewState.fetch).resolves.toBeUndefined();
|
||||
|
||||
fetch.mockResponseOnce(JSON.stringify(response));
|
||||
tree.find(".btn-outline-danger").simulate("click");
|
||||
tree.find(".btn-danger").simulate("click");
|
||||
await expect(tree.instance().deleteState.fetch).resolves.toBeUndefined();
|
||||
|
||||
return tree;
|
||||
@@ -211,7 +211,7 @@ describe("<DeleteSilenceModalContent />", () => {
|
||||
expect(fetch.mock.calls[1][1]).toMatchObject({ method: "DELETE" });
|
||||
|
||||
expect(fetch.mock.calls).toHaveLength(2);
|
||||
tree.find(".btn-outline-danger").simulate("click");
|
||||
tree.find(".btn-danger").simulate("click");
|
||||
expect(fetch.mock.calls).toHaveLength(2);
|
||||
tree.instance().onDelete();
|
||||
expect(fetch.mock.calls).toHaveLength(2);
|
||||
@@ -249,7 +249,7 @@ describe("<DeleteSilenceModalContent />", () => {
|
||||
fetch.resetMocks();
|
||||
fetch.mockReject("Fetch error");
|
||||
|
||||
tree.find(".btn-outline-danger").simulate("click");
|
||||
tree.find(".btn-danger").simulate("click");
|
||||
await expect(tree.instance().deleteState.fetch).resolves.toBeUndefined();
|
||||
|
||||
tree.update();
|
||||
@@ -265,7 +265,7 @@ describe("<DeleteSilenceModalContent />", () => {
|
||||
fetch.resetMocks();
|
||||
fetch.mockResponseOnce("500 Internal Server Error", { status: 500 });
|
||||
|
||||
tree.find(".btn-outline-danger").simulate("click");
|
||||
tree.find(".btn-danger").simulate("click");
|
||||
await expect(tree.instance().deleteState.fetch).resolves.toBeUndefined();
|
||||
|
||||
tree.update();
|
||||
|
||||
@@ -153,7 +153,7 @@ const SilenceDetails = ({
|
||||
<div className="flex-shrink-0 flex-grow-0 mt-lg-0 mt-2 ml-lg-2 ml-0">
|
||||
<div className="d-flex flex-fill flex-lg-column flex-row justify-content-around">
|
||||
<button
|
||||
className="btn btn-outline-secondary btn-sm mb-lg-2 mb-0"
|
||||
className="btn btn-primary btn-sm mb-lg-2 mb-0"
|
||||
onClick={onEditSilence}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
|
||||
@@ -76,7 +76,7 @@ const SilenceProgress = observer(
|
||||
return (
|
||||
<span className="badge badge-light nmb-05 align-text-bottom p-1">
|
||||
Expires <Moment fromNow>{silence.endsAt}</Moment>
|
||||
<div className="progress silence-progress bg-white">
|
||||
<div className="progress silence-progress">
|
||||
<div
|
||||
className={progressClass}
|
||||
role="progressbar"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.silence-progress.progress {
|
||||
.badge > .silence-progress.progress {
|
||||
height: 2px;
|
||||
margin-top: 0.125rem;
|
||||
}
|
||||
|
||||
.silence-progress.progress > .progress-bar {
|
||||
.badge > .silence-progress.progress > .progress-bar {
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ exports[`<ManagedSilence /> matches snapshot when collapsed 1`] = `
|
||||
<time datetime=\\"946688400000\\">
|
||||
in 30 minutes
|
||||
</time>
|
||||
<div class=\\"progress silence-progress bg-white\\">
|
||||
<div class=\\"progress silence-progress\\">
|
||||
<div class=\\"progress-bar bg-success\\"
|
||||
role=\\"progressbar\\"
|
||||
style=\\"width: 50%;\\"
|
||||
@@ -313,7 +313,7 @@ exports[`<ManagedSilence /> matches snapshot with expaned details 1`] = `
|
||||
</div>
|
||||
<div class=\\"flex-shrink-0 flex-grow-0 mt-lg-0 mt-2 ml-lg-2 ml-0\\">
|
||||
<div class=\\"d-flex flex-fill flex-lg-column flex-row justify-content-around\\">
|
||||
<button class=\\"btn btn-outline-secondary btn-sm mb-lg-2 mb-0\\">
|
||||
<button class=\\"btn btn-primary btn-sm mb-lg-2 mb-0\\">
|
||||
<svg aria-hidden=\\"true\\"
|
||||
focusable=\\"false\\"
|
||||
data-prefix=\\"fas\\"
|
||||
@@ -330,7 +330,7 @@ exports[`<ManagedSilence /> matches snapshot with expaned details 1`] = `
|
||||
</svg>
|
||||
Edit
|
||||
</button>
|
||||
<button class=\\"btn btn-outline-danger btn-sm\\">
|
||||
<button class=\\"btn btn-danger btn-sm\\">
|
||||
<svg aria-hidden=\\"true\\"
|
||||
focusable=\\"false\\"
|
||||
data-prefix=\\"fas\\"
|
||||
|
||||
@@ -11,8 +11,6 @@ import { MountFade } from "Components/Animations/MountFade";
|
||||
import { SilenceComment } from "./SilenceComment";
|
||||
import { SilenceDetails } from "./SilenceDetails";
|
||||
|
||||
import "./index.scss";
|
||||
|
||||
const ManagedSilence = observer(
|
||||
class ManagedSilence extends Component {
|
||||
static propTypes = {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { ManagedSilence } from ".";
|
||||
|
||||
import "Percy.scss";
|
||||
import "Styles/Percy.scss";
|
||||
|
||||
storiesOf("ManagedSilence", module)
|
||||
.addDecorator(storyFn => (
|
||||
|
||||
@@ -106,7 +106,7 @@ describe("<ManagedSilence />", () => {
|
||||
tree.instance().collapse.toggle();
|
||||
tree.update();
|
||||
|
||||
const button = tree.find(".btn-outline-secondary");
|
||||
const button = tree.find(".btn-primary");
|
||||
expect(button.text()).toBe("Edit");
|
||||
});
|
||||
|
||||
@@ -115,7 +115,7 @@ describe("<ManagedSilence />", () => {
|
||||
tree.instance().collapse.toggle();
|
||||
tree.update();
|
||||
|
||||
const button = tree.find(".btn-outline-danger");
|
||||
const button = tree.find(".btn-danger");
|
||||
expect(button.text()).toBe("Delete");
|
||||
});
|
||||
|
||||
@@ -125,7 +125,7 @@ describe("<ManagedSilence />", () => {
|
||||
tree.instance().collapse.toggle();
|
||||
tree.update();
|
||||
|
||||
const button = tree.find(".btn-outline-secondary");
|
||||
const button = tree.find(".btn-primary");
|
||||
expect(button.text()).toBe("Recreate");
|
||||
});
|
||||
|
||||
@@ -136,7 +136,7 @@ describe("<ManagedSilence />", () => {
|
||||
|
||||
expect(silenceFormStore.data.silenceID).toBeNull();
|
||||
|
||||
const button = tree.find(".btn-outline-secondary");
|
||||
const button = tree.find(".btn-primary");
|
||||
expect(button.text()).toBe("Edit");
|
||||
|
||||
const fillSpy = jest.spyOn(silenceFormStore.data, "fillFormFromSilence");
|
||||
|
||||
@@ -14,8 +14,6 @@ import {
|
||||
MountModalBackdrop
|
||||
} from "Components/Animations/MountModal";
|
||||
|
||||
import "./index.scss";
|
||||
|
||||
const Modal = observer(
|
||||
class Modal extends Component {
|
||||
static propTypes = {
|
||||
@@ -84,7 +82,12 @@ const Modal = observer(
|
||||
|
||||
return ReactDOM.createPortal(
|
||||
<React.Fragment>
|
||||
<MountModal in={isOpen} unmountOnExit {...props}>
|
||||
<MountModal
|
||||
in={isOpen}
|
||||
unmountOnExit
|
||||
className="modal-open"
|
||||
{...props}
|
||||
>
|
||||
<HotKeys
|
||||
innerRef={this.HotKeysRef}
|
||||
keyMap={{ CLOSE: "Escape" }}
|
||||
|
||||
@@ -1,6 +1,57 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<CustomMultiSelect /> matches snapshot when focused 1`] = `
|
||||
exports[`<MultiSelect /> matches snapshot without any extra props 1`] = `
|
||||
"
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<div class=\\"react-select__control css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container css-97xgis\\">
|
||||
<div class=\\"react-select__placeholder css-1wa3eu0-placeholder\\">
|
||||
Select...
|
||||
</div>
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
<div class=\\"react-select__input\\"
|
||||
style=\\"display: inline-block;\\"
|
||||
>
|
||||
<input autocapitalize=\\"none\\"
|
||||
autocomplete=\\"off\\"
|
||||
autocorrect=\\"off\\"
|
||||
id=\\"react-select-2-input\\"
|
||||
spellcheck=\\"false\\"
|
||||
tabindex=\\"0\\"
|
||||
type=\\"text\\"
|
||||
aria-autocomplete=\\"list\\"
|
||||
style=\\"box-sizing: content-box; width: 2px; border: 0px; font-size: inherit; opacity: 1; outline: 0; padding: 0px;\\"
|
||||
value
|
||||
>
|
||||
<div style=\\"position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-size: inherit; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;\\">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"react-select__indicators css-vcwr3k-IndicatorsContainer\\">
|
||||
<span class=\\"react-select__indicator-separator css-1okebmr-indicatorSeparator\\">
|
||||
</span>
|
||||
<div aria-hidden=\\"true\\"
|
||||
class=\\"react-select__indicator react-select__dropdown-indicator css-tlfecz-indicatorContainer\\"
|
||||
>
|
||||
<svg height=\\"20\\"
|
||||
width=\\"20\\"
|
||||
viewbox=\\"0 0 20 20\\"
|
||||
aria-hidden=\\"true\\"
|
||||
focusable=\\"false\\"
|
||||
class=\\"css-6q0nyr-Svg\\"
|
||||
>
|
||||
<path d=\\"M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z\\">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<WrappedCustomMultiSelect /> matches snapshot when focused 1`] = `
|
||||
"
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<span aria-live=\\"polite\\"
|
||||
@@ -13,8 +64,8 @@ exports[`<CustomMultiSelect /> matches snapshot when focused 1`] = `
|
||||
0 results available. Select is focused ,type to refine list, press Down to open the menu,
|
||||
</p>
|
||||
</span>
|
||||
<div class=\\"react-select__control react-select__control--is-focused css-nmfa0p-control\\">
|
||||
<div class=\\"react-select__value-container css-pb81dw\\">
|
||||
<div class=\\"react-select__control react-select__control--is-focused css-11rrdhm-control\\">
|
||||
<div class=\\"react-select__value-container css-97xgis\\">
|
||||
<div class=\\"react-select__placeholder css-1wa3eu0-placeholder\\">
|
||||
Select...
|
||||
</div>
|
||||
@@ -25,7 +76,7 @@ exports[`<CustomMultiSelect /> matches snapshot when focused 1`] = `
|
||||
<input autocapitalize=\\"none\\"
|
||||
autocomplete=\\"off\\"
|
||||
autocorrect=\\"off\\"
|
||||
id=\\"react-select-4-input\\"
|
||||
id=\\"react-select-5-input\\"
|
||||
spellcheck=\\"false\\"
|
||||
tabindex=\\"0\\"
|
||||
type=\\"text\\"
|
||||
@@ -61,30 +112,30 @@ exports[`<CustomMultiSelect /> matches snapshot when focused 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<CustomMultiSelect /> matches snapshot with a value 1`] = `
|
||||
exports[`<WrappedCustomMultiSelect /> matches snapshot with a value 1`] = `
|
||||
"
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<div class=\\"react-select__control css-au8pbo-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-pb81dw\\">
|
||||
<div class=\\"react-select__single-value css-1uccc91-singleValue\\">
|
||||
<div class=\\"react-select__control css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-97xgis\\">
|
||||
<div class=\\"react-select__single-value css-1wh03ml-singleValue\\">
|
||||
foo
|
||||
</div>
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
<div class=\\"react-select__input\\"
|
||||
style=\\"display:inline-block\\"
|
||||
style=\\"display: inline-block;\\"
|
||||
>
|
||||
<input type=\\"text\\"
|
||||
autocapitalize=\\"none\\"
|
||||
<input autocapitalize=\\"none\\"
|
||||
autocomplete=\\"off\\"
|
||||
autocorrect=\\"off\\"
|
||||
id=\\"react-select-5-input\\"
|
||||
id=\\"react-select-6-input\\"
|
||||
spellcheck=\\"false\\"
|
||||
tabindex=\\"0\\"
|
||||
value
|
||||
type=\\"text\\"
|
||||
aria-autocomplete=\\"list\\"
|
||||
style=\\"box-sizing:content-box;width:1px;label:input;background:0;border:0;font-size:inherit;opacity:1;outline:0;padding:0;color:inherit\\"
|
||||
style=\\"box-sizing: content-box; width: 2px; border: 0px; font-size: inherit; opacity: 1; outline: 0; padding: 0px;\\"
|
||||
value
|
||||
>
|
||||
<div style=\\"position:absolute;top:0;left:0;visibility:hidden;height:0;overflow:scroll;white-space:pre\\">
|
||||
<div style=\\"position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-size: inherit; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;\\">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -112,30 +163,30 @@ exports[`<CustomMultiSelect /> matches snapshot with a value 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<CustomMultiSelect /> matches snapshot with defaults 1`] = `
|
||||
exports[`<WrappedCustomMultiSelect /> matches snapshot with defaults 1`] = `
|
||||
"
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<div class=\\"react-select__control css-au8pbo-control\\">
|
||||
<div class=\\"react-select__value-container css-pb81dw\\">
|
||||
<div class=\\"react-select__control css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container css-97xgis\\">
|
||||
<div class=\\"react-select__placeholder css-1wa3eu0-placeholder\\">
|
||||
Select...
|
||||
</div>
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
<div class=\\"react-select__input\\"
|
||||
style=\\"display:inline-block\\"
|
||||
style=\\"display: inline-block;\\"
|
||||
>
|
||||
<input type=\\"text\\"
|
||||
autocapitalize=\\"none\\"
|
||||
<input autocapitalize=\\"none\\"
|
||||
autocomplete=\\"off\\"
|
||||
autocorrect=\\"off\\"
|
||||
id=\\"react-select-2-input\\"
|
||||
id=\\"react-select-3-input\\"
|
||||
spellcheck=\\"false\\"
|
||||
tabindex=\\"0\\"
|
||||
value
|
||||
type=\\"text\\"
|
||||
aria-autocomplete=\\"list\\"
|
||||
style=\\"box-sizing:content-box;width:1px;label:input;background:0;border:0;font-size:inherit;opacity:1;outline:0;padding:0;color:inherit\\"
|
||||
style=\\"box-sizing: content-box; width: 2px; border: 0px; font-size: inherit; opacity: 1; outline: 0; padding: 0px;\\"
|
||||
value
|
||||
>
|
||||
<div style=\\"position:absolute;top:0;left:0;visibility:hidden;height:0;overflow:scroll;white-space:pre\\">
|
||||
<div style=\\"position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-size: inherit; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;\\">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -163,36 +214,36 @@ exports[`<CustomMultiSelect /> matches snapshot with defaults 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<CustomMultiSelect /> matches snapshot with isDisabled=true 1`] = `
|
||||
exports[`<WrappedCustomMultiSelect /> matches snapshot with isDisabled=true 1`] = `
|
||||
"
|
||||
<div class=\\"react-select--is-disabled css-14jk2my-container\\">
|
||||
<div class=\\"react-select__control react-select__control--is-disabled css-jk9kg2-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-57r8lk\\">
|
||||
<div class=\\"react-select__single-value react-select__single-value--is-disabled css-107lb6w-singleValue\\">
|
||||
<div class=\\"react-select__control react-select__control--is-disabled css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-97xgis\\">
|
||||
<div class=\\"react-select__single-value react-select__single-value--is-disabled css-1wh03ml-singleValue\\">
|
||||
foo
|
||||
</div>
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
<div class=\\"react-select__input\\"
|
||||
style=\\"display:inline-block\\"
|
||||
style=\\"display: inline-block;\\"
|
||||
>
|
||||
<input type=\\"text\\"
|
||||
disabled
|
||||
<input disabled
|
||||
autocapitalize=\\"none\\"
|
||||
autocomplete=\\"off\\"
|
||||
autocorrect=\\"off\\"
|
||||
id=\\"react-select-7-input\\"
|
||||
id=\\"react-select-8-input\\"
|
||||
spellcheck=\\"false\\"
|
||||
tabindex=\\"0\\"
|
||||
value
|
||||
type=\\"text\\"
|
||||
aria-autocomplete=\\"list\\"
|
||||
style=\\"box-sizing:content-box;width:1px;label:input;background:0;border:0;font-size:inherit;opacity:1;outline:0;padding:0;color:inherit\\"
|
||||
style=\\"box-sizing: content-box; width: 2px; border: 0px; font-size: inherit; opacity: 1; outline: 0; padding: 0px;\\"
|
||||
value
|
||||
>
|
||||
<div style=\\"position:absolute;top:0;left:0;visibility:hidden;height:0;overflow:scroll;white-space:pre\\">
|
||||
<div style=\\"position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-size: inherit; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;\\">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"react-select__indicators css-15bdmde-IndicatorsContainer\\">
|
||||
<div class=\\"react-select__indicators css-vcwr3k-IndicatorsContainer\\">
|
||||
<span class=\\"react-select__indicator-separator css-109onse-indicatorSeparator\\">
|
||||
</span>
|
||||
<div aria-hidden=\\"true\\"
|
||||
@@ -215,30 +266,30 @@ exports[`<CustomMultiSelect /> matches snapshot with isDisabled=true 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<CustomMultiSelect /> matches snapshot with isMulti=true 1`] = `
|
||||
exports[`<WrappedCustomMultiSelect /> matches snapshot with isMulti=true 1`] = `
|
||||
"
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<div class=\\"react-select__control css-au8pbo-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--is-multi css-10war8y\\">
|
||||
<div class=\\"react-select__control css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--is-multi css-nj4yyx\\">
|
||||
<div class=\\"react-select__placeholder css-1wa3eu0-placeholder\\">
|
||||
Select...
|
||||
</div>
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
<div class=\\"react-select__input\\"
|
||||
style=\\"display:inline-block\\"
|
||||
style=\\"display: inline-block;\\"
|
||||
>
|
||||
<input type=\\"text\\"
|
||||
autocapitalize=\\"none\\"
|
||||
<input autocapitalize=\\"none\\"
|
||||
autocomplete=\\"off\\"
|
||||
autocorrect=\\"off\\"
|
||||
id=\\"react-select-3-input\\"
|
||||
id=\\"react-select-4-input\\"
|
||||
spellcheck=\\"false\\"
|
||||
tabindex=\\"0\\"
|
||||
value
|
||||
type=\\"text\\"
|
||||
aria-autocomplete=\\"list\\"
|
||||
style=\\"box-sizing:content-box;width:1px;label:input;background:0;border:0;font-size:inherit;opacity:1;outline:0;padding:0;color:inherit\\"
|
||||
style=\\"box-sizing: content-box; width: 2px; border: 0px; font-size: inherit; opacity: 1; outline: 0; padding: 0px;\\"
|
||||
value
|
||||
>
|
||||
<div style=\\"position:absolute;top:0;left:0;visibility:hidden;height:0;overflow:scroll;white-space:pre\\">
|
||||
<div style=\\"position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-size: inherit; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;\\">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -266,11 +317,11 @@ exports[`<CustomMultiSelect /> matches snapshot with isMulti=true 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`<CustomMultiSelect /> matches snapshot with isMulti=true and a value 1`] = `
|
||||
exports[`<WrappedCustomMultiSelect /> matches snapshot with isMulti=true and a value 1`] = `
|
||||
"
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<div class=\\"react-select__control css-au8pbo-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--is-multi react-select__value-container--has-value css-10war8y\\">
|
||||
<div class=\\"react-select__control css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--is-multi react-select__value-container--has-value css-nj4yyx\\">
|
||||
<div class=\\"css-owt1hd-multiValue react-select__multi-value\\">
|
||||
<div class=\\"css-xbn6jz react-select__multi-value__label\\">
|
||||
foo
|
||||
@@ -290,20 +341,20 @@ exports[`<CustomMultiSelect /> matches snapshot with isMulti=true and a value 1`
|
||||
</div>
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
<div class=\\"react-select__input\\"
|
||||
style=\\"display:inline-block\\"
|
||||
style=\\"display: inline-block;\\"
|
||||
>
|
||||
<input type=\\"text\\"
|
||||
autocapitalize=\\"none\\"
|
||||
<input autocapitalize=\\"none\\"
|
||||
autocomplete=\\"off\\"
|
||||
autocorrect=\\"off\\"
|
||||
id=\\"react-select-6-input\\"
|
||||
id=\\"react-select-7-input\\"
|
||||
spellcheck=\\"false\\"
|
||||
tabindex=\\"0\\"
|
||||
value
|
||||
type=\\"text\\"
|
||||
aria-autocomplete=\\"list\\"
|
||||
style=\\"box-sizing:content-box;width:1px;label:input;background:0;border:0;font-size:inherit;opacity:1;outline:0;padding:0;color:inherit\\"
|
||||
style=\\"box-sizing: content-box; width: 2px; border: 0px; font-size: inherit; opacity: 1; outline: 0; padding: 0px;\\"
|
||||
value
|
||||
>
|
||||
<div style=\\"position:absolute;top:0;left:0;visibility:hidden;height:0;overflow:scroll;white-space:pre\\">
|
||||
<div style=\\"position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-size: inherit; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;\\">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,32 +2,65 @@ import React from "react";
|
||||
|
||||
import Creatable from "react-select/creatable";
|
||||
|
||||
const ReactSelectStyles = {
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
|
||||
const ReactSelectColors = {
|
||||
Light: {
|
||||
color: "#fff",
|
||||
singleValueColor: "#000",
|
||||
backgroundColor: "#fff",
|
||||
borderColor: "#ced4da",
|
||||
focusedBoxShadow: "rgba(69, 90, 100, 0.25)",
|
||||
focusedBorderColor: "#819ba8",
|
||||
menuBackground: "#fff",
|
||||
optionHoverBackground: "#455a64",
|
||||
valueContainerBackground: "#fff",
|
||||
disabledValueContainerBackground: "#fff"
|
||||
},
|
||||
Dark: {
|
||||
color: "#fff",
|
||||
singleValueColor: "#fff",
|
||||
backgroundColor: "#fff",
|
||||
borderColor: "#444",
|
||||
focusedBoxShadow: "rgba(69, 90, 100, 0.25)",
|
||||
focusedBorderColor: "#819ba8",
|
||||
menuBackground: "#222",
|
||||
optionHoverBackground: "#455a64",
|
||||
valueContainerBackground: "#444",
|
||||
disabledValueContainerBackground: "#fff"
|
||||
}
|
||||
};
|
||||
|
||||
const ReactSelectStyles = theme => ({
|
||||
control: (base, state) =>
|
||||
state.isFocused
|
||||
? {
|
||||
...base,
|
||||
backgroundColor: theme.backgroundColor,
|
||||
outline: "0",
|
||||
outlineOffset: "-2px",
|
||||
boxShadow: "0 0 0 0.2rem rgba(69, 90, 100, 0.25)",
|
||||
boxShadow: `0 0 0 0.2rem ${theme.focusedBoxShadow}`,
|
||||
borderRadius: "0.25rem",
|
||||
borderColor: "#819ba8",
|
||||
borderColor: theme.focusedBorderColor,
|
||||
"&:hover": {
|
||||
borderColor: "#819ba8"
|
||||
borderColor: theme.focusedBorderColor
|
||||
}
|
||||
}
|
||||
: {
|
||||
...base,
|
||||
backgroundColor: "inherit",
|
||||
borderRadius: "0.25rem",
|
||||
borderColor: "#ced4da",
|
||||
"&:hover": { borderColor: "#ced4da" }
|
||||
borderColor: theme.borderColor,
|
||||
"&:hover": { borderColor: theme.borderColor }
|
||||
},
|
||||
valueContainer: (base, state) =>
|
||||
state.isMulti
|
||||
? {
|
||||
...base,
|
||||
borderRadius: "0.25rem",
|
||||
backgroundColor: state.isDisabled ? "#ecf0f1" : "#fff",
|
||||
borderRadius: 0,
|
||||
backgroundColor: state.isDisabled
|
||||
? theme.disabledValueContainerBackground
|
||||
: theme.valueContainerBackground,
|
||||
paddingLeft: "4px",
|
||||
paddingRight: "4px",
|
||||
display: "flex",
|
||||
@@ -38,57 +71,70 @@ const ReactSelectStyles = {
|
||||
}
|
||||
: {
|
||||
...base,
|
||||
borderRadius: "0.25rem",
|
||||
backgroundColor: state.isDisabled ? "#ecf0f1" : "#fff"
|
||||
borderRadius: 0,
|
||||
backgroundColor: state.isDisabled
|
||||
? theme.disabledValueContainerBackground
|
||||
: theme.valueContainerBackground
|
||||
},
|
||||
singleValue: (base, state) => ({
|
||||
...base,
|
||||
color: theme.singleValueColor
|
||||
}),
|
||||
multiValue: (base, state) => ({
|
||||
...base,
|
||||
borderRadius: "4px",
|
||||
backgroundColor: "#455a64",
|
||||
backgroundColor: theme.optionHoverBackground,
|
||||
"&:hover": {
|
||||
backgroundColor: "#455a64"
|
||||
backgroundColor: theme.optionHoverBackground
|
||||
}
|
||||
}),
|
||||
multiValueLabel: (base, state) => ({
|
||||
...base,
|
||||
color: "#fff",
|
||||
color: theme.color,
|
||||
whiteSpace: "normal",
|
||||
wordWrap: "break-word",
|
||||
wordBreak: "break-word",
|
||||
"&:hover": {
|
||||
color: "#fff"
|
||||
color: theme.color
|
||||
}
|
||||
}),
|
||||
multiValueRemove: (base, state) => ({
|
||||
...base,
|
||||
cursor: "pointer",
|
||||
color: "#fff",
|
||||
color: theme.color,
|
||||
backgroundColor: "inherit",
|
||||
opacity: "0.4",
|
||||
borderRadius: "inherit",
|
||||
"&:hover": {
|
||||
color: "#fff",
|
||||
color: theme.color,
|
||||
backgroundColor: "inherit",
|
||||
opacity: "0.75"
|
||||
}
|
||||
}),
|
||||
indicatorsContainer: (base, state) => ({
|
||||
...base,
|
||||
backgroundColor: state.isDisabled ? "#ecf0f1" : "#fff",
|
||||
backgroundColor: state.isDisabled
|
||||
? theme.disabledValueContainerBackground
|
||||
: theme.valueContainerBackground,
|
||||
borderTopRightRadius: "0.25rem",
|
||||
borderBottomRightRadius: "0.25rem"
|
||||
}),
|
||||
menu: (base, state) => ({
|
||||
...base,
|
||||
zIndex: 1500
|
||||
zIndex: 1500,
|
||||
backgroundColor: theme.menuBackground
|
||||
}),
|
||||
option: (base, state) => ({
|
||||
...base,
|
||||
color: "inherit",
|
||||
backgroundColor: "inherit",
|
||||
"&:hover": { color: "#fff", backgroundColor: "#455a64", cursor: "pointer" }
|
||||
"&:hover": {
|
||||
color: theme.color,
|
||||
backgroundColor: theme.optionHoverBackground,
|
||||
cursor: "pointer"
|
||||
}
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
class MultiSelect extends Creatable {
|
||||
renderProps = () => ({});
|
||||
@@ -96,12 +142,13 @@ class MultiSelect extends Creatable {
|
||||
render() {
|
||||
return (
|
||||
<Creatable
|
||||
styles={ReactSelectStyles}
|
||||
styles={this.context.reactSelectStyles}
|
||||
classNamePrefix="react-select"
|
||||
{...this.renderProps()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
MultiSelect.contextType = ThemeContext;
|
||||
|
||||
export { MultiSelect, ReactSelectStyles };
|
||||
export { MultiSelect, ReactSelectStyles, ReactSelectColors };
|
||||
|
||||
@@ -1,17 +1,29 @@
|
||||
import React from "react";
|
||||
|
||||
import { shallow, mount } from "enzyme";
|
||||
import { mount } from "enzyme";
|
||||
|
||||
import toDiffableHtml from "diffable-html";
|
||||
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { ReactSelectColors, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { MultiSelect } from ".";
|
||||
|
||||
const Option = value => ({ label: value, value: value });
|
||||
|
||||
const Wrapped = component => (
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Light)
|
||||
}}
|
||||
>
|
||||
{component}
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
|
||||
describe("<MultiSelect />", () => {
|
||||
it("renders without any extra props", () => {
|
||||
const tree = shallow(<MultiSelect />);
|
||||
expect(tree.text()).toBe("<StateManager />");
|
||||
it("matches snapshot without any extra props", () => {
|
||||
const tree = mount(Wrapped(<MultiSelect />));
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,27 +36,30 @@ class CustomMultiSelect extends MultiSelect {
|
||||
renderProps = () => this.extraProps;
|
||||
}
|
||||
|
||||
describe("<CustomMultiSelect />", () => {
|
||||
const WrappedCustomMultiSelect = props =>
|
||||
Wrapped(<CustomMultiSelect {...props} />);
|
||||
|
||||
describe("<WrappedCustomMultiSelect />", () => {
|
||||
it("matches snapshot with defaults", () => {
|
||||
const tree = shallow(<CustomMultiSelect />);
|
||||
const tree = mount(<WrappedCustomMultiSelect />);
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("matches snapshot with isMulti=true", () => {
|
||||
const tree = shallow(<CustomMultiSelect isMulti />);
|
||||
const tree = mount(<WrappedCustomMultiSelect isMulti />);
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("matches snapshot when focused", () => {
|
||||
// this test is to cover styles state.isFocused conditions
|
||||
const tree = mount(<CustomMultiSelect autoFocus />);
|
||||
const tree = mount(<WrappedCustomMultiSelect autoFocus />);
|
||||
tree.find("input").simulate("focus");
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("matches snapshot with a value", () => {
|
||||
const tree = shallow(
|
||||
<CustomMultiSelect
|
||||
const tree = mount(
|
||||
<WrappedCustomMultiSelect
|
||||
defaultValue={Option("foo")}
|
||||
options={[Option("foo"), Option("bar")]}
|
||||
/>
|
||||
@@ -53,8 +68,8 @@ describe("<CustomMultiSelect />", () => {
|
||||
});
|
||||
|
||||
it("matches snapshot with isMulti=true and a value", () => {
|
||||
const tree = shallow(
|
||||
<CustomMultiSelect
|
||||
const tree = mount(
|
||||
<WrappedCustomMultiSelect
|
||||
isMulti
|
||||
defaultValue={Option("foo")}
|
||||
options={[Option("foo"), Option("bar")]}
|
||||
@@ -64,8 +79,8 @@ describe("<CustomMultiSelect />", () => {
|
||||
});
|
||||
|
||||
it("matches snapshot with isDisabled=true", () => {
|
||||
const tree = shallow(
|
||||
<CustomMultiSelect
|
||||
const tree = mount(
|
||||
<WrappedCustomMultiSelect
|
||||
isDisabled
|
||||
defaultValue={Option("foo")}
|
||||
options={[Option("foo"), Option("bar")]}
|
||||
|
||||
@@ -21,13 +21,13 @@ describe("<FetchIndicator />", () => {
|
||||
it("shows a pause icon when fetching is paused", () => {
|
||||
alertStore.status.pause();
|
||||
const tree = MountedFetchIndicator();
|
||||
expect(tree.html()).toMatch(/fa-pause-circle/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/fa-pause-circle/);
|
||||
});
|
||||
|
||||
it("shows a cirle notch icon when fetching is resumed", () => {
|
||||
alertStore.status.resume();
|
||||
const tree = MountedFetchIndicator();
|
||||
expect(tree.html()).toMatch(/fa-circle-notch/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/fa-circle-notch/);
|
||||
});
|
||||
|
||||
it("opacity is 1 when fetch is in progress", () => {
|
||||
|
||||
@@ -22,8 +22,6 @@ import { Settings } from "Stores/Settings";
|
||||
import { DropdownSlide } from "Components/Animations/DropdownSlide";
|
||||
import { HistoryLabel } from "Components/Labels/HistoryLabel";
|
||||
|
||||
import "./History.css";
|
||||
|
||||
const defaultHistory = {
|
||||
filters: []
|
||||
};
|
||||
@@ -41,7 +39,7 @@ function ReduceFilter(filter) {
|
||||
|
||||
const ActionButton = ({ color, icon, title, action, afterClick }) => (
|
||||
<button
|
||||
className={`component-history-button btn btn-sm btn-outline-${color}`}
|
||||
className={`component-history-button btn btn-sm btn-${color}`}
|
||||
onClick={() => {
|
||||
action();
|
||||
afterClick();
|
||||
@@ -240,7 +238,7 @@ const History = observer(
|
||||
<button
|
||||
ref={ref}
|
||||
onClick={this.collapse.toggle}
|
||||
className="input-group-text border-left-0 border-right-0 border-top-0 rounded-0 bg-transparent text-white cursor-pointer components-navbar-history px-2"
|
||||
className="input-group-text border-left-0 border-right-0 border-top-0 border-light rounded-0 bg-transparent text-white cursor-pointer components-navbar-history px-2"
|
||||
type="button"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
|
||||
@@ -4,7 +4,7 @@ exports[`<FilterInput /> matches snapshot on default render 1`] = `
|
||||
"
|
||||
<div class=\\"input-group w-100 mr-2\\">
|
||||
<div class=\\"input-group-prepend\\">
|
||||
<span class=\\"input-group-text px-2 border-left-0 border-right-0 border-top-0 rounded-0 bg-transparent text-white\\">
|
||||
<span class=\\"input-group-text px-2 border-left-0 border-right-0 border-top-0 border-light rounded-0 bg-transparent text-white\\">
|
||||
<svg aria-hidden=\\"true\\"
|
||||
focusable=\\"false\\"
|
||||
data-prefix=\\"fas\\"
|
||||
@@ -21,7 +21,7 @@ exports[`<FilterInput /> matches snapshot on default render 1`] = `
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class=\\"form-control components-filterinput border-left-0 border-right-0 border-top-0 rounded-0 bg-transparent\\">
|
||||
<div class=\\"form-control components-filterinput border-left-0 border-right-0 border-top-0 border-light rounded-0 bg-transparent\\">
|
||||
<div role=\\"combobox\\"
|
||||
aria-haspopup=\\"listbox\\"
|
||||
aria-owns=\\"react-autowhatever-1\\"
|
||||
@@ -45,7 +45,7 @@ exports[`<FilterInput /> matches snapshot on default render 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div class=\\"input-group-append bg-transparent\\">
|
||||
<button class=\\"input-group-text border-left-0 border-right-0 border-top-0 rounded-0 bg-transparent text-white cursor-pointer components-navbar-history px-2\\"
|
||||
<button class=\\"input-group-text border-left-0 border-right-0 border-top-0 border-light rounded-0 bg-transparent text-white cursor-pointer components-navbar-history px-2\\"
|
||||
type=\\"button\\"
|
||||
data-toggle=\\"dropdown\\"
|
||||
aria-haspopup=\\"true\\"
|
||||
|
||||
@@ -20,8 +20,6 @@ import { FilterInputLabel } from "Components/Labels/FilterInputLabel";
|
||||
import { AutosuggestTheme } from "./Constants";
|
||||
import { History } from "./History";
|
||||
|
||||
import "./index.scss";
|
||||
|
||||
const FilterInput = observer(
|
||||
class FilterInput extends Component {
|
||||
static propTypes = {
|
||||
@@ -154,7 +152,7 @@ const FilterInput = observer(
|
||||
<div className="input-group w-100 mr-2">
|
||||
<div className="input-group-prepend">
|
||||
<span
|
||||
className={`input-group-text px-2 border-left-0 border-right-0 border-top-0 rounded-0 ${
|
||||
className={`input-group-text px-2 border-left-0 border-right-0 border-top-0 border-light rounded-0 ${
|
||||
this.inputStore.focused ? "bg-focused" : "bg-transparent"
|
||||
} text-white`}
|
||||
>
|
||||
@@ -162,7 +160,7 @@ const FilterInput = observer(
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={`form-control components-filterinput border-left-0 border-right-0 border-top-0 rounded-0 ${
|
||||
className={`form-control components-filterinput border-left-0 border-right-0 border-top-0 border-light rounded-0 ${
|
||||
this.inputStore.focused ? "bg-focused" : "bg-transparent"
|
||||
}`}
|
||||
onClick={event => {
|
||||
|
||||
@@ -19,8 +19,6 @@ import { SilenceModal } from "Components/SilenceModal";
|
||||
import { FetchIndicator } from "./FetchIndicator";
|
||||
import { FilterInput } from "./FilterInput";
|
||||
|
||||
import "./index.scss";
|
||||
|
||||
const DesktopIdleTimeout = 1000 * 60 * 3;
|
||||
const MobileIdleTimeout = 1000 * 12;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { HistoryMenuContent } from "./FilterInput/History";
|
||||
import { NavBar } from ".";
|
||||
|
||||
import "Percy.scss";
|
||||
import "Styles/Percy.scss";
|
||||
|
||||
const NewFilter = (raw, name, matcher, value, applied, isValid, hits) => {
|
||||
const filter = NewUnappliedFilter(raw);
|
||||
|
||||
@@ -96,8 +96,8 @@ const LabelsTable = observer(
|
||||
);
|
||||
|
||||
const NothingToShow = () => (
|
||||
<div className="jumbotron bg-white">
|
||||
<h1 className="display-5 text-secondary text-center">
|
||||
<div className="jumbotron bg-transparent">
|
||||
<h1 className="display-5 text-placeholder text-center">
|
||||
No labels to display
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -499,8 +499,8 @@ exports[`<OverviewModalContent /> matches snapshot with no labels to show 1`] =
|
||||
</button>
|
||||
</div>
|
||||
<div class=\\"modal-body\\">
|
||||
<div class=\\"jumbotron bg-white\\">
|
||||
<h1 class=\\"display-5 text-secondary text-center\\">
|
||||
<div class=\\"jumbotron bg-transparent\\">
|
||||
<h1 class=\\"display-5 text-placeholder text-center\\">
|
||||
No labels to display
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -13,8 +13,6 @@ import { AlertStore } from "Stores/AlertStore";
|
||||
import { TooltipWrapper } from "Components/TooltipWrapper";
|
||||
import { Modal } from "Components/Modal";
|
||||
|
||||
import "./index.scss";
|
||||
|
||||
// https://github.com/facebook/react/issues/14603
|
||||
const OverviewModalContent = React.lazy(() =>
|
||||
import("./OverviewModalContent").then(module => ({
|
||||
@@ -65,7 +63,7 @@ const OverviewModal = observer(
|
||||
>
|
||||
<React.Suspense
|
||||
fallback={
|
||||
<h1 className="display-1 text-secondary p-5 m-auto">
|
||||
<h1 className="display-1 text-placeholder p-5 m-auto">
|
||||
<FontAwesomeIcon icon={faSpinner} size="lg" spin />
|
||||
</h1>
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
@import "src/Theme.scss";
|
||||
|
||||
.navbar-brand {
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $green !important;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import { MockGrid } from "__mocks__/Stories.js";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { OverviewModalContent } from "./OverviewModalContent";
|
||||
|
||||
import "Percy.scss";
|
||||
import "Styles/Percy.scss";
|
||||
|
||||
storiesOf("OverviewModal", module)
|
||||
.addDecorator(storyFn => (
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
exports[`<AlertManagerInput /> matches snapshot 1`] = `
|
||||
"
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<div class=\\"react-select__control css-au8pbo-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--is-multi react-select__value-container--has-value css-10war8y\\">
|
||||
<div class=\\"react-select__control css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--is-multi react-select__value-container--has-value css-nj4yyx\\">
|
||||
<div class=\\"css-owt1hd-multiValue react-select__multi-value\\">
|
||||
<div class=\\"css-xbn6jz react-select__multi-value__label\\">
|
||||
am1 | am2
|
||||
@@ -41,20 +41,20 @@ exports[`<AlertManagerInput /> matches snapshot 1`] = `
|
||||
</div>
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
<div class=\\"react-select__input\\"
|
||||
style=\\"display:inline-block\\"
|
||||
style=\\"display: inline-block;\\"
|
||||
>
|
||||
<input type=\\"text\\"
|
||||
autocapitalize=\\"none\\"
|
||||
<input autocapitalize=\\"none\\"
|
||||
autocomplete=\\"off\\"
|
||||
autocorrect=\\"off\\"
|
||||
id=\\"react-select-silence-input-alertmanagers-input\\"
|
||||
spellcheck=\\"false\\"
|
||||
tabindex=\\"0\\"
|
||||
value
|
||||
type=\\"text\\"
|
||||
aria-autocomplete=\\"list\\"
|
||||
style=\\"box-sizing:content-box;width:1px;label:input;background:0;border:0;font-size:inherit;opacity:1;outline:0;padding:0;color:inherit\\"
|
||||
style=\\"box-sizing: content-box; width: 2px; border: 0px; font-size: inherit; opacity: 1; outline: 0; padding: 0px;\\"
|
||||
value
|
||||
>
|
||||
<div style=\\"position:absolute;top:0;left:0;visibility:hidden;height:0;overflow:scroll;white-space:pre\\">
|
||||
<div style=\\"position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-size: inherit; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;\\">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,8 @@ import {
|
||||
SilenceFormStore,
|
||||
AlertmanagerClustersToOption
|
||||
} from "Stores/SilenceFormStore";
|
||||
import { MultiSelect, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { MultiSelect } from "Components/MultiSelect";
|
||||
import { ValidationError } from "Components/MultiSelect/ValidationError";
|
||||
|
||||
const AlertManagerInput = observer(
|
||||
@@ -20,6 +21,7 @@ const AlertManagerInput = observer(
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired
|
||||
};
|
||||
static contextType = ThemeContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -68,7 +70,7 @@ const AlertManagerInput = observer(
|
||||
|
||||
return (
|
||||
<Select
|
||||
styles={ReactSelectStyles}
|
||||
styles={this.context.reactSelectStyles}
|
||||
classNamePrefix="react-select"
|
||||
instanceId="silence-input-alertmanagers"
|
||||
defaultValue={silenceFormStore.data.alertmanagers}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import React from "react";
|
||||
|
||||
import { shallow, mount } from "enzyme";
|
||||
import { mount } from "enzyme";
|
||||
|
||||
import toDiffableHtml from "diffable-html";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { ReactSelectColors, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { AlertManagerInput } from ".";
|
||||
|
||||
let alertStore;
|
||||
@@ -52,21 +54,18 @@ beforeEach(() => {
|
||||
silenceFormStore = new SilenceFormStore();
|
||||
});
|
||||
|
||||
const ShallowAlertManagerInput = () => {
|
||||
return shallow(
|
||||
<AlertManagerInput
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const MountedAlertManagerInput = () => {
|
||||
return mount(
|
||||
<AlertManagerInput
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Light)
|
||||
}}
|
||||
>
|
||||
<AlertManagerInput
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -84,27 +83,35 @@ const ValidateSuggestions = () => {
|
||||
|
||||
describe("<AlertManagerInput />", () => {
|
||||
it("matches snapshot", () => {
|
||||
const tree = ShallowAlertManagerInput();
|
||||
const tree = MountedAlertManagerInput();
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("doesn't render ValidationError after passed validation", () => {
|
||||
const tree = ShallowAlertManagerInput();
|
||||
const tree = MountedAlertManagerInput();
|
||||
silenceFormStore.data.wasValidated = true;
|
||||
expect(tree.html()).not.toMatch(/fa-exclamation-circle/);
|
||||
expect(tree.html()).not.toMatch(/Required/);
|
||||
expect(toDiffableHtml(tree.html())).not.toMatch(/fa-exclamation-circle/);
|
||||
expect(toDiffableHtml(tree.html())).not.toMatch(/Required/);
|
||||
});
|
||||
|
||||
it("renders ValidationError after failed validation", () => {
|
||||
const tree = ShallowAlertManagerInput();
|
||||
const tree = MountedAlertManagerInput();
|
||||
tree
|
||||
.find(".react-select__multi-value__remove")
|
||||
.at(0)
|
||||
.simulate("click");
|
||||
tree
|
||||
.find(".react-select__multi-value__remove")
|
||||
.at(0)
|
||||
.simulate("click");
|
||||
silenceFormStore.data.alertmanagers = [];
|
||||
silenceFormStore.data.wasValidated = true;
|
||||
expect(tree.html()).toMatch(/fa-exclamation-circle/);
|
||||
expect(tree.html()).toMatch(/Required/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/fa-exclamation-circle/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/Required/);
|
||||
});
|
||||
|
||||
it("all available Alertmanager instances are selected by default", () => {
|
||||
ShallowAlertManagerInput();
|
||||
MountedAlertManagerInput();
|
||||
expect(silenceFormStore.data.alertmanagers).toHaveLength(2);
|
||||
expect(silenceFormStore.data.alertmanagers).toContainEqual({
|
||||
label: "am1 | am2",
|
||||
@@ -118,7 +125,7 @@ describe("<AlertManagerInput />", () => {
|
||||
|
||||
it("doesn't override last selected Alertmanager instances on mount", () => {
|
||||
silenceFormStore.data.alertmanagers = [{ label: "am3", value: ["am3"] }];
|
||||
ShallowAlertManagerInput();
|
||||
MountedAlertManagerInput();
|
||||
expect(silenceFormStore.data.alertmanagers).toHaveLength(1);
|
||||
expect(silenceFormStore.data.alertmanagers).toContainEqual({
|
||||
label: "am3",
|
||||
@@ -152,7 +159,7 @@ describe("<AlertManagerInput />", () => {
|
||||
});
|
||||
|
||||
it("silenceFormStore.data.alertmanagers gets updated from alertStore.data.upstreams.instances on mismatch", () => {
|
||||
const tree = ShallowAlertManagerInput();
|
||||
const tree = MountedAlertManagerInput();
|
||||
alertStore.data.upstreams.clusters = {
|
||||
amNew: ["amNew"]
|
||||
};
|
||||
|
||||
@@ -39,8 +39,8 @@ FetchError.propTypes = {
|
||||
|
||||
const Placeholder = ({ content }) => (
|
||||
<MountFade in={true}>
|
||||
<div className="jumbotron bg-white">
|
||||
<h1 className="display-5 text-secondary text-center">{content}</h1>
|
||||
<div className="jumbotron bg-transparent">
|
||||
<h1 className="display-5 text-placeholder text-center">{content}</h1>
|
||||
</div>
|
||||
</MountFade>
|
||||
);
|
||||
@@ -208,7 +208,7 @@ const Browser = observer(
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline-secondary flex-grow-0 flex-shrink-0"
|
||||
className="btn btn-primary flex-grow-0 flex-shrink-0"
|
||||
onClick={() => {
|
||||
this.dataSource.toggleSortReverse();
|
||||
this.onDebouncedFetch();
|
||||
|
||||
@@ -111,7 +111,7 @@ describe("<Browser />", () => {
|
||||
);
|
||||
const tree = MountedBrowser();
|
||||
|
||||
const sortOrder = tree.find("button.btn-outline-secondary").at(0);
|
||||
const sortOrder = tree.find("button.btn-primary").at(0);
|
||||
expect(sortOrder.text()).toBe("Sort order");
|
||||
sortOrder.simulate("click");
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@ import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { Duration } from "./Duration";
|
||||
import { HourMinute } from "./HourMinute";
|
||||
|
||||
import "./index.scss";
|
||||
|
||||
const OffsetBadge = ({ startDate, endDate, prefixLabel }) => {
|
||||
const days = endDate.diff(startDate, "days");
|
||||
const hours = endDate.diff(startDate, "hours") % 24;
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
@import "src/Theme.scss";
|
||||
|
||||
$datepicker__background-color: $white;
|
||||
$datepicker__border-color: $gray-300;
|
||||
$datepicker__highlighted-color: $secondary;
|
||||
$datepicker__muted-color: $gray-600;
|
||||
$datepicker__selected-color: $primary;
|
||||
$datepicker__text-color: $black;
|
||||
$datepicker__header-color: $black;
|
||||
$datepicker__navigation-disabled-color: $gray-600;
|
||||
$datepicker__font-size: $font-size-base;
|
||||
$datepicker__font-family: $font-family-sans-serif;
|
||||
$datepicker__border-radius: 0.25rem;
|
||||
|
||||
@import "~react-datepicker/src/stylesheets/datepicker.scss";
|
||||
|
||||
.react-datepicker__day:not(.react-datepicker__day--disabled) {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
}
|
||||
.react-datepicker__day:hover:not(.react-datepicker__day--disabled) {
|
||||
border-color: $datepicker__border-color;
|
||||
}
|
||||
|
||||
.react-datepicker__today-button {
|
||||
border-bottom-left-radius: $datepicker__border-radius;
|
||||
border-bottom-right-radius: $datepicker__border-radius;
|
||||
}
|
||||
@@ -3,46 +3,66 @@
|
||||
exports[`<PayloadPreview /> matches snapshot 1`] = `
|
||||
"
|
||||
<div>
|
||||
<pre class=\\"__json-pretty__\\">
|
||||
<pre class=\\"__json-pretty__\\"
|
||||
style=\\"line-height:1.3;color:#66d9ef;background:#272822;overflow:auto;\\"
|
||||
>
|
||||
{
|
||||
"
|
||||
<span class=\\"__json-key__\\">
|
||||
<span class=\\"__json-key__\\"
|
||||
style=\\"color:#f92672;\\"
|
||||
>
|
||||
matchers
|
||||
</span>
|
||||
": [],
|
||||
"
|
||||
<span class=\\"__json-key__\\">
|
||||
<span class=\\"__json-key__\\"
|
||||
style=\\"color:#f92672;\\"
|
||||
>
|
||||
startsAt
|
||||
</span>
|
||||
":
|
||||
<span class=\\"__json-string__\\">
|
||||
<span class=\\"__json-string__\\"
|
||||
style=\\"color:#fd971f;\\"
|
||||
>
|
||||
"2000-02-01T00:00:00.000Z"
|
||||
</span>
|
||||
,
|
||||
"
|
||||
<span class=\\"__json-key__\\">
|
||||
<span class=\\"__json-key__\\"
|
||||
style=\\"color:#f92672;\\"
|
||||
>
|
||||
endsAt
|
||||
</span>
|
||||
":
|
||||
<span class=\\"__json-string__\\">
|
||||
<span class=\\"__json-string__\\"
|
||||
style=\\"color:#fd971f;\\"
|
||||
>
|
||||
"2000-02-01T01:00:00.000Z"
|
||||
</span>
|
||||
,
|
||||
"
|
||||
<span class=\\"__json-key__\\">
|
||||
<span class=\\"__json-key__\\"
|
||||
style=\\"color:#f92672;\\"
|
||||
>
|
||||
createdBy
|
||||
</span>
|
||||
":
|
||||
<span class=\\"__json-string__\\">
|
||||
<span class=\\"__json-string__\\"
|
||||
style=\\"color:#fd971f;\\"
|
||||
>
|
||||
""
|
||||
</span>
|
||||
,
|
||||
"
|
||||
<span class=\\"__json-key__\\">
|
||||
<span class=\\"__json-key__\\"
|
||||
style=\\"color:#f92672;\\"
|
||||
>
|
||||
comment
|
||||
</span>
|
||||
":
|
||||
<span class=\\"__json-string__\\">
|
||||
<span class=\\"__json-string__\\"
|
||||
style=\\"color:#fd971f;\\"
|
||||
>
|
||||
"PayloadPreview test"
|
||||
</span>
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import PropTypes from "prop-types";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
import JSONPretty from "react-json-pretty";
|
||||
import "react-json-pretty/themes/monikai.css";
|
||||
import * as theme from "react-json-pretty/dist/monikai";
|
||||
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
|
||||
@@ -19,7 +19,10 @@ const PayloadPreview = observer(
|
||||
|
||||
return (
|
||||
<div className="mt-3">
|
||||
<JSONPretty json={silenceFormStore.data.toAlertmanagerPayload} />
|
||||
<JSONPretty
|
||||
json={silenceFormStore.data.toAlertmanagerPayload}
|
||||
theme={theme}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ const SilenceForm = observer(
|
||||
<TooltipWrapper title="Add a matcher">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline-secondary mb-3"
|
||||
className="btn btn-primary mb-3"
|
||||
onClick={this.addMore}
|
||||
>
|
||||
<FontAwesomeIcon icon={faPlus} />
|
||||
@@ -206,14 +206,14 @@ const SilenceForm = observer(
|
||||
{silenceFormStore.data.silenceID === null ? null : (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline-danger mr-2"
|
||||
className="btn btn-danger mr-2"
|
||||
onClick={silenceFormStore.data.resetSilenceID}
|
||||
>
|
||||
<FontAwesomeIcon icon={faUndoAlt} className="mr-1" />
|
||||
Reset
|
||||
</button>
|
||||
)}
|
||||
<button type="submit" className="btn btn-outline-primary">
|
||||
<button type="submit" className="btn btn-primary">
|
||||
<FontAwesomeIcon icon={faSearch} className="mr-1" />
|
||||
Preview
|
||||
</button>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
import { mount, shallow } from "enzyme";
|
||||
import { mount } from "enzyme";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
SilenceFormStage,
|
||||
NewEmptyMatcher
|
||||
} from "Stores/SilenceFormStore";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { ReactSelectColors, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { SilenceForm } from "./SilenceForm";
|
||||
|
||||
let alertStore;
|
||||
@@ -25,38 +27,33 @@ beforeEach(() => {
|
||||
silenceFormStore = new SilenceFormStore();
|
||||
});
|
||||
|
||||
const ShallowSilenceForm = () => {
|
||||
return shallow(
|
||||
<SilenceForm
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
settingsStore={settingsStore}
|
||||
previewOpen={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const MountedSilenceForm = () => {
|
||||
return mount(
|
||||
<SilenceForm
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
settingsStore={settingsStore}
|
||||
previewOpen={false}
|
||||
/>
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Light)
|
||||
}}
|
||||
>
|
||||
<SilenceForm
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
settingsStore={settingsStore}
|
||||
previewOpen={false}
|
||||
/>
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
describe("<SilenceForm /> matchers", () => {
|
||||
it("has an empty matcher selects on default render", () => {
|
||||
const tree = ShallowSilenceForm();
|
||||
const tree = MountedSilenceForm();
|
||||
const matchers = tree.find("SilenceMatch");
|
||||
expect(matchers).toHaveLength(1);
|
||||
expect(silenceFormStore.data.matchers).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("clicking 'Add more' button adds another matcher", () => {
|
||||
const tree = ShallowSilenceForm();
|
||||
const tree = MountedSilenceForm();
|
||||
const button = tree.find("button[type='button']");
|
||||
button.simulate("click", { preventDefault: jest.fn() });
|
||||
const matchers = tree.find("SilenceMatch");
|
||||
@@ -101,21 +98,22 @@ describe("<SilenceForm /> matchers", () => {
|
||||
|
||||
describe("<SilenceForm /> preview", () => {
|
||||
it("doesn't render PayloadPreview when previewCollapse.hidden is true", () => {
|
||||
const tree = ShallowSilenceForm();
|
||||
const tree = MountedSilenceForm();
|
||||
const instance = tree.instance();
|
||||
instance.previewCollapse.hidden = true;
|
||||
expect(tree.find("PayloadPreview")).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("renders PayloadPreview when previewCollapse.hidden is false", () => {
|
||||
const tree = ShallowSilenceForm();
|
||||
const tree = MountedSilenceForm();
|
||||
const instance = tree.instance();
|
||||
instance.previewCollapse.hidden = false;
|
||||
tree.update();
|
||||
expect(tree.find("PayloadPreview")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("clicking on the toggle icon toggles PayloadPreview", () => {
|
||||
const tree = ShallowSilenceForm();
|
||||
const tree = MountedSilenceForm();
|
||||
const button = tree.find(".btn.cursor-pointer.text-muted");
|
||||
expect(tree.find("PayloadPreview")).toHaveLength(0);
|
||||
button.simulate("click");
|
||||
@@ -178,7 +176,7 @@ describe("<SilenceForm /> inputs", () => {
|
||||
|
||||
describe("<SilenceForm />", () => {
|
||||
it("calling submit doesn't move the form to Preview stage when form is invalid", () => {
|
||||
const tree = ShallowSilenceForm();
|
||||
const tree = MountedSilenceForm();
|
||||
tree.simulate("submit", { preventDefault: jest.fn() });
|
||||
expect(silenceFormStore.data.currentStage).toBe(SilenceFormStage.UserInput);
|
||||
});
|
||||
@@ -191,14 +189,14 @@ describe("<SilenceForm />", () => {
|
||||
silenceFormStore.data.alertmanagers = [{ label: "am1", value: ["am1"] }];
|
||||
silenceFormStore.data.author = "me@example.com";
|
||||
silenceFormStore.data.comment = "fake silence";
|
||||
const tree = ShallowSilenceForm();
|
||||
const tree = MountedSilenceForm();
|
||||
tree.simulate("submit", { preventDefault: jest.fn() });
|
||||
expect(silenceFormStore.data.currentStage).toBe(SilenceFormStage.Preview);
|
||||
});
|
||||
|
||||
it("calling submit saves author value to the Settings store", () => {
|
||||
silenceFormStore.data.author = "user@example.com";
|
||||
const tree = ShallowSilenceForm();
|
||||
const tree = MountedSilenceForm();
|
||||
tree.simulate("submit", { preventDefault: jest.fn() });
|
||||
expect(settingsStore.silenceFormConfig.config.author).toBe(
|
||||
"user@example.com"
|
||||
@@ -217,14 +215,14 @@ describe("<SilenceForm /> in edit mode", () => {
|
||||
it("opening form with silenceID shows reset button", () => {
|
||||
silenceFormStore.data.silenceID = "12345";
|
||||
const tree = MountedSilenceForm();
|
||||
const button = tree.find("button.btn-outline-danger");
|
||||
const button = tree.find("button.btn-danger");
|
||||
expect(button).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("clicking on Reset button unsets silenceFormStore.data.silenceID", () => {
|
||||
silenceFormStore.data.silenceID = "12345";
|
||||
const tree = MountedSilenceForm();
|
||||
const button = tree.find("button.btn-outline-danger");
|
||||
const button = tree.find("button.btn-danger");
|
||||
button.simulate("click");
|
||||
expect(silenceFormStore.data.silenceID).toBeNull();
|
||||
});
|
||||
@@ -232,15 +230,15 @@ describe("<SilenceForm /> in edit mode", () => {
|
||||
it("clicking on Reset button hides it", () => {
|
||||
silenceFormStore.data.silenceID = "12345";
|
||||
const tree = MountedSilenceForm();
|
||||
const button = tree.find("button.btn-outline-danger");
|
||||
const button = tree.find("button.btn-danger");
|
||||
button.simulate("click");
|
||||
expect(tree.find("button.btn-outline-danger")).toHaveLength(0);
|
||||
expect(tree.find("button.btn-danger")).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("clicking on Reset button enables AlertManagerInput", () => {
|
||||
silenceFormStore.data.silenceID = "12345";
|
||||
const tree = MountedSilenceForm();
|
||||
const button = tree.find("button.btn-outline-danger");
|
||||
const button = tree.find("button.btn-danger");
|
||||
button.simulate("click");
|
||||
const select = tree.find("StateManager").at(0);
|
||||
expect(select.props().isDisabled).toBeFalsy();
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import React from "react";
|
||||
|
||||
import { shallow, mount } from "enzyme";
|
||||
import { mount } from "enzyme";
|
||||
|
||||
import toDiffableHtml from "diffable-html";
|
||||
|
||||
import { NewEmptyMatcher, MatcherValueToObject } from "Stores/SilenceFormStore";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { ReactSelectColors, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { LabelNameInput } from "./LabelNameInput";
|
||||
|
||||
let matcher;
|
||||
@@ -30,12 +32,16 @@ afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
const ShallowLabelNameInput = isValid => {
|
||||
return shallow(<LabelNameInput matcher={matcher} isValid={isValid} />);
|
||||
};
|
||||
|
||||
const MountedLabelNameInput = isValid => {
|
||||
return mount(<LabelNameInput matcher={matcher} isValid={isValid} />);
|
||||
return mount(
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Light)
|
||||
}}
|
||||
>
|
||||
<LabelNameInput matcher={matcher} isValid={isValid} />
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const ValidateSuggestions = () => {
|
||||
@@ -48,26 +54,26 @@ const ValidateSuggestions = () => {
|
||||
|
||||
describe("<LabelNameInput />", () => {
|
||||
it("matches snapshot", () => {
|
||||
const tree = ShallowLabelNameInput(true);
|
||||
const tree = MountedLabelNameInput(true);
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("doesn't renders ValidationError after passed validation", () => {
|
||||
// clear the name so placeholder is rendered
|
||||
matcher.name = "";
|
||||
const tree = ShallowLabelNameInput(true);
|
||||
expect(tree.html()).toMatch(/Label name/);
|
||||
expect(tree.html()).not.toMatch(/fa-exclamation-circle/);
|
||||
expect(tree.html()).not.toMatch(/Required/);
|
||||
const tree = MountedLabelNameInput(true);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/Label name/);
|
||||
expect(toDiffableHtml(tree.html())).not.toMatch(/fa-exclamation-circle/);
|
||||
expect(toDiffableHtml(tree.html())).not.toMatch(/Required/);
|
||||
});
|
||||
|
||||
it("renders ValidationError after failed validation", () => {
|
||||
// clear the name so placeholder is rendered
|
||||
matcher.name = "";
|
||||
const tree = ShallowLabelNameInput(false);
|
||||
expect(tree.html()).not.toMatch(/Label name/);
|
||||
expect(tree.html()).toMatch(/fa-exclamation-circle/);
|
||||
expect(tree.html()).toMatch(/Required/);
|
||||
const tree = MountedLabelNameInput(false);
|
||||
expect(toDiffableHtml(tree.html())).not.toMatch(/Label name/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/fa-exclamation-circle/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/Required/);
|
||||
});
|
||||
|
||||
it("renders suggestions", () => {
|
||||
@@ -89,7 +95,7 @@ describe("<LabelNameInput />", () => {
|
||||
fetch
|
||||
.once(JSON.stringify(["name1", "name2", "name3"]))
|
||||
.once(JSON.stringify(["value1", "value2", "value3"]));
|
||||
const tree = ShallowLabelNameInput(true);
|
||||
const tree = MountedLabelNameInput(true);
|
||||
const instance = tree.instance();
|
||||
await expect(instance.nameSuggestionsFetch).resolves.toBeUndefined();
|
||||
await expect(instance.valueSuggestionsFetch).resolves.toBeUndefined();
|
||||
@@ -106,7 +112,7 @@ describe("<LabelNameInput />", () => {
|
||||
|
||||
it("handles fetch errors when populating suggestions", async () => {
|
||||
fetch.mockReject("error");
|
||||
const tree = ShallowLabelNameInput(true);
|
||||
const tree = MountedLabelNameInput(true);
|
||||
const instance = tree.instance();
|
||||
await expect(instance.nameSuggestionsFetch).resolves.toBeUndefined();
|
||||
await expect(instance.valueSuggestionsFetch).resolves.toBeUndefined();
|
||||
@@ -116,7 +122,7 @@ describe("<LabelNameInput />", () => {
|
||||
it("handles invalid JSON when populating suggestions", async () => {
|
||||
jest.spyOn(console, "error").mockImplementation(() => {});
|
||||
fetch.mockResponse("this is not JSON");
|
||||
const tree = ShallowLabelNameInput(true);
|
||||
const tree = MountedLabelNameInput(true);
|
||||
const instance = tree.instance();
|
||||
await expect(instance.nameSuggestionsFetch).resolves.toBeUndefined();
|
||||
await expect(instance.valueSuggestionsFetch).resolves.toBeUndefined();
|
||||
@@ -126,7 +132,7 @@ describe("<LabelNameInput />", () => {
|
||||
|
||||
it("suggestions are emptied on failed fetch", async () => {
|
||||
fetch.mockReject(new Error("fake error message"));
|
||||
const tree = ShallowLabelNameInput(true);
|
||||
const tree = MountedLabelNameInput(true);
|
||||
const instance = tree.instance();
|
||||
await expect(instance.nameSuggestionsFetch).resolves.toBeUndefined();
|
||||
await expect(instance.valueSuggestionsFetch).resolves.toBeUndefined();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
import { shallow, mount } from "enzyme";
|
||||
import { mount } from "enzyme";
|
||||
|
||||
import toDiffableHtml from "diffable-html";
|
||||
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
NewEmptyMatcher,
|
||||
MatcherValueToObject
|
||||
} from "Stores/SilenceFormStore";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { ReactSelectColors, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { LabelValueInput } from "./LabelValueInput";
|
||||
|
||||
let silenceFormStore;
|
||||
@@ -36,23 +38,19 @@ afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
const ShallowLabelValueInput = isValid => {
|
||||
return shallow(
|
||||
<LabelValueInput
|
||||
silenceFormStore={silenceFormStore}
|
||||
matcher={matcher}
|
||||
isValid={isValid}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const MountedLabelValueInput = isValid => {
|
||||
return mount(
|
||||
<LabelValueInput
|
||||
silenceFormStore={silenceFormStore}
|
||||
matcher={matcher}
|
||||
isValid={isValid}
|
||||
/>
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Light)
|
||||
}}
|
||||
>
|
||||
<LabelValueInput
|
||||
silenceFormStore={silenceFormStore}
|
||||
matcher={matcher}
|
||||
isValid={isValid}
|
||||
/>
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -66,20 +64,20 @@ const ValidateSuggestions = () => {
|
||||
|
||||
describe("<LabelValueInput />", () => {
|
||||
it("matches snapshot", () => {
|
||||
const tree = ShallowLabelValueInput(true);
|
||||
const tree = MountedLabelValueInput(true);
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("doesn't renders ValidationError after passed validation", () => {
|
||||
const tree = ShallowLabelValueInput(true);
|
||||
expect(tree.html()).not.toMatch(/fa-exclamation-circle/);
|
||||
expect(tree.html()).not.toMatch(/Required/);
|
||||
const tree = MountedLabelValueInput(true);
|
||||
expect(toDiffableHtml(tree.html())).not.toMatch(/fa-exclamation-circle/);
|
||||
expect(toDiffableHtml(tree.html())).not.toMatch(/Required/);
|
||||
});
|
||||
|
||||
it("renders ValidationError after failed validation", () => {
|
||||
const tree = ShallowLabelValueInput(false);
|
||||
expect(tree.html()).toMatch(/fa-exclamation-circle/);
|
||||
expect(tree.html()).toMatch(/Required/);
|
||||
const tree = MountedLabelValueInput(false);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/fa-exclamation-circle/);
|
||||
expect(toDiffableHtml(tree.html())).toMatch(/Required/);
|
||||
});
|
||||
|
||||
it("renders suggestions", () => {
|
||||
|
||||
@@ -3,27 +3,27 @@
|
||||
exports[`<LabelNameInput /> matches snapshot 1`] = `
|
||||
"
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<div class=\\"react-select__control css-au8pbo-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-pb81dw\\">
|
||||
<div class=\\"react-select__single-value css-1uccc91-singleValue\\">
|
||||
<div class=\\"react-select__control css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--has-value css-97xgis\\">
|
||||
<div class=\\"react-select__single-value css-1wh03ml-singleValue\\">
|
||||
name
|
||||
</div>
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
<div class=\\"react-select__input\\"
|
||||
style=\\"display:inline-block\\"
|
||||
style=\\"display: inline-block;\\"
|
||||
>
|
||||
<input type=\\"text\\"
|
||||
autocapitalize=\\"none\\"
|
||||
<input autocapitalize=\\"none\\"
|
||||
autocomplete=\\"off\\"
|
||||
autocorrect=\\"off\\"
|
||||
id=\\"react-select-silence-input-label-name-1-input\\"
|
||||
spellcheck=\\"false\\"
|
||||
tabindex=\\"0\\"
|
||||
value
|
||||
type=\\"text\\"
|
||||
aria-autocomplete=\\"list\\"
|
||||
style=\\"box-sizing:content-box;width:1px;label:input;background:0;border:0;font-size:inherit;opacity:1;outline:0;padding:0;color:inherit\\"
|
||||
style=\\"box-sizing: content-box; width: 2px; border: 0px; font-size: inherit; opacity: 1; outline: 0; padding: 0px;\\"
|
||||
value
|
||||
>
|
||||
<div style=\\"position:absolute;top:0;left:0;visibility:hidden;height:0;overflow:scroll;white-space:pre\\">
|
||||
<div style=\\"position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-size: inherit; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;\\">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,29 +3,18 @@
|
||||
exports[`<LabelValueInput /> matches snapshot 1`] = `
|
||||
"
|
||||
<div class=\\" css-2b097c-container\\">
|
||||
<div class=\\"react-select__control css-au8pbo-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--is-multi css-10war8y\\">
|
||||
<div title=\\"Number of alerts matching this label\\"
|
||||
class
|
||||
style=\\"display:inline-block;max-width:100%\\"
|
||||
<div class=\\"react-select__control css-r5n82u-control\\">
|
||||
<div class=\\"react-select__value-container react-select__value-container--is-multi css-nj4yyx\\">
|
||||
<div class
|
||||
style=\\"display: inline-block; max-width: 100%;\\"
|
||||
data-tooltipped
|
||||
aria-describedby=\\"tippy-tooltip-1\\"
|
||||
data-original-title=\\"Number of alerts matching this label\\"
|
||||
>
|
||||
<span class=\\"badge badge-light badge-pill d-block\\"
|
||||
style=\\"font-size:85%;line-height:1rem\\"
|
||||
style=\\"font-size: 85%; line-height: 1rem;\\"
|
||||
>
|
||||
<svg aria-hidden=\\"true\\"
|
||||
focusable=\\"false\\"
|
||||
data-prefix=\\"fas\\"
|
||||
data-icon=\\"spinner\\"
|
||||
class=\\"svg-inline--fa fa-spinner fa-w-16 fa-spin \\"
|
||||
role=\\"img\\"
|
||||
xmlns=\\"http://www.w3.org/2000/svg\\"
|
||||
viewbox=\\"0 0 512 512\\"
|
||||
>
|
||||
<path fill=\\"currentColor\\"
|
||||
d=\\"M304 48c0 26.51-21.49 48-48 48s-48-21.49-48-48 21.49-48 48-48 48 21.49 48 48zm-48 368c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zm208-208c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zM96 256c0-26.51-21.49-48-48-48S0 229.49 0 256s21.49 48 48 48 48-21.49 48-48zm12.922 99.078c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.491-48-48-48zm294.156 0c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.49-48-48-48zM108.922 60.922c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.491-48-48-48z\\"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
0
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
@@ -35,20 +24,20 @@ exports[`<LabelValueInput /> matches snapshot 1`] = `
|
||||
</div>
|
||||
<div class=\\"css-b8ldur-Input\\">
|
||||
<div class=\\"react-select__input\\"
|
||||
style=\\"display:inline-block\\"
|
||||
style=\\"display: inline-block;\\"
|
||||
>
|
||||
<input type=\\"text\\"
|
||||
autocapitalize=\\"none\\"
|
||||
<input autocapitalize=\\"none\\"
|
||||
autocomplete=\\"off\\"
|
||||
autocorrect=\\"off\\"
|
||||
id=\\"react-select-silence-input-label-value-1-input\\"
|
||||
spellcheck=\\"false\\"
|
||||
tabindex=\\"0\\"
|
||||
value
|
||||
type=\\"text\\"
|
||||
aria-autocomplete=\\"list\\"
|
||||
style=\\"box-sizing:content-box;width:1px;label:input;background:0;border:0;font-size:inherit;opacity:1;outline:0;padding:0;color:inherit\\"
|
||||
style=\\"box-sizing: content-box; width: 2px; border: 0px; font-size: inherit; opacity: 1; outline: 0; padding: 0px;\\"
|
||||
value
|
||||
>
|
||||
<div style=\\"position:absolute;top:0;left:0;visibility:hidden;height:0;overflow:scroll;white-space:pre\\">
|
||||
<div style=\\"position: absolute; top: 0px; left: 0px; visibility: hidden; height: 0px; overflow: scroll; white-space: pre; font-size: inherit; font-family: -webkit-small-control; letter-spacing: normal; text-transform: none;\\">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -75,7 +75,7 @@ const SilenceMatch = observer(
|
||||
<TooltipWrapper title="Remove this matcher">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline-danger"
|
||||
className="btn btn-danger"
|
||||
onClick={onDelete}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
|
||||
@@ -16,8 +16,6 @@ import { SilencePreview } from "./SilencePreview";
|
||||
import { SilenceSubmitController } from "./SilenceSubmit/SilenceSubmitController";
|
||||
import { Browser } from "./Browser";
|
||||
|
||||
import "./index.css";
|
||||
|
||||
const SilenceModalContent = observer(
|
||||
class SilenceModalContent extends Component {
|
||||
static propTypes = {
|
||||
|
||||
@@ -93,7 +93,7 @@ exports[`<SilencePreview /> matches snapshot 1`] = `
|
||||
</div>
|
||||
<div class=\\"d-flex flex-row-reverse\\">
|
||||
<button type=\\"button\\"
|
||||
class=\\"btn btn-outline-primary\\"
|
||||
class=\\"btn btn-primary\\"
|
||||
>
|
||||
<svg aria-hidden=\\"true\\"
|
||||
focusable=\\"false\\"
|
||||
@@ -112,7 +112,7 @@ exports[`<SilencePreview /> matches snapshot 1`] = `
|
||||
Submit
|
||||
</button>
|
||||
<button type=\\"button\\"
|
||||
class=\\"btn btn-outline-secondary mr-2\\"
|
||||
class=\\"btn btn-danger mr-2\\"
|
||||
>
|
||||
<svg aria-hidden=\\"true\\"
|
||||
focusable=\\"false\\"
|
||||
|
||||
@@ -32,8 +32,8 @@ FetchError.propTypes = {
|
||||
};
|
||||
|
||||
const Placeholder = () => (
|
||||
<div className="jumbotron bg-white">
|
||||
<h1 className="display-5 text-secondary text-center">
|
||||
<div className="jumbotron bg-transparent">
|
||||
<h1 className="display-5 text-placeholder text-center">
|
||||
<FontAwesomeIcon icon={faSpinner} size="lg" spin />
|
||||
</h1>
|
||||
</div>
|
||||
@@ -124,7 +124,7 @@ const SilencePreview = observer(
|
||||
<div className="d-flex flex-row-reverse">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline-primary"
|
||||
className="btn btn-primary"
|
||||
onClick={silenceFormStore.data.setStageSubmit}
|
||||
>
|
||||
<FontAwesomeIcon icon={faCheckCircle} className="pr-1" />
|
||||
@@ -132,7 +132,7 @@ const SilencePreview = observer(
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline-secondary mr-2"
|
||||
className="btn btn-danger mr-2"
|
||||
onClick={silenceFormStore.data.resetProgress}
|
||||
>
|
||||
<FontAwesomeIcon icon={faArrowLeft} className="pr-1" />
|
||||
|
||||
@@ -151,7 +151,7 @@ describe("<SilencePreview />", () => {
|
||||
fetch.mockResponse(JSON.stringify(MockAPIResponse()));
|
||||
|
||||
const tree = MountedSilencePreview();
|
||||
const button = tree.find(".btn-outline-primary");
|
||||
const button = tree.find(".btn-primary");
|
||||
button.simulate("click");
|
||||
expect(silenceFormStore.data.currentStage).toBe(SilenceFormStage.Submit);
|
||||
});
|
||||
|
||||
@@ -33,7 +33,7 @@ class SilenceSubmitController extends Component {
|
||||
<div className="d-flex flex-row-reverse">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline-primary"
|
||||
className="btn btn-primary"
|
||||
onClick={silenceFormStore.data.resetProgress}
|
||||
>
|
||||
<FontAwesomeIcon icon={faArrowLeft} className="pr-1" />
|
||||
|
||||
@@ -65,7 +65,7 @@ const SilenceModal = observer(
|
||||
>
|
||||
<React.Suspense
|
||||
fallback={
|
||||
<h1 className="display-1 text-secondary p-5 m-auto">
|
||||
<h1 className="display-1 text-placeholder p-5 m-auto">
|
||||
<FontAwesomeIcon icon={faSpinner} size="lg" spin />
|
||||
</h1>
|
||||
}
|
||||
|
||||
@@ -13,10 +13,12 @@ import {
|
||||
MatcherValueToObject,
|
||||
SilenceTabNames
|
||||
} from "Stores/SilenceFormStore";
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { ReactSelectColors, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { DateTimeSelect, TabNames } from "./DateTimeSelect";
|
||||
import { SilenceModalContent } from "./SilenceModalContent";
|
||||
|
||||
import "Percy.scss";
|
||||
import "Styles/Percy.scss";
|
||||
|
||||
const MockMatcher = (name, values, isRegex) => {
|
||||
const matcher = NewEmptyMatcher();
|
||||
@@ -77,32 +79,38 @@ storiesOf("SilenceModal", module)
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Modal>
|
||||
<SilenceModalContent
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={() => {}}
|
||||
previewOpen={true}
|
||||
onDeleteModalClose={() => {}}
|
||||
/>
|
||||
</Modal>
|
||||
<Modal>
|
||||
<div className="pt-2">
|
||||
<DateTimeSelect
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Light)
|
||||
}}
|
||||
>
|
||||
<Modal>
|
||||
<SilenceModalContent
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
openTab={TabNames.Start}
|
||||
settingsStore={settingsStore}
|
||||
onHide={() => {}}
|
||||
previewOpen={true}
|
||||
onDeleteModalClose={() => {}}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal>
|
||||
<div className="pt-2">
|
||||
<DateTimeSelect
|
||||
silenceFormStore={silenceFormStore}
|
||||
openTab={TabNames.End}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
</Modal>
|
||||
<Modal>
|
||||
<div className="pt-2">
|
||||
<DateTimeSelect
|
||||
silenceFormStore={silenceFormStore}
|
||||
openTab={TabNames.Start}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal>
|
||||
<div className="pt-2">
|
||||
<DateTimeSelect
|
||||
silenceFormStore={silenceFormStore}
|
||||
openTab={TabNames.End}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
</ThemeContext.Provider>
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
|
||||
@@ -2,6 +2,8 @@ import React from "react";
|
||||
|
||||
import { mount } from "enzyme";
|
||||
|
||||
import { ThemeContext } from "Components/Theme";
|
||||
import { ReactSelectColors, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { SilenceFormStore, SilenceFormStage } from "Stores/SilenceFormStore";
|
||||
@@ -24,11 +26,17 @@ beforeEach(() => {
|
||||
|
||||
const MountedSilenceModal = () => {
|
||||
return mount(
|
||||
<SilenceModal
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
settingsStore={settingsStore}
|
||||
/>
|
||||
<ThemeContext.Provider
|
||||
value={{
|
||||
reactSelectStyles: ReactSelectStyles(ReactSelectColors.Light)
|
||||
}}
|
||||
>
|
||||
<SilenceModal
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
settingsStore={settingsStore}
|
||||
/>
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
56
ui/src/Components/Theme/index.js
Normal file
56
ui/src/Components/Theme/index.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faSun } from "@fortawesome/free-solid-svg-icons/faSun";
|
||||
|
||||
const DarkTheme = React.lazy(() => import("Styles/DarkTheme"));
|
||||
const LightTheme = React.lazy(() => import("Styles/LightTheme"));
|
||||
|
||||
const Placeholder = () => {
|
||||
return ReactDOM.createPortal(
|
||||
<div
|
||||
style={{
|
||||
zIndex: 2000,
|
||||
backgroundColor: "#455a64",
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: "100vw",
|
||||
height: "100vh"
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faSun}
|
||||
size="lg"
|
||||
spin
|
||||
style={{
|
||||
color: "#5e7a88",
|
||||
fontSize: "8rem",
|
||||
position: "fixed",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
marginRight: "-50%",
|
||||
transform: "translate(-50%, -50%)"
|
||||
}}
|
||||
/>
|
||||
</div>,
|
||||
document.body
|
||||
);
|
||||
};
|
||||
|
||||
const Theme = observer(({ settingsStore }) => (
|
||||
<React.Suspense fallback={<Placeholder />}>
|
||||
{settingsStore.themeConfig.config.darkTheme ? (
|
||||
<DarkTheme />
|
||||
) : (
|
||||
<LightTheme />
|
||||
)}
|
||||
</React.Suspense>
|
||||
));
|
||||
|
||||
const ThemeContext = React.createContext();
|
||||
|
||||
export { Theme, ThemeContext };
|
||||
26
ui/src/Components/Theme/index.test.js
Normal file
26
ui/src/Components/Theme/index.test.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from "react";
|
||||
|
||||
import { mount } from "enzyme";
|
||||
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { Theme } from ".";
|
||||
|
||||
let settingsStore;
|
||||
|
||||
beforeEach(() => {
|
||||
settingsStore = new Settings();
|
||||
});
|
||||
|
||||
describe("<Theme />", () => {
|
||||
it("renders DarkTheme when settingsStore.themeConfig.config.darkTheme is true", () => {
|
||||
settingsStore.themeConfig.config.darkTheme = true;
|
||||
const tree = mount(<Theme settingsStore={settingsStore} />);
|
||||
expect(tree.text()).toBe("");
|
||||
});
|
||||
|
||||
it("renders LightTheme when settingsStore.themeConfig.config.darkTheme is false", () => {
|
||||
settingsStore.themeConfig.config.darkTheme = false;
|
||||
const tree = mount(<Theme settingsStore={settingsStore} />);
|
||||
expect(tree.text()).toBe("");
|
||||
});
|
||||
});
|
||||
@@ -1,155 +0,0 @@
|
||||
@import "src/Theme.scss";
|
||||
|
||||
$alertgroup-body-bg: #5a6e7b;
|
||||
$alertgroup-header-bg: #515658;
|
||||
$alertgroup-footer-bg: #728998;
|
||||
$alertgroup-border-inside: #485862;
|
||||
|
||||
$silence-bg: #c3cdd5;
|
||||
$silence-progress-bg: #eaeef0;
|
||||
|
||||
$badge-light-bg: #d2dae0;
|
||||
|
||||
.dark-theme .card.bg-light {
|
||||
border-color: #3c3e3e !important;
|
||||
}
|
||||
.dark-theme .components-grid-alertgrid-card {
|
||||
&.card-body,
|
||||
& > .list-group,
|
||||
& > .list-group > .list-group-item {
|
||||
background-color: $alertgroup-body-bg !important;
|
||||
}
|
||||
}
|
||||
.dark-theme .card.bg-light > .card-header {
|
||||
background-color: $alertgroup-header-bg !important;
|
||||
}
|
||||
.dark-theme .card > .card-header {
|
||||
border-bottom-color: $alertgroup-border-inside !important;
|
||||
}
|
||||
.dark-theme .bg-card-footer-default {
|
||||
background-color: $alertgroup-footer-bg !important;
|
||||
border-top-color: $alertgroup-border-inside !important;
|
||||
}
|
||||
|
||||
.dark-theme .btn.bg-white {
|
||||
background-color: $alertgroup-body-bg !important;
|
||||
}
|
||||
|
||||
.dark-theme {
|
||||
& .components-grid-annotation .text-muted,
|
||||
& .components-managed-silence-cite .text-muted,
|
||||
& .components-managed-silence svg.text-muted,
|
||||
& .text-muted.fa-bell-slash {
|
||||
color: $dark !important;
|
||||
}
|
||||
& .text-muted {
|
||||
color: $white !important;
|
||||
}
|
||||
}
|
||||
|
||||
.dark-theme .components-managed-silence {
|
||||
border-left-color: $dark;
|
||||
|
||||
& > .card-header,
|
||||
& > .card-body {
|
||||
background-color: $silence-bg !important;
|
||||
}
|
||||
}
|
||||
|
||||
.dark-theme {
|
||||
& .modal-content {
|
||||
background-color: $alertgroup-body-bg;
|
||||
|
||||
& .list-group-item {
|
||||
background-color: $alertgroup-body-bg;
|
||||
}
|
||||
}
|
||||
& .modal-header {
|
||||
border-bottom-color: $alertgroup-border-inside;
|
||||
}
|
||||
& .modal-footer {
|
||||
border-top-color: $alertgroup-border-inside;
|
||||
}
|
||||
|
||||
& .components-grid-annotation.bg-light {
|
||||
background-color: $silence-bg !important;
|
||||
}
|
||||
|
||||
& .badge.badge-light {
|
||||
background-color: $badge-light-bg !important;
|
||||
}
|
||||
& .components-filteredinputlabel > .badge-pill.badge-light {
|
||||
background-color: $badge-light-bg !important;
|
||||
filter: none;
|
||||
}
|
||||
|
||||
& .dropdown-menu {
|
||||
background-color: $silence-bg !important;
|
||||
}
|
||||
& .dropdown-header {
|
||||
color: $dark !important;
|
||||
}
|
||||
|
||||
& .progress.bg-white {
|
||||
background-color: $silence-progress-bg !important;
|
||||
}
|
||||
}
|
||||
|
||||
.dark-theme {
|
||||
& .react-select__control,
|
||||
& .react-select__indicators,
|
||||
& .react-select__value-container {
|
||||
background-color: $alertgroup-body-bg !important;
|
||||
}
|
||||
& .react-select__menu {
|
||||
background-color: $silence-bg !important;
|
||||
}
|
||||
|
||||
& .tab-content,
|
||||
& .tab-content .text-muted,
|
||||
& .nav-item:not(.components-tab-inactive),
|
||||
& .custom-control-label,
|
||||
& .react-select__placeholder {
|
||||
color: $white !important;
|
||||
}
|
||||
|
||||
& .nav-link.active {
|
||||
color: $black !important;
|
||||
background-color: $silence-bg !important;
|
||||
}
|
||||
|
||||
& .form-control {
|
||||
color: $gray-200 !important;
|
||||
}
|
||||
|
||||
& .react-datepicker,
|
||||
& .react-datepicker__header,
|
||||
& .react-datepicker__today-button {
|
||||
background-color: $silence-bg !important;
|
||||
}
|
||||
}
|
||||
|
||||
.dark-theme {
|
||||
& .jumbotron.bg-white {
|
||||
background-color: $alertgroup-body-bg !important;
|
||||
}
|
||||
}
|
||||
|
||||
.dark-theme {
|
||||
& .collapse,
|
||||
& .collapse > .card-body {
|
||||
color: $white;
|
||||
background-color: $alertgroup-body-bg !important;
|
||||
}
|
||||
& .Collapsible > .card-header {
|
||||
background-color: $alertgroup-footer-bg !important;
|
||||
border-bottom-color: $alertgroup-border-inside !important;
|
||||
}
|
||||
& .input-range__label,
|
||||
& .Collapsible__trigger.bg-light {
|
||||
color: $white;
|
||||
}
|
||||
& .Collapsible__trigger.is-closed {
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
@import "src/App.scss";
|
||||
|
||||
body {
|
||||
font-family: sans-serif !important;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user