fix(ui): move static & valueOnly label information to a map

This commit is contained in:
Łukasz Mierzwa
2021-11-01 00:18:05 +00:00
committed by Łukasz Mierzwa
parent c90a5063ef
commit 4fcab63639
11 changed files with 119 additions and 76 deletions
@@ -86,7 +86,11 @@ describe("<FilterInputLabel /> className", () => {
it("applied filter included in staticColorLabels with '=' matcher should use 'btn-info' class", () => {
alertStore.settings.setValues({
...alertStore.settings.values,
...{ staticColorLabels: ["foo"] },
...{
labels: {
foo: { isStatic: true, isValueOnly: false },
},
},
});
ValidateClass("=", true, "btn-info");
});
@@ -94,7 +98,11 @@ describe("<FilterInputLabel /> className", () => {
it("applied filter included in staticColorLabels with any matcher other than '=' should use 'btn-default' class", () => {
alertStore.settings.setValues({
...alertStore.settings.values,
...{ staticColorLabels: ["foo"] },
...{
labels: {
foo: { isStatic: true, isValueOnly: false },
},
},
});
for (const matcher of NonEqualMatchers) {
ValidateClass(matcher, true, "btn-default");
@@ -84,7 +84,11 @@ describe("<FilteringLabel />", () => {
it("doesn't render the name if it's included in valueOnlyLabels", () => {
alertStore.settings.setValues({
...alertStore.settings.values,
valueOnlyLabels: ["foo"],
...{
labels: {
foo: { isStatic: false, isValueOnly: true },
},
},
});
const tree = mount(
<FilteringLabel alertStore={alertStore} name="foo" value="bar" />
@@ -95,45 +99,15 @@ describe("<FilteringLabel />", () => {
it("renders the name if it's not included in valueOnlyLabels", () => {
alertStore.settings.setValues({
...alertStore.settings.values,
valueOnlyLabels: ["bar"],
...{
labels: {
bar: { isStatic: false, isValueOnly: true },
},
},
});
const tree = mount(
<FilteringLabel alertStore={alertStore} name="foo" value="bar" />
);
expect(tree.text()).toBe("foo: bar");
});
it("doesn't render the name if it matches a regex in valueOnlyRegexLabels", () => {
alertStore.settings.setValues({
...alertStore.settings.values,
valueOnlyRegexLabels: ["^fo*$"],
});
const tree = mount(
<FilteringLabel alertStore={alertStore} name="foo" value="bar" />
);
expect(tree.text()).toBe("bar");
});
it("renders the name if it does not match any regex in valueOnlyRegexLabels", () => {
alertStore.settings.setValues({
...alertStore.settings.values,
valueOnlyRegexLabels: ["^[^o]*$"],
});
const tree = mount(
<FilteringLabel alertStore={alertStore} name="foo" value="bar" />
);
expect(tree.text()).toBe("foo: bar");
});
it("renders the name if valueOnlyRegexLabels provides invalid regex", () => {
alertStore.settings.setValues({
...alertStore.settings.values,
valueOnlyRegexLabels: ["^fo**$"],
});
jest.spyOn(global.console, "error").mockImplementation(() => {});
const tree = mount(
<FilteringLabel alertStore={alertStore} name="foo" value="bar" />
);
expect(tree.text()).toBe("foo: bar");
});
});
@@ -33,23 +33,10 @@ const FilteringLabel: FC<{
"components-label-with-hover"
);
function isRegex(value: string): boolean {
try {
RegExp(value);
return true;
} catch {
console.error(`Invalid regex '${value}'`);
return false;
}
}
return (
<TooltipWrapper title="Click to only show alerts with this label or Alt+Click to hide them">
<span className={cs.className} style={cs.style} onClick={handleClick}>
{alertStore.settings.values.valueOnlyLabels.includes(name) ||
alertStore.settings.values.valueOnlyRegexLabels.some(
(regex) => isRegex(regex) && name.match(regex)
) ? null : (
{alertStore.settings.values.labels[name]?.isValueOnly ? null : (
<>
<span className="components-label-name">{name}:</span>{" "}
</>
+19 -4
View File
@@ -17,8 +17,15 @@ describe("<GetClassAndStyle />", () => {
it("static label uses StaticColorLabelClassMap.badge", () => {
alertStore.settings.setValues({
...alertStore.settings.values,
...{ staticColorLabels: ["foo", "job", "bar"] },
...{
labels: {
foo: { isStatic: true, isValueOnly: false },
job: { isStatic: true, isValueOnly: false },
bar: { isStatic: true, isValueOnly: false },
},
},
});
expect(alertStore.settings.values.labels.foo.isStatic).toBe(true);
const cs = GetClassAndStyle(alertStore, "foo", "bar");
expect(cs.colorClassNames).toContain(StaticColorLabelClassMap.badge);
});
@@ -27,7 +34,9 @@ describe("<GetClassAndStyle />", () => {
it(`non-static label doesn't use StaticColorLabelClassMap.${key}`, () => {
alertStore.settings.setValues({
...alertStore.settings.values,
...{ staticColorLabels: [] },
...{
labels: {},
},
});
const cs = GetClassAndStyle(alertStore, "foo", "bar");
expect(cs.colorClassNames).not.toContain(StaticColorLabelClassMap.badge);
@@ -73,7 +82,13 @@ describe("<GetClassAndStyle />", () => {
it("style prop on a label included in staticColorLabels should be empty", () => {
alertStore.settings.setValues({
...alertStore.settings.values,
...{ staticColorLabels: ["foo", "job", "bar"] },
...{
labels: {
foo: { isStatic: true, isValueOnly: false },
job: { isStatic: true, isValueOnly: false },
bar: { isStatic: true, isValueOnly: false },
},
},
});
const cs = GetClassAndStyle(alertStore, "foo", "bar");
expect(cs.style).toEqual({});
@@ -82,7 +97,7 @@ describe("<GetClassAndStyle />", () => {
it("style prop on a label without any color information should be empty", () => {
alertStore.settings.setValues({
...alertStore.settings.values,
...{ staticColorLabels: [] },
...{ labels: {} },
});
const cs = GetClassAndStyle(alertStore, "foo", "bar");
expect(cs.style).toEqual({});
+3 -1
View File
@@ -33,6 +33,8 @@ const GetClassAndStyle = (
colorClassNames: [],
};
const labelSettings = alertStore.settings.values.labels[name];
if (name === StaticLabels.AlertName) {
data.colorClassNames.push(AlertNameLabelClassMap[elementType]);
} else if (name === StaticLabels.State) {
@@ -41,7 +43,7 @@ const GetClassAndStyle = (
? `bg-${StateLabelClassMap[value as AlertStateT]} text-white`
: DefaultLabelClassMap[elementType]
);
} else if (alertStore.settings.values.staticColorLabels.includes(name)) {
} else if (labelSettings?.isStatic) {
data.colorClassNames.push(StaticColorLabelClassMap[elementType]);
} else {
const c = alertStore.data.getColorData(name, value);
+8 -3
View File
@@ -176,10 +176,14 @@ export interface APILabelCounterT {
hits: number;
}
export interface APILabelSettingsT {
isStatic: boolean;
isValueOnly: boolean;
}
export type APILabelsSettingsT = { [key: string]: APILabelSettingsT };
export interface APISettingsT {
staticColorLabels: string[];
valueOnlyLabels: string[];
valueOnlyRegexLabels: string[];
annotationsDefaultHidden: boolean;
annotationsHidden: string[];
annotationsVisible: string[];
@@ -205,6 +209,7 @@ export interface APISettingsT {
};
historyEnabled: boolean;
gridGroupLimit: number;
labels: APILabelsSettingsT;
}
export interface APIAlertsResponseT {
+12 -2
View File
@@ -693,7 +693,12 @@ describe("AlertStore.fetch", () => {
store.fetch("", false, "", "", false, {}, 5, {})
).resolves.toBeUndefined();
expect(store.settings.values).toMatchObject({
staticColorLabels: ["job"],
labels: {
job: {
isStatic: true,
isValueOnly: false,
},
},
annotationsDefaultHidden: false,
annotationsHidden: [],
annotationsVisible: [],
@@ -705,7 +710,12 @@ describe("AlertStore.fetch", () => {
store.fetch("", false, "", "", false, {}, 5, {})
).resolves.toBeUndefined();
expect(store.settings.values).toMatchObject({
staticColorLabels: ["job"],
labels: {
job: {
isStatic: true,
isValueOnly: false,
},
},
annotationsDefaultHidden: false,
annotationsHidden: [],
annotationsVisible: [],
+1 -3
View File
@@ -461,9 +461,6 @@ class AlertStore {
this.settings = observable(
{
values: {
staticColorLabels: [] as string[],
valueOnlyLabels: [] as string[],
valueOnlyRegexLabels: [] as string[],
annotationsDefaultHidden: false as boolean,
annotationsHidden: [] as string[],
annotationsVisible: [] as string[],
@@ -489,6 +486,7 @@ class AlertStore {
},
historyEnabled: true,
gridGroupLimit: 40,
labels: {},
} as APISettingsT,
setValues(v: APISettingsT) {
this.values = v;
+27 -6
View File
@@ -71,15 +71,15 @@ const EmptyAPIResponse = (): APIAlertsResponseT => ({
author: "karma / author missing",
comment: "ACK! Mock comment",
},
staticColorLabels: ["job"],
valueOnlyLabels: [],
valueOnlyRegexLabels: [],
annotationsDefaultHidden: false,
annotationsHidden: [],
annotationsVisible: [],
annotationsEnableHTML: false,
historyEnabled: true,
gridGroupLimit: 40,
labels: {
job: { isStatic: true, isValueOnly: false },
},
},
authentication: {
username: "",
@@ -95,10 +95,31 @@ const MockAPIResponse = (): APIAlertsResponseT => {
labelValue: "",
alertGroups: [
MockAlertGroup(
[{ name: "alertname", value: "foo" }],
[MockAlert([], [{ name: "instance", value: "foo" }], "suppressed")],
[
{
name: "alertname",
value: "foo",
},
],
[
MockAlert(
[],
[
{
name: "instance",
value: "foo",
},
],
"suppressed"
),
],
[],
[{ name: "cluster", value: "dev" }],
[
{
name: "cluster",
value: "dev",
},
],
{}
),
],
+18 -4
View File
@@ -64,7 +64,12 @@ const MockGroup = (
},
]
: [],
[{ name: "instance", value: `instance${i}` }],
[
{
name: "instance",
value: `instance${i}`,
},
],
state
);
alert.startsAt = subMinutes(new Date(), alertCount).toISOString();
@@ -72,7 +77,10 @@ const MockGroup = (
}
const group = MockAlertGroup(
[
{ name: "alertname", value: "Fake Alert" },
{
name: "alertname",
value: "Fake Alert",
},
{ name: "group", value: groupName },
],
alerts,
@@ -174,8 +182,14 @@ const MockGrid = (alertStore: AlertStore): void => {
}
if (i < 3) {
group.shared.labels = [
{ name: "cluster", value: `prod${i}` },
{ name: "job", value: "textfile_exporter" },
{
name: "cluster",
value: `prod${i}`,
},
{
name: "job",
value: "textfile_exporter",
},
];
}
if (i < 5) {
+10 -1
View File
@@ -105,7 +105,16 @@ const useFetchGetMock = (
},
{
re: /^\.\/alertList\.json\?q=/,
response: { alerts: [[{ name: "instance", value: "foo" }]] },
response: {
alerts: [
[
{
name: "instance",
value: "foo",
},
],
],
},
},
// silence browser
{