From da0584f09a7291ed767fbc5542d6318cc8804453 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Mon, 12 Jun 2023 16:07:16 +0200 Subject: [PATCH 1/2] fix: X sorting (column charts) --- app/charts/column/columns-grouped-state.tsx | 41 +++++++++++++++---- app/charts/column/columns-stacked-state.tsx | 44 +++++++++++++++++---- 2 files changed, 70 insertions(+), 15 deletions(-) diff --git a/app/charts/column/columns-grouped-state.tsx b/app/charts/column/columns-grouped-state.tsx index 426cde8c4..6224c984e 100644 --- a/app/charts/column/columns-grouped-state.tsx +++ b/app/charts/column/columns-grouped-state.tsx @@ -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, @@ -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); @@ -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, @@ -362,7 +389,7 @@ const useGroupedColumnsState = ( aspectRatio, interactiveFiltersConfig, formatNumber, - bandDomainLabels + xDomainLabels ); const margins = { diff --git a/app/charts/column/columns-stacked-state.tsx b/app/charts/column/columns-stacked-state.tsx index 2505b0a6d..9de292dec 100644 --- a/app/charts/column/columns-stacked-state.tsx +++ b/app/charts/column/columns-stacked-state.tsx @@ -284,6 +284,18 @@ 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, @@ -291,7 +303,7 @@ const useColumnsStackedState = ( xScaleInteraction, xEntireScale, yStackDomain, - bandDomainLabels, + xDomainLabels, } = useMemo(() => { const colors = scaleOrdinal(); @@ -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); @@ -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, @@ -425,7 +453,7 @@ const useColumnsStackedState = ( aspectRatio, interactiveFiltersConfig, formatNumber, - bandDomainLabels + xDomainLabels ); const margins = { From 37d5061bbbdace4885f1631b04ba2c9dd1009d23 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Mon, 12 Jun 2023 16:17:35 +0200 Subject: [PATCH 2/2] test: Sorting byMeasure --- app/utils/sorting-values.spec.ts | 52 +++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/app/utils/sorting-values.spec.ts b/app/utils/sorting-values.spec.ts index 1bbdb1cae..4b9e1e565 100644 --- a/app/utils/sorting-values.spec.ts +++ b/app/utils/sorting-values.spec.ts @@ -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; @@ -123,15 +130,21 @@ const temporalDimensionFullDate = { } as unknown as DimensionMetadataFragment; describe("makeDimensionValueSorters", () => { - const sorting: NonNullable = { + const sortingByAuto: NonNullable = { sortingType: "byAuto", sortingOrder: "asc", }; + const sortingByMeasure: NonNullable = { + 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", @@ -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", @@ -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", @@ -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",