diff --git a/ui/src/Components/Animations/MountFade/index.css b/ui/src/Components/Animations/MountFade/index.css
index 37b4a1323..ae5a758ab 100644
--- a/ui/src/Components/Animations/MountFade/index.css
+++ b/ui/src/Components/Animations/MountFade/index.css
@@ -1,17 +1,7 @@
-.components-animation-fade-appear,
-.components-animation-fade-enter {
+.components-animation-fade-appear {
opacity: 0.01;
}
-.components-animation-fade-appear-active,
-.components-animation-fade-enter-active {
+.components-animation-fade-appear-active {
opacity: 1;
transition: all 0.3s ease-in;
}
-
-.components-animation-fade-exit {
- opacity: 1;
-}
-.components-animation-fade-exit-active {
- opacity: 0.01;
- transition: all 0.3s ease-out;
-}
diff --git a/ui/src/Components/Animations/MountFade/index.js b/ui/src/Components/Animations/MountFade/index.js
index 5ae94cf10..e7104b3af 100644
--- a/ui/src/Components/Animations/MountFade/index.js
+++ b/ui/src/Components/Animations/MountFade/index.js
@@ -10,8 +10,6 @@ const MountFade = ({ children, duration, ...props }) => (
classNames="components-animation-fade"
timeout={300}
appear={true}
- enter={true}
- exit={true}
{...props}
>
{children}
diff --git a/ui/src/Components/Grid/AlertGrid/GridSize.js b/ui/src/Components/Grid/AlertGrid/GridSize.js
index 97eae2934..b7923e300 100644
--- a/ui/src/Components/Grid/AlertGrid/GridSize.js
+++ b/ui/src/Components/Grid/AlertGrid/GridSize.js
@@ -1,33 +1,25 @@
-const baseWidth = 400;
-
-const MinWidth = canvasWidth =>
- Math.floor(
- baseWidth + (canvasWidth / Math.min(canvasWidth, baseWidth * 2)) * 10
- );
-
// grid sizes, defines how many columns are used depending on the screen width
// this is config as expected by https://github.com/callmecavs/bricks.js#sizes
-const GridSizesConfig = canvasWidth => {
+const GridSizesConfig = (canvasWidth, baseWidth) => {
const generatedSizes = [];
for (let i = 2; i < 20; i++) {
generatedSizes.push({
- mq: `${i * MinWidth(i * baseWidth)}px`,
+ mq: `${i * baseWidth}px`,
columns: i,
gutter: 0
});
}
- //console.info(JSON.stringify(generatedSizes));
return [...[{ columns: 1, gutter: 0 }], ...generatedSizes];
};
-const GetGridElementWidth = canvasWidth => {
- const mw = MinWidth(canvasWidth);
- return Math.floor(
- Math.min(
- mw + (canvasWidth % mw) / Math.floor(canvasWidth / mw),
- canvasWidth
- )
- );
-};
+const GetColumnsCount = (canvasWidth, baseWidth) =>
+ [{ mq: "0px", columns: 1 }, ...GridSizesConfig(canvasWidth, baseWidth)]
+ .filter(gs => gs.mq !== undefined)
+ .filter(gs => canvasWidth >= Number.parseInt(gs.mq))
+ .map(gs => gs.columns)
+ .pop();
-export { MinWidth, GridSizesConfig, GetGridElementWidth };
+const GetGridElementWidth = (canvasWidth, baseWidth) =>
+ Math.floor(canvasWidth / GetColumnsCount(canvasWidth, baseWidth));
+
+export { GridSizesConfig, GetColumnsCount, GetGridElementWidth };
diff --git a/ui/src/Components/Grid/AlertGrid/index.js b/ui/src/Components/Grid/AlertGrid/index.js
index 032bc6502..5e5c350c6 100644
--- a/ui/src/Components/Grid/AlertGrid/index.js
+++ b/ui/src/Components/Grid/AlertGrid/index.js
@@ -42,14 +42,20 @@ const AlertGrid = observer(
this.viewport = observable(
{
width: document.body.clientWidth,
- get gridSizesConfig() {
- return GridSizesConfig(this.width);
- },
- get groupWidth() {
- return GetGridElementWidth(this.width);
- },
update() {
this.width = document.body.clientWidth;
+ },
+ get gridSizesConfig() {
+ return GridSizesConfig(
+ this.width,
+ props.settingsStore.gridConfig.config.groupWidth
+ );
+ },
+ get groupWidth() {
+ return GetGridElementWidth(
+ this.width,
+ props.settingsStore.gridConfig.config.groupWidth
+ );
}
},
{
@@ -194,12 +200,6 @@ const AlertGrid = observer(
font700.load(null, 30000).then(this.masonryRepack, () => {});
}
- componentDidUpdate() {
- // whenever grid component re-renders we need to ensure that grid elements
- // are packed correctly
- this.masonryRepack();
- }
-
render() {
const { alertStore, settingsStore, silenceFormStore } = this.props;
@@ -210,6 +210,7 @@ const AlertGrid = observer(
onResize={debounce(this.viewport.update, 100)}
/>
", () => {
expect(alertGroups).toHaveLength(80);
});
- it("calls masonryRepack() after update`", () => {
+ it("calling masonryRepack() calls forcePack() on Masonry instance`", () => {
const tree = ShallowAlertGrid();
const instance = tree.instance();
- const repackSpy = jest.spyOn(instance, "masonryRepack");
// it's a shallow render so we don't really have masonry mounted, fake it
instance.masonryComponentReference.ref = {
forcePack: jest.fn()
};
- instance.componentDidUpdate();
- expect(repackSpy).toHaveBeenCalled();
+ instance.masonryRepack();
expect(instance.masonryComponentReference.ref.forcePack).toHaveBeenCalled();
});
it("masonryRepack() doesn't crash when masonryComponentReference.ref=false`", () => {
const tree = ShallowAlertGrid();
const instance = tree.instance();
- const repackSpy = jest.spyOn(instance, "masonryRepack");
instance.masonryComponentReference.ref = false;
- instance.componentDidUpdate();
- expect(repackSpy).toHaveBeenCalled();
+ instance.masonryRepack();
});
it("masonryRepack() doesn't crash when masonryComponentReference.ref=null`", () => {
const tree = ShallowAlertGrid();
const instance = tree.instance();
- const repackSpy = jest.spyOn(instance, "masonryRepack");
instance.masonryComponentReference.ref = null;
- instance.componentDidUpdate();
- expect(repackSpy).toHaveBeenCalled();
+ instance.masonryRepack();
});
it("masonryRepack() doesn't crash when masonryComponentReference.ref=undefined`", () => {
const tree = ShallowAlertGrid();
const instance = tree.instance();
- const repackSpy = jest.spyOn(instance, "masonryRepack");
instance.masonryComponentReference.ref = undefined;
- instance.componentDidUpdate();
- expect(repackSpy).toHaveBeenCalled();
+ instance.masonryRepack();
});
it("calling storeMasonryRef() saves the ref in local store", () => {
@@ -345,17 +337,18 @@ describe("", () => {
// known breakpoints calculated from GridSize logic
[
{ breakpoint: 400, columns: 1 },
- { breakpoint: 820, columns: 2 },
- { breakpoint: 1245, columns: 3 },
- { breakpoint: 1680, columns: 4 },
- { breakpoint: 2125, columns: 5 },
- { breakpoint: 2580, columns: 6 },
- { breakpoint: 3045, columns: 7 },
- { breakpoint: 3520, columns: 8 },
- { breakpoint: 4005, columns: 9 },
- { breakpoint: 4500, columns: 1 }
+ { breakpoint: 800, columns: 2 },
+ { breakpoint: 1200, columns: 3 },
+ { breakpoint: 1600, columns: 4 },
+ { breakpoint: 2000, columns: 5 },
+ { breakpoint: 2400, columns: 6 },
+ { breakpoint: 3000, columns: 7 },
+ { breakpoint: 3400, columns: 8 },
+ { breakpoint: 3800, columns: 9 },
+ { breakpoint: 4200, columns: 10 }
].map(t =>
it(`renders ${t.columns} column(s) on ${t.breakpoint} breakpoint`, () => {
+ settingsStore.gridConfig.config.groupWidth = 400;
VerifyColumnCount(t.canvas - 1, Math.max(1, t.columns - 1));
VerifyColumnCount(t.canvas, t.columns);
VerifyColumnCount(t.canvas + 1, t.columns);
@@ -369,24 +362,25 @@ describe("", () => {
{ canvas: 1280, columns: 3 },
{ canvas: 1366, columns: 3 },
{ canvas: 1440, columns: 3 },
- { canvas: 1600, columns: 3 },
- { canvas: 1680, columns: 3 },
+ { canvas: 1600, columns: 4 },
+ { canvas: 1680, columns: 4 },
{ canvas: 1920, columns: 4 },
- { canvas: 2048, columns: 4 },
- { canvas: 2560, columns: 5 },
- { canvas: 3840, columns: 8 }
+ { canvas: 2048, columns: 5 },
+ { canvas: 2560, columns: 6 },
+ { canvas: 3840, columns: 9 }
].map(t =>
it(`renders ${t.columns} column(s) with ${t.canvas} resolution`, () => {
+ settingsStore.gridConfig.config.groupWidth = 400;
VerifyColumnCount(t.canvas, t.columns);
})
);
it("renders expected number of columns for every resolution", () => {
+ const minWidth = 400;
let lastColumns = 1;
for (let i = 100; i <= 4096; i++) {
- const minWidth = MinWidth(i);
const expectedColumns = Math.max(Math.floor(i / minWidth), 1);
- const columns = Math.floor(i / GetGridElementWidth(i));
+ const columns = Math.floor(i / GetGridElementWidth(i, minWidth));
expect({
resolution: i,
diff --git a/ui/src/Components/MainModal/Configuration/AlertGroupWidthConfiguration.js b/ui/src/Components/MainModal/Configuration/AlertGroupWidthConfiguration.js
new file mode 100644
index 000000000..87aefdf59
--- /dev/null
+++ b/ui/src/Components/MainModal/Configuration/AlertGroupWidthConfiguration.js
@@ -0,0 +1,64 @@
+import React, { Component } from "react";
+import PropTypes from "prop-types";
+
+import { observable, action, toJS } from "mobx";
+import { observer } from "mobx-react";
+
+import { debounce } from "lodash";
+
+import InputRange from "react-input-range";
+
+import { Settings } from "Stores/Settings";
+
+import "./InputRange.scss";
+
+const AlertGroupWidthConfiguration = observer(
+ class AlertGroupWidthConfiguration extends Component {
+ static propTypes = {
+ settingsStore: PropTypes.instanceOf(Settings).isRequired
+ };
+
+ constructor(props) {
+ super(props);
+
+ this.config = observable({
+ groupWidth: toJS(props.settingsStore.gridConfig.config.groupWidth)
+ });
+ }
+
+ onChange = action(value => {
+ this.config.groupWidth = value;
+ });
+
+ onChangeComplete = debounce(
+ action(value => {
+ const { settingsStore } = this.props;
+
+ settingsStore.gridConfig.config.groupWidth = value;
+ }),
+ 200
+ );
+
+ render() {
+ return (
+
+
+
+
+ );
+ }
+ }
+);
+
+export { AlertGroupWidthConfiguration };
diff --git a/ui/src/Components/MainModal/Configuration/AlertGroupWidthConfiguration.test.js b/ui/src/Components/MainModal/Configuration/AlertGroupWidthConfiguration.test.js
new file mode 100644
index 000000000..a8f85d773
--- /dev/null
+++ b/ui/src/Components/MainModal/Configuration/AlertGroupWidthConfiguration.test.js
@@ -0,0 +1,42 @@
+import React from "react";
+
+import { mount } from "enzyme";
+
+import toDiffableHtml from "diffable-html";
+
+import { Settings } from "Stores/Settings";
+import { AlertGroupWidthConfiguration } from "./AlertGroupWidthConfiguration";
+
+let settingsStore;
+beforeEach(() => {
+ settingsStore = new Settings();
+});
+
+const FakeConfiguration = () => {
+ return mount();
+};
+
+describe("", () => {
+ it("matches snapshot with default values", () => {
+ const tree = FakeConfiguration();
+ expect(toDiffableHtml(tree.html())).toMatchSnapshot();
+ });
+
+ it("call to onChange() updates internal state", () => {
+ const tree = FakeConfiguration();
+ tree.instance().onChange(500);
+ expect(tree.instance().config.groupWidth).toBe(500);
+ });
+
+ it("settings are updated on completed change", () => {
+ const tree = FakeConfiguration();
+ tree.instance().onChangeComplete(555);
+ expect(settingsStore.gridConfig.config.groupWidth).toBe(555);
+ });
+
+ it("custom interval value is rendered correctly", () => {
+ settingsStore.gridConfig.config.groupWidth = 455;
+ const component = FakeConfiguration();
+ expect(component.find("InputRange").props().value).toBe(455);
+ });
+});
diff --git a/ui/src/Components/MainModal/Configuration/__snapshots__/AlertGroupWidthConfiguration.test.js.snap b/ui/src/Components/MainModal/Configuration/__snapshots__/AlertGroupWidthConfiguration.test.js.snap
new file mode 100644
index 000000000..55b020c4f
--- /dev/null
+++ b/ui/src/Components/MainModal/Configuration/__snapshots__/AlertGroupWidthConfiguration.test.js.snap
@@ -0,0 +1,49 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` matches snapshot with default values 1`] = `
+"
+
+"
+`;
diff --git a/ui/src/Components/MainModal/Configuration/__snapshots__/index.test.js.snap b/ui/src/Components/MainModal/Configuration/__snapshots__/index.test.js.snap
index 12da2f170..5f0f81a6f 100644
--- a/ui/src/Components/MainModal/Configuration/__snapshots__/index.test.js.snap
+++ b/ui/src/Components/MainModal/Configuration/__snapshots__/index.test.js.snap
@@ -72,6 +72,51 @@ exports[` matches snapshot 1`] = `
+
+
+
+
+
+