From 4465745feae21eb767555112bdfc1bb59ef404de Mon Sep 17 00:00:00 2001 From: Noah Onyejese <129368606+noahonyejese@users.noreply.github.com> Date: Tue, 3 Dec 2024 09:36:24 +0100 Subject: [PATCH] refactor: fixed all erros that occured due to chart config refactor --- app/charts/area/areas-state.tsx | 12 +- app/charts/chart-config-ui-options.ts | 59 +++--- app/charts/column/columns-grouped-state.tsx | 10 +- app/charts/column/columns-stacked-state.tsx | 14 +- .../combo/combo-line-column-state-props.ts | 4 +- .../combo/combo-line-dual-state-props.ts | 4 +- .../combo/combo-line-single-state-props.ts | 2 +- app/charts/index.spec.ts | 5 + app/charts/index.ts | 195 ++++++++++++++---- app/charts/line/lines-state.tsx | 9 +- app/charts/pie/pie-state.tsx | 10 +- app/charts/scatterplot/scatterplot-state.tsx | 9 +- app/components/chart-footnotes.tsx | 10 +- .../components/add-dataset-dialog.mock.ts | 5 + .../chart-controls/color-palette.tsx | 5 +- .../configurator-state/actions.tsx | 8 +- app/configurator/configurator-state/mocks.ts | 43 ++-- .../configurator-state/reducer.spec.tsx | 8 +- .../configurator-state/reducer.tsx | 18 +- 19 files changed, 285 insertions(+), 145 deletions(-) diff --git a/app/charts/area/areas-state.tsx b/app/charts/area/areas-state.tsx index 13b7d2154..9d429103c 100644 --- a/app/charts/area/areas-state.tsx +++ b/app/charts/area/areas-state.tsx @@ -248,7 +248,7 @@ const useAreasState = ( const xScaleTimeRange = scaleTime().domain(xScaleTimeRangeDomain); const colors = scaleOrdinal(); - if (segmentDimension && fields.segment?.colorMapping) { + if (segmentDimension && fields.color) { const orderedSegmentLabelsAndColors = allSegments.map((segment) => { const dvIri = segmentsByAbbreviationOrLabel.get(segment)?.value ?? @@ -257,7 +257,10 @@ const useAreasState = ( return { label: segment, - color: fields.segment?.colorMapping![dvIri] ?? schemeCategory10[0], + color: + fields.color.type === "segment" + ? fields.color.colorMapping![dvIri] ?? schemeCategory10[0] + : schemeCategory10[0], }; }); @@ -265,7 +268,7 @@ const useAreasState = ( colors.range(orderedSegmentLabelsAndColors.map((s) => s.color)); } else { colors.domain(allSegments); - colors.range(getPalette(fields.segment?.palette)); + colors.range(getPalette(fields.color.paletteId)); } colors.unknown(() => undefined); @@ -276,8 +279,7 @@ const useAreasState = ( xScaleTimeRange, }; }, [ - fields.segment?.palette, - fields.segment?.colorMapping, + fields.color, getX, scalesData, timeRangeData, diff --git a/app/charts/chart-config-ui-options.ts b/app/charts/chart-config-ui-options.ts index 3ae078c9a..e35b07750 100644 --- a/app/charts/chart-config-ui-options.ts +++ b/app/charts/chart-config-ui-options.ts @@ -485,8 +485,8 @@ export const defaultSegmentOnChange: OnEncodingChange< const component = components.find((d) => d.id === id); const palette = getDefaultCategoricalPaletteName( component, - chartConfig.fields.segment && "palette" in chartConfig.fields.segment - ? chartConfig.fields.segment.palette + chartConfig.fields.color && "palette" in chartConfig.fields.color + ? chartConfig.fields.color.paletteId : undefined ); const colorMapping = mapValueIrisToColor({ @@ -496,12 +496,14 @@ export const defaultSegmentOnChange: OnEncodingChange< if (chartConfig.fields.segment && "palette" in chartConfig.fields.segment) { chartConfig.fields.segment.componentId = id; - chartConfig.fields.segment.colorMapping = colorMapping; } else { chartConfig.fields.segment = { componentId: id, - palette, sorting: DEFAULT_SORTING, + }; + chartConfig.fields.color = { + type: "segment", + paletteId: palette, colorMapping, }; } @@ -955,12 +957,12 @@ const chartConfigOptionsUISpec: ChartSpecs = { onChange: (ids, options) => { const { chartConfig } = options; const { fields } = chartConfig; - const { y } = fields; - const palette = getPalette(y.palette); + const { color } = fields; + const palette = getPalette(color.paletteId); const newColorMapping = Object.fromEntries( - ids.map((id, i) => [id, y.colorMapping[i] ?? palette[i]]) + ids.map((id, i) => [id, color.colorMapping[i] ?? palette[i]]) ); - chartConfig.fields.y.colorMapping = newColorMapping; + chartConfig.fields.color.colorMapping = newColorMapping; }, }, }, @@ -990,11 +992,11 @@ const chartConfigOptionsUISpec: ChartSpecs = { onChange: (id, options) => { const { chartConfig } = options; const { fields } = chartConfig; - const { y } = fields; - chartConfig.fields.y.colorMapping = { - [id]: y.colorMapping[y.leftAxisComponentId], + const { y, color } = fields; + chartConfig.fields.color.colorMapping = { + [id]: color.colorMapping[y.leftAxisComponentId], [y.rightAxisComponentId]: - y.colorMapping[y.rightAxisComponentId], + color.colorMapping[y.rightAxisComponentId], }; }, }, @@ -1002,10 +1004,11 @@ const chartConfigOptionsUISpec: ChartSpecs = { onChange: (id, options) => { const { chartConfig } = options; const { fields } = chartConfig; - const { y } = fields; - chartConfig.fields.y.colorMapping = { - [y.leftAxisComponentId]: y.colorMapping[y.leftAxisComponentId], - [id]: y.colorMapping[y.rightAxisComponentId], + const { y, color } = fields; + chartConfig.fields.color.colorMapping = { + [y.leftAxisComponentId]: + color.colorMapping[y.leftAxisComponentId], + [id]: color.colorMapping[y.rightAxisComponentId], }; }, }, @@ -1036,11 +1039,11 @@ const chartConfigOptionsUISpec: ChartSpecs = { onChange: (id, options) => { const { chartConfig } = options; const { fields } = chartConfig; - const { y } = fields; - const lineColor = y.colorMapping[y.lineComponentId]; - const columnColor = y.colorMapping[y.columnComponentId]; + const { y, color } = fields; + const lineColor = color.colorMapping[y.lineComponentId]; + const columnColor = color.colorMapping[y.columnComponentId]; - chartConfig.fields.y.colorMapping = + chartConfig.fields.color.colorMapping = y.lineAxisOrientation === "left" ? { [id]: lineColor, [y.columnComponentId]: columnColor } : { [y.columnComponentId]: columnColor, [id]: lineColor }; @@ -1050,11 +1053,11 @@ const chartConfigOptionsUISpec: ChartSpecs = { onChange: (id, options) => { const { chartConfig } = options; const { fields } = chartConfig; - const { y } = fields; - const columnColor = y.colorMapping[y.columnComponentId]; - const lineColor = y.colorMapping[y.lineComponentId]; + const { y, color } = fields; + const columnColor = color.colorMapping[y.columnComponentId]; + const lineColor = color.colorMapping[y.lineComponentId]; - chartConfig.fields.y.colorMapping = + chartConfig.fields.color.colorMapping = y.lineAxisOrientation === "left" ? { [y.lineComponentId]: lineColor, [id]: columnColor } : { [id]: columnColor, [y.lineComponentId]: lineColor }; @@ -1064,7 +1067,7 @@ const chartConfigOptionsUISpec: ChartSpecs = { onChange: (_, options) => { const { chartConfig } = options; const { fields } = chartConfig; - const { y } = fields; + const { y, color } = fields; const lineAxisLeft = y.lineAxisOrientation === "left"; // Need the correct order to not enable "Reset color palette" button. const firstId = lineAxisLeft @@ -1074,9 +1077,9 @@ const chartConfigOptionsUISpec: ChartSpecs = { ? y.lineComponentId : y.columnComponentId; - chartConfig.fields.y.colorMapping = { - [firstId]: y.colorMapping[secondId], - [secondId]: y.colorMapping[firstId], + chartConfig.fields.color.colorMapping = { + [firstId]: color.colorMapping[secondId], + [secondId]: color.colorMapping[firstId], }; }, }, diff --git a/app/charts/column/columns-grouped-state.tsx b/app/charts/column/columns-grouped-state.tsx index 7c837ecc4..279e912d1 100644 --- a/app/charts/column/columns-grouped-state.tsx +++ b/app/charts/column/columns-grouped-state.tsx @@ -189,7 +189,7 @@ const useColumnsGroupedState = ( } = useMemo(() => { const colors = scaleOrdinal(); - if (fields.segment && segmentDimension && fields.segment.colorMapping) { + if (fields.segment && segmentDimension && fields.color) { const orderedSegmentLabelsAndColors = allSegments.map((segment) => { const dvIri = segmentsByAbbreviationOrLabel.get(segment)?.value || @@ -198,7 +198,10 @@ const useColumnsGroupedState = ( return { label: segment, - color: fields.segment?.colorMapping![dvIri] ?? schemeCategory10[0], + color: + fields.color.type === "segment" + ? fields.color.colorMapping![dvIri] ?? schemeCategory10[0] + : schemeCategory10[0], }; }); @@ -206,7 +209,7 @@ const useColumnsGroupedState = ( colors.range(orderedSegmentLabelsAndColors.map((s) => s.color)); } else { colors.domain(allSegments); - colors.range(getPalette(fields.segment?.palette)); + colors.range(getPalette(fields.color.paletteId)); } colors.unknown(() => undefined); @@ -278,6 +281,7 @@ const useColumnsGroupedState = ( }; }, [ fields.segment, + fields.color, fields.x?.sorting, fields.x?.useAbbreviations, segmentDimension, diff --git a/app/charts/column/columns-stacked-state.tsx b/app/charts/column/columns-stacked-state.tsx index 99cf9e869..9661bacde 100644 --- a/app/charts/column/columns-stacked-state.tsx +++ b/app/charts/column/columns-stacked-state.tsx @@ -224,11 +224,7 @@ const useColumnsStackedState = ( } = useMemo(() => { const colors = scaleOrdinal(); - if ( - fields.segment && - segmentsByAbbreviationOrLabel && - fields.segment.colorMapping - ) { + if (fields.segment && segmentsByAbbreviationOrLabel && fields.color) { const orderedSegmentLabelsAndColors = allSegments.map((segment) => { // FIXME: Labels in observations can differ from dimension values because the latter can be concatenated to only appear once per value // See https://github.com/visualize-admin/visualization-tool/issues/97 @@ -244,7 +240,10 @@ const useColumnsStackedState = ( return { label: segment, - color: fields.segment?.colorMapping![dvIri] ?? schemeCategory10[0], + color: + fields.color.type === "segment" + ? fields.color.colorMapping![dvIri] ?? schemeCategory10[0] + : schemeCategory10[0], }; }); @@ -252,7 +251,7 @@ const useColumnsStackedState = ( colors.range(orderedSegmentLabelsAndColors.map((s) => s.color)); } else { colors.domain(allSegments); - colors.range(getPalette(fields.segment?.palette)); + colors.range(getPalette(fields.color.paletteId)); } colors.unknown(() => undefined); @@ -295,6 +294,7 @@ const useColumnsStackedState = ( }; }, [ fields.segment, + fields.color, fields.x.sorting, fields.x.useAbbreviations, xDimension, diff --git a/app/charts/combo/combo-line-column-state-props.ts b/app/charts/combo/combo-line-column-state-props.ts index d4e53abc9..bb9b37757 100644 --- a/app/charts/combo/combo-line-column-state-props.ts +++ b/app/charts/combo/combo-line-column-state-props.ts @@ -77,7 +77,7 @@ export const useComboLineColumnStateVariables = ( dimension: measuresById[lineId], id: lineId, label: getLabelWithUnit(measuresById[lineId]), - color: fields.y.colorMapping[lineId], + color: fields.color.colorMapping[lineId], getY: (d) => (d[lineId] !== null ? Number(d[lineId]) : null), getMinY: (data) => { const minY = @@ -94,7 +94,7 @@ export const useComboLineColumnStateVariables = ( dimension: measuresById[columnId], id: columnId, label: getLabelWithUnit(measuresById[columnId]), - color: fields.y.colorMapping[columnId], + color: fields.color.colorMapping[columnId], getY: (d) => (d[columnId] !== null ? Number(d[columnId]) : null), getMinY: (data) => { const minY = diff --git a/app/charts/combo/combo-line-dual-state-props.ts b/app/charts/combo/combo-line-dual-state-props.ts index 0394e5a39..f9ba2ae7e 100644 --- a/app/charts/combo/combo-line-dual-state-props.ts +++ b/app/charts/combo/combo-line-dual-state-props.ts @@ -60,7 +60,7 @@ export const useComboLineDualStateVariables = ( dimension: measuresById[leftId], id: leftId, label: getLabelWithUnit(measuresById[leftId]), - color: fields.y.colorMapping[leftId], + color: fields.color.colorMapping[leftId], getY: (d) => (d[leftId] !== null ? Number(d[leftId]) : null), getMinY: (data) => { const minY = @@ -77,7 +77,7 @@ export const useComboLineDualStateVariables = ( dimension: measuresById[rightId], id: rightId, label: getLabelWithUnit(measuresById[rightId]), - color: fields.y.colorMapping[rightId], + color: fields.color.colorMapping[rightId], getY: (d) => (d[rightId] !== null ? Number(d[rightId]) : null), getMinY: (data) => { const minY = diff --git a/app/charts/combo/combo-line-single-state-props.ts b/app/charts/combo/combo-line-single-state-props.ts index 86a3b2e14..731ca89e4 100644 --- a/app/charts/combo/combo-line-single-state-props.ts +++ b/app/charts/combo/combo-line-single-state-props.ts @@ -53,7 +53,7 @@ export const useComboLineSingleStateVariables = ( dimension: measuresById[id], id, label: measuresById[id].label, - color: fields.y.colorMapping[id], + color: fields.color.colorMapping[id], getY: (d) => (d[id] !== null ? Number(d[id]) : null), getMinY: (data) => { const minY = diff --git a/app/charts/index.spec.ts b/app/charts/index.spec.ts index cc422c430..fa4355c1a 100644 --- a/app/charts/index.spec.ts +++ b/app/charts/index.spec.ts @@ -275,6 +275,11 @@ describe("chart type switch", () => { y: { componentId: "https://environment.ld.admin.ch/foen/ubd0104/value", }, + color: { + type: "segment", + paletteId: "category10", + colorMapping: {}, + }, }, interactiveFiltersConfig: { legend: { diff --git a/app/charts/index.ts b/app/charts/index.ts index 6827c25a3..5f5d8f0c6 100644 --- a/app/charts/index.ts +++ b/app/charts/index.ts @@ -51,6 +51,7 @@ import { MapConfig, MapSymbolLayer, Meta, + NewColorField, PieSegmentField, RegularChartType, ScatterPlotSegmentField, @@ -398,6 +399,14 @@ export const getInitialConfig = ( fields: { x: { componentId: areaXComponentId }, y: { componentId: numericalMeasures[0].id, imputationType: "none" }, + color: { + type: "segment", + paletteId: DEFAULT_CATEGORICAL_PALETTE_NAME, + colorMapping: mapValueIrisToColor({ + palette: DEFAULT_CATEGORICAL_PALETTE_NAME, + dimensionValues: dimensions[0].values, + }), + }, }, }; case "column": @@ -422,6 +431,11 @@ export const getInitialConfig = ( sorting: DEFAULT_SORTING, }, y: { componentId: numericalMeasures[0].id }, + color: { + type: "single", + paletteId: DEFAULT_CATEGORICAL_PALETTE_NAME, + color: dimensions[0].values[0].color || "#000", + }, }, }; case "line": @@ -436,6 +450,11 @@ export const getInitialConfig = ( fields: { x: { componentId: lineXComponentId }, y: { componentId: numericalMeasures[0].id }, + color: { + type: "single", + paletteId: DEFAULT_CATEGORICAL_PALETTE_NAME, + color: dimensions[0].values[0].color || "#000", + }, }, }; case "map": @@ -487,8 +506,11 @@ export const getInitialConfig = ( y: { componentId: numericalMeasures[0].id }, segment: { componentId: pieSegmentComponent.id, - palette: piePalette, sorting: { sortingType: "byMeasure", sortingOrder: "asc" }, + }, + color: { + type: "segment", + paletteId: piePalette, colorMapping: mapValueIrisToColor({ palette: piePalette, dimensionValues: pieSegmentComponent.values, @@ -518,16 +540,25 @@ export const getInitialConfig = ( }, ...(scatterplotSegmentComponent ? { - segment: { - componentId: scatterplotSegmentComponent.id, - palette: scatterplotPalette, + color: { + type: "segment", + paletteId: scatterplotPalette, colorMapping: mapValueIrisToColor({ palette: scatterplotPalette, dimensionValues: scatterplotSegmentComponent.values, }), }, + segment: { + componentId: scatterplotSegmentComponent.id, + }, } - : {}), + : { + color: { + type: "single", + paletteId: DEFAULT_CATEGORICAL_PALETTE_NAME, + color: dimensions[0].values[0].color || "#000", + }, + }), }, }; case "table": @@ -585,7 +616,10 @@ export const getInitialConfig = ( // Use all measures with the most common unit. y: { componentIds: yComponentIds, - palette: DEFAULT_CATEGORICAL_PALETTE_NAME, + }, + color: { + type: "measures", + paletteId: DEFAULT_CATEGORICAL_PALETTE_NAME, colorMapping: mapValueIrisToColor({ palette: DEFAULT_CATEGORICAL_PALETTE_NAME, dimensionValues: yComponentIds.map((id) => ({ @@ -620,7 +654,10 @@ export const getInitialConfig = ( y: { leftAxisComponentId, rightAxisComponentId, - palette: DEFAULT_CATEGORICAL_PALETTE_NAME, + }, + color: { + type: "measures", + paletteId: DEFAULT_CATEGORICAL_PALETTE_NAME, colorMapping: mapValueIrisToColor({ palette: DEFAULT_CATEGORICAL_PALETTE_NAME, dimensionValues: [leftAxisComponentId, rightAxisComponentId].map( @@ -658,7 +695,10 @@ export const getInitialConfig = ( lineComponentId, lineAxisOrientation: "right", columnComponentId, - palette: DEFAULT_CATEGORICAL_PALETTE_NAME, + }, + color: { + type: "measures", + paletteId: DEFAULT_CATEGORICAL_PALETTE_NAME, colorMapping: mapValueIrisToColor({ palette: DEFAULT_CATEGORICAL_PALETTE_NAME, dimensionValues: [lineComponentId, columnComponentId].map( @@ -933,6 +973,8 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { measures, }) => { let newSegment: ColumnSegmentField | undefined; + let newColor: NewColorField | undefined; + const yMeasure = measures.find( (d) => d.id === newChartConfig.fields.y.componentId ); @@ -947,10 +989,11 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { if (tableSegment) { newSegment = { - ...tableSegment, + ...tableSegment.segment, sorting: DEFAULT_SORTING, type: disableStacked(yMeasure) ? "grouped" : "stacked", }; + newColor = tableSegment.color; } // Otherwise we are dealing with a segment field. We shouldn't take // the segment from oldValue if the component has already been used as @@ -970,12 +1013,25 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { }), type: disableStacked(yMeasure) ? "grouped" : "stacked", }; + newColor = { + type: "segment", + paletteId: DEFAULT_CATEGORICAL_PALETTE_NAME, + colorMapping: mapValueIrisToColor({ + palette: DEFAULT_CATEGORICAL_PALETTE_NAME, + dimensionValues: + dimensions.find((d) => d.id === oldValue.componentId)?.values || + [], + }), + }; } return produce(newChartConfig, (draft) => { if (newSegment) { draft.fields.segment = newSegment; } + if (newColor?.type === "segment") { + draft.fields.color = newColor; + } }); }, animation: ({ oldValue, newChartConfig }) => { @@ -1027,6 +1083,7 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { measures, }) => { let newSegment: LineSegmentField | undefined; + let newColor: NewColorField | undefined; if (oldChartConfig.chartType === "table") { const tableSegment = convertTableFieldsToSegmentField({ @@ -1036,7 +1093,8 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { }); if (tableSegment) { - newSegment = tableSegment; + newSegment = tableSegment.segment; + newColor = tableSegment.color; } } else { const oldSegment = oldValue as Exclude; @@ -1047,8 +1105,6 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { if (!isTemporalDimension(segmentDimension)) { newSegment = { componentId: oldSegment.componentId, - palette: oldSegment.palette, - colorMapping: oldSegment.colorMapping, sorting: "sorting" in oldSegment && oldSegment.sorting && @@ -1056,6 +1112,14 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { ? oldSegment.sorting ?? DEFAULT_FIXED_COLOR_FIELD : DEFAULT_SORTING, }; + newColor = { + type: "segment", + paletteId: DEFAULT_CATEGORICAL_PALETTE_NAME, + colorMapping: mapValueIrisToColor({ + palette: DEFAULT_CATEGORICAL_PALETTE_NAME, + dimensionValues: segmentDimension?.values || [], + }), + }; } } @@ -1063,6 +1127,9 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { if (newSegment) { draft.fields.segment = newSegment; } + if (newColor?.type === "segment") { + draft.fields.color = newColor; + } }); }, }, @@ -1115,6 +1182,7 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { } let newSegment: AreaSegmentField | undefined; + let newColor: NewColorField | undefined; if (oldChartConfig.chartType === "table") { const tableSegment = convertTableFieldsToSegmentField({ @@ -1125,9 +1193,10 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { if (tableSegment) { newSegment = { - ...tableSegment, + ...tableSegment.segment, sorting: DEFAULT_SORTING, }; + newColor = tableSegment.color; } } else { const oldSegment = oldValue as Exclude; @@ -1138,14 +1207,20 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { if (!isTemporalDimension(segmentDimension)) { newSegment = { componentId: oldSegment.componentId, - palette: oldSegment.palette, - colorMapping: oldSegment.colorMapping, sorting: adjustSegmentSorting({ segment: oldSegment, acceptedValues: AREA_SEGMENT_SORTING.map((d) => d.sortingType), defaultValue: "byTotalSize", }), }; + newColor = { + type: "segment", + paletteId: DEFAULT_CATEGORICAL_PALETTE_NAME, + colorMapping: mapValueIrisToColor({ + palette: DEFAULT_CATEGORICAL_PALETTE_NAME, + dimensionValues: segmentDimension?.values || [], + }), + }; } } @@ -1153,6 +1228,9 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { if (newSegment) { draft.fields.segment = newSegment; } + if (newColor?.type === "segment") { + draft.fields.color = newColor; + } }); }, }, @@ -1190,6 +1268,7 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { measures, }) => { let newSegment: ScatterPlotSegmentField | undefined; + let newColor: NewColorField | undefined; if (oldChartConfig.chartType === "table") { const tableSegment = convertTableFieldsToSegmentField({ @@ -1199,14 +1278,13 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { }); if (tableSegment) { - newSegment = tableSegment; + newSegment = tableSegment.segment; + newColor = tableSegment.color; } } else { const oldSegment = oldValue as Exclude; newSegment = { componentId: oldSegment.componentId, - palette: oldSegment.palette, - colorMapping: oldSegment.colorMapping, }; } @@ -1214,6 +1292,9 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { if (newSegment) { draft.fields.segment = newSegment; } + if (newColor?.type === "segment") { + draft.fields.color = newColor; + } }); }, animation: ({ oldValue, newChartConfig }) => { @@ -1246,6 +1327,7 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { measures, }) => { let newSegment: PieSegmentField | undefined; + let newColor: NewColorField | undefined; if (oldChartConfig.chartType === "table") { const tableSegment = convertTableFieldsToSegmentField({ @@ -1256,28 +1338,40 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { if (tableSegment) { newSegment = { - ...tableSegment, + ...tableSegment.segment, sorting: DEFAULT_SORTING, }; + newColor = tableSegment.color; } } else { const oldSegment = oldValue as Exclude; newSegment = { componentId: oldSegment.componentId, - palette: oldSegment.palette, - colorMapping: oldSegment.colorMapping, sorting: adjustSegmentSorting({ segment: oldSegment, acceptedValues: PIE_SEGMENT_SORTING.map((d) => d.sortingType), defaultValue: "byMeasure", }), }; + newColor = { + type: "segment", + paletteId: DEFAULT_CATEGORICAL_PALETTE_NAME, + colorMapping: mapValueIrisToColor({ + palette: DEFAULT_CATEGORICAL_PALETTE_NAME, + dimensionValues: + dimensions.find((d) => d.id === oldSegment.componentId) + ?.values || [], + }), + }; } return produce(newChartConfig, (draft) => { if (newSegment) { draft.fields.segment = newSegment; } + if (newColor?.type === "segment") { + draft.fields.color = newColor; + } }); }, animation: ({ oldValue, newChartConfig }) => { @@ -1405,16 +1499,19 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { .filter((d) => d.unit === unit) .map((d) => d.id); const palette = isSegmentInConfig(oldChartConfig) - ? oldChartConfig.fields.segment?.palette ?? + ? oldChartConfig.fields.color.paletteId ?? DEFAULT_CATEGORICAL_PALETTE_NAME : isComboChartConfig(oldChartConfig) - ? oldChartConfig.fields.y.palette + ? oldChartConfig.fields.color.paletteId : DEFAULT_CATEGORICAL_PALETTE_NAME; return produce(newChartConfig, (draft) => { draft.fields.y = { componentIds, - palette, + }; + draft.fields.color = { + type: "measures", + paletteId: palette, colorMapping: mapValueIrisToColor({ palette, dimensionValues: componentIds.map((id) => ({ @@ -1518,25 +1615,26 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { leftMeasure = getLeftMeasure(leftMeasure.id); const palette = isSegmentInConfig(oldChartConfig) - ? oldChartConfig.fields.segment?.palette ?? + ? oldChartConfig.fields.color.paletteId ?? DEFAULT_CATEGORICAL_PALETTE_NAME : isComboChartConfig(oldChartConfig) - ? oldChartConfig.fields.y.palette + ? oldChartConfig.fields.color.paletteId : DEFAULT_CATEGORICAL_PALETTE_NAME; return produce(newChartConfig, (draft) => { draft.fields.y = { leftAxisComponentId: leftMeasure.id, rightAxisComponentId: rightMeasureId as string, - palette, + }; + draft.fields.color = { + type: "measures", + paletteId: palette, colorMapping: mapValueIrisToColor({ palette, - dimensionValues: [leftMeasure.id, rightMeasureId as string].map( - (id) => ({ - value: id, - label: id, - }) - ), + dimensionValues: [leftMeasure.id, rightMeasureId].map((id) => ({ + value: id, + label: id, + })), }), }; }); @@ -1610,10 +1708,10 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { ).id; const palette = isSegmentInConfig(oldChartConfig) - ? oldChartConfig.fields.segment?.palette ?? + ? oldChartConfig.fields.color.paletteId ?? DEFAULT_CATEGORICAL_PALETTE_NAME : isComboChartConfig(oldChartConfig) - ? oldChartConfig.fields.y.palette + ? oldChartConfig.fields.color.paletteId : DEFAULT_CATEGORICAL_PALETTE_NAME; return produce(newChartConfig, (draft) => { @@ -1621,7 +1719,10 @@ const chartConfigsAdjusters: ChartConfigsAdjusters = { columnComponentId: leftMeasure.id, lineComponentId, lineAxisOrientation: "right", - palette, + }; + draft.fields.color = { + type: "measures", + paletteId: palette, colorMapping: mapValueIrisToColor({ palette, dimensionValues: [leftMeasure.id, lineComponentId].map((id) => ({ @@ -2181,7 +2282,7 @@ const convertTableFieldsToSegmentField = ({ fields: TableFields; dimensions: Dimension[]; measures: Measure[]; -}): GenericSegmentField | undefined => { +}): { segment: GenericSegmentField; color: NewColorField } | undefined => { const groupedColumns = group(Object.values(fields), (d) => d.isGroup) .get(true) ?.filter((d) => SEGMENT_ENABLED_COMPONENTS.includes(d.componentType)) @@ -2199,12 +2300,20 @@ const convertTableFieldsToSegmentField = ({ const palette = getDefaultCategoricalPaletteName(actualComponent); return { - componentId, - palette, - colorMapping: mapValueIrisToColor({ - palette, - dimensionValues: actualComponent.values, - }), + segment: { + componentId, + }, + color: { + type: "segment", + paletteId: palette, + colorMapping: mapValueIrisToColor({ + palette: palette, + dimensionValues: [componentId].map((id) => ({ + value: id, + label: id, + })), + }), + }, }; }; diff --git a/app/charts/line/lines-state.tsx b/app/charts/line/lines-state.tsx index 24f219675..1fed350e7 100644 --- a/app/charts/line/lines-state.tsx +++ b/app/charts/line/lines-state.tsx @@ -195,7 +195,7 @@ const useLinesState = ( // Map ordered segments to colors const colors = scaleOrdinal(); - if (fields.segment && segmentDimension && fields.segment.colorMapping) { + if (fields.segment && segmentDimension && fields.color) { const orderedSegmentLabelsAndColors = allSegments.map((segment) => { const dvIri = segmentsByAbbreviationOrLabel.get(segment)?.value || @@ -204,7 +204,10 @@ const useLinesState = ( return { label: segment, - color: fields.segment?.colorMapping![dvIri] ?? schemeCategory10[0], + color: + fields.color.type === "segment" + ? fields.color.colorMapping![dvIri] ?? schemeCategory10[0] + : schemeCategory10[0], }; }); @@ -212,7 +215,7 @@ const useLinesState = ( colors.range(orderedSegmentLabelsAndColors.map((s) => s.color)); } else { colors.domain(allSegments); - colors.range(getPalette(fields.segment?.palette)); + colors.range(getPalette(fields.color.paletteId)); } // Dimensions diff --git a/app/charts/pie/pie-state.tsx b/app/charts/pie/pie-state.tsx index 892ae3dfe..eebbbeb0c 100644 --- a/app/charts/pie/pie-state.tsx +++ b/app/charts/pie/pie-state.tsx @@ -101,7 +101,7 @@ const usePieState = ( ); const segments = allSegments.filter((d) => uniqueSegments.includes(d)); - if (fields.segment && segmentDimension && fields.segment.colorMapping) { + if (fields.segment && segmentDimension && fields.color) { const orderedSegmentLabelsAndColors = allSegments.map((segment) => { const dvIri = segmentsByAbbreviationOrLabel.get(segment)?.value || @@ -110,7 +110,10 @@ const usePieState = ( return { label: segment, - color: fields.segment?.colorMapping![dvIri] ?? schemeCategory10[0], + color: + fields.color.type === "segment" + ? fields.color.colorMapping![dvIri] ?? schemeCategory10[0] + : schemeCategory10[0], }; }); @@ -118,7 +121,7 @@ const usePieState = ( colors.range(orderedSegmentLabelsAndColors.map((s) => s.color)); } else { colors.domain(allSegments); - colors.range(getPalette(fields.segment?.palette)); + colors.range(getPalette(fields.color.paletteId)); } // Do not let the scale be implicitly extended by children calling it // on unknown values @@ -133,6 +136,7 @@ const usePieState = ( ySum, }; }, [ + fields.color, fields.segment, getSegment, getY, diff --git a/app/charts/scatterplot/scatterplot-state.tsx b/app/charts/scatterplot/scatterplot-state.tsx index 6459b6b74..d3deae1cc 100644 --- a/app/charts/scatterplot/scatterplot-state.tsx +++ b/app/charts/scatterplot/scatterplot-state.tsx @@ -127,7 +127,7 @@ const useScatterplotState = ( // Map ordered segments to colors const colors = scaleOrdinal(); - if (fields.segment && segmentDimension && fields.segment.colorMapping) { + if (fields.segment && segmentDimension && fields.color) { const orderedSegmentLabelsAndColors = allSegments.map((segment) => { const dvIri = segmentsByAbbreviationOrLabel.get(segment)?.value || @@ -136,7 +136,10 @@ const useScatterplotState = ( return { label: segment, - color: fields.segment!.colorMapping![dvIri] ?? schemeCategory10[0], + color: + fields.color.type === "segment" + ? fields.color.colorMapping![dvIri] ?? schemeCategory10[0] + : schemeCategory10[0], }; }); @@ -144,7 +147,7 @@ const useScatterplotState = ( colors.range(orderedSegmentLabelsAndColors.map((s) => s.color)); } else { colors.domain(allSegments); - colors.range(getPalette(fields.segment?.palette)); + colors.range(getPalette(fields.color.paletteId)); } // Dimensions const { left, bottom } = useChartPadding({ diff --git a/app/components/chart-footnotes.tsx b/app/components/chart-footnotes.tsx index e3af0fce4..3abe611ae 100644 --- a/app/components/chart-footnotes.tsx +++ b/app/components/chart-footnotes.tsx @@ -197,7 +197,7 @@ const ChartFootnotesComboLineColumn = ({ {firstComponent && ( ; - }; + value: NewColorField; } | { type: "CHART_PALETTE_RESET"; diff --git a/app/configurator/configurator-state/mocks.ts b/app/configurator/configurator-state/mocks.ts index 589c9f9b4..f8e20d4cc 100644 --- a/app/configurator/configurator-state/mocks.ts +++ b/app/configurator/configurator-state/mocks.ts @@ -170,14 +170,9 @@ export const configStateMock = { componentId: "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/anzahlanlagen", }, - segment: { - componentId: - "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/Kanton", - palette: "category10", - sorting: { - sortingType: "byAuto", - sortingOrder: "asc", - }, + color: { + type: "segment", + paletteId: "category10", colorMapping: { "https://ld.admin.ch/canton/1": "#1f77b4", "https://ld.admin.ch/canton/10": "#ff7f0e", @@ -206,6 +201,16 @@ export const configStateMock = { "https://ld.admin.ch/canton/8": "#9467bd", "https://ld.admin.ch/canton/9": "#8c564b", }, + }, + segment: { + componentId: + "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/Kanton", + + sorting: { + sortingType: "byAuto", + sortingOrder: "asc", + }, + type: "stacked", }, }, @@ -1619,15 +1624,9 @@ export const configJoinedCubes: Partial< "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/AnzahlAnlagen", }), }, - segment: { - componentId: stringifyComponentId({ - unversionedCubeIri: - "https://energy.ld.admin.ch/elcom/electricityprice-canton", - unversionedComponentIri: - "https://energy.ld.admin.ch/elcom/electricityprice/dimension/category", - }), - palette: "category10", - sorting: { sortingType: "byMeasure", sortingOrder: "asc" }, + color: { + type: "segment", + paletteId: "category10", colorMapping: { "https://energy.ld.admin.ch/elcom/electricityprice/category/C1": "#1f77b4", @@ -1661,6 +1660,16 @@ export const configJoinedCubes: Partial< "#9467bd", }, }, + segment: { + componentId: stringifyComponentId({ + unversionedCubeIri: + "https://energy.ld.admin.ch/elcom/electricityprice-canton", + unversionedComponentIri: + "https://energy.ld.admin.ch/elcom/electricityprice/dimension/category", + }), + + sorting: { sortingType: "byMeasure", sortingOrder: "asc" }, + }, }, }, }; diff --git a/app/configurator/configurator-state/reducer.spec.tsx b/app/configurator/configurator-state/reducer.spec.tsx index 16abe91bc..3d6e3ad9f 100644 --- a/app/configurator/configurator-state/reducer.spec.tsx +++ b/app/configurator/configurator-state/reducer.spec.tsx @@ -700,8 +700,12 @@ describe("colorMapping", () => { chartConfig.fields.segment?.componentId === "mapDataset___newAreaLayerColorIri" ); - expect(chartConfig.fields.segment?.palette === "dimension"); - expect(chartConfig.fields.segment?.colorMapping).toEqual({ + expect(chartConfig.fields.color.paletteId === "dimension"); + expect( + chartConfig.fields.color.type === "single" + ? chartConfig.fields.color.color + : chartConfig.fields.color.colorMapping + ).toEqual({ orange: "rgb(255, 153, 0)", }); }); diff --git a/app/configurator/configurator-state/reducer.tsx b/app/configurator/configurator-state/reducer.tsx index a3d43629c..f84a4978b 100644 --- a/app/configurator/configurator-state/reducer.tsx +++ b/app/configurator/configurator-state/reducer.tsx @@ -657,22 +657,16 @@ const reducer_: Reducer = ( const chartConfig = getChartConfig(draft); setWith( chartConfig, - `fields["${action.value.field}"].${ - action.value.colorConfigPath - ? `${action.value.colorConfigPath}.` - : "" - }palette`, - action.value.palette, + `fields.color.paletteId`, + action.value.paletteId, Object ); setWith( chartConfig, - `fields["${action.value.field}"].${ - action.value.colorConfigPath - ? `${action.value.colorConfigPath}.` - : "" - }colorMapping`, - action.value.colorMapping, + `fields.color.${action.value.type === "single" ? "color" : "colorMapping"}`, + action.value.type === "single" + ? action.value.color + : action.value.colorMapping, Object ); }