From fa3317eb34ac51d31cc32f6b0345a1e3206b5fa4 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Tue, 11 Apr 2023 13:13:29 +0200 Subject: [PATCH] feat: Handle same labels in column chart --- app/charts/column/columns-state.tsx | 83 ++++++++++++++++++++------- app/charts/shared/axis-width-band.tsx | 8 +-- 2 files changed, 67 insertions(+), 24 deletions(-) diff --git a/app/charts/column/columns-state.tsx b/app/charts/column/columns-state.tsx index 65e75510cd..43cbc43760 100644 --- a/app/charts/column/columns-state.tsx +++ b/app/charts/column/columns-state.tsx @@ -14,7 +14,6 @@ import { ScaleTime, } from "d3"; import get from "lodash/get"; -import keyBy from "lodash/keyBy"; import orderBy from "lodash/orderBy"; import { ReactNode, useCallback, useMemo } from "react"; @@ -57,7 +56,6 @@ import { TemporalDimension, TimeUnit, } from "@/graphql/query-hooks"; -import { getPalette } from "@/palettes"; import { getSortingOrders, makeDimensionValueSorters, @@ -68,6 +66,7 @@ export interface ColumnsState extends CommonChartState { preparedData: Observation[]; allData: Observation[]; getX: (d: Observation) => string; + getXLabel: (d: string) => string; getXAsDate: (d: Observation) => Date; xIsTime: boolean; timeUnit: TimeUnit | undefined; @@ -113,10 +112,42 @@ const useColumnsState = ( ? (xDimension as TemporalDimension).timeUnit : undefined; - const { getAbbreviationOrLabelByValue: getX } = useMaybeAbbreviations({ - useAbbreviations: fields.x.useAbbreviations ?? false, - dimension: xDimension, - }); + const { getAbbreviationOrLabelByValue: getXLabelOrAbbrevation } = + useMaybeAbbreviations({ + useAbbreviations: fields.x.useAbbreviations ?? false, + dimension: xDimension, + }); + + const getXIri = useCallback( + (d: Observation) => { + return d[`${fields.x.componentIri}/__iri__`] as string | undefined; + }, + [fields.x.componentIri] + ); + + const observationLabelsLookup = useMemo(() => { + const lookup = new Map(); + data.forEach((d) => { + const label = getXLabelOrAbbrevation(d); + const iri = getXIri(d); + lookup.set(iri ?? label, label); + }); + + return lookup; + }, [data, getXLabelOrAbbrevation, getXIri]); + + const getX = useCallback( + (d: Observation) => { + return getXIri(d) ?? getXLabelOrAbbrevation(d); + }, + [getXIri, getXLabelOrAbbrevation] + ); + const getXLabel = useCallback( + (d: string) => { + return observationLabelsLookup.get(d) ?? d; + }, + [observationLabelsLookup] + ); const getXAsDate = useTemporalVariable(fields.x.componentIri); const getY = useOptionalNumericVariable(fields.y.componentIri); @@ -131,7 +162,13 @@ const useColumnsState = ( // All data const sortedData = useMemo(() => { - return sortData({ data, sortingType, sortingOrder, getX, getY }); + return sortData({ + data, + sortingType, + sortingOrder, + getX, + getY, + }); }, [data, getX, getY, sortingType, sortingOrder]); // Data @@ -155,7 +192,7 @@ const useColumnsState = ( sorting: fields.x.sorting, useAbbreviations: fields.x.useAbbreviations, dimensionFilter: xDimension?.iri - ? chartConfig.filters[xDimension?.iri] + ? chartConfig.filters[xDimension.iri] : undefined, }); const sortingOrders = getSortingOrders(sorters, fields.x.sorting); @@ -195,7 +232,14 @@ const useColumnsState = ( ); const yScale = scaleLinear().domain([minValue, maxValue]).nice(); - return { xScale, yScale, xEntireScale, xScaleInteraction, bandDomain }; + + return { + xScale, + yScale, + xEntireScale, + xScaleInteraction, + bandDomain, + }; }, [ getX, getXAsDate, @@ -258,10 +302,10 @@ const useColumnsState = ( }, []); // Tooltip - const getAnnotationInfo = (datum: Observation): TooltipInfo => { - const xRef = xScale(getX(datum)) as number; + const getAnnotationInfo = (d: Observation): TooltipInfo => { + const xRef = xScale(getX(d)) as number; const xOffset = xScale.bandwidth() / 2; - const yRef = yScale(Math.max(getY(datum) ?? NaN, 0)); + const yRef = yScale(Math.max(getY(d) ?? NaN, 0)); const yAnchor = yRef; const yPlacement = "top"; @@ -285,6 +329,7 @@ const useColumnsState = ( : xRef + xOffset * 2; }; const xAnchor = getXAnchor(); + const xLabel = getXLabelOrAbbrevation(d); const yValueFormatter = (value: number | null) => formatNumberWithUnit( @@ -297,17 +342,14 @@ const useColumnsState = ( xAnchor, yAnchor, placement: { x: xPlacement, y: yPlacement }, - xValue: - xIsTime && timeUnit - ? timeFormatUnit(getX(datum), timeUnit) - : getX(datum), + xValue: xIsTime && timeUnit ? timeFormatUnit(xLabel, timeUnit) : xLabel, datum: { - label: fields.segment?.componentIri && getSegment(datum), - value: `${yValueFormatter(getY(datum))}`, + label: fields.segment?.componentIri && getSegment(d), + value: `${yValueFormatter(getY(d))}`, error: getYError - ? `${getYError(datum)}${errorMeasure?.unit ?? ""}` + ? `${getYError(d)}${errorMeasure?.unit ?? ""}` : undefined, - color: colors(getSegment(datum)) as string, + color: colors(getSegment(d)) as string, }, values: undefined, }; @@ -319,6 +361,7 @@ const useColumnsState = ( preparedData, allData: plottableSortedData, getX, + getXLabel, getXAsDate, xScale, xEntireScale, diff --git a/app/charts/shared/axis-width-band.tsx b/app/charts/shared/axis-width-band.tsx index 2ae64a882f..117d8ea6de 100644 --- a/app/charts/shared/axis-width-band.tsx +++ b/app/charts/shared/axis-width-band.tsx @@ -1,5 +1,4 @@ -import { axisBottom } from "d3"; -import { select, Selection } from "d3"; +import { axisBottom, select, Selection } from "d3"; import { useEffect, useRef } from "react"; import { ColumnsState } from "@/charts/column/columns-state"; @@ -9,7 +8,7 @@ import { useTimeFormatUnit } from "@/formatters"; export const AxisWidthBand = () => { const ref = useRef(null); - const { xScale, yScale, bounds, xIsTime, timeUnit } = + const { xScale, yScale, bounds, xIsTime, timeUnit, getXLabel } = useChartState() as ColumnsState; const formatDate = useTimeFormatUnit(); @@ -29,8 +28,9 @@ export const AxisWidthBand = () => { if (xIsTime && timeUnit) { axis.tickFormat((d) => formatDate(d, timeUnit)); + } else { + axis.tickFormat((d) => getXLabel(d)); } - const fontSize = xScale.bandwidth() > labelFontSize ? labelFontSize : xScale.bandwidth();