Skip to content

Commit 64b17b6

Browse files
committed
Add status history series sorting
Signed-off-by: Alexander Belyakin <[email protected]>
1 parent 1820803 commit 64b17b6

File tree

6 files changed

+65
-31
lines changed

6 files changed

+65
-31
lines changed

statushistorychart/schemas/migrate/migrate.cue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import "list"
3232

3333
kind: "StatusHistoryChart"
3434
spec: {
35+
sorting: "asc"
3536
#showLegend: *#panel.options.legend.showLegend | true
3637
if #showLegend {
3738
legend: {

statushistorychart/schemas/status-history.cue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ kind: "StatusHistoryChart"
2121
spec: close({
2222
legend?: common.#legend
2323
mappings?: [...common.#mappings]
24+
sorting?: "asc" | "desc"
2425
})

statushistorychart/schemas/status-history.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"spec": {
44
"legend": {
55
"position": "bottom"
6-
}
6+
},
7+
"sorting": "asc"
78
}
89
}

statushistorychart/src/StatusHistoryChartOptionsEditorSettings.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@
1313

1414
import { LegendOptionsEditor, LegendOptionsEditorProps } from '@perses-dev/plugin-system';
1515
import { produce } from 'immer';
16-
import { OptionsEditorGroup, OptionsEditorGrid, OptionsEditorColumn } from '@perses-dev/components';
16+
import {
17+
OptionsEditorGroup,
18+
OptionsEditorGrid,
19+
OptionsEditorColumn,
20+
SortSelector,
21+
SortOption,
22+
SortSelectorProps,
23+
} from '@perses-dev/components';
1724
import { Button } from '@mui/material';
1825
import { ReactElement } from 'react';
1926
import { StatusHistoryChartOptions, StatusHistroyChartEditorProps } from './status-history-model.js';
@@ -30,10 +37,19 @@ export function StatusHistoryChartOptionsEditorSettings(props: StatusHistroyChar
3037
);
3138
};
3239

40+
const handleSortChange: SortSelectorProps['onChange'] = (newSort: SortOption) => {
41+
onChange(
42+
produce(value, (draft: StatusHistoryChartOptions) => {
43+
draft.sorting = newSort;
44+
})
45+
);
46+
};
47+
3348
return (
3449
<OptionsEditorGrid>
3550
<OptionsEditorColumn>
3651
<LegendOptionsEditor showValuesEditor={false} value={value.legend} onChange={handleLegendChange} />
52+
<SortSelector value={value.sorting} onChange={handleSortChange} />
3753
</OptionsEditorColumn>
3854
<OptionsEditorColumn>
3955
<OptionsEditorGroup title="Reset Settings">

statushistorychart/src/status-history-model.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ export function createInitialStatusHistoryChartOptions(): Record<string, unknown
2121
export interface StatusHistoryChartOptions {
2222
legend?: LegendSpecOptions;
2323
mappings?: ValueMapping[];
24+
sorting?: StatusHistorySorting;
2425
}
2526

27+
export type StatusHistorySorting = 'asc' | 'desc';
28+
2629
export type StatusHistroyChartEditorProps = OptionsEditorProps<StatusHistoryChartOptions>;

statushistorychart/src/utils/data-transform.ts

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2024 The Perses Authors
1+
// Copyright 2025 The Perses Authors
22
// Licensed under the Apache License, Version 2.0 (the "License");
33
// you may not use this file except in compliance with the License.
44
// You may obtain a copy of the License at
@@ -50,7 +50,9 @@ export function useStatusHistoryDataModel(
5050
spec: StatusHistoryChartOptions
5151
): StatusHistoryDataModel {
5252
return useMemo(() => {
53-
if (!queryResults || queryResults.length === 0) {
53+
const allQueriesResolved = queryResults.every((q) => !q.isLoading && !q.isFetching);
54+
55+
if (!queryResults || queryResults.length === 0 || !allQueriesResolved) {
5456
return {
5557
legendItems: [],
5658
statusHistoryData: [],
@@ -60,6 +62,25 @@ export function useStatusHistoryDataModel(
6062
};
6163
}
6264

65+
const allSeries = queryResults.reduce<TimeSeriesData['series']>((acc, { data }) => {
66+
if (data && data.series) {
67+
acc.push(...data.series);
68+
}
69+
return acc;
70+
}, []);
71+
72+
if (spec.sorting) {
73+
allSeries.sort((a, b) => {
74+
const nameA = a.formattedName || '';
75+
const nameB = b.formattedName || '';
76+
if (spec.sorting === 'asc') {
77+
return nameA.localeCompare(nameA);
78+
} else {
79+
return nameB.localeCompare(nameB);
80+
}
81+
});
82+
}
83+
6384
const timeScale = getCommonTimeScaleForQueries(queryResults);
6485
const statusHistoryData: StatusHistoryDataItem[] = [];
6586
const yAxisCategories: string[] = [];
@@ -68,33 +89,24 @@ export function useStatusHistoryDataModel(
6889

6990
const xAxisCategories = generateCompleteTimestamps(timeScale);
7091

71-
queryResults.forEach(({ data }) => {
72-
if (!data) {
73-
return;
74-
}
75-
76-
data.series.forEach((item) => {
77-
const instance = item.formattedName || '';
78-
79-
yAxisCategories.push(instance);
80-
81-
const yIndex = yAxisCategories.length - 1;
82-
83-
item.values.forEach(([time, value]) => {
84-
const itemIndexOnXaxis = xAxisCategories.findIndex((v) => v === time);
85-
if (value !== null && itemIndexOnXaxis !== -1) {
86-
let itemLabel: string | number = value;
87-
if (hasValueMappings) {
88-
const mappedValue = applyValueMapping(value, spec.mappings);
89-
itemLabel = mappedValue.value;
90-
}
91-
legendSet.add(value);
92-
statusHistoryData.push({
93-
value: [itemIndexOnXaxis, yIndex, value],
94-
label: String(itemLabel),
95-
});
92+
allSeries.forEach((item) => {
93+
const instance = item.formattedName || '';
94+
yAxisCategories.push(instance);
95+
const yIndex = yAxisCategories.length - 1;
96+
item.values.forEach(([time, value]) => {
97+
const itemIndexOnXaxis = xAxisCategories.findIndex((v) => v === time);
98+
if (value !== null && itemIndexOnXaxis !== -1) {
99+
let itemLabel: string | number = value;
100+
if (hasValueMappings) {
101+
const mappedValue = applyValueMapping(value, spec.mappings);
102+
itemLabel = mappedValue.value;
96103
}
97-
});
104+
legendSet.add(value);
105+
statusHistoryData.push({
106+
value: [itemIndexOnXaxis, yIndex, value],
107+
label: String(itemLabel),
108+
});
109+
}
98110
});
99111
});
100112

@@ -141,5 +153,5 @@ export function useStatusHistoryDataModel(
141153
timeScale,
142154
colors,
143155
};
144-
}, [queryResults, spec.mappings, themeColors]);
156+
}, [queryResults, themeColors, spec]);
145157
}

0 commit comments

Comments
 (0)