From a166c0fdd9a6815256e3da5948bd742d095c2802 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Thu, 27 Oct 2022 12:41:27 +0200 Subject: [PATCH] feat: Show default color palette in ColorPalette picker ...if default colors are there --- app/charts/index.ts | 81 ++++++++++++++----- app/charts/shared/colors.tsx | 11 +++ .../chart-controls/color-palette.tsx | 26 ++++-- app/configurator/components/filters.tsx | 5 +- app/configurator/components/ui-helpers.ts | 4 +- app/configurator/configurator-state.spec.tsx | 4 +- app/configurator/configurator-state.tsx | 21 +++-- .../table/table-chart-options.tsx | 11 ++- app/palettes.ts | 76 +++++++++++------ 9 files changed, 174 insertions(+), 65 deletions(-) diff --git a/app/charts/index.ts b/app/charts/index.ts index 154dda04bc..23518ef1cb 100644 --- a/app/charts/index.ts +++ b/app/charts/index.ts @@ -27,9 +27,9 @@ import { TableColumn, TableFields, } from "@/configurator/config-types"; -import { DEFAULT_PALETTE } from "@/configurator/configurator-state"; import { FIELD_VALUE_NONE } from "@/configurator/constants"; import { HierarchyValue } from "@/graphql/resolver-types"; +import { getDefaultCategoricalPaletteName } from "@/palettes"; import { visitHierarchy } from "@/rdf/tree-utils"; import { CHART_CONFIG_VERSION } from "@/utils/chart-config/versioning"; @@ -160,6 +160,8 @@ export const getInitialAreaLayer = ({ // color measure: NumericalMeasure | OrdinalMeasure; }): MapAreaLayer => { + const palette = getDefaultCategoricalPaletteName(measure); + return { componentIri: component.iri, color: isNumericalMeasure(measure) @@ -173,9 +175,9 @@ export const getInitialAreaLayer = ({ : { type: "categorical", componentIri: measure.iri, - palette: "oranges", + palette, colorMapping: mapValueIrisToColor({ - palette: DEFAULT_PALETTE, + palette, dimensionValues: measure.values, }), }, @@ -271,6 +273,9 @@ export const getInitialConfig = ({ const scatterplotSegmentComponent = getCategoricalDimensions(dimensions)[0] || getGeoDimensions(dimensions)[0]; + const scatterplotPalette = getDefaultCategoricalPaletteName( + scatterplotSegmentComponent + ); return { version: CHART_CONFIG_VERSION, @@ -288,9 +293,9 @@ export const getInitialConfig = ({ ...(scatterplotSegmentComponent ? { componentIri: scatterplotSegmentComponent.iri, - palette: DEFAULT_PALETTE, + palette: scatterplotPalette, colorMapping: mapValueIrisToColor({ - palette: DEFAULT_PALETTE, + palette: scatterplotPalette, dimensionValues: scatterplotSegmentComponent.values, }), } @@ -301,6 +306,7 @@ export const getInitialConfig = ({ const pieSegmentComponent = getCategoricalDimensions(dimensions)[0] || getGeoDimensions(dimensions)[0]; + const piePalette = getDefaultCategoricalPaletteName(pieSegmentComponent); return { version: CHART_CONFIG_VERSION, @@ -311,10 +317,10 @@ export const getInitialConfig = ({ y: { componentIri: numericalMeasures[0].iri }, segment: { componentIri: pieSegmentComponent.iri, - palette: DEFAULT_PALETTE, + palette: piePalette, sorting: { sortingType: "byMeasure", sortingOrder: "asc" }, colorMapping: mapValueIrisToColor({ - palette: DEFAULT_PALETTE, + palette: piePalette, dimensionValues: pieSegmentComponent.values, }), }, @@ -602,7 +608,13 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { }); }, }, - segment: ({ oldValue, oldChartConfig, newChartConfig, dimensions }) => { + segment: ({ + oldValue, + oldChartConfig, + newChartConfig, + dimensions, + measures, + }) => { let newSegment: ColumnSegmentField | undefined; // When switching from a table chart, a whole fields object is passed as oldValue. @@ -610,6 +622,7 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { const tableSegment = convertTableFieldsToSegmentField({ fields: oldValue as TableFields, dimensions, + measures, }); if (tableSegment) { @@ -673,13 +686,20 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { }); }, }, - segment: ({ oldValue, oldChartConfig, newChartConfig, dimensions }) => { + segment: ({ + oldValue, + oldChartConfig, + newChartConfig, + dimensions, + measures, + }) => { let newSegment: LineSegmentField | undefined; if (oldChartConfig.chartType === "table") { const tableSegment = convertTableFieldsToSegmentField({ fields: oldValue as TableFields, dimensions, + measures, }); if (tableSegment) { @@ -738,13 +758,20 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { }); }, }, - segment: ({ oldValue, oldChartConfig, newChartConfig, dimensions }) => { + segment: ({ + oldValue, + oldChartConfig, + newChartConfig, + dimensions, + measures, + }) => { let newSegment: AreaSegmentField | undefined; if (oldChartConfig.chartType === "table") { const tableSegment = convertTableFieldsToSegmentField({ fields: oldValue as TableFields, dimensions, + measures, }); if (tableSegment) { @@ -800,13 +827,20 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { return newChartConfig; }, }, - segment: ({ oldValue, oldChartConfig, newChartConfig, dimensions }) => { + segment: ({ + oldValue, + oldChartConfig, + newChartConfig, + dimensions, + measures, + }) => { let newSegment: ScatterPlotSegmentField | undefined; if (oldChartConfig.chartType === "table") { const tableSegment = convertTableFieldsToSegmentField({ fields: oldValue as TableFields, dimensions, + measures, }); if (tableSegment) { @@ -844,13 +878,20 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { }); }, }, - segment: ({ oldValue, oldChartConfig, newChartConfig, dimensions }) => { + segment: ({ + oldValue, + oldChartConfig, + newChartConfig, + dimensions, + measures, + }) => { let newSegment: PieSegmentField | undefined; if (oldChartConfig.chartType === "table") { const tableSegment = convertTableFieldsToSegmentField({ fields: oldValue as TableFields, dimensions, + measures, }); if (tableSegment) { @@ -1136,9 +1177,11 @@ export const getFieldComponentIri = (fields: GenericFields, field: string) => { const convertTableFieldsToSegmentField = ({ fields, dimensions, + measures, }: { fields: TableFields; dimensions: DataCubeMetadata["dimensions"]; + measures: DataCubeMetadata["measures"]; }): GenericSegmentField | undefined => { const groupedColumns = group(Object.values(fields), (d) => d.isGroup) .get(true) @@ -1153,17 +1196,17 @@ const convertTableFieldsToSegmentField = ({ if (component) { const { componentIri } = component; + const actualComponent = [...dimensions, ...measures].find( + (d) => d.iri === componentIri + ) as DimensionMetadataFragment; + const palette = getDefaultCategoricalPaletteName(actualComponent); return { componentIri, - palette: DEFAULT_PALETTE, + palette, colorMapping: mapValueIrisToColor({ - palette: DEFAULT_PALETTE, - dimensionValues: ( - dimensions.find( - (d) => d.iri === componentIri - ) as DimensionMetadataFragment - )?.values, + palette, + dimensionValues: actualComponent.values, }), }; } diff --git a/app/charts/shared/colors.tsx b/app/charts/shared/colors.tsx index 3018df0d87..1375545df2 100644 --- a/app/charts/shared/colors.tsx +++ b/app/charts/shared/colors.tsx @@ -1,5 +1,8 @@ import { color, RGBColor } from "d3"; +import { DimensionValue } from "@/domain/data"; +import { DimensionMetadataFragment } from "@/graphql/query-hooks"; + export const colorToRgbArray = (_color: string, opacity?: number): number[] => { const { r, g, b } = color(_color) as RGBColor; @@ -18,3 +21,11 @@ export const rgbArrayToHex = (rgbArray: number[]): string => { ); } }; + +export const hasDimensionDefaultColors = ( + d?: DimensionMetadataFragment +): d is DimensionMetadataFragment => { + return !!(d?.values as DimensionValue[] | undefined)?.some( + (d) => d.color !== undefined + ); +}; diff --git a/app/configurator/components/chart-controls/color-palette.tsx b/app/configurator/components/chart-controls/color-palette.tsx index bd16cc2b92..5c53c9a65d 100644 --- a/app/configurator/components/chart-controls/color-palette.tsx +++ b/app/configurator/components/chart-controls/color-palette.tsx @@ -12,19 +12,21 @@ import { makeStyles } from "@mui/styles"; import get from "lodash/get"; import { useCallback } from "react"; +import { hasDimensionDefaultColors } from "@/charts/shared/colors"; import Flex from "@/components/flex"; import { Label } from "@/components/form"; import { ConfiguratorStateConfiguringChart, useConfiguratorState, - DEFAULT_PALETTE, } from "@/configurator"; import { mapValueIrisToColor } from "@/configurator/components/ui-helpers"; -import { isNumericalMeasure } from "@/domain/data"; +import { DimensionValue, isNumericalMeasure } from "@/domain/data"; import { DimensionMetadataFragment } from "@/graphql/query-hooks"; import { categoricalPalettes, + DEFAULT_CATEGORICAL_PALETTE_NAME, divergingSteppedPalettes, + getDefaultCategoricalPalette, getPalette, } from "@/palettes"; import useEvent from "@/utils/use-event"; @@ -58,9 +60,20 @@ export const ColorPalette = ({ }: Props) => { const [state, dispatch] = useConfiguratorState(); const classes = useStyles(); + const hasDefaultColors = hasDimensionDefaultColors(component); + const defaultPalette = + hasDefaultColors && component + ? getDefaultCategoricalPalette( + (component.values as DimensionValue[]) + .map((d) => d.color) + .filter(Boolean) as string[] + ) + : null; const palettes = isNumericalMeasure(component) ? divergingSteppedPalettes + : defaultPalette + ? [defaultPalette, ...categoricalPalettes] : categoricalPalettes; const currentPaletteName = get( @@ -78,6 +91,7 @@ export const ColorPalette = ({ if (!component || !palette) { return; } + dispatch({ type: "CHART_PALETTE_CHANGED", value: { @@ -200,7 +214,7 @@ const ColorPaletteReset = ({ `chartConfig.fields["${field}"].${ colorConfigPath ? `${colorConfigPath}.` : "" }palette`, - DEFAULT_PALETTE + DEFAULT_CATEGORICAL_PALETTE_NAME ) as string; const colorMapping = get( @@ -233,9 +247,9 @@ const ColorPaletteReset = ({ const nbMatchedColors = colorMappingColors.length; const matchedColorsInPalette = currentPalette.slice(0, nbMatchedColors); - const same = matchedColorsInPalette.every( - (pc, i) => pc === colorMappingColors[i] - ); + const same = + matchedColorsInPalette.every((d, i) => d === colorMappingColors[i]) || + palette === "default"; return (