mirror of
https://github.com/prymitive/karma
synced 2026-05-07 03:26:52 +00:00
feat(ui): more storybooks
This commit is contained in:
170
ui/src/Components/Grid/index.stories.js
Normal file
170
ui/src/Components/Grid/index.stories.js
Normal file
@@ -0,0 +1,170 @@
|
||||
import React from "react";
|
||||
|
||||
import { storiesOf } from "@storybook/react";
|
||||
|
||||
import { Provider } from "mobx-react";
|
||||
|
||||
import { MockAlert, MockAlertGroup } from "__mocks__/Alerts.js";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { UpstreamError } from "./UpstreamError";
|
||||
import { FatalError } from "./FatalError";
|
||||
import { UpgradeNeeded } from "./UpgradeNeeded";
|
||||
import { AlertGrid } from "./AlertGrid";
|
||||
|
||||
import "App.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";
|
||||
}
|
||||
alerts.push(
|
||||
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
|
||||
)
|
||||
);
|
||||
}
|
||||
const group = MockAlertGroup(
|
||||
{ alertname: "Fake Alert", group: groupName },
|
||||
alerts,
|
||||
[],
|
||||
{},
|
||||
{}
|
||||
);
|
||||
return group;
|
||||
};
|
||||
|
||||
storiesOf("Grid", module)
|
||||
.addDecorator(storyFn => <div className="p-2">{storyFn()}</div>)
|
||||
.add("UpstreamError", () => {
|
||||
return <UpstreamError name="am1" message="Something failed" />;
|
||||
})
|
||||
.add("FatalError", () => {
|
||||
return <FatalError message="Something failed" />;
|
||||
})
|
||||
.add("UpgradeNeeded", () => {
|
||||
return <UpgradeNeeded newVersion="1.2.3" />;
|
||||
})
|
||||
.add("AlertGrid", () => {
|
||||
const alertStore = new AlertStore([]);
|
||||
const settingsStore = new Settings();
|
||||
const silenceFormStore = new SilenceFormStore();
|
||||
|
||||
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);
|
||||
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: 0, healthy: 1, failed: 0 },
|
||||
instances: [{ name: "am", uri: "http://am", error: "" }],
|
||||
clusters: { am: ["am"] }
|
||||
};
|
||||
alertStore.data.groups = groups;
|
||||
|
||||
return (
|
||||
<Provider alertStore={alertStore}>
|
||||
<AlertGrid
|
||||
alertStore={alertStore}
|
||||
settingsStore={settingsStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
</Provider>
|
||||
);
|
||||
});
|
||||
@@ -1,41 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
import { storiesOf } from "@storybook/react";
|
||||
|
||||
import { AlertStore, NewUnappliedFilter } from "Stores/AlertStore";
|
||||
import { FilterInputLabel } from ".";
|
||||
|
||||
import "App.scss";
|
||||
|
||||
storiesOf("FilterInputLabel", module)
|
||||
.addDecorator(storyFn => <div className="p-2">{storyFn()}</div>)
|
||||
.add("applied => false", () => {
|
||||
const alertStore = new AlertStore([]);
|
||||
const filter = NewUnappliedFilter(`@state=active`);
|
||||
alertStore.filters.values = [filter];
|
||||
return <FilterInputLabel alertStore={alertStore} filter={filter} />;
|
||||
})
|
||||
.add("isValid => false", () => {
|
||||
const alertStore = new AlertStore([]);
|
||||
const filter = NewUnappliedFilter(`@state=active`);
|
||||
filter.isValid = false;
|
||||
alertStore.filters.values = [filter];
|
||||
return <FilterInputLabel alertStore={alertStore} filter={filter} />;
|
||||
})
|
||||
.add("applied => true without counter badge", () => {
|
||||
const alertStore = new AlertStore([]);
|
||||
alertStore.info.totalAlerts = 99;
|
||||
const filter = NewUnappliedFilter(`@state=active`);
|
||||
filter.applied = true;
|
||||
filter.hits = 99;
|
||||
alertStore.filters.values = [filter];
|
||||
return <FilterInputLabel alertStore={alertStore} filter={filter} />;
|
||||
})
|
||||
.add("applied => true with counter badge", () => {
|
||||
const alertStore = new AlertStore([]);
|
||||
const filter = NewUnappliedFilter(`@state=active`);
|
||||
filter.applied = true;
|
||||
filter.hits = 99;
|
||||
alertStore.filters.values = [filter];
|
||||
return <FilterInputLabel alertStore={alertStore} filter={filter} />;
|
||||
});
|
||||
@@ -35,18 +35,26 @@ const MainModalContent = observer(
|
||||
static propTypes = {
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
onHide: PropTypes.func.isRequired
|
||||
onHide: PropTypes.func.isRequired,
|
||||
openTab: PropTypes.oneOf(Object.values(TabNames))
|
||||
};
|
||||
static defaultProps = {
|
||||
openTab: TabNames.Configuration
|
||||
};
|
||||
|
||||
tab = observable(
|
||||
{
|
||||
current: TabNames.Configuration,
|
||||
setTab(newTab) {
|
||||
this.current = newTab;
|
||||
}
|
||||
},
|
||||
{ setTab: action.bound }
|
||||
);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.tab = observable(
|
||||
{
|
||||
current: props.openTab,
|
||||
setTab(newTab) {
|
||||
this.current = newTab;
|
||||
}
|
||||
},
|
||||
{ setTab: action.bound }
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { alertStore, settingsStore, onHide } = this.props;
|
||||
@@ -87,4 +95,4 @@ const MainModalContent = observer(
|
||||
}
|
||||
);
|
||||
|
||||
export { MainModalContent };
|
||||
export { MainModalContent, TabNames };
|
||||
|
||||
@@ -4,19 +4,19 @@ import { storiesOf } from "@storybook/react";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { MainModalContent } from "./MainModalContent";
|
||||
import { MainModalContent, TabNames } from "./MainModalContent";
|
||||
|
||||
import "App.scss";
|
||||
|
||||
storiesOf("MainModal", module)
|
||||
.addDecorator(storyFn => (
|
||||
<div className="modal d-block" role="dialog">
|
||||
<div className="overflow-auto modal d-block" role="dialog">
|
||||
<div className="modal-dialog modal-lg" role="document">
|
||||
<div className="modal-content">{storyFn()}</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
.add("content", () => {
|
||||
.add("Configuration", () => {
|
||||
const alertStore = new AlertStore([]);
|
||||
const settingsStore = new Settings();
|
||||
return (
|
||||
@@ -27,4 +27,17 @@ storiesOf("MainModal", module)
|
||||
isVisible={true}
|
||||
/>
|
||||
);
|
||||
})
|
||||
.add("Help", () => {
|
||||
const alertStore = new AlertStore([]);
|
||||
const settingsStore = new Settings();
|
||||
return (
|
||||
<MainModalContent
|
||||
alertStore={alertStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={() => {}}
|
||||
isVisible={true}
|
||||
openTab={TabNames.Help}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
75
ui/src/Components/NavBar/index.stories.js
Normal file
75
ui/src/Components/NavBar/index.stories.js
Normal file
@@ -0,0 +1,75 @@
|
||||
import React from "react";
|
||||
|
||||
import { storiesOf } from "@storybook/react";
|
||||
|
||||
import { AlertStore, NewUnappliedFilter } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { NavBar } from ".";
|
||||
|
||||
import "App.scss";
|
||||
|
||||
const NewFilter = (raw, name, matcher, value, applied, isValid, hits) => {
|
||||
const filter = NewUnappliedFilter(raw);
|
||||
filter.name = name;
|
||||
filter.matcher = matcher;
|
||||
filter.value = value;
|
||||
filter.applied = applied;
|
||||
filter.isValid = isValid;
|
||||
filter.hits = hits;
|
||||
return filter;
|
||||
};
|
||||
|
||||
storiesOf("NavBar", module)
|
||||
.addDecorator(storyFn => (
|
||||
<div className="overflow-auto modal d-block" role="dialog">
|
||||
<div className="modal-dialog modal-lg" role="document">
|
||||
<div className="modal-content">{storyFn()}</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
.add("NavBar", () => {
|
||||
const alertStore = new AlertStore([]);
|
||||
const settingsStore = new Settings();
|
||||
const silenceFormStore = new SilenceFormStore();
|
||||
|
||||
alertStore.info.totalAlerts = 197;
|
||||
alertStore.data.colors = {
|
||||
cluster: {
|
||||
staging: {
|
||||
brightness: 205,
|
||||
background: { red: 246, green: 176, blue: 247, alpha: 255 }
|
||||
}
|
||||
},
|
||||
region: {
|
||||
AF: {
|
||||
brightness: 111,
|
||||
background: { red: 115, green: 101, blue: 152, alpha: 255 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
alertStore.filters.values = [
|
||||
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("server!!!=", "", "", "", true, false, 0),
|
||||
NewFilter("foo", "", "", "", true, true, 2)
|
||||
];
|
||||
|
||||
return (
|
||||
<NavBar
|
||||
alertStore={alertStore}
|
||||
settingsStore={settingsStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@@ -61,21 +61,29 @@ const SilenceForm = observer(
|
||||
static propTypes = {
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired,
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
previewOpen: PropTypes.bool
|
||||
};
|
||||
static defaultProps = {
|
||||
previewOpen: false
|
||||
};
|
||||
|
||||
// store preview visibility state here, by default preview is collapsed
|
||||
// and user needs to expand it
|
||||
previewCollapse = observable(
|
||||
{
|
||||
hidden: true,
|
||||
toggle() {
|
||||
this.hidden = !this.hidden;
|
||||
}
|
||||
},
|
||||
{ toggle: action.bound },
|
||||
{ name: "Silence preview collpase toggle" }
|
||||
);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// store preview visibility state here, by default preview is collapsed
|
||||
// and user needs to expand it
|
||||
this.previewCollapse = observable(
|
||||
{
|
||||
hidden: !props.previewOpen,
|
||||
toggle() {
|
||||
this.hidden = !this.hidden;
|
||||
}
|
||||
},
|
||||
{ toggle: action.bound },
|
||||
{ name: "Silence preview collpase toggle" }
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { silenceFormStore } = this.props;
|
||||
|
||||
@@ -10,6 +10,8 @@ import { SilenceForm } from "./SilenceForm";
|
||||
import { SilencePreview } from "./SilencePreview";
|
||||
import { SilenceSubmitController } from "./SilenceSubmit/SilenceSubmitController";
|
||||
|
||||
import "./index.css";
|
||||
|
||||
const SilenceModalContent = observer(
|
||||
class SilenceModalContent extends Component {
|
||||
static propTypes = {
|
||||
|
||||
@@ -13,8 +13,6 @@ import { Settings } from "Stores/Settings";
|
||||
import { Modal } from "Components/Modal";
|
||||
import { TooltipWrapper } from "Components/TooltipWrapper";
|
||||
|
||||
import "./index.css";
|
||||
|
||||
// https://github.com/facebook/react/issues/14603
|
||||
const SilenceModalContent = React.lazy(() =>
|
||||
import("./SilenceModalContent").then(module => ({
|
||||
|
||||
@@ -4,30 +4,61 @@ import { storiesOf } from "@storybook/react";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import {
|
||||
SilenceFormStore,
|
||||
NewEmptyMatcher,
|
||||
MatcherValueToObject
|
||||
} from "Stores/SilenceFormStore";
|
||||
import { SilenceModalContent } from "./SilenceModalContent";
|
||||
|
||||
import "App.scss";
|
||||
|
||||
const MockMatcher = (name, values, isRegex) => {
|
||||
const matcher = NewEmptyMatcher();
|
||||
matcher.name = name;
|
||||
matcher.values = values.map(v => MatcherValueToObject(v));
|
||||
matcher.isRegex = isRegex;
|
||||
return matcher;
|
||||
};
|
||||
|
||||
storiesOf("SilenceModalContent", module)
|
||||
.addDecorator(storyFn => (
|
||||
<div className="modal d-block" role="dialog">
|
||||
<div className="modal d-block overflow-auto" role="dialog">
|
||||
<div className="modal-dialog modal-lg" role="document">
|
||||
<div className="modal-content">{storyFn()}</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
.add("content", () => {
|
||||
.add("SilenceModalContent", () => {
|
||||
const alertStore = new AlertStore([]);
|
||||
const settingsStore = new Settings();
|
||||
const silenceFormStore = new SilenceFormStore();
|
||||
|
||||
silenceFormStore.toggle.visible = true;
|
||||
silenceFormStore.data.matchers = [
|
||||
MockMatcher("cluster", ["prod"], false),
|
||||
MockMatcher("instance", ["server1", "server3"], true),
|
||||
MockMatcher(
|
||||
"tooLong",
|
||||
[
|
||||
"12345",
|
||||
"Some Alerts With A Ridiculously Long Name To Test Label Truncation In All The Places We Render Those Alerts"
|
||||
],
|
||||
true
|
||||
)
|
||||
];
|
||||
silenceFormStore.data.addEmptyMatcher();
|
||||
silenceFormStore.data.author = "me@example.com";
|
||||
silenceFormStore.data.comment = "fake silence";
|
||||
silenceFormStore.data.resetStartEnd();
|
||||
|
||||
return (
|
||||
<SilenceModalContent
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={() => {}}
|
||||
previewOpen={true}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user