From 516c8b60ee0b706b14c5c503b7c7c80cab472e76 Mon Sep 17 00:00:00 2001 From: Joe Blubaugh Date: Mon, 18 Apr 2022 10:42:45 +0800 Subject: [PATCH] Unified Alerting: Stable order for state history annotations (#47674) This change sorts the State History list returned by the backend by the id in addition to the timeEnd and time fields by which it is already sorted. This results in a stable view of the State History table. Fixes #45873 Signed-off-by: Joe Blubaugh --- .../components/rules/StateHistory.test.tsx | 40 +++++++++++++++++++ .../unified/components/rules/StateHistory.tsx | 32 ++++++++++++++- 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 public/app/features/alerting/unified/components/rules/StateHistory.test.tsx diff --git a/public/app/features/alerting/unified/components/rules/StateHistory.test.tsx b/public/app/features/alerting/unified/components/rules/StateHistory.test.tsx new file mode 100644 index 00000000000..31d0bc9e188 --- /dev/null +++ b/public/app/features/alerting/unified/components/rules/StateHistory.test.tsx @@ -0,0 +1,40 @@ +import { StateHistoryItem } from 'app/types/unified-alerting'; +import { sortStateHistory } from './StateHistory'; + +describe(sortStateHistory, () => { + describe('should stably sort', () => { + describe('when timeEnd is different', () => { + it('should not sort by rule id', () => { + let data: StateHistoryItem[] = [ + { timeEnd: 23, time: 22, id: 1 } as StateHistoryItem, + { timeEnd: 22, time: 21, id: 3 } as StateHistoryItem, + { timeEnd: 22, time: 22, id: 2 } as StateHistoryItem, + { timeEnd: 24, id: 3 } as StateHistoryItem, + ]; + + data.sort(sortStateHistory); + expect(data[0].timeEnd).toBe(24); + expect(data[1].timeEnd).toBe(23); + expect(data[2].time).toBe(22); + expect(data[3].id).toBe(3); + }); + }); + + describe('when only the rule id is different', () => { + it('should sort by rule id', () => { + let data: StateHistoryItem[] = [ + { timeEnd: 23, time: 22, id: 1 } as StateHistoryItem, + { timeEnd: 23, time: 22, id: 3 } as StateHistoryItem, + { timeEnd: 23, time: 22, id: 2 } as StateHistoryItem, + { timeEnd: 23, time: 22, id: 6 } as StateHistoryItem, + ]; + + data.sort(sortStateHistory); + expect(data[0].id).toBe(6); + expect(data[1].id).toBe(3); + expect(data[2].id).toBe(2); + expect(data[3].id).toBe(1); + }); + }); + }); +}); diff --git a/public/app/features/alerting/unified/components/rules/StateHistory.tsx b/public/app/features/alerting/unified/components/rules/StateHistory.tsx index f7050eb198c..75d17e883fe 100644 --- a/public/app/features/alerting/unified/components/rules/StateHistory.tsx +++ b/public/app/features/alerting/unified/components/rules/StateHistory.tsx @@ -24,6 +24,32 @@ interface RuleStateHistoryProps { alertId: string; } +function sortStateHistory(a: StateHistoryItem, b: StateHistoryItem): number { + const compareDesc = (a: number, b: number): number => { + // Larger numbers first. + if (a > b) { + return -1; + } + + if (b > a) { + return 1; + } + return 0; + }; + + const endNeq = compareDesc(a.timeEnd, b.timeEnd); + if (endNeq) { + return endNeq; + } + + const timeNeq = compareDesc(a.time, b.time); + if (timeNeq) { + return timeNeq; + } + + return compareDesc(a.id, b.id); +} + const StateHistory: FC = ({ alertId }) => { const { loading, error, result = [] } = useManagedAlertStateHistory(alertId); @@ -42,6 +68,7 @@ const StateHistory: FC = ({ alertId }) => { ]; const items: StateHistoryRow[] = result + .sort(sortStateHistory) .reduce((acc: StateHistoryRowItem[], item, index) => { acc.push({ id: String(item.id), @@ -123,4 +150,7 @@ function hasMatchingPrecedingState(index: number, items: StateHistoryItem[]): bo return previousHistoryItem.newState === currentHistoryItem.prevState; } -export { StateHistory }; +export { + StateHistory, + sortStateHistory, // exported for testing. +};