Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Horizontal axis sorting (column charts) #1064

Merged
merged 2 commits into from
Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 34 additions & 7 deletions app/charts/column/columns-grouped-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,18 @@ const useGroupedColumnsState = (
]);

/* Scales */
const xFilter = chartConfig.filters[fields.x.componentIri];
const sumsByX = useMemo(() => {
return Object.fromEntries([
...rollup(
plottableSortedData,
(v) => sum(v, (x) => getY(x)),
(x) => getX(x)
),
]);
}, [plottableSortedData, getX, getY]);
const {
bandDomainLabels,
xDomainLabels,
colors,
yScale,
xEntireScale,
Expand Down Expand Up @@ -269,14 +279,26 @@ const useGroupedColumnsState = (
colors.unknown(() => undefined);
}

const bandDomain = [...new Set(scalesData.map(getX))];
const bandDomainLabels = bandDomain.map(getXLabel);
const xValues = [...new Set(scalesData.map(getX))];
const xSorting = fields.x?.sorting;
const xSorters = makeDimensionValueSorters(xDimension, {
sorting: xSorting,
useAbbreviations: fields.x?.useAbbreviations,
measureBySegment: sumsByX,
dimensionFilter: xFilter,
});
const xDomain = orderBy(
xValues,
xSorters,
getSortingOrders(xSorters, xSorting)
);
const xDomainLabels = xDomain.map(getXLabel);
const xScale = scaleBand()
.domain(bandDomain)
.domain(xDomain)
.paddingInner(PADDING_INNER)
.paddingOuter(PADDING_OUTER);
const xScaleInteraction = scaleBand()
.domain(bandDomain)
.domain(xDomain)
.paddingInner(0)
.paddingOuter(0);
const xScaleIn = scaleBand().domain(segments).padding(PADDING_WITHIN);
Expand Down Expand Up @@ -310,11 +332,16 @@ const useGroupedColumnsState = (
xScale,
xScaleIn,
xScaleInteraction,
bandDomainLabels,
xDomainLabels,
};
}, [
dimensions,
fields.segment,
xFilter,
fields.x.sorting,
fields.x.useAbbreviations,
sumsByX,
xDimension,
segments,
segmentsByAbbreviationOrLabel,
segmentsByValue,
Expand Down Expand Up @@ -362,7 +389,7 @@ const useGroupedColumnsState = (
aspectRatio,
interactiveFiltersConfig,
formatNumber,
bandDomainLabels
xDomainLabels
);

const margins = {
Expand Down
44 changes: 36 additions & 8 deletions app/charts/column/columns-stacked-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -284,14 +284,26 @@ const useColumnsStackedState = (
]);

// Scales
const xFilter = chartConfig.filters[xDimension.iri];
const sumsByX = useMemo(
() =>
Object.fromEntries([
...rollup(
plottableSortedData,
(v) => sum(v, (x) => getY(x)),
(x) => getX(x)
),
]),
[plottableSortedData, getX, getY]
);
// Map ordered segments labels to colors
const {
colors,
xScale,
xScaleInteraction,
xEntireScale,
yStackDomain,
bandDomainLabels,
xDomainLabels,
} = useMemo(() => {
const colors = scaleOrdinal<string, string>();

Expand Down Expand Up @@ -328,15 +340,26 @@ const useColumnsStackedState = (
colors.unknown(() => undefined);
}

// x
const bandDomain = [...new Set(scalesData.map(getX))];
const bandDomainLabels = bandDomain.map(getXLabel);
const xValues = [...new Set(scalesData.map(getX))];
const xSorting = fields.x?.sorting;
const xSorters = makeDimensionValueSorters(xDimension, {
sorting: xSorting,
useAbbreviations: fields.x?.useAbbreviations,
measureBySegment: sumsByX,
dimensionFilter: xFilter,
});
const xDomain = orderBy(
xValues,
xSorters,
getSortingOrders(xSorters, xSorting)
);
const xDomainLabels = xDomain.map(getXLabel);
const xScale = scaleBand()
.domain(bandDomain)
.domain(xDomain)
.paddingInner(PADDING_INNER)
.paddingOuter(PADDING_OUTER);
const xScaleInteraction = scaleBand()
.domain(bandDomain)
.domain(xDomain)
.paddingInner(0)
.paddingOuter(0);

Expand Down Expand Up @@ -367,11 +390,16 @@ const useColumnsStackedState = (
xScale,
xEntireScale,
xScaleInteraction,
bandDomainLabels,
xDomainLabels,
};
}, [
scalesWideData,
fields.segment,
fields.x.sorting,
fields.x.useAbbreviations,
xDimension,
xFilter,
sumsByX,
getX,
getXLabel,
getXAsDate,
Expand Down Expand Up @@ -425,7 +453,7 @@ const useColumnsStackedState = (
aspectRatio,
interactiveFiltersConfig,
formatNumber,
bandDomainLabels
xDomainLabels
);

const margins = {
Expand Down
52 changes: 41 additions & 11 deletions app/utils/sorting-values.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ const dimension = {
],
} as unknown as DimensionMetadataFragment;

const measureBySegment = {
A: 10,
B: 20,
C: 15,
D: 5,
};

const measure = {
__typename: "NumericalMeasure",
} as unknown as DimensionMetadataFragment;
Expand Down Expand Up @@ -123,15 +130,21 @@ const temporalDimensionFullDate = {
} as unknown as DimensionMetadataFragment;

describe("makeDimensionValueSorters", () => {
const sorting: NonNullable<SortingField["sorting"]> = {
const sortingByAuto: NonNullable<SortingField["sorting"]> = {
sortingType: "byAuto",
sortingOrder: "asc",
};
const sortingByMeasure: NonNullable<SortingField["sorting"]> = {
sortingType: "byMeasure",
sortingOrder: "asc",
};

it("should correctly sort dimensions byAuto", () => {
const values = dimension.values.map((d) => d.value);
const sorters = makeDimensionValueSorters(dimension, { sorting });
const sortingOrders = getSortingOrders(sorters, sorting);
const sorters = makeDimensionValueSorters(dimension, {
sorting: sortingByAuto,
});
const sortingOrders = getSortingOrders(sorters, sortingByAuto);
expect(orderBy(values, sorters, sortingOrders)).toEqual([
"C",
"D",
Expand All @@ -140,19 +153,36 @@ describe("makeDimensionValueSorters", () => {
]);
});

it("should correctly sort dimensions byMeasure", () => {
const values = dimension.values.map((d) => d.value);
const sorters = makeDimensionValueSorters(dimension, {
sorting: sortingByMeasure,
measureBySegment,
});
const sortingOrders = getSortingOrders(sorters, sortingByAuto);
expect(orderBy(values, sorters, sortingOrders)).toEqual([
"D",
"A",
"C",
"B",
]);
});

it("should correctly sort numerical measures byAuto", () => {
const values = [1, 10, 5, 100, 2];
const sorters = makeDimensionValueSorters(measure, { sorting });
const sortingOrders = getSortingOrders(sorters, sorting);
const sorters = makeDimensionValueSorters(measure, {
sorting: sortingByAuto,
});
const sortingOrders = getSortingOrders(sorters, sortingByAuto);
expect(orderBy(values, sorters, sortingOrders)).toEqual([1, 2, 5, 10, 100]);
});

it("should correctly sort hierarchical dimensions byAuto", () => {
const values = hierarchicalDimension.values.map((d) => d.value);
const sorters = makeDimensionValueSorters(hierarchicalDimension, {
sorting,
sorting: sortingByAuto,
});
const sortingOrders = getSortingOrders(sorters, sorting);
const sortingOrders = getSortingOrders(sorters, sortingByAuto);
expect(orderBy(values, sorters, sortingOrders)).toEqual([
"CH",
"CH-PROD-EAST",
Expand All @@ -164,9 +194,9 @@ describe("makeDimensionValueSorters", () => {
it("should correctly sort temporal dimensions (year) byAuto", () => {
const values = temporalDimensionYear.values.map((d) => d.value);
const sorters = makeDimensionValueSorters(temporalDimensionYear, {
sorting,
sorting: sortingByAuto,
});
const sortingOrders = getSortingOrders(sorters, sorting);
const sortingOrders = getSortingOrders(sorters, sortingByAuto);
expect(orderBy(values, sorters, sortingOrders)).toEqual([
"1996",
"2019",
Expand All @@ -177,9 +207,9 @@ describe("makeDimensionValueSorters", () => {
it("should correctly sort temporal dimensions (full date) byAuto", () => {
const values = temporalDimensionFullDate.values.map((d) => d.value);
const sorters = makeDimensionValueSorters(temporalDimensionFullDate, {
sorting,
sorting: sortingByAuto,
});
const sortingOrders = getSortingOrders(sorters, sorting);
const sortingOrders = getSortingOrders(sorters, sortingByAuto);
expect(orderBy(values, sorters, sortingOrders)).toEqual([
"1996-05-05",
"2019-12-12",
Expand Down