diff --git a/CHANGELOG.md b/CHANGELOG.md index f4c22252b..aeb71422b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,8 +20,12 @@ You can also check the - Fixed preview via API (iframe) - Fixed cut table scroll-bars and unnecessary scroll of bar charts when switching between chart types - - Fixed map dimension symbols to increase the elements size for small values, whilst preventing any 0 and undefined values from displaying + - Fixed map dimension symbols to increase the elements size for small values, + whilst preventing any 0 and undefined values from displaying - Added Footer to the Profile Page +- Docs + - Added auto-generated JSON Schema files for configurator state and chart + config and improved preview charts via API documentation # [5.0.2] - 2024-11-28 diff --git a/app/charts/bar/bars-grouped-state-props.ts b/app/charts/bar/bars-grouped-state-props.ts index 201aecb2a..23884670a 100644 --- a/app/charts/bar/bars-grouped-state-props.ts +++ b/app/charts/bar/bars-grouped-state-props.ts @@ -22,8 +22,9 @@ import { useSegmentVariables, } from "@/charts/shared/chart-state"; import { useRenderingKeyVariable } from "@/charts/shared/rendering-utils"; -import { BarConfig, useChartConfigFilters } from "@/configurator"; -import { Observation, isTemporalEntityDimension } from "@/domain/data"; +import { useChartConfigFilters } from "@/config-utils"; +import { BarConfig } from "@/configurator"; +import { isTemporalEntityDimension, Observation } from "@/domain/data"; import { sortByIndex } from "@/utils/array"; import { ChartProps } from "../shared/ChartProps"; diff --git a/app/charts/bar/bars-stacked-state-props.ts b/app/charts/bar/bars-stacked-state-props.ts index dcffa2a00..7e75fc11b 100644 --- a/app/charts/bar/bars-stacked-state-props.ts +++ b/app/charts/bar/bars-stacked-state-props.ts @@ -19,8 +19,9 @@ import { useSegmentVariables, } from "@/charts/shared/chart-state"; import { useRenderingKeyVariable } from "@/charts/shared/rendering-utils"; -import { BarConfig, useChartConfigFilters } from "@/configurator"; -import { Observation, isTemporalEntityDimension } from "@/domain/data"; +import { useChartConfigFilters } from "@/config-utils"; +import { BarConfig } from "@/configurator"; +import { isTemporalEntityDimension, Observation } from "@/domain/data"; import { sortByIndex } from "@/utils/array"; import { ChartProps } from "../shared/ChartProps"; diff --git a/app/charts/bar/bars-state-props.ts b/app/charts/bar/bars-state-props.ts index 0a9c40906..ea7374b5d 100644 --- a/app/charts/bar/bars-state-props.ts +++ b/app/charts/bar/bars-state-props.ts @@ -19,7 +19,8 @@ import { useNumericalXVariables, } from "@/charts/shared/chart-state"; import { useRenderingKeyVariable } from "@/charts/shared/rendering-utils"; -import { BarConfig, useChartConfigFilters } from "@/configurator"; +import { useChartConfigFilters } from "@/config-utils"; +import { BarConfig } from "@/configurator"; import { isTemporalEntityDimension } from "@/domain/data"; import { ChartProps } from "../shared/ChartProps"; diff --git a/app/charts/bar/chart-bar.tsx b/app/charts/bar/chart-bar.tsx index d4d791577..6b622fade 100644 --- a/app/charts/bar/chart-bar.tsx +++ b/app/charts/bar/chart-bar.tsx @@ -24,7 +24,8 @@ import { } from "@/charts/shared/containers"; import { Tooltip } from "@/charts/shared/interaction/tooltip"; import { LegendColor } from "@/charts/shared/legend-color"; -import { BarConfig, useChartConfigFilters } from "@/config-types"; +import { BarConfig } from "@/config-types"; +import { useChartConfigFilters } from "@/config-utils"; import { hasChartConfigs } from "@/configurator"; import { TimeSlider } from "@/configurator/interactive-filters/time-slider"; import { useConfiguratorState } from "@/src"; diff --git a/app/charts/chart-config-ui-options.ts b/app/charts/chart-config-ui-options.ts index 9f848270f..afba69c72 100644 --- a/app/charts/chart-config-ui-options.ts +++ b/app/charts/chart-config-ui-options.ts @@ -29,6 +29,8 @@ import { ComboLineSingleConfig, ComponentType, GenericField, + getAnimationField, + isSortingInConfig, LineConfig, MapConfig, PaletteType, @@ -37,25 +39,23 @@ import { SortingOrder, SortingType, TableConfig, - getAnimationField, - isSortingInConfig, - makeMultiFilter, } from "@/config-types"; +import { makeMultiFilter } from "@/config-utils"; import { getFieldLabel } from "@/configurator/components/field-i18n"; import { mapValueIrisToColor } from "@/configurator/components/ui-helpers"; import { ANIMATION_ENABLED_COMPONENTS, Component, Dimension, - MULTI_FILTER_ENABLED_COMPONENTS, - Measure, - Observation, - SEGMENT_ENABLED_COMPONENTS, isNumericalMeasure, isOrdinalMeasure, isTemporalDimension, isTemporalEntityDimension, isTemporalOrdinalDimension, + Measure, + MULTI_FILTER_ENABLED_COMPONENTS, + Observation, + SEGMENT_ENABLED_COMPONENTS, } from "@/domain/data"; import { getDefaultCategoricalPaletteName, getPalette } from "@/palettes"; diff --git a/app/charts/column/chart-column.tsx b/app/charts/column/chart-column.tsx index 0d2e7a1a4..0b4f4f618 100644 --- a/app/charts/column/chart-column.tsx +++ b/app/charts/column/chart-column.tsx @@ -24,7 +24,8 @@ import { } from "@/charts/shared/containers"; import { Tooltip } from "@/charts/shared/interaction/tooltip"; import { LegendColor } from "@/charts/shared/legend-color"; -import { ColumnConfig, useChartConfigFilters } from "@/config-types"; +import { ColumnConfig } from "@/config-types"; +import { useChartConfigFilters } from "@/config-utils"; import { hasChartConfigs } from "@/configurator"; import { TimeSlider } from "@/configurator/interactive-filters/time-slider"; import { useConfiguratorState } from "@/src"; diff --git a/app/charts/column/columns-grouped-state-props.ts b/app/charts/column/columns-grouped-state-props.ts index ccf4aa85a..d2dc1ef81 100644 --- a/app/charts/column/columns-grouped-state-props.ts +++ b/app/charts/column/columns-grouped-state-props.ts @@ -22,8 +22,9 @@ import { useSegmentVariables, } from "@/charts/shared/chart-state"; import { useRenderingKeyVariable } from "@/charts/shared/rendering-utils"; -import { ColumnConfig, useChartConfigFilters } from "@/configurator"; -import { Observation, isTemporalEntityDimension } from "@/domain/data"; +import { useChartConfigFilters } from "@/config-utils"; +import { ColumnConfig } from "@/configurator"; +import { isTemporalEntityDimension, Observation } from "@/domain/data"; import { sortByIndex } from "@/utils/array"; import { ChartProps } from "../shared/ChartProps"; diff --git a/app/charts/column/columns-stacked-state-props.ts b/app/charts/column/columns-stacked-state-props.ts index 678910678..8478cef1f 100644 --- a/app/charts/column/columns-stacked-state-props.ts +++ b/app/charts/column/columns-stacked-state-props.ts @@ -19,8 +19,9 @@ import { useSegmentVariables, } from "@/charts/shared/chart-state"; import { useRenderingKeyVariable } from "@/charts/shared/rendering-utils"; -import { ColumnConfig, useChartConfigFilters } from "@/configurator"; -import { Observation, isTemporalEntityDimension } from "@/domain/data"; +import { useChartConfigFilters } from "@/config-utils"; +import { ColumnConfig } from "@/configurator"; +import { isTemporalEntityDimension, Observation } from "@/domain/data"; import { sortByIndex } from "@/utils/array"; import { ChartProps } from "../shared/ChartProps"; diff --git a/app/charts/column/columns-state-props.ts b/app/charts/column/columns-state-props.ts index 428f22f62..f76528734 100644 --- a/app/charts/column/columns-state-props.ts +++ b/app/charts/column/columns-state-props.ts @@ -19,7 +19,8 @@ import { useNumericalYVariables, } from "@/charts/shared/chart-state"; import { useRenderingKeyVariable } from "@/charts/shared/rendering-utils"; -import { ColumnConfig, useChartConfigFilters } from "@/configurator"; +import { useChartConfigFilters } from "@/config-utils"; +import { ColumnConfig } from "@/configurator"; import { isTemporalEntityDimension } from "@/domain/data"; import { ChartProps } from "../shared/ChartProps"; diff --git a/app/charts/combo/combo-line-column-state-props.ts b/app/charts/combo/combo-line-column-state-props.ts index 355bd5118..2c5ec08e2 100644 --- a/app/charts/combo/combo-line-column-state-props.ts +++ b/app/charts/combo/combo-line-column-state-props.ts @@ -12,15 +12,16 @@ import { ChartStateData, InteractiveFiltersVariables, RenderingVariables, - SortingVariables, shouldUseDynamicMinScaleValue, + SortingVariables, useBandXVariables, useBaseVariables, useChartData, useInteractiveFiltersVariables, } from "@/charts/shared/chart-state"; import { useRenderingKeyVariable } from "@/charts/shared/rendering-utils"; -import { ComboLineColumnConfig, useChartConfigFilters } from "@/configurator"; +import { useChartConfigFilters } from "@/config-utils"; +import { ComboLineColumnConfig } from "@/configurator"; import { ChartProps } from "../shared/ChartProps"; diff --git a/app/charts/index.ts b/app/charts/index.ts index d6ef4c986..20c65a4a6 100644 --- a/app/charts/index.ts +++ b/app/charts/index.ts @@ -16,11 +16,15 @@ import { getDefaultCategoricalColorField, getDefaultNumericalColorField, } from "@/charts/map/constants"; +import { + ChartConfigsAdjusters, + FieldAdjuster, + InteractiveFiltersAdjusters, +} from "@/config-adjusters"; import { AreaSegmentField, canBeNormalized, ChartConfig, - ChartConfigsAdjusters, ChartSegmentField, ChartType, ColumnSegmentField, @@ -28,12 +32,10 @@ import { ComboLineColumnFields, ComboLineSingleFields, Cube, - FieldAdjuster, Filters, GenericField, GenericFields, GenericSegmentField, - InteractiveFiltersAdjusters, InteractiveFiltersConfig, isAreaConfig, isColumnConfig, diff --git a/app/charts/map/chart-map.tsx b/app/charts/map/chart-map.tsx index d69764370..a704a2215 100644 --- a/app/charts/map/chart-map.tsx +++ b/app/charts/map/chart-map.tsx @@ -11,12 +11,13 @@ import { ChartControlsContainer, } from "@/charts/shared/containers"; import { NoGeometriesHint } from "@/components/hint"; -import { Cube, MapConfig, useChartConfigFilters } from "@/config-types"; +import { Cube, MapConfig } from "@/config-types"; +import { useChartConfigFilters } from "@/config-utils"; import { TimeSlider } from "@/configurator/interactive-filters/time-slider"; import { + dimensionValuesToGeoCoordinates, GeoCoordinates, GeoShapes, - dimensionValuesToGeoCoordinates, } from "@/domain/data"; import { useDataCubesComponentsQuery } from "@/graphql/hooks"; import { getResolvedJoinById, isJoinById } from "@/graphql/join"; @@ -180,6 +181,7 @@ const ChartMap = memo((props: ChartMapProps) => { const { chartConfig, dimensions, observations } = props; const { fields } = chartConfig; const filters = useChartConfigFilters(chartConfig); + return ( diff --git a/app/charts/map/map-state-props.ts b/app/charts/map/map-state-props.ts index f97a2fe59..bfcc07d62 100644 --- a/app/charts/map/map-state-props.ts +++ b/app/charts/map/map-state-props.ts @@ -16,7 +16,8 @@ import { useBaseVariables, useChartData, } from "@/charts/shared/chart-state"; -import { MapConfig, useChartConfigFilters } from "@/configurator"; +import { useChartConfigFilters } from "@/config-utils"; +import { MapConfig } from "@/configurator"; import { GeoData, GeoPoint, diff --git a/app/charts/pie/chart-pie.tsx b/app/charts/pie/chart-pie.tsx index a83aff54d..1d489a566 100644 --- a/app/charts/pie/chart-pie.tsx +++ b/app/charts/pie/chart-pie.tsx @@ -11,7 +11,8 @@ import { import { Tooltip } from "@/charts/shared/interaction/tooltip"; import { LegendColor } from "@/charts/shared/legend-color"; import { OnlyNegativeDataHint } from "@/components/hint"; -import { PieConfig, useChartConfigFilters } from "@/config-types"; +import { PieConfig } from "@/config-types"; +import { useChartConfigFilters } from "@/config-utils"; import { TimeSlider } from "@/configurator/interactive-filters/time-slider"; import { ChartProps, VisualizationProps } from "../shared/ChartProps"; diff --git a/app/charts/pie/pie-state-props.ts b/app/charts/pie/pie-state-props.ts index 6a8580e71..e05de7f22 100644 --- a/app/charts/pie/pie-state-props.ts +++ b/app/charts/pie/pie-state-props.ts @@ -11,7 +11,8 @@ import { useSegmentVariables, } from "@/charts/shared/chart-state"; import { useRenderingKeyVariable } from "@/charts/shared/rendering-utils"; -import { PieConfig, useChartConfigFilters } from "@/configurator"; +import { useChartConfigFilters } from "@/config-utils"; +import { PieConfig } from "@/configurator"; import { ChartProps } from "../shared/ChartProps"; diff --git a/app/charts/scatterplot/chart-scatterplot.tsx b/app/charts/scatterplot/chart-scatterplot.tsx index 507a6994d..801d4df35 100644 --- a/app/charts/scatterplot/chart-scatterplot.tsx +++ b/app/charts/scatterplot/chart-scatterplot.tsx @@ -19,7 +19,8 @@ import { import { Tooltip } from "@/charts/shared/interaction/tooltip"; import { LegendColor } from "@/charts/shared/legend-color"; import { InteractionVoronoi } from "@/charts/shared/overlay-voronoi"; -import { ScatterPlotConfig, useChartConfigFilters } from "@/config-types"; +import { ScatterPlotConfig } from "@/config-types"; +import { useChartConfigFilters } from "@/config-utils"; import { TimeSlider } from "@/configurator/interactive-filters/time-slider"; import { ChartProps, VisualizationProps } from "../shared/ChartProps"; @@ -35,6 +36,7 @@ const ChartScatterplot = memo((props: ChartProps) => { const { chartConfig, dimensions } = props; const { fields, interactiveFiltersConfig } = chartConfig; const filters = useChartConfigFilters(chartConfig); + return ( diff --git a/app/charts/scatterplot/scatterplot-state-props.ts b/app/charts/scatterplot/scatterplot-state-props.ts index 76ade26f0..c989bbc55 100644 --- a/app/charts/scatterplot/scatterplot-state-props.ts +++ b/app/charts/scatterplot/scatterplot-state-props.ts @@ -13,7 +13,8 @@ import { useSegmentVariables, } from "@/charts/shared/chart-state"; import { useRenderingKeyVariable } from "@/charts/shared/rendering-utils"; -import { ScatterPlotConfig, useChartConfigFilters } from "@/configurator"; +import { useChartConfigFilters } from "@/config-utils"; +import { ScatterPlotConfig } from "@/configurator"; import { ChartProps } from "../shared/ChartProps"; diff --git a/app/charts/shared/chart-data-filters.tsx b/app/charts/shared/chart-data-filters.tsx index fad510e1f..0b5c06245 100644 --- a/app/charts/shared/chart-data-filters.tsx +++ b/app/charts/shared/chart-data-filters.tsx @@ -18,6 +18,7 @@ import { Select } from "@/components/form"; import { Loading } from "@/components/hint"; import { OpenMetadataPanelWrapper } from "@/components/metadata-panel"; import SelectTree, { Tree } from "@/components/select-tree"; +import { useChartConfigFilters } from "@/config-utils"; import { areDataFiltersActive, ChartConfig, @@ -26,7 +27,6 @@ import { Filters, getFiltersByMappingStatus, SingleFilters, - useChartConfigFilters, useConfiguratorState, } from "@/configurator"; import { FieldLabel, LoadingIndicator } from "@/configurator/components/field"; diff --git a/app/charts/shared/chart-helpers.tsx b/app/charts/shared/chart-helpers.tsx index a0ca20a1d..91c5f1556 100644 --- a/app/charts/shared/chart-helpers.tsx +++ b/app/charts/shared/chart-helpers.tsx @@ -20,12 +20,12 @@ import { InteractiveFiltersTimeRange, MapConfig, } from "@/config-types"; +import { getChartConfigFilters } from "@/config-utils"; import { CategoricalColorField, ComboChartConfig, DashboardFiltersConfig, GenericField, - getChartConfigFilters, isComboChartConfig, NumericalColorField, } from "@/configurator"; diff --git a/app/charts/shared/containers.tsx b/app/charts/shared/containers.tsx index be500bcbc..aea7f5164 100644 --- a/app/charts/shared/containers.tsx +++ b/app/charts/shared/containers.tsx @@ -6,8 +6,8 @@ import { ReactNode, useEffect, useRef } from "react"; import { useChartState } from "@/charts/shared/chart-state"; import { CalculationToggle } from "@/charts/shared/interactive-filter-calculation-toggle"; import { useObserverRef } from "@/charts/shared/use-size"; +import { getChartConfig } from "@/config-utils"; import { - getChartConfig, hasChartConfigs, isLayoutingFreeCanvas, useConfiguratorState, diff --git a/app/charts/shared/legend-color.tsx b/app/charts/shared/legend-color.tsx index a8d4a8b79..68bdce7f2 100644 --- a/app/charts/shared/legend-color.tsx +++ b/app/charts/shared/legend-color.tsx @@ -13,21 +13,21 @@ import { Checkbox, CheckboxProps } from "@/components/form"; import { MaybeTooltip } from "@/components/maybe-tooltip"; import { OpenMetadataPanelWrapper } from "@/components/metadata-panel"; import { TooltipTitle } from "@/components/tooltip-utils"; +import { useChartConfigFilters } from "@/config-utils"; import { ChartConfig, GenericSegmentField, - MapConfig, isSegmentInConfig, - useChartConfigFilters, + MapConfig, useReadOnlyConfiguratorState, } from "@/configurator"; import { Component, Dimension, - Measure, - Observation, isOrdinalDimension, isOrdinalMeasure, + Measure, + Observation, } from "@/domain/data"; import SvgIcChevronRight from "@/icons/components/IcChevronRight"; import { useChartInteractiveFilters } from "@/stores/interactive-filters"; diff --git a/app/charts/shared/use-sync-interactive-filters.tsx b/app/charts/shared/use-sync-interactive-filters.tsx index 9ba7cb4fe..e278440c7 100644 --- a/app/charts/shared/use-sync-interactive-filters.tsx +++ b/app/charts/shared/use-sync-interactive-filters.tsx @@ -5,8 +5,8 @@ import { DashboardFiltersConfig, FilterValueSingle, isSegmentInConfig, - useChartConfigFilters, } from "@/config-types"; +import { useChartConfigFilters } from "@/config-utils"; import { parseDate } from "@/configurator/components/ui-helpers"; import { FIELD_VALUE_NONE } from "@/configurator/constants"; import useFilterChanges from "@/configurator/use-filter-changes"; diff --git a/app/components/chart-preview.tsx b/app/components/chart-preview.tsx index 6e7082bc7..3be409981 100644 --- a/app/components/chart-preview.tsx +++ b/app/components/chart-preview.tsx @@ -51,10 +51,10 @@ import { MetadataPanelStoreContext, } from "@/components/metadata-panel-store"; import { BANNER_MARGIN_TOP } from "@/components/presence"; +import { getChartConfig } from "@/config-utils"; import { ChartConfig, DataSource, - getChartConfig, hasChartConfigs, isConfiguring, Layout, diff --git a/app/components/chart-published.tsx b/app/components/chart-published.tsx index 5702645dd..d2e84cf9a 100644 --- a/app/components/chart-published.tsx +++ b/app/components/chart-published.tsx @@ -31,11 +31,11 @@ import { createMetadataPanelStore, MetadataPanelStoreContext, } from "@/components/metadata-panel-store"; +import { getChartConfig } from "@/config-utils"; import { ChartConfig, ConfiguratorStatePublished, DataSource, - getChartConfig, isPublished, useConfiguratorState, } from "@/configurator"; diff --git a/app/components/chart-selection-tabs.tsx b/app/components/chart-selection-tabs.tsx index ccc921bd3..83bc79ea2 100644 --- a/app/components/chart-selection-tabs.tsx +++ b/app/components/chart-selection-tabs.tsx @@ -19,12 +19,12 @@ import { DragHandle } from "@/components/drag-handle"; import Flex from "@/components/flex"; import { MenuActionItem } from "@/components/menu-action-item"; import { VisualizeTab, VisualizeTabList } from "@/components/tabs"; +import { getChartConfig } from "@/config-utils"; import { ChartConfig, ChartType, ConfiguratorStatePublished, ConfiguratorStateWithChartConfigs, - getChartConfig, hasChartConfigs, isConfiguring, isLayouting, diff --git a/app/components/chart-shared.tsx b/app/components/chart-shared.tsx index 03bb67893..c192b8f5b 100644 --- a/app/components/chart-shared.tsx +++ b/app/components/chart-shared.tsx @@ -38,11 +38,11 @@ import { import { useChartWithFiltersClasses } from "@/components/chart-with-filters"; import { MenuActionItem } from "@/components/menu-action-item"; import { MetadataPanel } from "@/components/metadata-panel"; +import { getChartConfig } from "@/config-utils"; import { ChartConfig, DashboardFiltersConfig, DataSource, - getChartConfig, hasChartConfigs, isConfiguring, isPublished, diff --git a/app/components/debug-panel/DebugPanel.tsx b/app/components/debug-panel/DebugPanel.tsx index 779ed2dd4..6776d4a51 100644 --- a/app/components/debug-panel/DebugPanel.tsx +++ b/app/components/debug-panel/DebugPanel.tsx @@ -15,11 +15,8 @@ import { makeStyles } from "@mui/styles"; import { useState } from "react"; import { Inspector } from "react-inspector"; -import { - DataSource, - getChartConfig, - useConfiguratorState, -} from "@/configurator"; +import { getChartConfig } from "@/config-utils"; +import { DataSource, useConfiguratorState } from "@/configurator"; import { dataSourceToSparqlEditorUrl } from "@/domain/datasource"; import { useDataCubesComponentsQuery } from "@/graphql/hooks"; import { Icon } from "@/icons"; diff --git a/app/config-adjusters.ts b/app/config-adjusters.ts new file mode 100644 index 000000000..824969d88 --- /dev/null +++ b/app/config-adjusters.ts @@ -0,0 +1,261 @@ +import { + AnimationField, + AreaConfig, + AreaFields, + AreaSegmentField, + BarConfig, + BarFields, + BarSegmentField, + ChartConfig, + ColumnConfig, + ColumnFields, + ColumnSegmentField, + ComboLineColumnConfig, + ComboLineColumnFields, + ComboLineDualConfig, + ComboLineDualFields, + ComboLineSingleConfig, + ComboLineSingleFields, + GenericChartConfig, + InteractiveFiltersCalculation, + InteractiveFiltersConfig, + InteractiveFiltersDataConfig, + InteractiveFiltersLegend, + LineConfig, + LineFields, + LineSegmentField, + MapConfig, + MapFields, + PieConfig, + PieFields, + PieSegmentField, + ScatterPlotConfig, + ScatterPlotFields, + ScatterPlotSegmentField, + TableConfig, + TableFields, +} from "@/config-types"; +import { Dimension, Measure } from "@/domain/data"; + +export type FieldAdjuster< + NewChartConfigType extends ChartConfig, + OldValueType extends unknown, +> = (params: { + oldValue: OldValueType; + oldChartConfig: ChartConfig; + newChartConfig: NewChartConfigType; + dimensions: Dimension[]; + measures: Measure[]; + isAddingNewCube?: boolean; +}) => NewChartConfigType; + +type AssureKeys = { + [K in keyof T]: U[K]; +}; + +export type InteractiveFiltersAdjusters = AssureKeys< + InteractiveFiltersConfig, + _InteractiveFiltersAdjusters +>; + +type _InteractiveFiltersAdjusters = { + legend: FieldAdjuster; + timeRange: { + active: FieldAdjuster; + componentId: FieldAdjuster; + presets: { + type: FieldAdjuster; + from: FieldAdjuster; + to: FieldAdjuster; + }; + }; + dataFilters: FieldAdjuster; + calculation: FieldAdjuster; +}; + +type BaseAdjusters = { + cubes: FieldAdjuster; + interactiveFiltersConfig: InteractiveFiltersAdjusters; +}; + +type ColumnAdjusters = BaseAdjusters & { + fields: { + x: { componentId: FieldAdjuster }; + y: { componentId: FieldAdjuster }; + segment: FieldAdjuster< + ColumnConfig, + | BarSegmentField + | LineSegmentField + | AreaSegmentField + | ScatterPlotSegmentField + | PieSegmentField + | TableFields + >; + animation: FieldAdjuster; + }; +}; + +type BarAdjusters = BaseAdjusters & { + fields: { + x: { componentId: FieldAdjuster }; + y: { componentId: FieldAdjuster }; + segment: FieldAdjuster< + BarConfig, + | ColumnSegmentField + | LineSegmentField + | AreaSegmentField + | ScatterPlotSegmentField + | PieSegmentField + | TableFields + >; + animation: FieldAdjuster; + }; +}; + +type LineAdjusters = BaseAdjusters & { + fields: { + x: { componentId: FieldAdjuster }; + y: { componentId: FieldAdjuster }; + segment: FieldAdjuster< + LineConfig, + | ColumnSegmentField + | BarSegmentField + | AreaSegmentField + | ScatterPlotSegmentField + | PieSegmentField + | TableFields + >; + }; +}; + +type AreaAdjusters = BaseAdjusters & { + fields: { + x: { componentId: FieldAdjuster }; + y: { componentId: FieldAdjuster }; + segment: FieldAdjuster< + AreaConfig, + | ColumnSegmentField + | BarSegmentField + | LineSegmentField + | ScatterPlotSegmentField + | PieSegmentField + | TableFields + >; + }; +}; + +type ScatterPlotAdjusters = BaseAdjusters & { + fields: { + y: { componentId: FieldAdjuster }; + segment: FieldAdjuster< + ScatterPlotConfig, + | ColumnSegmentField + | BarSegmentField + | LineSegmentField + | AreaSegmentField + | PieSegmentField + | TableFields + >; + animation: FieldAdjuster; + }; +}; + +type PieAdjusters = BaseAdjusters & { + fields: { + y: { componentId: FieldAdjuster }; + segment: FieldAdjuster< + PieConfig, + | ColumnSegmentField + | BarSegmentField + | LineSegmentField + | AreaSegmentField + | ScatterPlotSegmentField + | TableFields + >; + animation: FieldAdjuster; + }; +}; + +type TableAdjusters = { + cubes: FieldAdjuster; + fields: FieldAdjuster< + TableConfig, + | ColumnSegmentField + | BarSegmentField + | LineSegmentField + | AreaSegmentField + | ScatterPlotSegmentField + | PieSegmentField + >; +}; + +type MapAdjusters = BaseAdjusters & { + fields: { + areaLayer: { + componentId: FieldAdjuster; + color: { + componentId: FieldAdjuster; + }; + }; + animation: FieldAdjuster; + }; +}; + +type ComboLineSingleAdjusters = BaseAdjusters & { + fields: { + x: { componentId: FieldAdjuster }; + y: { componentIds: FieldAdjuster }; + }; +}; + +type ComboLineDualAdjusters = BaseAdjusters & { + fields: { + x: { componentId: FieldAdjuster }; + y: FieldAdjuster< + ComboLineDualConfig, + | AreaFields + | ColumnFields + | BarFields + | LineFields + | MapFields + | PieFields + | ScatterPlotFields + | TableFields + | ComboLineSingleFields + | ComboLineColumnFields + >; + }; +}; + +type ComboLineColumnAdjusters = BaseAdjusters & { + fields: { + x: { componentId: FieldAdjuster }; + y: FieldAdjuster< + ComboLineColumnConfig, + | AreaFields + | ColumnFields + | BarFields + | LineFields + | MapFields + | PieFields + | ScatterPlotFields + | TableFields + | ComboLineSingleFields + | ComboLineDualFields + >; + }; +}; + +export type ChartConfigsAdjusters = { + column: ColumnAdjusters; + bar: BarAdjusters; + line: LineAdjusters; + area: AreaAdjusters; + scatterplot: ScatterPlotAdjusters; + pie: PieAdjusters; + table: TableAdjusters; + map: MapAdjusters; + comboLineSingle: ComboLineSingleAdjusters; + comboLineDual: ComboLineDualAdjusters; + comboLineColumn: ComboLineColumnAdjusters; +}; diff --git a/app/config-types.spec.ts b/app/config-types.spec.ts index 8f8efa4cc..7b8fba0fb 100644 --- a/app/config-types.spec.ts +++ b/app/config-types.spec.ts @@ -1,4 +1,4 @@ -import { getChartConfigFilters } from "./config-types"; +import { getChartConfigFilters } from "./config-utils"; describe("getChartConfigFilters", () => { it("should return filters for a specific cube with joinBy (joined = true)", () => { diff --git a/app/config-types.ts b/app/config-types.ts index 8f905c312..788260c79 100644 --- a/app/config-types.ts +++ b/app/config-types.ts @@ -2,10 +2,6 @@ import { fold } from "fp-ts/lib/Either"; import { pipe } from "fp-ts/lib/function"; import * as t from "io-ts"; -import { useMemo } from "react"; - -import { Dimension, Measure, ObservationValue } from "@/domain/data"; -import { mkJoinById } from "@/graphql/join"; const DimensionType = t.union([ t.literal("NominalDimension"), @@ -31,48 +27,33 @@ const ComponentType = t.union([DimensionType, MeasureType]); export type ComponentType = t.TypeOf; // Filters -const FilterValueMulti = t.intersection([ +const FilterValueSingle = t.intersection([ t.type( { - type: t.literal("multi"), - values: t.record(t.string, t.literal(true)), // undefined values will be removed when serializing to JSON + type: t.literal("single"), + value: t.union([t.string, t.number]), }, - "FilterValueMulti" + "FilterValueSingle" ), t.partial({ position: t.number, }), ]); -export type FilterValueMulti = t.TypeOf; - -export const makeMultiFilter = ( - values: ObservationValue[] -): FilterValueMulti => { - return { - type: "multi", - values: Object.fromEntries(values.map((d) => [d, true])), - }; -}; +export type FilterValueSingle = t.TypeOf; -const FilterValueSingle = t.intersection([ +const FilterValueMulti = t.intersection([ t.type( { - type: t.literal("single"), - value: t.union([t.string, t.number]), + type: t.literal("multi"), + values: t.record(t.string, t.literal(true)), // undefined values will be removed when serializing to JSON }, - "FilterValueSingle" + "FilterValueMulti" ), t.partial({ position: t.number, }), ]); -export type FilterValueSingle = t.TypeOf; - -const isFilterValueSingle = ( - filterValue: FilterValue -): filterValue is FilterValueSingle => { - return filterValue.type === "single"; -}; +export type FilterValueMulti = t.TypeOf; const FilterValueRange = t.intersection([ t.type( @@ -105,15 +86,6 @@ export type Filters = t.TypeOf; const SingleFilters = t.record(t.string, FilterValueSingle, "SingleFilters"); export type SingleFilters = t.TypeOf; -export const isSingleFilters = (filters: Filters): filters is SingleFilters => { - return Object.values(filters).every(isFilterValueSingle); -}; -export const extractSingleFilters = (filters: Filters): SingleFilters => { - return Object.fromEntries( - Object.entries(filters).filter(([, value]) => value.type === "single") - ) as SingleFilters; -}; - const Title = t.type({ de: t.string, fr: t.string, @@ -121,6 +93,7 @@ const Title = t.type({ en: t.string, }); export type Title = t.TypeOf; + const Description = t.type({ de: t.string, fr: t.string, @@ -128,6 +101,7 @@ const Description = t.type({ en: t.string, }); export type Description = t.TypeOf; + const Label = t.type({ de: t.string, fr: t.string, @@ -135,6 +109,7 @@ const Label = t.type({ en: t.string, }); export type Label = t.TypeOf; + const Meta = t.type({ title: Title, description: Description, @@ -660,7 +635,7 @@ export type MapAreaLayer = t.TypeOf; const MapSymbolLayer = t.type({ componentId: t.string, - // symbol radius (size) + /** symbol radius (size) */ measureId: t.string, color: t.union([FixedColorField, CategoricalColorField, NumericalColorField]), }); @@ -794,7 +769,7 @@ const ComboChartConfig = t.union([ ]); export type ComboChartConfig = t.TypeOf; -const ChartConfig = t.union([RegularChartConfig, ComboChartConfig]); +export const ChartConfig = t.union([RegularChartConfig, ComboChartConfig]); export type ChartConfig = t.TypeOf; export const decodeChartConfig = ( @@ -960,230 +935,6 @@ export const isColorFieldInConfig = ( return isMapConfig(chartConfig); }; -// Chart Config Adjusters -export type FieldAdjuster< - NewChartConfigType extends ChartConfig, - OldValueType extends unknown, -> = (params: { - oldValue: OldValueType; - oldChartConfig: ChartConfig; - newChartConfig: NewChartConfigType; - dimensions: Dimension[]; - measures: Measure[]; - isAddingNewCube?: boolean; -}) => NewChartConfigType; - -type AssureKeys = { - [K in keyof T]: U[K]; -}; - -export type InteractiveFiltersAdjusters = AssureKeys< - InteractiveFiltersConfig, - _InteractiveFiltersAdjusters ->; - -type _InteractiveFiltersAdjusters = { - legend: FieldAdjuster; - timeRange: { - active: FieldAdjuster; - componentId: FieldAdjuster; - presets: { - type: FieldAdjuster; - from: FieldAdjuster; - to: FieldAdjuster; - }; - }; - dataFilters: FieldAdjuster; - calculation: FieldAdjuster; -}; - -type BaseAdjusters = { - cubes: FieldAdjuster; - interactiveFiltersConfig: InteractiveFiltersAdjusters; -}; - -type ColumnAdjusters = BaseAdjusters & { - fields: { - x: { componentId: FieldAdjuster }; - y: { componentId: FieldAdjuster }; - segment: FieldAdjuster< - ColumnConfig, - | BarSegmentField - | LineSegmentField - | AreaSegmentField - | ScatterPlotSegmentField - | PieSegmentField - | TableFields - >; - animation: FieldAdjuster; - }; -}; - -type BarAdjusters = BaseAdjusters & { - fields: { - x: { componentId: FieldAdjuster }; - y: { componentId: FieldAdjuster }; - segment: FieldAdjuster< - BarConfig, - | ColumnSegmentField - | LineSegmentField - | AreaSegmentField - | ScatterPlotSegmentField - | PieSegmentField - | TableFields - >; - animation: FieldAdjuster; - }; -}; - -type LineAdjusters = BaseAdjusters & { - fields: { - x: { componentId: FieldAdjuster }; - y: { componentId: FieldAdjuster }; - segment: FieldAdjuster< - LineConfig, - | ColumnSegmentField - | BarSegmentField - | AreaSegmentField - | ScatterPlotSegmentField - | PieSegmentField - | TableFields - >; - }; -}; - -type AreaAdjusters = BaseAdjusters & { - fields: { - x: { componentId: FieldAdjuster }; - y: { componentId: FieldAdjuster }; - segment: FieldAdjuster< - AreaConfig, - | ColumnSegmentField - | BarSegmentField - | LineSegmentField - | ScatterPlotSegmentField - | PieSegmentField - | TableFields - >; - }; -}; - -type ScatterPlotAdjusters = BaseAdjusters & { - fields: { - y: { componentId: FieldAdjuster }; - segment: FieldAdjuster< - ScatterPlotConfig, - | ColumnSegmentField - | BarSegmentField - | LineSegmentField - | AreaSegmentField - | PieSegmentField - | TableFields - >; - animation: FieldAdjuster; - }; -}; - -type PieAdjusters = BaseAdjusters & { - fields: { - y: { componentId: FieldAdjuster }; - segment: FieldAdjuster< - PieConfig, - | ColumnSegmentField - | BarSegmentField - | LineSegmentField - | AreaSegmentField - | ScatterPlotSegmentField - | TableFields - >; - animation: FieldAdjuster; - }; -}; - -type TableAdjusters = { - cubes: FieldAdjuster; - fields: FieldAdjuster< - TableConfig, - | ColumnSegmentField - | BarSegmentField - | LineSegmentField - | AreaSegmentField - | ScatterPlotSegmentField - | PieSegmentField - >; -}; - -type MapAdjusters = BaseAdjusters & { - fields: { - areaLayer: { - componentId: FieldAdjuster; - color: { - componentId: FieldAdjuster; - }; - }; - animation: FieldAdjuster; - }; -}; - -type ComboLineSingleAdjusters = BaseAdjusters & { - fields: { - x: { componentId: FieldAdjuster }; - y: { componentIds: FieldAdjuster }; - }; -}; - -type ComboLineDualAdjusters = BaseAdjusters & { - fields: { - x: { componentId: FieldAdjuster }; - y: FieldAdjuster< - ComboLineDualConfig, - | AreaFields - | ColumnFields - | BarFields - | LineFields - | MapFields - | PieFields - | ScatterPlotFields - | TableFields - | ComboLineSingleFields - | ComboLineColumnFields - >; - }; -}; - -type ComboLineColumnAdjusters = BaseAdjusters & { - fields: { - x: { componentId: FieldAdjuster }; - y: FieldAdjuster< - ComboLineColumnConfig, - | AreaFields - | ColumnFields - | BarFields - | LineFields - | MapFields - | PieFields - | ScatterPlotFields - | TableFields - | ComboLineSingleFields - | ComboLineDualFields - >; - }; -}; - -export type ChartConfigsAdjusters = { - column: ColumnAdjusters; - bar: BarAdjusters; - line: LineAdjusters; - area: AreaAdjusters; - scatterplot: ScatterPlotAdjusters; - pie: PieAdjusters; - table: TableAdjusters; - map: MapAdjusters; - comboLineSingle: ComboLineSingleAdjusters; - comboLineDual: ComboLineDualAdjusters; - comboLineColumn: ComboLineColumnAdjusters; -}; - const DataSource = t.type({ type: t.union([t.literal("sql"), t.literal("sparql")]), url: t.string, @@ -1292,7 +1043,7 @@ export type DashboardFiltersConfig = t.TypeOf; export const areDataFiltersActive = ( dashboardFilters: DashboardFiltersConfig | undefined ) => { - return dashboardFilters?.dataFilters.componentIds.length; + return !!dashboardFilters?.dataFilters.componentIds.length; }; const Config = t.intersection([ @@ -1381,7 +1132,7 @@ export type ConfiguratorStatePublished = t.TypeOf< typeof ConfiguratorStatePublished >; -const ConfiguratorState = t.union([ +export const ConfiguratorState = t.union([ ConfiguratorStateInitial, ConfiguratorStateSelectingDataSet, ConfiguratorStateConfiguringChart, @@ -1405,77 +1156,3 @@ export const decodeConfiguratorState = ( ) ); }; - -/** Use to extract the chart config from configurator state. Handy in the editor mode, - * where the is a need to edit the active chart config. - * - * @param state configurator state - * @param chartKey optional chart key. If not provided, the active chart config will be returned. - * - */ -export const getChartConfig = ( - state: ConfiguratorState, - chartKey?: string | null -): ChartConfig => { - if (state.state === "INITIAL" || state.state === "SELECTING_DATASET") { - throw Error("No chart config available!"); - } - - const { chartConfigs, activeChartKey } = state; - const key = chartKey ?? activeChartKey; - - return chartConfigs.find((d) => d.key === key) ?? chartConfigs[0]; -}; - -/** - * Get all filters from cubes and returns an object containing all values. - */ -export const getChartConfigFilters = ( - cubes: Cube[], - { - cubeIri, - joined, - }: { - /** If passed, only filters for a particular cube will be considered */ - cubeIri?: string; - /** - * If true, filters for joined dimensions will be deduped. Useful in contexts where filters - * for multiple join dimensions should be considered unique (for example, in the left data filters). - */ - joined?: boolean; - } = {} -): Filters => { - const relevantCubes = cubes.filter((c) => - cubeIri ? c.iri === cubeIri : true - ); - const dimIdToJoinId = joined - ? Object.fromEntries( - relevantCubes.flatMap((x) => - (x.joinBy ?? []).map( - (iri, index) => [iri, mkJoinById(index)] as const - ) - ) - ) - : {}; - - return Object.fromEntries( - relevantCubes.flatMap((c) => - Object.entries(c.filters).map(([id, value]) => [ - dimIdToJoinId[id] ?? id, - value, - ]) - ) - ); -}; - -export const useChartConfigFilters = ( - chartConfig: ChartConfig, - options?: Parameters[1] -): Filters => { - return useMemo(() => { - return getChartConfigFilters(chartConfig.cubes, { - cubeIri: options?.cubeIri, - joined: options?.joined, - }); - }, [chartConfig.cubes, options?.cubeIri, options?.joined]); -}; diff --git a/app/config-utils.ts b/app/config-utils.ts new file mode 100644 index 000000000..bcc6354d7 --- /dev/null +++ b/app/config-utils.ts @@ -0,0 +1,111 @@ +import { useMemo } from "react"; + +import { + ChartConfig, + ConfiguratorState, + Cube, + Filters, + FilterValue, + FilterValueMulti, + FilterValueSingle, + SingleFilters, +} from "@/config-types"; +import { ObservationValue } from "@/domain/data"; +import { mkJoinById } from "@/graphql/join"; + +const isFilterValueSingle = (v: FilterValue): v is FilterValueSingle => { + return v.type === "single"; +}; + +export const isSingleFilters = (filters: Filters): filters is SingleFilters => { + return Object.values(filters).every(isFilterValueSingle); +}; + +export const extractSingleFilters = (filters: Filters): SingleFilters => { + return Object.fromEntries( + Object.entries(filters).filter(([, value]) => value.type === "single") + ) as SingleFilters; +}; + +export const makeMultiFilter = ( + values: ObservationValue[] +): FilterValueMulti => { + return { + type: "multi", + values: Object.fromEntries(values.map((d) => [d, true])), + }; +}; + +/** Use to extract the chart config from configurator state. Handy in the editor mode, + * where the is a need to edit the active chart config. + * + * @param state configurator state + * @param chartKey optional chart key. If not provided, the active chart config will be returned. + * + */ +export const getChartConfig = ( + state: ConfiguratorState, + chartKey?: string | null +): ChartConfig => { + if (state.state === "INITIAL" || state.state === "SELECTING_DATASET") { + throw Error("No chart config available!"); + } + + const { chartConfigs, activeChartKey } = state; + const key = chartKey ?? activeChartKey; + + return chartConfigs.find((d) => d.key === key) ?? chartConfigs[0]; +}; + +/** + * Get all filters from cubes and returns an object containing all values. + */ +export const getChartConfigFilters = ( + cubes: Cube[], + { + cubeIri, + joined, + }: { + /** If passed, only filters for a particular cube will be considered */ + cubeIri?: string; + /** + * If true, filters for joined dimensions will be deduped. Useful in contexts where filters + * for multiple join dimensions should be considered unique (for example, in the left data filters). + */ + joined?: boolean; + } = {} +): Filters => { + const relevantCubes = cubes.filter((c) => + cubeIri ? c.iri === cubeIri : true + ); + const dimIdToJoinId = joined + ? Object.fromEntries( + relevantCubes.flatMap((x) => + (x.joinBy ?? []).map( + (iri, index) => [iri, mkJoinById(index)] as const + ) + ) + ) + : {}; + + return Object.fromEntries( + relevantCubes.flatMap((c) => + Object.entries(c.filters).map(([id, value]) => [ + dimIdToJoinId[id] ?? id, + value, + ]) + ) + ); +}; + +export const useChartConfigFilters = ( + chartConfig: ChartConfig, + options?: Parameters[1] +): Filters => { + return useMemo(() => { + return getChartConfigFilters(chartConfig.cubes, { + cubeIri: options?.cubeIri, + joined: options?.joined, + }); + }, [chartConfig.cubes, options?.cubeIri, options?.joined]); +}; diff --git a/app/configurator/components/add-dataset-dialog.tsx b/app/configurator/components/add-dataset-dialog.tsx index 16d247495..048280286 100644 --- a/app/configurator/components/add-dataset-dialog.tsx +++ b/app/configurator/components/add-dataset-dialog.tsx @@ -62,14 +62,13 @@ import { getEnabledChartTypes } from "@/charts"; import Flex from "@/components/flex"; import { Error as ErrorHint, Loading } from "@/components/hint"; import Tag from "@/components/tag"; +import { ConfiguratorStateConfiguringChart, DataSource } from "@/config-types"; +import { getChartConfig } from "@/config-utils"; import { addDatasetInConfig, - ConfiguratorStateConfiguringChart, - DataSource, - getChartConfig, isConfiguring, useConfiguratorState, -} from "@/configurator"; +} from "@/configurator/configurator-state"; import { Dimension, isJoinByComponent, diff --git a/app/configurator/components/annotation-options.tsx b/app/configurator/components/annotation-options.tsx index c95073cbb..9a31f5a27 100644 --- a/app/configurator/components/annotation-options.tsx +++ b/app/configurator/components/annotation-options.tsx @@ -1,7 +1,8 @@ import { Box, Button, Typography } from "@mui/material"; import { useEffect, useRef } from "react"; -import { Meta, getChartConfig } from "@/config-types"; +import { Meta } from "@/config-types"; +import { getChartConfig } from "@/config-utils"; import { isAnnotationField, isConfiguring, @@ -22,6 +23,7 @@ import useEvent from "@/utils/use-event"; export const ChartAnnotationsSelector = () => { const [state] = useConfiguratorState(isConfiguring); const chartConfig = getChartConfig(state); + return ( { export const LayoutAnnotationsSelector = () => { const [state] = useConfiguratorState(isLayouting); + return ( { const iframeWindow = iframe?.contentWindow; if (iframeWindow) { - iframeWindow.postMessage(chartState, "*"); + iframeWindow.postMessage(configuratorState, "*"); } }; `} @@ -55,8 +55,8 @@ iframe.onload = () => { ### Controlling the language -You can set the desired language for the chart preview by adding a /de, /fr, /it -or /en part to the iframe URL, like so: +You can set the desired language for the chart preview by adding a `/de`, `/fr`, +`/it` or `/en` part to the iframe URL, like so: ```html