From f3e2ed2b00cfa27bd56fe29f8643b3175878f28b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Tue, 26 Nov 2019 21:20:54 +0000 Subject: [PATCH] feat(storybook): add more stories to storybook --- ui/src/Components/Grid/index.stories.js | 174 +----------- .../__snapshots__/index.test.js.snap | 60 ++--- .../MainModal/Configuration/index.js | 11 +- .../MainModal/Configuration/index.test.js | 4 +- ui/src/Components/MainModal/Help.js | 7 +- ui/src/Components/MainModal/Help.test.js | 2 +- .../Components/MainModal/MainModalContent.js | 19 +- .../MainModal/MainModalContent.test.js | 2 + .../MainModal/__snapshots__/Help.test.js.snap | 20 +- .../MainModalContent.test.js.snap | 60 ++--- ui/src/Components/MainModal/index.js | 1 + ui/src/Components/MainModal/index.stories.js | 2 + ui/src/Components/ManagedSilence/index.js | 32 ++- .../ManagedSilence/index.stories.js | 91 +++++++ .../Components/NavBar/FilterInput/History.js | 160 ++++++------ ui/src/Components/NavBar/index.stories.js | 60 ++++- .../Components/OverviewModal/index.stories.js | 25 ++ .../SilenceModal/DateTimeSelect/index.js | 62 +++-- .../Components/SilenceModal/index.stories.js | 68 +++-- ui/src/__mocks__/Stories.js | 247 ++++++++++++++++++ 20 files changed, 713 insertions(+), 394 deletions(-) create mode 100644 ui/src/Components/ManagedSilence/index.stories.js create mode 100644 ui/src/Components/OverviewModal/index.stories.js create mode 100644 ui/src/__mocks__/Stories.js diff --git a/ui/src/Components/Grid/index.stories.js b/ui/src/Components/Grid/index.stories.js index c4d5ab3af..449e0d476 100644 --- a/ui/src/Components/Grid/index.stories.js +++ b/ui/src/Components/Grid/index.stories.js @@ -2,9 +2,7 @@ import React from "react"; import { storiesOf } from "@storybook/react"; -import moment from "moment"; - -import { MockAlert, MockAlertGroup, MockSilence } from "__mocks__/Alerts.js"; +import { MockGrid } from "__mocks__/Stories.js"; import { AlertStore } from "Stores/AlertStore"; import { Settings } from "Stores/Settings"; import { SilenceFormStore } from "Stores/SilenceFormStore"; @@ -14,61 +12,6 @@ import { Grid } from "."; import "Percy.scss"; -const MockGroup = (groupName, alertCount, active, suppressed, unprocessed) => { - let alerts = []; - for (let i = 1; i <= alertCount; i++) { - let state; - switch (true) { - case i > active && i <= active + suppressed: - state = "suppressed"; - break; - case i > active + suppressed: - state = "unprocessed"; - break; - default: - state = "active"; - } - const alert = MockAlert( - alertCount < 4 - ? [ - { - name: "dashboard", - value: "http://localhost", - visible: true, - isLink: true - }, - { - name: "help", - value: "this is a summary text", - visible: true, - isLink: false - }, - { - name: "hidden", - value: "this is hidden by default", - visible: false, - isLink: false - } - ] - : [], - { instance: `instance${i}` }, - state - ); - alert.startsAt = moment() - .subtract(alertCount, "minutes") - .toISOString(); - alerts.push(alert); - } - const group = MockAlertGroup( - { alertname: "Fake Alert", group: groupName }, - alerts, - [], - {}, - {} - ); - return group; -}; - storiesOf("Grid", module) .add("FatalError", () => { return ( @@ -83,120 +26,7 @@ storiesOf("Grid", module) const settingsStore = new Settings(); const silenceFormStore = new SilenceFormStore(); - alertStore.settings.values.alertAcknowledgement.enabled = true; - - const silence = MockSilence(); - silence.startsAt = "2018-08-14T12:00:00Z"; - silence.endsAt = "2018-08-14T18:00:00Z"; - alertStore.data.silences = { am: {} }; - alertStore.data.silences["am"][silence.id] = silence; - - alertStore.data.colors = { - group: { - group1: { - brightness: 50, - background: { red: 178, green: 55, blue: 247, alpha: 255 } - }, - group2: { - brightness: 50, - background: { red: 200, green: 100, blue: 66, alpha: 255 } - }, - group3: { - brightness: 205, - background: { red: 246, green: 176, blue: 247, alpha: 255 } - }, - group4: { - brightness: 111, - background: { red: 115, green: 101, blue: 152, alpha: 255 } - } - }, - instance: { - instance1: { - brightness: 50, - background: { red: 111, green: 65, blue: 40, alpha: 255 } - }, - instance2: { - brightness: 50, - background: { red: 66, green: 99, blue: 66, alpha: 255 } - }, - instance3: { - brightness: 150, - background: { red: 66, green: 250, blue: 123, alpha: 255 } - } - } - }; - - let groups = []; - for (let i = 1; i <= 10; i++) { - const active = Math.max(1, Math.ceil(i / 3)); - const suppressed = Math.max(0, i - 2 * Math.ceil(i / 3)); - const unprocessed = Math.max(0, i - active - suppressed); - - const id = `id${i}`; - const hash = `hash${i}`; - const group = MockGroup(`group${i}`, i, active, suppressed, unprocessed); - - for (let j = 0; j < group.alerts.length; j++) { - if (group.alerts[j].state === "suppressed") { - group.alerts[j].silencedBy = [silence.id]; - group.alerts[j].alertmanager = [ - { - name: "am1", - cluster: "am", - state: "suppressed", - startsAt: "2018-08-14T17:36:40.017867056Z", - source: "localhost/prometheus", - silencedBy: [ - j < 2 ? "'Fake Silence ID / Should be fallback'" : silence.id - ], - inhibitedBy: [] - } - ]; - } - } - - group.id = id; - group.hash = hash; - group.stateCount.active = active; - group.stateCount.suppressed = suppressed; - group.stateCount.unprocessed = unprocessed; - if (i < 3) { - group.shared.labels = { - cluster: `prod${i}`, - job: "textfile_exporter" - }; - } - if (i < 5) { - group.shared.annotations = [ - { - name: "summary", - value: "Only 5% free space left on /disk", - visible: true, - isLink: false - } - ]; - } - groups.push(group); - } - alertStore.data.upstreams = { - counters: { total: 3, healthy: 1, failed: 2 }, - instances: [ - { name: "am1", uri: "http://localhost:9093", error: "" }, - { - name: "am2", - uri: "http://localhost:9094", - error: "Failed to connect to http://localhost:9094" - }, - { - name: "failed", - uri: "https://am.example.com", - error: - "Failed to connect to https://am.example.com veryLongStringToTestTextWrappingveryLongStringToTestTextWrappingveryLongStringToTestTextWrappingveryLongStringToTestTextWrappingveryLongStringToTestTextWrappingveryLongStringToTestTextWrapping" - } - ], - clusters: { am: ["am1", "am2"], failed: ["failed"] } - }; - alertStore.data.groups = groups; + MockGrid(alertStore); return ( matches snapshot 1`] = `
-
+
Filter bar configuration @@ -84,14 +84,14 @@ exports[` matches snapshot 1`] = ` @@ -99,7 +99,7 @@ exports[` matches snapshot 1`] = `
@@ -123,7 +123,7 @@ exports[` matches snapshot 1`] = `
-
+
Theme @@ -132,14 +132,14 @@ exports[` matches snapshot 1`] = ` @@ -147,7 +147,7 @@ exports[` matches snapshot 1`] = `
@@ -189,7 +189,7 @@ exports[` matches snapshot 1`] = `
-
+
Minimal alert group width @@ -198,14 +198,14 @@ exports[` matches snapshot 1`] = ` @@ -213,7 +213,7 @@ exports[` matches snapshot 1`] = `
@@ -260,7 +260,7 @@ exports[` matches snapshot 1`] = `
-
+
Default number of alerts to show per group @@ -269,14 +269,14 @@ exports[` matches snapshot 1`] = ` @@ -284,7 +284,7 @@ exports[` matches snapshot 1`] = `
@@ -331,7 +331,7 @@ exports[` matches snapshot 1`] = `
-
+
Default alert group display @@ -340,14 +340,14 @@ exports[` matches snapshot 1`] = ` @@ -355,7 +355,7 @@ exports[` matches snapshot 1`] = `
@@ -410,7 +410,7 @@ exports[` matches snapshot 1`] = `
-
+
Grid sort order @@ -419,14 +419,14 @@ exports[` matches snapshot 1`] = ` @@ -434,7 +434,7 @@ exports[` matches snapshot 1`] = `
diff --git a/ui/src/Components/MainModal/Configuration/index.js b/ui/src/Components/MainModal/Configuration/index.js index 9f7065dcb..c322b9f8a 100644 --- a/ui/src/Components/MainModal/Configuration/index.js +++ b/ui/src/Components/MainModal/Configuration/index.js @@ -12,7 +12,7 @@ import { AlertGroupCollapseConfiguration } from "./AlertGroupCollapseConfigurati import { AlertGroupTitleBarColor } from "./AlertGroupTitleBarColor"; import { ThemeConfiguration } from "./ThemeConfiguration"; -const Configuration = ({ settingsStore }) => ( +const Configuration = ({ settingsStore, defaultIsOpen }) => (
( } + extraProps={{ open: defaultIsOpen }} /> ( } + extraProps={{ open: defaultIsOpen }} /> } + extraProps={{ open: defaultIsOpen }} /> } + extraProps={{ open: defaultIsOpen }} /> } + extraProps={{ open: defaultIsOpen }} /> } + extraProps={{ open: defaultIsOpen }} /> ); Configuration.propTypes = { - settingsStore: PropTypes.instanceOf(Settings).isRequired + settingsStore: PropTypes.instanceOf(Settings).isRequired, + defaultIsOpen: PropTypes.bool.isRequired }; export { Configuration }; diff --git a/ui/src/Components/MainModal/Configuration/index.test.js b/ui/src/Components/MainModal/Configuration/index.test.js index 3fbab613b..1fa0b31ca 100644 --- a/ui/src/Components/MainModal/Configuration/index.test.js +++ b/ui/src/Components/MainModal/Configuration/index.test.js @@ -10,7 +10,9 @@ import { Configuration } from "."; describe("", () => { it("matches snapshot", () => { const settingsStore = new Settings(); - const tree = shallow(); + const tree = shallow( + + ); expect(toDiffableHtml(tree.html())).toMatchSnapshot(); }); }); diff --git a/ui/src/Components/MainModal/Help.js b/ui/src/Components/MainModal/Help.js index 9c5ac33c6..def31ea7c 100644 --- a/ui/src/Components/MainModal/Help.js +++ b/ui/src/Components/MainModal/Help.js @@ -71,7 +71,7 @@ FilterExample.propTypes = { children: PropTypes.node }; -const Help = () => ( +const Help = ({ defaultIsOpen }) => (
( } + extraProps={{ open: defaultIsOpen }} /> ( } + extraProps={{ open: defaultIsOpen }} />
); +Help.propTypes = { + defaultIsOpen: PropTypes.bool.isRequired +}; export { Help }; diff --git a/ui/src/Components/MainModal/Help.test.js b/ui/src/Components/MainModal/Help.test.js index 36d41b3fd..e54fa6085 100644 --- a/ui/src/Components/MainModal/Help.test.js +++ b/ui/src/Components/MainModal/Help.test.js @@ -8,7 +8,7 @@ import { Help } from "./Help"; describe("", () => { it("matches snapshot", () => { - const tree = shallow(); + const tree = shallow(); expect(toDiffableHtml(tree.html())).toMatchSnapshot(); }); }); diff --git a/ui/src/Components/MainModal/MainModalContent.js b/ui/src/Components/MainModal/MainModalContent.js index 66d44c340..e4a211f9b 100644 --- a/ui/src/Components/MainModal/MainModalContent.js +++ b/ui/src/Components/MainModal/MainModalContent.js @@ -21,7 +21,8 @@ const MainModalContent = observer( alertStore: PropTypes.instanceOf(AlertStore).isRequired, settingsStore: PropTypes.instanceOf(Settings).isRequired, onHide: PropTypes.func.isRequired, - openTab: PropTypes.oneOf(Object.values(TabNames)) + openTab: PropTypes.oneOf(Object.values(TabNames)), + expandAllOptions: PropTypes.bool.isRequired }; static defaultProps = { openTab: TabNames.Configuration @@ -42,7 +43,12 @@ const MainModalContent = observer( } render() { - const { alertStore, settingsStore, onHide } = this.props; + const { + alertStore, + settingsStore, + onHide, + expandAllOptions + } = this.props; return ( @@ -64,9 +70,14 @@ const MainModalContent = observer(
- {this.tab.current === TabNames.Help ? : null} + {this.tab.current === TabNames.Help ? ( + + ) : null} {this.tab.current === TabNames.Configuration ? ( - + ) : null}
diff --git a/ui/src/Components/MainModal/MainModalContent.test.js b/ui/src/Components/MainModal/MainModalContent.test.js index 495f8a683..c8eb8a69b 100644 --- a/ui/src/Components/MainModal/MainModalContent.test.js +++ b/ui/src/Components/MainModal/MainModalContent.test.js @@ -28,6 +28,7 @@ const FakeModal = () => { alertStore={alertStore} settingsStore={settingsStore} onHide={onHide} + expandAllOptions={true} /> ); }; @@ -54,6 +55,7 @@ describe("", () => { alertStore={alertStore} settingsStore={settingsStore} onHide={onHide} + expandAllOptions={true} /> ); diff --git a/ui/src/Components/MainModal/__snapshots__/Help.test.js.snap b/ui/src/Components/MainModal/__snapshots__/Help.test.js.snap index ef0a08358..dd27b6fe6 100644 --- a/ui/src/Components/MainModal/__snapshots__/Help.test.js.snap +++ b/ui/src/Components/MainModal/__snapshots__/Help.test.js.snap @@ -163,7 +163,7 @@ exports[` matches snapshot 1`] = `
-
+
Filtering using alert labels @@ -172,14 +172,14 @@ exports[` matches snapshot 1`] = ` @@ -187,7 +187,7 @@ exports[` matches snapshot 1`] = `
@@ -339,7 +339,7 @@ exports[` matches snapshot 1`] = `
-
+
Filtering alerts using special filters @@ -348,14 +348,14 @@ exports[` matches snapshot 1`] = ` @@ -363,7 +363,7 @@ exports[` matches snapshot 1`] = `
diff --git a/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap b/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap index 7ceeaf58a..624908780 100644 --- a/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap +++ b/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap @@ -94,7 +94,7 @@ exports[` matches snapshot 1`] = `
-
+
Filter bar configuration @@ -103,14 +103,14 @@ exports[` matches snapshot 1`] = ` @@ -118,7 +118,7 @@ exports[` matches snapshot 1`] = `
@@ -142,7 +142,7 @@ exports[` matches snapshot 1`] = `
-
+
Theme @@ -151,14 +151,14 @@ exports[` matches snapshot 1`] = ` @@ -166,7 +166,7 @@ exports[` matches snapshot 1`] = `
@@ -208,7 +208,7 @@ exports[` matches snapshot 1`] = `
-
+
Minimal alert group width @@ -217,14 +217,14 @@ exports[` matches snapshot 1`] = ` @@ -232,7 +232,7 @@ exports[` matches snapshot 1`] = `
@@ -279,7 +279,7 @@ exports[` matches snapshot 1`] = `
-
+
Default number of alerts to show per group @@ -288,14 +288,14 @@ exports[` matches snapshot 1`] = ` @@ -303,7 +303,7 @@ exports[` matches snapshot 1`] = `
@@ -350,7 +350,7 @@ exports[` matches snapshot 1`] = `
-
+
Default alert group display @@ -359,14 +359,14 @@ exports[` matches snapshot 1`] = ` @@ -374,7 +374,7 @@ exports[` matches snapshot 1`] = `
@@ -429,7 +429,7 @@ exports[` matches snapshot 1`] = `
-
+
Grid sort order @@ -438,14 +438,14 @@ exports[` matches snapshot 1`] = ` @@ -453,7 +453,7 @@ exports[` matches snapshot 1`] = `
diff --git a/ui/src/Components/MainModal/index.js b/ui/src/Components/MainModal/index.js index 542c480dc..9b570428a 100644 --- a/ui/src/Components/MainModal/index.js +++ b/ui/src/Components/MainModal/index.js @@ -72,6 +72,7 @@ const MainModal = observer( settingsStore={settingsStore} onHide={this.toggle.hide} isVisible={this.toggle.show} + expandAllOptions={false} /> diff --git a/ui/src/Components/MainModal/index.stories.js b/ui/src/Components/MainModal/index.stories.js index fccf34890..5b2ab9e86 100644 --- a/ui/src/Components/MainModal/index.stories.js +++ b/ui/src/Components/MainModal/index.stories.js @@ -25,6 +25,7 @@ storiesOf("MainModal", module) settingsStore={settingsStore} onHide={() => {}} isVisible={true} + expandAllOptions={true} /> ); }) @@ -38,6 +39,7 @@ storiesOf("MainModal", module) onHide={() => {}} isVisible={true} openTab={TabNames.Help} + expandAllOptions={true} /> ); }); diff --git a/ui/src/Components/ManagedSilence/index.js b/ui/src/Components/ManagedSilence/index.js index 8db044877..f8620865c 100644 --- a/ui/src/Components/ManagedSilence/index.js +++ b/ui/src/Components/ManagedSilence/index.js @@ -23,20 +23,28 @@ const ManagedSilence = observer( alertStore: PropTypes.instanceOf(AlertStore).isRequired, silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired, onDidUpdate: PropTypes.func, - onDeleteModalClose: PropTypes.func + onDeleteModalClose: PropTypes.func, + isOpen: PropTypes.bool + }; + static defaultProps = { + isOpen: false }; - // store collapse state, by default only silence comment is visible - // the rest of the silence is hidden until expanded by a click - collapse = observable( - { - value: true, - toggle() { - this.value = !this.value; - } - }, - { toggle: action.bound } - ); + constructor(props) { + super(props); + + // store collapse state, by default only silence comment is visible + // the rest of the silence is hidden until expanded by a click + this.collapse = observable( + { + value: !props.isOpen, + toggle() { + this.value = !this.value; + } + }, + { toggle: action.bound } + ); + } getAlertmanager = () => this.props.alertStore.data.upstreams.instances diff --git a/ui/src/Components/ManagedSilence/index.stories.js b/ui/src/Components/ManagedSilence/index.stories.js new file mode 100644 index 000000000..0dd2902ff --- /dev/null +++ b/ui/src/Components/ManagedSilence/index.stories.js @@ -0,0 +1,91 @@ +import React from "react"; + +import { storiesOf } from "@storybook/react"; + +import { MockSilence } from "__mocks__/Alerts"; +import { AlertStore } from "Stores/AlertStore"; +import { SilenceFormStore } from "Stores/SilenceFormStore"; +import { ManagedSilence } from "."; + +import "Percy.scss"; + +storiesOf("ManagedSilence", module) + .addDecorator(storyFn => ( +
+
+
{storyFn()}
+
+
+ )) + .add("Silence", () => { + const alertStore = new AlertStore([]); + const silenceFormStore = new SilenceFormStore(); + const cluster = "am"; + + alertStore.data.upstreams = { + instances: [ + { + name: "am1", + cluster: cluster, + clusterMembers: ["am1"], + uri: "http://localhost:9093", + publicURI: "http://example.com", + error: "", + version: "0.15.3", + headers: {} + } + ], + clusters: { am: ["am1"] } + }; + + const silence = MockSilence(); + silence.startsAt = "2018-08-14T16:00:00Z"; + silence.endsAt = "2018-08-14T18:00:00Z"; + + const expiredSilence = MockSilence(); + expiredSilence.startsAt = "2018-08-14T10:00:00Z"; + expiredSilence.endsAt = "2018-08-14T11:00:00Z"; + + return ( + + {}} + /> + {}} + isOpen={true} + /> + {}} + /> + {}} + isOpen={true} + /> + + ); + }); diff --git a/ui/src/Components/NavBar/FilterInput/History.js b/ui/src/Components/NavBar/FilterInput/History.js index d3d384be8..b90072ee0 100644 --- a/ui/src/Components/NavBar/FilterInput/History.js +++ b/ui/src/Components/NavBar/FilterInput/History.js @@ -59,87 +59,85 @@ ActionButton.propTypes = { afterClick: PropTypes.func.isRequired }; -const HistoryMenu = onClickOutside( - ({ - popperPlacement, - popperRef, - popperStyle, - filters, - alertStore, - settingsStore, - afterClick, - onClear - }) => { - return ( -
-
- - Last used filters -
- {filters.length === 0 ? ( -
Empty
- ) : ( - filters.map(historyFilters => ( - - )) - )} -
-
- { - settingsStore.savedFilters.save( - alertStore.filters.values.map(f => f.raw) - ); +const HistoryMenuContent = ({ + popperPlacement, + popperRef, + popperStyle, + filters, + alertStore, + settingsStore, + afterClick, + onClear +}) => { + return ( +
+
+ + Last used filters +
+ {filters.length === 0 ? ( +
Empty
+ ) : ( + filters.map(historyFilters => ( +
+ > +
+ {historyFilters.map(f => ( + + ))} +
+ + )) + )} +
+
+ { + settingsStore.savedFilters.save( + alertStore.filters.values.map(f => f.raw) + ); + }} + afterClick={afterClick} + /> + +
- ); - } -); -HistoryMenu.propTypes = { +
+ ); +}; +HistoryMenuContent.propTypes = { popperPlacement: PropTypes.string, popperRef: PropTypes.func, popperStyle: PropTypes.object, @@ -150,6 +148,8 @@ HistoryMenu.propTypes = { onClear: PropTypes.func.isRequired }; +const HistoryMenu = onClickOutside(HistoryMenuContent); + const History = observer( class History extends Component { static propTypes = { @@ -278,4 +278,4 @@ const History = observer( } ); -export { History, HistoryMenu, ReduceFilter }; +export { History, HistoryMenu, HistoryMenuContent, ReduceFilter }; diff --git a/ui/src/Components/NavBar/index.stories.js b/ui/src/Components/NavBar/index.stories.js index 4698f760f..068bd2fe0 100644 --- a/ui/src/Components/NavBar/index.stories.js +++ b/ui/src/Components/NavBar/index.stories.js @@ -5,6 +5,7 @@ import { storiesOf } from "@storybook/react"; import { AlertStore, NewUnappliedFilter } from "Stores/AlertStore"; import { Settings } from "Stores/Settings"; import { SilenceFormStore } from "Stores/SilenceFormStore"; +import { HistoryMenuContent } from "./FilterInput/History"; import { NavBar } from "."; import "Percy.scss"; @@ -49,11 +50,60 @@ storiesOf("NavBar", module).add("NavBar", () => { NewFilter("foo", "", "", "", true, true, 2) ]; + const history = [ + [NewFilter("alertname=Foo", "alertname", "=", "foo", true, true, 15)], + [NewFilter("cluster=staging", "cluster", "=", "staging", true, true, 15)], + [ + NewFilter("cluster=staging", "cluster", "=", "staging", true, true, 15), + NewFilter("region=AF", "region", "=", "AF", true, true, 180) + ], + [ + NewFilter("cluster=staging", "cluster", "=", "staging", true, true, 15), + NewFilter("region=AF", "region", "=", "AF", true, true, 180), + NewFilter( + "instance!=server1", + "instance", + "!=", + "server1", + false, + true, + 0 + ) + ], + [ + NewFilter( + "alertname=VeryVeryVeryVeryVeryLoooooooooooooooongAlertnameeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "alertname", + "=", + "VeryVeryVeryVeryVeryLoooooooooooooooongAlertnameeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + true, + true, + 15 + ) + ] + ]; + return ( - + + +
+ {}} + popperStyle={{}} + filters={history} + onClear={() => {}} + alertStore={alertStore} + settingsStore={settingsStore} + afterClick={() => {}} + handleClickOutside={() => {}} + outsideClickIgnoreClass="components-navbar-history" + /> +
+
); }); diff --git a/ui/src/Components/OverviewModal/index.stories.js b/ui/src/Components/OverviewModal/index.stories.js new file mode 100644 index 000000000..5fa80bb8c --- /dev/null +++ b/ui/src/Components/OverviewModal/index.stories.js @@ -0,0 +1,25 @@ +import React from "react"; + +import { storiesOf } from "@storybook/react"; + +import { MockGrid } from "__mocks__/Stories.js"; +import { AlertStore } from "Stores/AlertStore"; +import { OverviewModalContent } from "./OverviewModalContent"; + +import "Percy.scss"; + +storiesOf("OverviewModal", module) + .addDecorator(storyFn => ( +
+
+
{storyFn()}
+
+
+ )) + .add("OverviewModal", () => { + const alertStore = new AlertStore([]); + + MockGrid(alertStore); + + return {}} />; + }); diff --git a/ui/src/Components/SilenceModal/DateTimeSelect/index.js b/ui/src/Components/SilenceModal/DateTimeSelect/index.js index afa187647..2c9f92595 100644 --- a/ui/src/Components/SilenceModal/DateTimeSelect/index.js +++ b/ui/src/Components/SilenceModal/DateTimeSelect/index.js @@ -171,33 +171,41 @@ TabContentDuration.propTypes = { const DateTimeSelect = observer( class DateTimeSelect extends Component { static propTypes = { - silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired + silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired, + openTab: PropTypes.oneOf(Object.values(TabNames)) + }; + static defaultProps = { + openTab: TabNames.Duration }; - tab = observable( - { - current: TabNames.Duration, - setStart() { - this.current = TabNames.Start; + constructor(props) { + super(props); + + this.tab = observable( + { + current: props.openTab, + setStart() { + this.current = TabNames.Start; + }, + setEnd() { + this.current = TabNames.End; + }, + setDuration() { + this.current = TabNames.Duration; + }, + timeNow: moment().seconds(0), + updateTimeNow() { + this.timeNow = moment().seconds(0); + } }, - setEnd() { - this.current = TabNames.End; - }, - setDuration() { - this.current = TabNames.Duration; - }, - timeNow: moment().seconds(0), - updateTimeNow() { - this.timeNow = moment().seconds(0); + { + setStart: action.bound, + setEnd: action.bound, + setDuration: action.bound, + updateTimeNow: action.bound } - }, - { - setStart: action.bound, - setEnd: action.bound, - setDuration: action.bound, - updateTimeNow: action.bound - } - ); + ); + } componentDidMount() { this.tab.updateTimeNow(); @@ -274,4 +282,10 @@ const DateTimeSelect = observer( } ); -export { DateTimeSelect, TabContentStart, TabContentEnd, TabContentDuration }; +export { + DateTimeSelect, + TabContentStart, + TabContentEnd, + TabContentDuration, + TabNames +}; diff --git a/ui/src/Components/SilenceModal/index.stories.js b/ui/src/Components/SilenceModal/index.stories.js index f63743163..8a83b0000 100644 --- a/ui/src/Components/SilenceModal/index.stories.js +++ b/ui/src/Components/SilenceModal/index.stories.js @@ -13,6 +13,7 @@ import { MatcherValueToObject, SilenceTabNames } from "Stores/SilenceFormStore"; +import { DateTimeSelect, TabNames } from "./DateTimeSelect"; import { SilenceModalContent } from "./SilenceModalContent"; import "Percy.scss"; @@ -25,14 +26,15 @@ const MockMatcher = (name, values, isRegex) => { return matcher; }; -storiesOf("SilenceModal", module) - .addDecorator(storyFn => ( -
-
-
{storyFn()}
-
+const Modal = ({ children }) => ( +
+
+
{children}
- )) +
+); + +storiesOf("SilenceModal", module) .add("Editor", () => { const alertStore = new AlertStore([]); const settingsStore = new Settings(); @@ -74,14 +76,34 @@ storiesOf("SilenceModal", module) ); return ( - {}} - previewOpen={true} - onDeleteModalClose={() => {}} - /> + + + {}} + previewOpen={true} + onDeleteModalClose={() => {}} + /> + + +
+ +
+
+ +
+ +
+
+
); }) .add("Browser", () => { @@ -135,12 +157,14 @@ storiesOf("SilenceModal", module) }); return ( - {}} - onDeleteModalClose={() => {}} - /> + + {}} + onDeleteModalClose={() => {}} + /> + ); }); diff --git a/ui/src/__mocks__/Stories.js b/ui/src/__mocks__/Stories.js new file mode 100644 index 000000000..441c1c48e --- /dev/null +++ b/ui/src/__mocks__/Stories.js @@ -0,0 +1,247 @@ +import moment from "moment"; + +import { MockAlert, MockAlertGroup, MockSilence } from "./Alerts.js"; + +const MockGroup = (groupName, alertCount, active, suppressed, unprocessed) => { + let alerts = []; + for (let i = 1; i <= alertCount; i++) { + let state; + switch (true) { + case i > active && i <= active + suppressed: + state = "suppressed"; + break; + case i > active + suppressed: + state = "unprocessed"; + break; + default: + state = "active"; + } + const alert = MockAlert( + alertCount < 4 + ? [ + { + name: "dashboard", + value: "http://localhost", + visible: true, + isLink: true + }, + { + name: "help", + value: "this is a summary text", + visible: true, + isLink: false + }, + { + name: "hidden", + value: "this is hidden by default", + visible: false, + isLink: false + } + ] + : [], + { instance: `instance${i}` }, + state + ); + alert.startsAt = moment() + .subtract(alertCount, "minutes") + .toISOString(); + alerts.push(alert); + } + const group = MockAlertGroup( + { alertname: "Fake Alert", group: groupName }, + alerts, + [], + {}, + {} + ); + return group; +}; + +const MockGrid = alertStore => { + alertStore.settings.values.alertAcknowledgement.enabled = true; + + const silence = MockSilence(); + silence.startsAt = "2018-08-14T12:00:00Z"; + silence.endsAt = "2018-08-14T18:00:00Z"; + alertStore.data.silences = { am: {} }; + alertStore.data.silences["am"][silence.id] = silence; + + alertStore.data.colors = { + group: { + group1: { + brightness: 50, + background: { red: 178, green: 55, blue: 247, alpha: 255 } + }, + group2: { + brightness: 50, + background: { red: 200, green: 100, blue: 66, alpha: 255 } + }, + group3: { + brightness: 205, + background: { red: 246, green: 176, blue: 247, alpha: 255 } + }, + group4: { + brightness: 111, + background: { red: 115, green: 101, blue: 152, alpha: 255 } + } + }, + instance: { + instance1: { + brightness: 50, + background: { red: 111, green: 65, blue: 40, alpha: 255 } + }, + instance2: { + brightness: 50, + background: { red: 66, green: 99, blue: 66, alpha: 255 } + }, + instance3: { + brightness: 150, + background: { red: 66, green: 250, blue: 123, alpha: 255 } + } + } + }; + + alertStore.data.counters = [ + { + name: "@receiver", + hits: 2, + values: [ + { + value: "by-cluster-service", + raw: "@receiver=by-cluster-service", + hits: 2, + percent: 100, + offset: 0 + } + ] + }, + { + name: "alertname", + hits: 90, + values: [ + { + value: "Fake Alert", + raw: "alertname=Fake Alert", + hits: 45, + percent: 50, + offset: 0 + }, + { + value: "Second Fake Alert", + raw: "alertname=Second Fake Alert", + hits: 45, + percent: 50, + offset: 50 + } + ] + }, + { + name: "group", + hits: 100, + values: [ + { + value: "group1", + raw: "group=group1", + hits: 25, + percent: 25, + offset: 0 + }, + { + value: "group2", + raw: "group=group2", + hits: 70, + percent: 70, + offset: 25 + }, + { + value: "group3", + raw: "group=group3", + hits: 4, + percent: 4, + offset: 95 + }, + { + value: "group4", + raw: "group=group4", + hits: 1, + percent: 1, + offset: 99 + } + ] + } + ]; + + let groups = []; + for (let i = 1; i <= 10; i++) { + const active = Math.max(1, Math.ceil(i / 3)); + const suppressed = Math.max(0, i - 2 * Math.ceil(i / 3)); + const unprocessed = Math.max(0, i - active - suppressed); + + const id = `id${i}`; + const hash = `hash${i}`; + const group = MockGroup(`group${i}`, i, active, suppressed, unprocessed); + + for (let j = 0; j < group.alerts.length; j++) { + if (group.alerts[j].state === "suppressed") { + group.alerts[j].silencedBy = [silence.id]; + group.alerts[j].alertmanager = [ + { + name: "am1", + cluster: "am", + state: "suppressed", + startsAt: "2018-08-14T17:36:40.017867056Z", + source: "localhost/prometheus", + silencedBy: [ + j < 2 ? "'Fake Silence ID / Should be fallback'" : silence.id + ], + inhibitedBy: [] + } + ]; + } + } + + group.id = id; + group.hash = hash; + group.stateCount.active = active; + group.stateCount.suppressed = suppressed; + group.stateCount.unprocessed = unprocessed; + if (i < 3) { + group.shared.labels = { + cluster: `prod${i}`, + job: "textfile_exporter" + }; + } + if (i < 5) { + group.shared.annotations = [ + { + name: "summary", + value: "Only 5% free space left on /disk", + visible: true, + isLink: false + } + ]; + } + groups.push(group); + } + alertStore.data.upstreams = { + counters: { total: 3, healthy: 1, failed: 2 }, + instances: [ + { name: "am1", uri: "http://localhost:9093", error: "" }, + { + name: "am2", + uri: "http://localhost:9094", + error: "Failed to connect to http://localhost:9094" + }, + { + name: "failed", + uri: "https://am.example.com", + error: + "Failed to connect to https://am.example.com veryLongStringToTestTextWrappingveryLongStringToTestTextWrappingveryLongStringToTestTextWrappingveryLongStringToTestTextWrappingveryLongStringToTestTextWrappingveryLongStringToTestTextWrapping" + } + ], + clusters: { am: ["am1", "am2"], failed: ["failed"] } + }; + alertStore.data.groups = groups; +}; + +export { MockGrid };