Merge pull request #111 from prymitive/cra2

chore(ui): upgrade to react-scripts 2.0
This commit is contained in:
Łukasz Mierzwa
2018-10-03 11:51:49 +01:00
committed by GitHub
44 changed files with 4231 additions and 9487 deletions
+1
View File
@@ -0,0 +1 @@
8.12.0
+4 -1
View File
@@ -7,7 +7,6 @@ defaults_go: &DEFAULTS_GO
defaults_js: &DEFAULTS_JS
language: node_js
node_js: "8.12.0"
# we run make to install everything
install: []
cache:
@@ -63,6 +62,10 @@ jobs:
cache:
# disable cache so we always build from scratch
directories: []
before_script:
# this stage needs to build everything including assets file and that
# requires running webpack, so we need nodejs here
- nvm install $(< .nvmrc)
script:
# compile assets via webpack and build those into bindata_assetfs.go file
- make bindata_assetfs.go
-1
View File
@@ -46,7 +46,6 @@ endif
.build/artifacts-ui.ok: .build/deps-build-node.ok $(ASSET_SOURCES)
@mkdir -p .build
cd ui && yarn build-css
cd ui && yarn build
touch $@
-1
View File
@@ -1,3 +1,2 @@
extends:
- react-app
- plugin:react/recommended
+16 -7
View File
@@ -40,28 +40,37 @@
"react-onclickoutside": "6.7.1",
"react-popper": "1.0.2",
"react-resize-detector": "3.1.2",
"react-scripts": "1.1.5",
"react-scripts": "2.0.3",
"react-select": "2.0.0",
"react-transition-group": "2.5.0"
},
"scripts": {
"start": "NODE_ENV=dev REACT_APP_BACKEND_URI=http://localhost:8080 react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"build-css": "node_modules/.bin/node-sass --recursive src/ -o src/"
"test": "react-scripts test"
},
"devDependencies": {
"@commitlint/travis-cli": "7.1.2",
"diffable-html": "3.0.0",
"enzyme": "3.6.0",
"enzyme-adapter-react-16": "1.5.0",
"eslint-plugin-react": "7.11.1",
"jest-canvas-mock": "1.1.0",
"jest-date-mock": "1.0.5",
"jest-fetch-mock": "1.6.6",
"jest-localstorage-mock": "2.2.0",
"markdownlint-cli": "0.13.0",
"node-sass": "4.9.3"
}
},
"browserslist": [
">= 1%",
"last 1 major version",
"not dead",
"Chrome >= 45",
"Firefox >= 38",
"Edge >= 12",
"Explorer >= 10",
"iOS >= 9",
"Safari >= 9",
"Android >= 4.4",
"Opera >= 30"
]
}
-6430
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -12,7 +12,7 @@ import { Fetcher } from "Components/Fetcher";
import { FaviconBadge } from "Components/FaviconBadge";
import { ErrorBoundary } from "./ErrorBoundary";
import "./App.css";
import "./App.scss";
class App extends Component {
static propTypes = {
+3 -3
View File
@@ -14,9 +14,9 @@ $dark: #3b4247;
// fix active tab color, for some reason it ends up with $primary as bg color
$nav-tabs-link-active-bg: #fff;
@import "../node_modules/bootswatch/dist/flatly/variables";
@import "../node_modules/bootstrap/scss/bootstrap";
@import "../node_modules/bootswatch/dist/flatly/bootswatch";
@import "~bootswatch/dist/flatly/variables";
@import "~bootstrap/scss/bootstrap";
@import "~bootswatch/dist/flatly/bootswatch";
// negative margin used for silence expiry badges with progress
.nmb-05 {
+14 -3
View File
@@ -12,6 +12,7 @@ beforeEach(() => {
});
afterEach(() => {
jest.restoreAllMocks();
window.history.pushState({}, "App", "/");
});
@@ -36,10 +37,13 @@ describe("<App />", () => {
})
);
// https://github.com/facebook/jest/issues/6798#issuecomment-412871616
const getItemSpy = jest.spyOn(Storage.prototype, "getItem");
const tree = shallow(<App defaultFilters={["ignore=defaults"]} />);
const instance = tree.instance();
expect(localStorage.getItem).toHaveBeenCalledWith("savedFilters");
expect(getItemSpy).toHaveBeenCalledWith("savedFilters");
expect(instance.alertStore.filters.values).toHaveLength(2);
expect(instance.alertStore.filters.values[0]).toMatchObject(
@@ -48,9 +52,11 @@ describe("<App />", () => {
expect(instance.alertStore.filters.values[1]).toMatchObject(
NewUnappliedFilter("abc!=cba")
);
getItemSpy.mockRestore();
});
it("ignores saved filters if 'preset' key is falsey (use passed defaults)", () => {
it("ignores saved filters if 'present' key is falsey (use passed defaults)", () => {
expect(window.location.search).toBe("");
localStorage.setItem(
"savedFilters",
@@ -60,15 +66,20 @@ describe("<App />", () => {
})
);
// https://github.com/facebook/jest/issues/6798#issuecomment-412871616
const getItemSpy = jest.spyOn(Storage.prototype, "getItem");
const tree = shallow(<App defaultFilters={["use=defaults"]} />);
const instance = tree.instance();
expect(localStorage.getItem).toHaveBeenCalledWith("savedFilters");
expect(getItemSpy).toHaveBeenCalledWith("savedFilters");
expect(instance.alertStore.filters.values).toHaveLength(1);
expect(instance.alertStore.filters.values[0]).toMatchObject(
NewUnappliedFilter("use=defaults")
);
getItemSpy.mockRestore();
});
it("uses filters passed via ?q= query args (ignoring saved filters and passed defaults)", () => {
+9
View File
@@ -2,7 +2,16 @@ import * as Sentry from "@sentry/browser";
import { SettingsElement, SetupSentry, ParseDefaultFilters } from "./AppBoot";
beforeAll(() => {
// https://github.com/jamesmfriedman/rmwc/issues/103#issuecomment-360007979
Object.defineProperty(window.HTMLElement.prototype, "dataset", {
writable: true,
value: {}
});
});
afterEach(() => {
jest.restoreAllMocks();
// reset sentry state before each mock, that's the only way to revert
// Sentry.init() that I found
global.__SENTRY__ = {};
@@ -11,6 +11,10 @@ beforeEach(() => {
alertStore = new AlertStore([]);
});
afterEach(() => {
jest.restoreAllMocks();
});
const MountedFaviconBadge = () => {
return mount(<FaviconBadge alertStore={alertStore} />);
};
+1
View File
@@ -34,6 +34,7 @@ beforeEach(() => {
afterEach(() => {
jest.clearAllTimers();
jest.clearAllMocks();
jest.restoreAllMocks();
clear();
});
@@ -52,6 +52,7 @@ exports[`<Alert /> matches snapshot with showAlertmanagers=false showReceiver=fa
role=\\"img\\"
xmlns=\\"http://www.w3.org/2000/svg\\"
viewbox=\\"0 0 320 512\\"
style=\\"width: 0.8rem;\\"
>
<path fill=\\"currentColor\\"
d=\\"M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z\\"
@@ -114,7 +114,7 @@ const GroupMenu = observer(
<Manager>
<Reference>
{({ ref }) => (
<a
<span
ref={ref}
onClick={this.collapse.toggle}
className={`text-muted cursor-pointer badge text-nowrap text-truncate pl-0 components-grid-alertgroup-${
@@ -123,7 +123,7 @@ const GroupMenu = observer(
data-toggle="dropdown"
>
<FontAwesomeIcon icon={faEllipsisV} />
</a>
</span>
)}
</Reference>
<DropdownSlide in={!this.collapse.value} unmountOnExit>
@@ -39,7 +39,7 @@ describe("<GroupMenu />", () => {
it("clicking toggle sets collapse value to 'false'", () => {
const group = MockAlertGroup({ alertname: "Fake Alert" }, [], [], {});
const tree = MountedGroupMenu(group);
const toggle = tree.find("a.cursor-pointer");
const toggle = tree.find(".cursor-pointer");
toggle.simulate("click");
expect(tree.instance().collapse.value).toBe(false);
});
@@ -48,7 +48,7 @@ describe("<GroupMenu />", () => {
const group = MockAlertGroup({ alertname: "Fake Alert" }, [], [], {});
const tree = MountedGroupMenu(group);
const toggle = tree.find("a.cursor-pointer");
const toggle = tree.find(".cursor-pointer");
toggle.simulate("click");
expect(tree.instance().collapse.value).toBe(false);
@@ -48,14 +48,14 @@ const GroupHeader = observer(
value="active"
counter={group.stateCount.active}
/>
<a
<span
className="text-muted cursor-pointer badge text-nowrap text-truncate pr-0"
onClick={collapseStore.toggle}
>
<FontAwesomeIcon
icon={collapseStore.value ? faChevronUp : faChevronDown}
/>
</a>
</span>
</span>
<span>
{Object.keys(group.labels).map(name => (
@@ -17,7 +17,7 @@ exports[`<Silence /> matches snapshot when data is present in alertStore 1`] = `
<span class=\\"text-muted my-1\\">
Fake silence
<span class=\\"blockquote-footer pt-1\\">
<a class=\\"float-right cursor-pointer\\">
<span class=\\"float-right cursor-pointer\\">
<svg aria-hidden=\\"true\\"
data-prefix=\\"fas\\"
data-icon=\\"chevron-up\\"
@@ -31,7 +31,7 @@ exports[`<Silence /> matches snapshot when data is present in alertStore 1`] = `
>
</path>
</svg>
</a>
</span>
<cite class=\\"components-grid-alertgroup-silences mr-2\\">
me@example.com
</cite>
@@ -65,7 +65,7 @@ exports[`<Silence /> matches snapshot with expaned details 1`] = `
<span class=\\"text-muted my-1\\">
Fake silence
<span class=\\"blockquote-footer pt-1\\">
<a class=\\"float-right cursor-pointer\\">
<span class=\\"float-right cursor-pointer\\">
<svg aria-hidden=\\"true\\"
data-prefix=\\"fas\\"
data-icon=\\"chevron-down\\"
@@ -79,7 +79,7 @@ exports[`<Silence /> matches snapshot with expaned details 1`] = `
>
</path>
</svg>
</a>
</span>
<cite class=\\"components-grid-alertgroup-silences mr-2\\">
me@example.com
</cite>
@@ -295,14 +295,14 @@ const Silence = inject("alertStore")(
<span className="text-muted my-1">
<SilenceComment silence={silence} />
<span className="blockquote-footer pt-1">
<a
<span
className="float-right cursor-pointer"
onClick={this.collapse.toggle}
>
<FontAwesomeIcon
icon={this.collapse.value ? faChevronUp : faChevronDown}
/>
</a>
</span>
<cite className="components-grid-alertgroup-silences mr-2">
{silence.createdBy}
</cite>
@@ -77,6 +77,7 @@ beforeEach(() => {
});
afterEach(() => {
jest.restoreAllMocks();
// reset Date() to current time
clear();
});
@@ -147,7 +148,7 @@ describe("<Silence />", () => {
it("clicking on expand toggle shows silence details", () => {
const tree = MountedSilence(alertmanager);
const toggle = tree.find("a.float-right.cursor-pointer");
const toggle = tree.find(".float-right.cursor-pointer");
toggle.simulate("click");
const details = tree.find("SilenceDetails");
expect(details).toHaveLength(1);
@@ -203,7 +204,7 @@ describe("<Silence />", () => {
const tree = MountedSilence(alertmanager);
// expand silence
tree.find("a.float-right.cursor-pointer").simulate("click");
tree.find(".float-right.cursor-pointer").simulate("click");
const button = tree.find(".badge-secondary.components-label-with-hover");
expect(button.text()).toBe("Edit");
@@ -215,7 +216,7 @@ describe("<Silence />", () => {
const tree = MountedSilence(alertmanager);
// expand silence
tree.find("a.float-right.cursor-pointer").simulate("click");
tree.find(".float-right.cursor-pointer").simulate("click");
const button = tree.find(".badge-secondary.components-label-with-hover");
expect(button.text()).toBe("Edit");
@@ -18,6 +18,10 @@ beforeEach(() => {
silenceFormStore = new SilenceFormStore();
});
afterEach(() => {
jest.restoreAllMocks();
});
const ShallowAlertGrid = () => {
return shallow(
<AlertGrid
@@ -13,6 +13,7 @@ beforeEach(() => {
afterEach(() => {
jest.clearAllTimers();
jest.restoreAllMocks();
});
describe("<UpgradeNeeded />", () => {
@@ -22,7 +23,9 @@ describe("<UpgradeNeeded />", () => {
});
it("calls window.location.reload after timer is done", () => {
const reloadSpy = jest.spyOn(global.window.location, "reload");
const reloadSpy = jest
.spyOn(global.window.location, "reload")
.mockImplementation(() => {});
mount(<UpgradeNeeded newVersion="1.2.3" />);
jest.runOnlyPendingTimers();
expect(reloadSpy).toBeCalled();
@@ -3,6 +3,7 @@ import PropTypes from "prop-types";
import { inject, observer } from "mobx-react";
import { AlertStore } from "Stores/AlertStore";
import { BaseLabel } from "Components/Labels/BaseLabel";
// Same as FilteringLabel but for labels that are counters (usually @state)
@@ -12,7 +13,9 @@ const FilteringCounterBadge = inject("alertStore")(
observer(
class FilteringCounterBadge extends BaseLabel {
static propTypes = {
...BaseLabel.propTypes,
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
name: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
counter: PropTypes.number.isRequired
};
@@ -5,6 +5,7 @@ import { observer } from "mobx-react";
import { DefaultLabelClass } from "Common/Colors";
import { QueryOperators } from "Common/Query";
import { AlertStore } from "Stores/AlertStore";
import { BaseLabel } from "Components/Labels/BaseLabel";
import "./index.css";
@@ -12,7 +13,9 @@ import "./index.css";
const HistoryLabel = observer(
class HistoryLabel extends BaseLabel {
static propTypes = {
...BaseLabel.propTypes,
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
name: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
matcher: PropTypes.string.isRequired
};
@@ -8,7 +8,7 @@ import InputRange from "react-input-range";
import { Settings } from "Stores/Settings";
import "./InputRange.css";
import "./InputRange.scss";
const AlertGroupConfiguration = observer(
class AlertGroupConfiguration extends Component {
@@ -8,7 +8,7 @@ import InputRange from "react-input-range";
import { Settings } from "Stores/Settings";
import "./InputRange.css";
import "./InputRange.scss";
const FetchConfiguration = observer(
class FetchConfiguration extends Component {
@@ -1,81 +0,0 @@
.input-range__slider {
appearance: none;
background: #2C3E50;
border: 1px solid #2C3E50;
border-radius: 100%;
cursor: pointer;
display: block;
height: 1rem;
margin-left: -0.5rem;
margin-top: -0.65rem;
outline: none;
position: absolute;
top: 50%;
transition: transform 0.3s ease-out, box-shadow 0.3s ease-out;
width: 1rem; }
.input-range__slider:active {
transform: scale(1.3); }
.input-range__slider:focus {
box-shadow: 0 0 0 5px rgba(44, 62, 80, 0.2); }
.input-range--disabled .input-range__slider {
background: #95a5a6;
border: 1px solid #95a5a6;
box-shadow: none;
transform: none; }
.input-range__slider-container {
transition: left 0.3s ease-out; }
.input-range__label {
color: #7b8a8b;
font-family: "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 0.8rem;
transform: translateZ(0);
white-space: nowrap; }
.input-range__label--min,
.input-range__label--max {
bottom: -1.4rem;
position: absolute; }
.input-range__label--min {
left: 0; }
.input-range__label--max {
right: 0; }
.input-range__label--value {
position: absolute;
top: -1.8rem; }
.input-range__label-container {
left: -50%;
position: relative; }
.input-range__label--max .input-range__label-container {
left: 50%; }
.input-range__track {
background: #ecf0f1;
border-radius: 0.3rem;
cursor: pointer;
display: block;
height: 0.3rem;
position: relative;
transition: left 0.3s ease-out, width 0.3s ease-out; }
.input-range--disabled .input-range__track {
background: #ecf0f1; }
.input-range__track--background {
left: 0;
margin-top: -0.15rem;
position: absolute;
right: 0;
top: 50%; }
.input-range__track--active {
background: #2C3E50; }
.input-range {
height: 1rem;
position: relative;
width: 100%; }
@@ -1,5 +1,5 @@
// customize colors and fonts using bootstrap variables
@import "../../../../node_modules/bootswatch/dist/flatly/variables";
@import "~bootswatch/dist/flatly/variables";
$input-range-font-family: $font-family-sans-serif;
$input-range-primary-color: $primary;
@@ -7,4 +7,4 @@ $input-range-neutral-color: $dark;
$input-range-neutral-light-color: $light;
$input-range-disabled-color: $secondary;
@import "../../../../node_modules/react-input-range/src/scss/input-range/input-range";
@import "~react-input-range/src/scss/input-range/input-range";
@@ -13,14 +13,14 @@ import { Configuration } from "./Configuration";
import { Help } from "./Help";
const Tab = ({ title, active, onClick }) => (
<a
<span
className={`nav-item nav-link cursor-pointer ${
active ? "active" : "text-primary"
}`}
onClick={onClick}
>
{title}
</a>
</span>
);
Tab.propTypes = {
title: PropTypes.string.isRequired,
@@ -18,6 +18,10 @@ beforeEach(() => {
onHide.mockClear();
});
afterEach(() => {
jest.restoreAllMocks();
});
const FakeModal = () => {
return mount(
<MainModalContent
@@ -11,12 +11,12 @@ exports[`<MainModalContent /> matches snapshot 1`] = `
<div class=\\"modal-content\\">
<div class=\\"modal-header py-2\\">
<nav class=\\"nav nav-pills nav-justified w-100\\">
<a class=\\"nav-item nav-link cursor-pointer active\\">
<span class=\\"nav-item nav-link cursor-pointer active\\">
Configuration
</a>
<a class=\\"nav-item nav-link cursor-pointer text-primary\\">
</span>
<span class=\\"nav-item nav-link cursor-pointer text-primary\\">
Help
</a>
</span>
<button type=\\"button\\"
class=\\"close\\"
>
+5 -2
View File
@@ -46,9 +46,12 @@ const MainModal = observer(
return (
<React.Fragment>
<li className="nav-item">
<a className="nav-link cursor-pointer" onClick={this.toggle.toggle}>
<span
className="nav-link cursor-pointer"
onClick={this.toggle.toggle}
>
<FontAwesomeIcon icon={faCog} />
</a>
</span>
</li>
<MountFade in={this.toggle.show} unmountOnExit>
<MainModalContent
@@ -18,6 +18,10 @@ beforeEach(() => {
fetch.resetMocks();
});
afterEach(() => {
jest.restoreAllMocks();
});
const MountedInput = () => {
return mount(
<FilterInput alertStore={alertStore} settingsStore={settingsStore} />
@@ -25,7 +25,7 @@ exports[`<NavBar /> matches snapshot with 0 alerts 1`] = `
</span>
<ul class=\\"navbar-nav float-right d-flex flex-row\\">
<li class=\\"nav-item\\">
<a class=\\"nav-link cursor-pointer\\">
<span class=\\"nav-link cursor-pointer\\">
<svg aria-hidden=\\"true\\"
data-prefix=\\"fas\\"
data-icon=\\"bell-slash\\"
@@ -39,10 +39,10 @@ exports[`<NavBar /> matches snapshot with 0 alerts 1`] = `
>
</path>
</svg>
</a>
</span>
</li>
<li class=\\"nav-item\\">
<a class=\\"nav-link cursor-pointer\\">
<span class=\\"nav-link cursor-pointer\\">
<svg aria-hidden=\\"true\\"
data-prefix=\\"fas\\"
data-icon=\\"cog\\"
@@ -56,7 +56,7 @@ exports[`<NavBar /> matches snapshot with 0 alerts 1`] = `
>
</path>
</svg>
</a>
</span>
</li>
</ul>
<form class=\\"form-inline mw-100\\"
@@ -156,7 +156,7 @@ exports[`<NavBar /> matches snapshot with 5 alerts 1`] = `
</span>
<ul class=\\"navbar-nav float-right d-flex flex-row\\">
<li class=\\"nav-item\\">
<a class=\\"nav-link cursor-pointer\\">
<span class=\\"nav-link cursor-pointer\\">
<svg aria-hidden=\\"true\\"
data-prefix=\\"fas\\"
data-icon=\\"bell-slash\\"
@@ -170,10 +170,10 @@ exports[`<NavBar /> matches snapshot with 5 alerts 1`] = `
>
</path>
</svg>
</a>
</span>
</li>
<li class=\\"nav-item\\">
<a class=\\"nav-link cursor-pointer\\">
<span class=\\"nav-link cursor-pointer\\">
<svg aria-hidden=\\"true\\"
data-prefix=\\"fas\\"
data-icon=\\"cog\\"
@@ -187,7 +187,7 @@ exports[`<NavBar /> matches snapshot with 5 alerts 1`] = `
>
</path>
</svg>
</a>
</span>
</li>
</ul>
<form class=\\"form-inline mw-100\\"
@@ -1,484 +0,0 @@
.react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle, .react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle, .react-datepicker__year-read-view--down-arrow,
.react-datepicker__month-read-view--down-arrow,
.react-datepicker__month-year-read-view--down-arrow {
margin-left: -8px;
position: absolute; }
.react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle, .react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle, .react-datepicker__year-read-view--down-arrow,
.react-datepicker__month-read-view--down-arrow,
.react-datepicker__month-year-read-view--down-arrow, .react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle::before, .react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle::before, .react-datepicker__year-read-view--down-arrow::before,
.react-datepicker__month-read-view--down-arrow::before,
.react-datepicker__month-year-read-view--down-arrow::before {
box-sizing: content-box;
position: absolute;
border: 8px solid transparent;
height: 0;
width: 1px; }
.react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle::before, .react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle::before, .react-datepicker__year-read-view--down-arrow::before,
.react-datepicker__month-read-view--down-arrow::before,
.react-datepicker__month-year-read-view--down-arrow::before {
content: "";
z-index: -1;
border-width: 8px;
left: -8px;
border-bottom-color: #dee2e6; }
.react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle {
top: 0;
margin-top: -8px; }
.react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle, .react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle::before {
border-top: none;
border-bottom-color: #fff; }
.react-datepicker-popper[data-placement^="bottom"] .react-datepicker__triangle::before {
top: -1px;
border-bottom-color: #dee2e6; }
.react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle, .react-datepicker__year-read-view--down-arrow,
.react-datepicker__month-read-view--down-arrow,
.react-datepicker__month-year-read-view--down-arrow {
bottom: 0;
margin-bottom: -8px; }
.react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle, .react-datepicker__year-read-view--down-arrow,
.react-datepicker__month-read-view--down-arrow,
.react-datepicker__month-year-read-view--down-arrow, .react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle::before, .react-datepicker__year-read-view--down-arrow::before,
.react-datepicker__month-read-view--down-arrow::before,
.react-datepicker__month-year-read-view--down-arrow::before {
border-bottom: none;
border-top-color: #fff; }
.react-datepicker-popper[data-placement^="top"] .react-datepicker__triangle::before, .react-datepicker__year-read-view--down-arrow::before,
.react-datepicker__month-read-view--down-arrow::before,
.react-datepicker__month-year-read-view--down-arrow::before {
bottom: -1px;
border-top-color: #dee2e6; }
.react-datepicker-wrapper {
display: inline-block; }
.react-datepicker {
font-family: "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 0.9375rem;
background-color: #fff;
color: #000;
border: 1px solid #dee2e6;
border-radius: 0.25rem;
display: inline-block;
position: relative; }
.react-datepicker--time-only .react-datepicker__triangle {
left: 35px; }
.react-datepicker--time-only .react-datepicker__time-container {
border-left: 0; }
.react-datepicker--time-only .react-datepicker__time {
border-radius: 0.3rem; }
.react-datepicker--time-only .react-datepicker__time-box {
border-radius: 0.3rem; }
.react-datepicker__triangle {
position: absolute;
left: 50px; }
.react-datepicker-popper {
z-index: 1; }
.react-datepicker-popper[data-placement^="bottom"] {
margin-top: 10px; }
.react-datepicker-popper[data-placement^="top"] {
margin-bottom: 10px; }
.react-datepicker-popper[data-placement^="right"] {
margin-left: 8px; }
.react-datepicker-popper[data-placement^="right"] .react-datepicker__triangle {
left: auto;
right: 42px; }
.react-datepicker-popper[data-placement^="left"] {
margin-right: 8px; }
.react-datepicker-popper[data-placement^="left"] .react-datepicker__triangle {
left: 42px;
right: auto; }
.react-datepicker__header {
text-align: center;
background-color: #fff;
border-bottom: 1px solid #dee2e6;
border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem;
padding-top: 8px;
position: relative; }
.react-datepicker__header--time {
padding-bottom: 8px;
padding-left: 5px;
padding-right: 5px; }
.react-datepicker__year-dropdown-container--select,
.react-datepicker__month-dropdown-container--select,
.react-datepicker__month-year-dropdown-container--select,
.react-datepicker__year-dropdown-container--scroll,
.react-datepicker__month-dropdown-container--scroll,
.react-datepicker__month-year-dropdown-container--scroll {
display: inline-block;
margin: 0 2px; }
.react-datepicker__current-month,
.react-datepicker-time__header {
margin-top: 0;
color: #000;
font-weight: bold;
font-size: 1.10625rem; }
.react-datepicker-time__header {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden; }
.react-datepicker__navigation {
background: none;
line-height: 1.7rem;
text-align: center;
cursor: pointer;
position: absolute;
top: 10px;
width: 0;
padding: 0;
border: 0.45rem solid transparent;
z-index: 1;
height: 10px;
width: 10px;
text-indent: -999em;
overflow: hidden; }
.react-datepicker__navigation--previous {
left: 10px;
border-right-color: #95a5a6; }
.react-datepicker__navigation--previous:hover {
border-right-color: #798d8f; }
.react-datepicker__navigation--previous--disabled, .react-datepicker__navigation--previous--disabled:hover {
border-right-color: #95a5a6;
cursor: default; }
.react-datepicker__navigation--next {
right: 10px;
border-left-color: #95a5a6; }
.react-datepicker__navigation--next--with-time:not(.react-datepicker__navigation--next--with-today-button) {
right: 80px; }
.react-datepicker__navigation--next:hover {
border-left-color: #798d8f; }
.react-datepicker__navigation--next--disabled, .react-datepicker__navigation--next--disabled:hover {
border-left-color: #95a5a6;
cursor: default; }
.react-datepicker__navigation--years {
position: relative;
top: 0;
display: block;
margin-left: auto;
margin-right: auto; }
.react-datepicker__navigation--years-previous {
top: 4px;
border-top-color: #95a5a6; }
.react-datepicker__navigation--years-previous:hover {
border-top-color: #798d8f; }
.react-datepicker__navigation--years-upcoming {
top: -4px;
border-bottom-color: #95a5a6; }
.react-datepicker__navigation--years-upcoming:hover {
border-bottom-color: #798d8f; }
.react-datepicker__month-container {
float: left; }
.react-datepicker__month {
margin: 0.4rem;
text-align: center; }
.react-datepicker__time-container {
float: right;
border-left: 1px solid #dee2e6;
width: 70px; }
.react-datepicker__time-container--with-today-button {
display: inline;
border: 1px solid #aeaeae;
border-radius: 0.3rem;
position: absolute;
right: -72px;
top: 0; }
.react-datepicker__time-container .react-datepicker__time {
position: relative;
background: white; }
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box {
width: 70px;
overflow-x: hidden;
margin: 0 auto;
text-align: center; }
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list {
list-style: none;
margin: 0;
height: calc(195px + (1.7rem / 2));
overflow-y: scroll;
padding-right: 30px;
width: 100%;
box-sizing: content-box; }
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item {
padding: 5px 10px; }
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item:hover {
cursor: pointer;
background-color: #fff; }
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected {
background-color: #2C3E50;
color: white;
font-weight: bold; }
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected:hover {
background-color: #2C3E50; }
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--disabled {
color: #95a5a6; }
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--disabled:hover {
cursor: default;
background-color: transparent; }
.react-datepicker__week-number {
color: #95a5a6;
display: inline-block;
width: 1.7rem;
line-height: 1.7rem;
text-align: center;
margin: 0.166rem; }
.react-datepicker__week-number.react-datepicker__week-number--clickable {
cursor: pointer; }
.react-datepicker__week-number.react-datepicker__week-number--clickable:hover {
border-radius: 0.25rem;
background-color: #fff; }
.react-datepicker__day-names,
.react-datepicker__week {
white-space: nowrap; }
.react-datepicker__day-name,
.react-datepicker__day,
.react-datepicker__time-name {
color: #000;
display: inline-block;
width: 1.7rem;
line-height: 1.7rem;
text-align: center;
margin: 0.166rem; }
.react-datepicker__day {
cursor: pointer; }
.react-datepicker__day:hover {
border-radius: 0.25rem;
background-color: #fff; }
.react-datepicker__day--today {
font-weight: bold; }
.react-datepicker__day--highlighted {
border-radius: 0.25rem;
background-color: #95a5a6;
color: #fff; }
.react-datepicker__day--highlighted:hover {
background-color: #87999a; }
.react-datepicker__day--highlighted-custom-1 {
color: magenta; }
.react-datepicker__day--highlighted-custom-2 {
color: green; }
.react-datepicker__day--selected, .react-datepicker__day--in-selecting-range, .react-datepicker__day--in-range {
border-radius: 0.25rem;
background-color: #2C3E50;
color: #fff; }
.react-datepicker__day--selected:hover, .react-datepicker__day--in-selecting-range:hover, .react-datepicker__day--in-range:hover {
background-color: #233140; }
.react-datepicker__day--keyboard-selected {
border-radius: 0.25rem;
background-color: #3e5871;
color: #fff; }
.react-datepicker__day--keyboard-selected:hover {
background-color: #233140; }
.react-datepicker__day--in-selecting-range:not(.react-datepicker__day--in-range) {
background-color: rgba(44, 62, 80, 0.5); }
.react-datepicker__month--selecting-range .react-datepicker__day--in-range:not(.react-datepicker__day--in-selecting-range) {
background-color: #fff;
color: #000; }
.react-datepicker__day--disabled {
cursor: default;
color: #95a5a6; }
.react-datepicker__day--disabled:hover {
background-color: transparent; }
.react-datepicker__input-container {
position: relative;
display: inline-block; }
.react-datepicker__year-read-view,
.react-datepicker__month-read-view,
.react-datepicker__month-year-read-view {
border: 1px solid transparent;
border-radius: 0.25rem; }
.react-datepicker__year-read-view:hover,
.react-datepicker__month-read-view:hover,
.react-datepicker__month-year-read-view:hover {
cursor: pointer; }
.react-datepicker__year-read-view:hover .react-datepicker__year-read-view--down-arrow,
.react-datepicker__year-read-view:hover .react-datepicker__month-read-view--down-arrow,
.react-datepicker__month-read-view:hover .react-datepicker__year-read-view--down-arrow,
.react-datepicker__month-read-view:hover .react-datepicker__month-read-view--down-arrow,
.react-datepicker__month-year-read-view:hover .react-datepicker__year-read-view--down-arrow,
.react-datepicker__month-year-read-view:hover .react-datepicker__month-read-view--down-arrow {
border-top-color: #798d8f; }
.react-datepicker__year-read-view--down-arrow,
.react-datepicker__month-read-view--down-arrow,
.react-datepicker__month-year-read-view--down-arrow {
border-top-color: #95a5a6;
float: right;
margin-left: 20px;
top: 8px;
position: relative;
border-width: 0.45rem; }
.react-datepicker__year-dropdown,
.react-datepicker__month-dropdown,
.react-datepicker__month-year-dropdown {
background-color: #fff;
position: absolute;
width: 50%;
left: 25%;
top: 30px;
z-index: 1;
text-align: center;
border-radius: 0.25rem;
border: 1px solid #dee2e6; }
.react-datepicker__year-dropdown:hover,
.react-datepicker__month-dropdown:hover,
.react-datepicker__month-year-dropdown:hover {
cursor: pointer; }
.react-datepicker__year-dropdown--scrollable,
.react-datepicker__month-dropdown--scrollable,
.react-datepicker__month-year-dropdown--scrollable {
height: 150px;
overflow-y: scroll; }
.react-datepicker__year-option,
.react-datepicker__month-option,
.react-datepicker__month-year-option {
line-height: 20px;
width: 100%;
display: block;
margin-left: auto;
margin-right: auto; }
.react-datepicker__year-option:first-of-type,
.react-datepicker__month-option:first-of-type,
.react-datepicker__month-year-option:first-of-type {
border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem; }
.react-datepicker__year-option:last-of-type,
.react-datepicker__month-option:last-of-type,
.react-datepicker__month-year-option:last-of-type {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
border-bottom-left-radius: 0.25rem;
border-bottom-right-radius: 0.25rem; }
.react-datepicker__year-option:hover,
.react-datepicker__month-option:hover,
.react-datepicker__month-year-option:hover {
background-color: #95a5a6; }
.react-datepicker__year-option:hover .react-datepicker__navigation--years-upcoming,
.react-datepicker__month-option:hover .react-datepicker__navigation--years-upcoming,
.react-datepicker__month-year-option:hover .react-datepicker__navigation--years-upcoming {
border-bottom-color: #798d8f; }
.react-datepicker__year-option:hover .react-datepicker__navigation--years-previous,
.react-datepicker__month-option:hover .react-datepicker__navigation--years-previous,
.react-datepicker__month-year-option:hover .react-datepicker__navigation--years-previous {
border-top-color: #798d8f; }
.react-datepicker__year-option--selected,
.react-datepicker__month-option--selected,
.react-datepicker__month-year-option--selected {
position: absolute;
left: 15px; }
.react-datepicker__close-icon {
background-color: transparent;
border: 0;
cursor: pointer;
outline: 0;
padding: 0;
vertical-align: middle;
position: absolute;
height: 16px;
width: 16px;
top: 25%;
right: 7px; }
.react-datepicker__close-icon::after {
background-color: #2C3E50;
border-radius: 50%;
bottom: 0;
box-sizing: border-box;
color: #fff;
content: "\00d7";
cursor: pointer;
font-size: 12px;
height: 16px;
width: 16px;
line-height: 1;
margin: -8px auto 0;
padding: 2px;
position: absolute;
right: 0px;
text-align: center; }
.react-datepicker__today-button {
background: #fff;
border-top: 1px solid #dee2e6;
cursor: pointer;
text-align: center;
font-weight: bold;
padding: 5px 0;
clear: left; }
.react-datepicker__portal {
position: fixed;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.8);
left: 0;
top: 0;
justify-content: center;
align-items: center;
display: flex;
z-index: 2147483647; }
.react-datepicker__portal .react-datepicker__day-name,
.react-datepicker__portal .react-datepicker__day,
.react-datepicker__portal .react-datepicker__time-name {
width: 3rem;
line-height: 3rem; }
@media (max-width: 400px), (max-height: 550px) {
.react-datepicker__portal .react-datepicker__day-name,
.react-datepicker__portal .react-datepicker__day,
.react-datepicker__portal .react-datepicker__time-name {
width: 2rem;
line-height: 2rem; } }
.react-datepicker__portal .react-datepicker__current-month,
.react-datepicker__portal .react-datepicker-time__header {
font-size: 1.6875rem; }
.react-datepicker__portal .react-datepicker__navigation {
border: 0.81rem solid transparent; }
.react-datepicker__portal .react-datepicker__navigation--previous {
border-right-color: #95a5a6; }
.react-datepicker__portal .react-datepicker__navigation--previous:hover {
border-right-color: #798d8f; }
.react-datepicker__portal .react-datepicker__navigation--previous--disabled, .react-datepicker__portal .react-datepicker__navigation--previous--disabled:hover {
border-right-color: #95a5a6;
cursor: default; }
.react-datepicker__portal .react-datepicker__navigation--next {
border-left-color: #95a5a6; }
.react-datepicker__portal .react-datepicker__navigation--next:hover {
border-left-color: #798d8f; }
.react-datepicker__portal .react-datepicker__navigation--next--disabled, .react-datepicker__portal .react-datepicker__navigation--next--disabled:hover {
border-left-color: #95a5a6;
cursor: default; }
.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: #dee2e6; }
.react-datepicker__today-button {
border-bottom-left-radius: 0.25rem;
border-bottom-right-radius: 0.25rem; }
@@ -12,7 +12,7 @@ import { SilenceFormStore } from "Stores/SilenceFormStore";
import { Duration } from "./Duration";
import { HourMinute } from "./HourMinute";
import "./index.css";
import "./index.scss";
const OffsetBadge = ({ startDate, endDate, prefixLabel }) => {
const days = endDate.diff(startDate, "days");
@@ -36,12 +36,12 @@ OffsetBadge.propTypes = {
const Tab = ({ title, active, onClick }) => (
<li className="nav-item">
<a
<span
className={`nav-link cursor-pointer ${active ? "active" : ""}`}
onClick={onClick}
>
{title}
</a>
</span>
</li>
);
Tab.propTypes = {
@@ -1,4 +1,4 @@
@import "../../../../node_modules/bootswatch/dist/flatly/variables";
@import "~bootswatch/dist/flatly/variables";
$datepicker__background-color: $white;
$datepicker__border-color: $gray-300;
@@ -12,7 +12,7 @@ $datepicker__font-size: $font-size-base;
$datepicker__font-family: $font-family-sans-serif;
$datepicker__border-radius: 0.25rem;
@import "../../../../node_modules/react-datepicker/src/stylesheets/datepicker.scss";
@import "~react-datepicker/src/stylesheets/datepicker.scss";
.react-datepicker__day:not(.react-datepicker__day--disabled) {
border-width: 1px;
@@ -26,6 +26,10 @@ beforeEach(() => {
];
});
afterEach(() => {
jest.restoreAllMocks();
});
const ShallowLabelNameInput = isValid => {
return shallow(<LabelNameInput matcher={matcher} isValid={isValid} />);
};
@@ -174,14 +174,14 @@ const SilenceForm = observer(
onChange={this.onCommentChange}
/>
<div className="d-flex flex-row justify-content-between">
<a
<span
className="btn px-0 cursor-pointer text-muted"
onClick={this.previewCollapse.toggle}
>
<FontAwesomeIcon
icon={this.previewCollapse.hidden ? faChevronUp : faChevronDown}
/>
</a>
</span>
<span>
{silenceFormStore.data.silenceID === null ? null : (
<button
@@ -110,7 +110,7 @@ describe("<SilenceForm /> preview", () => {
it("clicking on the toggle icon toggles SilencePreview", () => {
const tree = ShallowSilenceForm();
const button = tree.find("a.btn.cursor-pointer.text-muted");
const button = tree.find(".btn.cursor-pointer.text-muted");
expect(tree.find("SilencePreview")).toHaveLength(0);
button.simulate("click");
expect(tree.find("SilencePreview")).toHaveLength(1);
+5 -2
View File
@@ -53,9 +53,12 @@ const SilenceModal = observer(
return (
<React.Fragment>
<li className="nav-item">
<a className="nav-link cursor-pointer" onClick={this.toggleModal}>
<span
className="nav-link cursor-pointer"
onClick={this.toggleModal}
>
<FontAwesomeIcon icon={faBellSlash} />
</a>
</span>
</li>
<MountFade in={silenceFormStore.toggle.visible} unmountOnExit>
<SilenceModalContent
+5 -4
View File
@@ -8,12 +8,16 @@ import * as Sentry from "@sentry/browser";
import { ErrorBoundary } from "./ErrorBoundary";
let consoleSpy;
beforeEach(() => {
jest.useFakeTimers();
consoleSpy = jest.spyOn(console, "error").mockImplementation(() => {});
});
afterEach(() => {
jest.clearAllTimers();
jest.restoreAllMocks();
});
const FailingComponent = () => {
@@ -32,6 +36,7 @@ describe("<ErrorBoundary />", () => {
it("matches snapshot", () => {
const tree = MountedFailingComponent();
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
expect(consoleSpy).toHaveBeenCalled();
});
it("componentDidCatch should catch an error from FailingComponent", () => {
@@ -61,9 +66,6 @@ describe("<ErrorBoundary />", () => {
});
it("calls window.location.reload after 60s", () => {
const consoleSpy = jest
.spyOn(console, "error")
.mockImplementation(() => {});
const reloadSpy = jest.spyOn(global.window.location, "reload");
MountedFailingComponent();
jest.runTimersToTime(1000 * 61);
@@ -72,7 +74,6 @@ describe("<ErrorBoundary />", () => {
});
it("reloadSeconds is 40 after 20s with multiple exceptions", () => {
jest.spyOn(console, "error").mockImplementation(() => {});
const tree = MountedFailingComponent();
const instance = tree.instance();
+2
View File
@@ -14,6 +14,7 @@ beforeEach(() => {
});
afterEach(() => {
jest.restoreAllMocks();
// wipe REACT_APP_BACKEND_URI env on each run as it's used by some tests
delete process.env.REACT_APP_BACKEND_URI;
});
@@ -279,6 +280,7 @@ describe("AlertStore.fetch", () => {
});
it("unapplied filters are marked as applied on fetch error", async () => {
jest.spyOn(console, "trace").mockImplementation(() => {});
fetch.mockReject("Fetch error");
const store = new AlertStore([NewUnappliedFilter("foo")]);
store.filters.values[0].applied = false;
+2 -5
View File
@@ -4,14 +4,11 @@ import Adapter from "enzyme-adapter-react-16";
// https://github.com/airbnb/enzyme
Enzyme.configure({ adapter: new Adapter() });
// localStorage is used for Settings store
require("jest-localstorage-mock");
// favico.js needs canvas
require("jest-canvas-mock");
import("jest-canvas-mock");
// used to mock current time since we render moment.fromNow() in some places
require("jest-date-mock");
import("jest-date-mock");
// fetch is used in multiple places to interact with Go backend
// or upstream Alertmanager API
+4088 -2418
View File
File diff suppressed because it is too large Load Diff