Skip to content

Commit 86a7e68

Browse files
authored
Merge pull request #873 from visualize-admin/feat/sort-table-values-properly
feat: Sort table values properly
2 parents a3130fb + cf1d223 commit 86a7e68

3 files changed

Lines changed: 53 additions & 24 deletions

File tree

app/charts/table/table-state.tsx

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ import {
99
scaleOrdinal,
1010
scaleDiverging,
1111
scaleLinear,
12+
ascending,
1213
} from "d3";
1314
import mapKeys from "lodash/mapKeys";
1415
import mapValues from "lodash/mapValues";
1516
import { ReactNode, useMemo } from "react";
16-
import { Cell, Column } from "react-table";
17+
import { Cell, Column, Row } from "react-table";
1718

1819
import {
1920
getLabelWithUnit,
@@ -38,6 +39,7 @@ import { useFormatNumber, useDimensionFormatters } from "@/formatters";
3839
import { getColorInterpolator } from "@/palettes";
3940
import { useTheme } from "@/themes";
4041
import { estimateTextWidth } from "@/utils/estimate-text-width";
42+
import { makeDimensionValueSorters } from "@/utils/sorting-values";
4143

4244
export type MKColumnMeta<T> = {
4345
iri: string;
@@ -145,7 +147,7 @@ const useTableState = ({
145147
// Data used by react-table
146148
const memoizedData = useMemo(
147149
function replaceKeys() {
148-
//Only read keys once
150+
// Only read keys once
149151
const keys = Object.keys(data[0]);
150152
const slugifiedKeys = keys.map(getSlugifiedIri);
151153

@@ -165,18 +167,25 @@ const useTableState = ({
165167
},
166168
[data, types]
167169
);
170+
168171
// Columns used by react-table
169172
const tableColumns = useMemo(() => {
173+
const allComponents = [...dimensions, ...measures];
174+
170175
return orderedTableColumns.map((c) => {
171-
const headerDimension = [...dimensions, ...measures].find(
172-
(dim) => dim.iri === c.componentIri
176+
const headerComponent = allComponents.find(
177+
(d) => d.iri === c.componentIri
173178
);
174179

175-
if (!headerDimension) {
180+
if (!headerComponent) {
176181
throw Error(`No dimension <${c.componentIri}> in cube!`);
177182
}
178183

179-
const headerLabel = getLabelWithUnit(headerDimension);
184+
const sorters = makeDimensionValueSorters(headerComponent, {
185+
sorting: { sortingType: "byTableSortingType", sortingOrder: "asc" },
186+
});
187+
188+
const headerLabel = getLabelWithUnit(headerComponent);
180189

181190
// The column width depends on the estimated width of the
182191
// longest value in the column, with a minimum of 150px.
@@ -203,9 +212,24 @@ const useTableState = ({
203212
accessor: getSlugifiedIri(c.componentIri),
204213

205214
width,
206-
// If sort type is not "basic", react-table default to "alphanumeric"
207-
// which doesn't sort negative values properly.
208-
sortType: "basic",
215+
sortType: (
216+
rowA: Row<Observation>,
217+
rowB: Row<Observation>,
218+
colId: string
219+
) => {
220+
for (const d of sorters) {
221+
const result = ascending(
222+
d(rowA.values[colId]),
223+
d(rowB.values[colId])
224+
);
225+
226+
if (result) {
227+
return result;
228+
}
229+
}
230+
231+
return 0;
232+
},
209233
};
210234
});
211235
}, [orderedTableColumns, data, dimensions, measures, formatNumber]);
@@ -337,22 +361,16 @@ const useTableState = ({
337361
),
338362
];
339363
const columnItemSizes = columnItems.map((item) => {
340-
// @ts-ignore
341364
const itemAsString = formatter(item);
342365
return estimateTextWidth(`${itemAsString}`, 16) + 80;
343366
});
344367
const width =
345368
Math.max(max(columnItemSizes, (d) => d) || 150, 150) -
346369
BAR_CELL_PADDING * 2;
347-
// const hasNegativeValue =
348-
// (min(data, (d) => Math.abs(+d[iri])) || 0) < 0;
349370
const domain = extent(columnItems, (d) => d) as [number, number];
350371
const widthScale = scaleLinear().domain(domain).range([0, width]);
351372

352-
return {
353-
...common,
354-
widthScale,
355-
} as BarColumnMeta;
373+
return { ...common, widthScale } as BarColumnMeta;
356374
} else {
357375
return null as never;
358376
}

app/utils/sorting-values.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@ import { DimensionValue } from "@/domain/data";
33

44
import { DataCubeObservationsQuery } from "../graphql/query-hooks";
55

6-
const maybeInt = (s?: string) => {
7-
if (!s) {
6+
const maybeInt = (value?: string): number | string => {
7+
if (!value) {
88
return Infinity;
99
}
10-
try {
11-
return parseInt(s, 10);
12-
} catch {
13-
return s;
10+
11+
const maybeInt = parseInt(value, 10);
12+
13+
if (isNaN(maybeInt)) {
14+
return value;
1415
}
16+
17+
return maybeInt;
1518
};
1619

1720
export const makeDimensionValueSorters = (
@@ -21,7 +24,13 @@ export const makeDimensionValueSorters = (
2124
>["dimensions"][number]
2225
| undefined,
2326
options: {
24-
sorting?: NonNullable<SortingField["sorting"]> | undefined;
27+
sorting?:
28+
| NonNullable<SortingField["sorting"]>
29+
| {
30+
sortingType: "byTableSortingType";
31+
sortingOrder: "asc";
32+
}
33+
| undefined;
2534
sumsBySegment?: Record<string, number | null>;
2635
measureBySegment?: Record<string, number | null>;
2736
} = {}
@@ -71,6 +80,8 @@ export const makeDimensionValueSorters = (
7180
case "byAuto":
7281
sorters = [getPosition, getIdentifier];
7382
break;
83+
case "byTableSortingType":
84+
sorters = [getPosition, getLabel];
7485
default:
7586
sorters = defaultSorters;
7687
}

e2e/filters.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe("Filters", () => {
2424
// ---
2525
"3. Evaluation type",
2626
"4. Reference area",
27-
"5. Inventory",
27+
"5. Grid",
2828
]);
2929

3030
const productionRegionFilter = selectors.edition.dataFilterInput(

0 commit comments

Comments
 (0)