Skip to content

Commit 998dcb9

Browse files
ashtonGthiagodallacqua-hpe
authored andcommitted
fix: improve sorting for multi-type columns (#10123)
1 parent 918d6c7 commit 998dcb9

File tree

2 files changed

+84
-13
lines changed

2 files changed

+84
-13
lines changed

webui/react/src/components/MultiSortMenu.tsx

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1+
import Badge from 'hew/Badge';
12
import Button from 'hew/Button';
23
import { DirectionType, Sort, validSort } from 'hew/DataGrid/DataGrid';
34
import Dropdown, { MenuItem } from 'hew/Dropdown';
45
import Icon from 'hew/Icon';
56
import Select, { Option } from 'hew/Select';
67
import { Loadable } from 'hew/utils/loadable';
8+
import { groupBy, mapValues } from 'lodash';
9+
import { Fragment, useMemo } from 'react';
710

11+
import { runColumns } from 'pages/FlatRuns/columns';
812
import { V1ColumnType } from 'services/api-ts-sdk';
913
import { ProjectColumn } from 'types';
14+
import { removeColumnTypePrefix } from 'utils/flatRun';
1015

1116
import css from './MultiSortMenu.module.scss';
1217

@@ -25,6 +30,7 @@ interface MultiSortRowProps {
2530
onChange?: (sort: Sort) => void;
2631
onRemove?: () => void;
2732
bannedSortColumns: Set<string>;
33+
typeMap: Record<string, V1ColumnType[]>;
2834
}
2935
interface DirectionOptionsProps {
3036
onChange?: (direction: DirectionType) => void;
@@ -36,6 +42,7 @@ interface ColumnOptionsProps {
3642
onChange?: (column: string) => void;
3743
value?: string;
3844
bannedSortColumns: Set<string>;
45+
typeMap: Record<string, V1ColumnType[]>;
3946
}
4047

4148
export const optionsByColumnType = {
@@ -153,6 +160,7 @@ const ColumnOptions: React.FC<ColumnOptionsProps> = ({
153160
columns,
154161
value,
155162
bannedSortColumns,
163+
typeMap,
156164
}) => (
157165
<Select
158166
autoFocus
@@ -161,10 +169,31 @@ const ColumnOptions: React.FC<ColumnOptionsProps> = ({
161169
loading={Loadable.isNotLoaded(columns)}
162170
options={Loadable.getOrElse([], columns)
163171
.filter((c) => !bannedSortColumns.has(c.column))
164-
.map((c) => ({
165-
label: c.displayName || c.column,
166-
value: c.column,
167-
}))}
172+
.map((c) => {
173+
const badges = typeMap[c.column].map((type) => {
174+
const copy =
175+
(runColumns as readonly string[]).includes(c.column) &&
176+
type === V1ColumnType.UNSPECIFIED
177+
? 'BOOLEAN'
178+
: removeColumnTypePrefix(type);
179+
return (
180+
<Fragment key={type}>
181+
<Badge text={copy} />{' '}
182+
</Fragment>
183+
);
184+
});
185+
const title = c.displayName || c.column;
186+
const label = (
187+
<>
188+
{title} {badges}
189+
</>
190+
);
191+
return {
192+
label,
193+
title,
194+
value: c.column,
195+
};
196+
})}
168197
placeholder="Select column"
169198
value={value}
170199
width="100%"
@@ -178,6 +207,7 @@ const MultiSortRow: React.FC<MultiSortRowProps> = ({
178207
onChange,
179208
onRemove,
180209
bannedSortColumns,
210+
typeMap,
181211
}) => {
182212
const valueType =
183213
Loadable.getOrElse([], columns).find((c) => c.column === sort.column)?.type ||
@@ -188,6 +218,7 @@ const MultiSortRow: React.FC<MultiSortRowProps> = ({
188218
<ColumnOptions
189219
bannedSortColumns={bannedSortColumns}
190220
columns={columns}
221+
typeMap={typeMap}
191222
value={sort.column}
192223
onChange={(column) => onChange?.({ ...sort, column })}
193224
/>
@@ -231,14 +262,35 @@ const MultiSort: React.FC<MultiSortProps> = ({ sorts, columns, onChange, bannedS
231262
window.document.body.dispatchEvent(new Event('mousedown', { bubbles: true }));
232263
onChange?.([EMPTY_SORT]);
233264
};
265+
// replace duplicate columns with a single unspecified column for copy
266+
// reasons. maintain types so we can display in the select
267+
const [mergedColumns, typeMap] = useMemo(() => {
268+
const loadableTuple = columns.map((c) => {
269+
const columnGroups = groupBy(c, 'column');
270+
const fixedColumns = Object.values(columnGroups).flatMap((group) => {
271+
if (group.length > 1) {
272+
return [
273+
{
274+
...group[0],
275+
type: V1ColumnType.UNSPECIFIED,
276+
},
277+
];
278+
}
279+
return group;
280+
});
281+
const typeMap = mapValues(columnGroups, (group) => group.map((column) => column.type));
282+
return [fixedColumns, typeMap] as const;
283+
});
284+
return [loadableTuple.map((l) => l[0]), loadableTuple.map((l) => l[1]).getOrElse({})];
285+
}, [columns]);
234286

235287
return (
236288
<div className={css.base} data-test-component="multiSort">
237289
<div>{SORT_MENU_TITLE}</div>
238290
<div className={css.rows} data-test="rows">
239291
{sorts.map((sort, idx) => {
240292
const seenColumns = sorts.slice(0, idx).map((s) => s.column);
241-
const columnOptions = Loadable.map(columns, (cols) =>
293+
const columnOptions = mergedColumns.map((cols) =>
242294
cols.filter((c) => !seenColumns.includes(c.column)),
243295
);
244296
return (
@@ -247,6 +299,7 @@ const MultiSort: React.FC<MultiSortProps> = ({ sorts, columns, onChange, bannedS
247299
columns={columnOptions}
248300
key={sort.column || idx}
249301
sort={sort}
302+
typeMap={typeMap}
250303
onChange={makeOnRowChange(idx)}
251304
onRemove={makeOnRowRemove(idx)}
252305
/>

webui/react/src/pages/FlatRuns/FlatRuns.tsx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import Pagination from 'hew/Pagination';
2222
import Row from 'hew/Row';
2323
import { useToast } from 'hew/Toast';
2424
import { Loadable, Loaded, NotLoaded } from 'hew/utils/loadable';
25+
import { groupBy, keyBy, mapValues } from 'lodash';
2526
import { useObservable } from 'micro-observables';
2627
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
2728
import { v4 as uuidv4 } from 'uuid';
@@ -178,6 +179,7 @@ const FlatRuns: React.FC<Props> = ({ projectId, workspaceId, searchId }) => {
178179
const [runs, setRuns] = useState<Loadable<FlatRun>[]>(INITIAL_LOADING_RUNS);
179180

180181
const [sorts, setSorts] = useState<Sort[]>([EMPTY_SORT]);
182+
181183
const sortString = useMemo(() => makeSortString(sorts.filter(validSort.is)), [sorts]);
182184
const loadableFormset = useObservable(formStore.formset);
183185
const rootFilterChildren: Array<FormGroup | FormField> = Loadable.match(loadableFormset, {
@@ -237,6 +239,24 @@ const FlatRuns: React.FC<Props> = ({ projectId, workspaceId, searchId }) => {
237239
}
238240
}, [projectId]);
239241

242+
// expand sorts to include types from metadata columns so the column ids match
243+
const expandedSorts = useMemo(() => {
244+
return projectColumns.match({
245+
_: () => sorts,
246+
Loaded(pc) {
247+
const groupedColumns = groupBy(pc, (c) => c.column);
248+
const columnToExpandedColumns = mapValues(groupedColumns, (cols) =>
249+
cols.map((c) => formatColumnKey(c)),
250+
);
251+
return sorts
252+
.filter(validSort.is)
253+
.flatMap((sort) =>
254+
columnToExpandedColumns[sort.column].map((ec) => ({ ...sort, column: ec })),
255+
);
256+
},
257+
});
258+
}, [sorts, projectColumns]);
259+
240260
const arrayTypeColumns = useMemo(() => {
241261
const arrayTypeColumns = projectColumns
242262
.getOrElse([])
@@ -292,13 +312,11 @@ const FlatRuns: React.FC<Props> = ({ projectId, workspaceId, searchId }) => {
292312
return [...STATIC_COLUMNS, ...settings.columns.slice(0, settings.pinnedColumnsCount)];
293313
}, [settings.columns, settings.pinnedColumnsCount]);
294314

315+
const projectColumnsMap = useMemo(() => {
316+
return projectColumns.map((columns) => keyBy(columns, formatColumnKey));
317+
}, [projectColumns]);
318+
295319
const columns: ColumnDef<FlatRun>[] = useMemo(() => {
296-
const projectColumnsMap: Loadable<Record<string, ProjectColumn>> = Loadable.map(
297-
projectColumns,
298-
(columns) => {
299-
return columns.reduce((acc, col) => ({ ...acc, [formatColumnKey(col)]: col }), {});
300-
},
301-
);
302320
const columnDefs = getColumnDefs({
303321
appTheme,
304322
columnWidths: settings.columnWidths,
@@ -455,7 +473,7 @@ const FlatRuns: React.FC<Props> = ({ projectId, workspaceId, searchId }) => {
455473
appTheme,
456474
columnsIfLoaded,
457475
isDarkMode,
458-
projectColumns,
476+
projectColumnsMap,
459477
projectHeatmap,
460478
dataGridSelection.rows,
461479
settings.columnWidths,
@@ -956,7 +974,7 @@ const FlatRuns: React.FC<Props> = ({ projectId, workspaceId, searchId }) => {
956974
[
957975
bannedFilterColumns,
958976
bannedSortColumns,
959-
projectColumns,
977+
projectColumnsMap,
960978
settings.pinnedColumnsCount,
961979
settings.heatmapOn,
962980
settings.heatmapSkipped,

0 commit comments

Comments
 (0)