diff --git a/ui/src/Components/Labels/FilterInputLabel/index.test.tsx b/ui/src/Components/Labels/FilterInputLabel/index.test.tsx index 889d72e72..07bb5d6ae 100644 --- a/ui/src/Components/Labels/FilterInputLabel/index.test.tsx +++ b/ui/src/Components/Labels/FilterInputLabel/index.test.tsx @@ -86,7 +86,11 @@ describe(" 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(" 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"); diff --git a/ui/src/Components/Labels/FilteringLabel/index.test.tsx b/ui/src/Components/Labels/FilteringLabel/index.test.tsx index 3ecb19c17..6a7cfd67d 100644 --- a/ui/src/Components/Labels/FilteringLabel/index.test.tsx +++ b/ui/src/Components/Labels/FilteringLabel/index.test.tsx @@ -84,7 +84,11 @@ describe("", () => { 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( @@ -95,45 +99,15 @@ describe("", () => { 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( ); 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( - - ); - 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( - - ); - 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( - - ); - expect(tree.text()).toBe("foo: bar"); - }); }); diff --git a/ui/src/Components/Labels/FilteringLabel/index.tsx b/ui/src/Components/Labels/FilteringLabel/index.tsx index c82e566c0..14cb1e5e7 100644 --- a/ui/src/Components/Labels/FilteringLabel/index.tsx +++ b/ui/src/Components/Labels/FilteringLabel/index.tsx @@ -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 ( - {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 : ( <> {name}:{" "} diff --git a/ui/src/Components/Labels/Utils.test.ts b/ui/src/Components/Labels/Utils.test.ts index 93f2b55c4..b7ce615a9 100644 --- a/ui/src/Components/Labels/Utils.test.ts +++ b/ui/src/Components/Labels/Utils.test.ts @@ -17,8 +17,15 @@ describe("", () => { 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("", () => { 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("", () => { 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("", () => { 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({}); diff --git a/ui/src/Components/Labels/Utils.ts b/ui/src/Components/Labels/Utils.ts index 4ff7df111..d9521f03c 100644 --- a/ui/src/Components/Labels/Utils.ts +++ b/ui/src/Components/Labels/Utils.ts @@ -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); diff --git a/ui/src/Models/APITypes.ts b/ui/src/Models/APITypes.ts index 7ada29a7d..951f3a98b 100644 --- a/ui/src/Models/APITypes.ts +++ b/ui/src/Models/APITypes.ts @@ -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 { diff --git a/ui/src/Stores/AlertStore.test.ts b/ui/src/Stores/AlertStore.test.ts index 0297cdea6..91a28ecfb 100644 --- a/ui/src/Stores/AlertStore.test.ts +++ b/ui/src/Stores/AlertStore.test.ts @@ -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: [], diff --git a/ui/src/Stores/AlertStore.ts b/ui/src/Stores/AlertStore.ts index 6b1a2c59a..cce3e37d4 100644 --- a/ui/src/Stores/AlertStore.ts +++ b/ui/src/Stores/AlertStore.ts @@ -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; diff --git a/ui/src/__fixtures__/Fetch.ts b/ui/src/__fixtures__/Fetch.ts index 47bc98446..3ed95a9f6 100644 --- a/ui/src/__fixtures__/Fetch.ts +++ b/ui/src/__fixtures__/Fetch.ts @@ -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", + }, + ], {} ), ], diff --git a/ui/src/__fixtures__/Stories.ts b/ui/src/__fixtures__/Stories.ts index 6685ea4a2..cdc5fcabc 100644 --- a/ui/src/__fixtures__/Stories.ts +++ b/ui/src/__fixtures__/Stories.ts @@ -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) { diff --git a/ui/src/__fixtures__/useFetchGet.ts b/ui/src/__fixtures__/useFetchGet.ts index 5408b1f29..6502c172d 100644 --- a/ui/src/__fixtures__/useFetchGet.ts +++ b/ui/src/__fixtures__/useFetchGet.ts @@ -105,7 +105,16 @@ const useFetchGetMock = ( }, { re: /^\.\/alertList\.json\?q=/, - response: { alerts: [[{ name: "instance", value: "foo" }]] }, + response: { + alerts: [ + [ + { + name: "instance", + value: "foo", + }, + ], + ], + }, }, // silence browser {