diff --git a/app/charts/area/areas-state.tsx b/app/charts/area/areas-state.tsx index 7e4ac9dc71..daed187ee1 100644 --- a/app/charts/area/areas-state.tsx +++ b/app/charts/area/areas-state.tsx @@ -29,7 +29,7 @@ import { stackOffsetDivergingPositiveZeros, useOptionalNumericVariable, usePlottableData, - usePreparedData, + useDataAfterInteractiveFilters, useSegment, useStringVariable, useTemporalVariable, @@ -38,7 +38,6 @@ import { TooltipInfo } from "@/charts/shared/interaction/tooltip"; import useChartFormatters from "@/charts/shared/use-chart-formatters"; import { ChartContext, ChartProps } from "@/charts/shared/use-chart-state"; import { InteractionProvider } from "@/charts/shared/use-interaction"; -import { useInteractiveFilters } from "@/charts/shared/use-interactive-filters"; import { Bounds, Observer, useWidth } from "@/charts/shared/use-width"; import { AreaFields } from "@/configurator"; import { isTemporalDimension, Observation } from "@/domain/data"; @@ -93,7 +92,6 @@ const useAreasState = ( const width = useWidth(); const formatNumber = useFormatNumber(); const timeFormatUnit = useTimeFormatUnit(); - const [interactiveFilters] = useInteractiveFilters(); const xDimension = dimensions.find((d) => d.iri === fields.x.componentIri); @@ -169,11 +167,9 @@ const useAreasState = ( }); // Data for chart - const preparedData = usePreparedData({ - legendFilterActive: interactiveFiltersConfig?.legend.active, - timeRangeFilterActive: interactiveFiltersConfig?.timeRange.active, + const preparedData = useDataAfterInteractiveFilters({ sortedData: plottableSortedData, - interactiveFilters, + interactiveFiltersConfig, getX, getSegment, }); diff --git a/app/charts/column/columns-grouped-state.tsx b/app/charts/column/columns-grouped-state.tsx index e7164e0dec..4491e53c69 100644 --- a/app/charts/column/columns-grouped-state.tsx +++ b/app/charts/column/columns-grouped-state.tsx @@ -29,9 +29,9 @@ import { } from "@/charts/column/constants"; import { getLabelWithUnit, + useDataAfterInteractiveFilters, useOptionalNumericVariable, usePlottableData, - usePreparedData, useSegment, useStringVariable, useTemporalVariable, @@ -41,7 +41,6 @@ import { useChartPadding } from "@/charts/shared/padding"; import useChartFormatters from "@/charts/shared/use-chart-formatters"; import { ChartContext, ChartProps } from "@/charts/shared/use-chart-state"; import { InteractionProvider } from "@/charts/shared/use-interaction"; -import { useInteractiveFilters } from "@/charts/shared/use-interactive-filters"; import { Bounds, Observer, useWidth } from "@/charts/shared/use-width"; import { ColumnFields, SortingField } from "@/configurator"; import { @@ -101,8 +100,6 @@ const useGroupedColumnsState = ( const width = useWidth(); const formatNumber = useFormatNumber(); - const [interactiveFilters] = useInteractiveFilters(); - const xDimension = dimensions.find((d) => d.iri === fields.x.componentIri); if (!xDimension) { @@ -166,11 +163,9 @@ const useGroupedColumnsState = ( }); // Data for chart - const preparedData = usePreparedData({ - legendFilterActive: interactiveFiltersConfig?.legend.active, - timeRangeFilterActive: interactiveFiltersConfig?.timeRange.active, + const preparedData = useDataAfterInteractiveFilters({ sortedData: plottableSortedData, - interactiveFilters, + interactiveFiltersConfig, getX: getXAsDate, getSegment, }); diff --git a/app/charts/column/columns-stacked-state.tsx b/app/charts/column/columns-stacked-state.tsx index d71646ed17..b5d5f52341 100644 --- a/app/charts/column/columns-stacked-state.tsx +++ b/app/charts/column/columns-stacked-state.tsx @@ -34,9 +34,9 @@ import { import { getLabelWithUnit, getWideData, + useDataAfterInteractiveFilters, useOptionalNumericVariable, usePlottableData, - usePreparedData, useSegment, useStringVariable, useTemporalVariable, @@ -46,7 +46,6 @@ import { useChartPadding } from "@/charts/shared/padding"; import useChartFormatters from "@/charts/shared/use-chart-formatters"; import { ChartContext, ChartProps } from "@/charts/shared/use-chart-state"; import { InteractionProvider } from "@/charts/shared/use-interaction"; -import { useInteractiveFilters } from "@/charts/shared/use-interactive-filters"; import { Bounds, Observer, useWidth } from "@/charts/shared/use-width"; import { ColumnFields, SortingOrder, SortingType } from "@/configurator"; import { isTemporalDimension, Observation } from "@/domain/data"; @@ -100,7 +99,6 @@ const useColumnsStackedState = ( } = chartProps; const width = useWidth(); const formatNumber = useFormatNumber(); - const [interactiveFilters] = useInteractiveFilters(); const xDimension = dimensions.find((d) => d.iri === fields.x.componentIri); @@ -159,11 +157,9 @@ const useColumnsStackedState = ( }); // Data for Chart - const preparedData = usePreparedData({ - legendFilterActive: interactiveFiltersConfig?.legend.active, - timeRangeFilterActive: interactiveFiltersConfig?.timeRange.active, + const preparedData = useDataAfterInteractiveFilters({ sortedData: plottableSortedData, - interactiveFilters, + interactiveFiltersConfig, getX: getXAsDate, getSegment, }); diff --git a/app/charts/column/columns-state.tsx b/app/charts/column/columns-state.tsx index a8cce40e56..a65c3cfdae 100644 --- a/app/charts/column/columns-state.tsx +++ b/app/charts/column/columns-state.tsx @@ -26,9 +26,9 @@ import { } from "@/charts/column/constants"; import { getLabelWithUnit, + useDataAfterInteractiveFilters, useOptionalNumericVariable, usePlottableData, - usePreparedData, useSegment, useStringVariable, useTemporalVariable, @@ -38,7 +38,6 @@ import { useChartPadding } from "@/charts/shared/padding"; import useChartFormatters from "@/charts/shared/use-chart-formatters"; import { ChartContext, ChartProps } from "@/charts/shared/use-chart-state"; import { InteractionProvider } from "@/charts/shared/use-interaction"; -import { useInteractiveFilters } from "@/charts/shared/use-interactive-filters"; import { Bounds, Observer, useWidth } from "@/charts/shared/use-width"; import { ColumnFields, SortingOrder, SortingType } from "@/configurator"; import { @@ -102,7 +101,6 @@ const useColumnsState = ( const width = useWidth(); const formatNumber = useFormatNumber(); const timeFormatUnit = useTimeFormatUnit(); - const [interactiveFilters] = useInteractiveFilters(); const dimensionsByIri = useMemo( () => Object.fromEntries(dimensions.map((d) => [d.iri, d])), @@ -159,10 +157,9 @@ const useColumnsState = ( plotters: [getXAsDate, getY], }); - const preparedData = usePreparedData({ - timeRangeFilterActive: interactiveFiltersConfig?.timeRange.active, + const preparedData = useDataAfterInteractiveFilters({ sortedData: plottableSortedData, - interactiveFilters, + interactiveFiltersConfig, getX: getXAsDate, }); diff --git a/app/charts/line/lines-state.tsx b/app/charts/line/lines-state.tsx index ffadc41d0f..752a95df35 100644 --- a/app/charts/line/lines-state.tsx +++ b/app/charts/line/lines-state.tsx @@ -20,9 +20,9 @@ import { BRUSH_BOTTOM_SPACE } from "@/charts/shared/brush"; import { getLabelWithUnit, getWideData, + useDataAfterInteractiveFilters, useOptionalNumericVariable, usePlottableData, - usePreparedData, useSegment, useStringVariable, useTemporalVariable, @@ -31,7 +31,6 @@ import { TooltipInfo } from "@/charts/shared/interaction/tooltip"; import useChartFormatters from "@/charts/shared/use-chart-formatters"; import { ChartContext, ChartProps } from "@/charts/shared/use-chart-state"; import { InteractionProvider } from "@/charts/shared/use-interaction"; -import { useInteractiveFilters } from "@/charts/shared/use-interactive-filters"; import { Bounds, Observer, useWidth } from "@/charts/shared/use-width"; import { LineFields } from "@/configurator"; import { isTemporalDimension, Observation } from "@/domain/data"; @@ -91,7 +90,6 @@ const useLinesState = ( const width = useWidth(); const formatNumber = useFormatNumber(); const timeFormatUnit = useTimeFormatUnit(); - const [interactiveFilters] = useInteractiveFilters(); const xDimension = dimensions.find((d) => d.iri === fields.x.componentIri); @@ -132,11 +130,9 @@ const useLinesState = ( }); // All Data - const preparedData = usePreparedData({ - legendFilterActive: interactiveFiltersConfig?.legend.active, - timeRangeFilterActive: interactiveFiltersConfig?.timeRange.active, + const preparedData = useDataAfterInteractiveFilters({ sortedData: plottableSortedData, - interactiveFilters, + interactiveFiltersConfig, getX, getSegment, }); diff --git a/app/charts/pie/pie-state.tsx b/app/charts/pie/pie-state.tsx index 5ceb0d5f4e..a54acafa32 100644 --- a/app/charts/pie/pie-state.tsx +++ b/app/charts/pie/pie-state.tsx @@ -12,16 +12,15 @@ import orderBy from "lodash/orderBy"; import React, { ReactNode, useMemo, useCallback } from "react"; import { + useDataAfterInteractiveFilters, useOptionalNumericVariable, usePlottableData, - usePreparedData, useSegment, } from "@/charts/shared/chart-helpers"; import { TooltipInfo } from "@/charts/shared/interaction/tooltip"; import useChartFormatters from "@/charts/shared/use-chart-formatters"; import { ChartContext, ChartProps } from "@/charts/shared/use-chart-state"; import { InteractionProvider } from "@/charts/shared/use-interaction"; -import { useInteractiveFilters } from "@/charts/shared/use-interactive-filters"; import { Bounds, Observer, useWidth } from "@/charts/shared/use-width"; import { PieFields } from "@/configurator"; import { DimensionValue, Observation } from "@/domain/data"; @@ -58,7 +57,6 @@ const usePieState = ( } = chartProps; const width = useWidth(); const formatNumber = useFormatNumber(); - const [interactiveFilters] = useInteractiveFilters(); const yMeasure = measures.find((d) => d.iri === fields.y.componentIri); @@ -79,10 +77,9 @@ const usePieState = ( }); // Apply end-user-activated interactive filters to the stack - const preparedData = usePreparedData({ - legendFilterActive: interactiveFiltersConfig?.legend.active, + const preparedData = useDataAfterInteractiveFilters({ sortedData: plottableData, - interactiveFilters, + interactiveFiltersConfig, getSegment: getX, }); diff --git a/app/charts/scatterplot/scatterplot-state.tsx b/app/charts/scatterplot/scatterplot-state.tsx index 9c87c3d27a..6fb33c32f9 100644 --- a/app/charts/scatterplot/scatterplot-state.tsx +++ b/app/charts/scatterplot/scatterplot-state.tsx @@ -13,16 +13,15 @@ import React, { ReactNode, useMemo } from "react"; import { LEFT_MARGIN_OFFSET } from "@/charts/scatterplot/constants"; import { getLabelWithUnit, + useDataAfterInteractiveFilters, useOptionalNumericVariable, usePlottableData, - usePreparedData, useSegment, } from "@/charts/shared/chart-helpers"; import { TooltipInfo } from "@/charts/shared/interaction/tooltip"; import { TooltipScatterplot } from "@/charts/shared/interaction/tooltip-content"; import { ChartContext, ChartProps } from "@/charts/shared/use-chart-state"; import { InteractionProvider } from "@/charts/shared/use-interaction"; -import { useInteractiveFilters } from "@/charts/shared/use-interactive-filters"; import { Bounds, Observer, useWidth } from "@/charts/shared/use-width"; import { ScatterPlotFields } from "@/configurator"; import { mkNumber } from "@/configurator/components/ui-helpers"; @@ -64,8 +63,6 @@ const useScatterplotState = ({ fields: ScatterPlotFields; aspectRatio: number; }): ScatterplotState => { - const [interactiveFilters] = useInteractiveFilters(); - const width = useWidth(); const formatNumber = useFormatNumber(); @@ -83,10 +80,9 @@ const useScatterplotState = ({ }); // Data for chart - const preparedData = usePreparedData({ - legendFilterActive: interactiveFiltersConfig?.legend.active, + const preparedData = useDataAfterInteractiveFilters({ sortedData: plottableSortedData, - interactiveFilters, + interactiveFiltersConfig, getSegment, }); const xMeasure = measures.find((d) => d.iri === fields.x.componentIri); diff --git a/app/charts/shared/chart-helpers.tsx b/app/charts/shared/chart-helpers.tsx index e4412d3284..f5519079e0 100644 --- a/app/charts/shared/chart-helpers.tsx +++ b/app/charts/shared/chart-helpers.tsx @@ -17,6 +17,7 @@ import { Filters, FilterValueSingle, ImputationType, + InteractiveFiltersConfig, isAreaConfig, } from "@/configurator/config-types"; import { FIELD_VALUE_NONE } from "@/configurator/constants"; @@ -87,48 +88,46 @@ export const usePlottableData = ({ return useMemo(() => data.filter(isPlottable), [data, isPlottable]); }; -// Prepare data used in charts. -// Different than the full dataset because -// interactive filters may be applied (legend + brush) -export const usePreparedData = ({ - timeRangeFilterActive, - legendFilterActive, +/** Prepares the data to be used in charts. + * + * Different than the full dataset, because interactive filters might be applied. + */ +export const useDataAfterInteractiveFilters = ({ sortedData, - interactiveFilters, + interactiveFiltersConfig, getX, getSegment, }: { - timeRangeFilterActive?: boolean; - legendFilterActive?: boolean; sortedData: Array; - interactiveFilters: InteractiveFiltersState; + interactiveFiltersConfig: InteractiveFiltersConfig; getX?: (d: Observation) => Date; getSegment?: (d: Observation) => string; }) => { + const [interactiveFilters] = useInteractiveFilters(); const { from, to } = interactiveFilters.timeRange; const { categories } = interactiveFilters; const activeInteractiveFilters = Object.keys(categories); const allFilters = useMemo(() => { - const timeFilter: ValuePredicate | null = - getX && from && to && timeRangeFilterActive + const timeRangeFilter: ValuePredicate | null = + getX && from && to && interactiveFiltersConfig?.timeRange.active ? (d: Observation) => getX(d).getTime() >= from.getTime() && getX(d).getTime() <= to.getTime() : null; const legendFilter: ValuePredicate | null = - legendFilterActive && getSegment + interactiveFiltersConfig?.legend.active && getSegment ? (d: Observation) => !activeInteractiveFilters.includes(getSegment(d)) : null; - return overEvery([legendFilter, timeFilter].filter(truthy)); + return overEvery([timeRangeFilter, legendFilter].filter(truthy)); }, [ activeInteractiveFilters, from, getSegment, getX, - legendFilterActive, - timeRangeFilterActive, to, + interactiveFiltersConfig?.legend.active, + interactiveFiltersConfig?.timeRange.active, ]); const preparedData = useMemo(() => {