diff --git a/src/platform/packages/shared/chart-expressions-common/color_categories.test.ts b/src/platform/packages/shared/chart-expressions-common/color_categories.test.ts index ac314eee36f5b..19baacfc3fcf4 100644 --- a/src/platform/packages/shared/chart-expressions-common/color_categories.test.ts +++ b/src/platform/packages/shared/chart-expressions-common/color_categories.test.ts @@ -7,49 +7,38 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { DatatableRow } from '@kbn/expressions-plugin/common'; +import { DatatableColumn, DatatableRow } from '@kbn/expressions-plugin/common'; import { getColorCategories } from './color_categories'; -const extensions = ['gz', 'css', '', 'rpm', 'deb', 'zip', null]; -const getExtension = (i: number) => extensions[i % extensions.length]; +const getNextExtension = (() => { + let i = 0; + const extensions = ['gz', 'css', '', 'rpm', 'deb', 'zip', null]; + return () => extensions[i++ % extensions.length]; +})(); -const basicDatatableRows: DatatableRow[] = Array.from({ length: 30 }).map((_, i) => ({ - count: i, - extension: getExtension(i), -})); - -const isTransposedDatatableRows: DatatableRow[] = Array.from({ length: 30 }).map((_, i) => ({ - count: i, - ['safari---extension']: getExtension(i), - ['chrome---extension']: getExtension(i + 1), - ['firefox---extension']: getExtension(i + 2), -})); +const basicDatatable = { + columns: ['count', 'extension'].map((id) => ({ id } as DatatableColumn)), + rows: Array.from({ length: 10 }).map((_, i) => ({ + count: i, + extension: getNextExtension(), + })) as DatatableRow[], +}; describe('getColorCategories', () => { - it('should return all categories from datatable rows', () => { - expect(getColorCategories(basicDatatableRows, 'extension')).toEqual([ - 'gz', - 'css', - '', - 'rpm', - 'deb', - 'zip', - 'null', - ]); + it('should return no categories when accessor is undefined', () => { + expect(getColorCategories(basicDatatable.rows)).toEqual([]); }); - it('should exclude selected categories from datatable rows', () => { - expect(getColorCategories(basicDatatableRows, 'extension', false, ['', null])).toEqual([ - 'gz', - 'css', - 'rpm', - 'deb', - 'zip', - ]); + it('should return no categories when accessor is not found', () => { + expect(getColorCategories(basicDatatable.rows, 'N/A')).toEqual([]); + }); + + it('should return no categories when no rows are defined', () => { + expect(getColorCategories(undefined, 'extension')).toEqual([]); }); - it('should return categories across all transpose columns of datatable rows', () => { - expect(getColorCategories(isTransposedDatatableRows, 'extension', true)).toEqual([ + it('should return all categories from non-transpose datatable', () => { + expect(getColorCategories(basicDatatable.rows, 'extension')).toEqual([ 'gz', 'css', '', @@ -60,8 +49,8 @@ describe('getColorCategories', () => { ]); }); - it('should exclude selected categories across all transpose columns of datatable rows', () => { - expect(getColorCategories(isTransposedDatatableRows, 'extension', true, ['', null])).toEqual([ + it('should exclude selected categories from non-transpose datatable', () => { + expect(getColorCategories(basicDatatable.rows, 'extension', ['', null])).toEqual([ 'gz', 'css', 'rpm', diff --git a/src/platform/packages/shared/chart-expressions-common/color_categories.ts b/src/platform/packages/shared/chart-expressions-common/color_categories.ts index 1f8ed39e7dae0..d1ee8a2514789 100644 --- a/src/platform/packages/shared/chart-expressions-common/color_categories.ts +++ b/src/platform/packages/shared/chart-expressions-common/color_categories.ts @@ -13,33 +13,27 @@ import { isMultiFieldKey } from '@kbn/data-plugin/common'; /** * Get the stringified version of all the categories that needs to be colored in the chart. * Multifield keys will return as array of string and simple fields (numeric, string) will be returned as a plain unformatted string. + * + * Note: This does **NOT** support transposed columns */ export function getColorCategories( - rows: DatatableRow[], + rows: DatatableRow[] = [], accessor?: string, - isTransposed?: boolean, exclude?: any[] ): Array { - const ids = isTransposed - ? Object.keys(rows[0]).filter((key) => accessor && key.endsWith(accessor)) - : accessor - ? [accessor] - : []; + if (!accessor) return []; return rows - .flatMap((r) => - ids - .map((id) => r[id]) - .filter((v) => !(v === undefined || exclude?.includes(v))) - .map((v) => { - // The categories needs to be stringified in their unformatted version. - // We can't distinguish between a number and a string from a text input and the match should - // work with both numeric field values and string values. - const key = (isMultiFieldKey(v) ? v.keys : [v]).map(String); - const stringifiedKeys = key.join(','); - return { key, stringifiedKeys }; - }) - ) + .filter(({ [accessor]: v }) => !(v === undefined || exclude?.includes(v))) + .map((r) => { + const v = r[accessor]; + // The categories needs to be stringified in their unformatted version. + // We can't distinguish between a number and a string from a text input and the match should + // work with both numeric field values and string values. + const key = (isMultiFieldKey(v) ? v.keys : [v]).map(String); + const stringifiedKeys = key.join(','); + return { key, stringifiedKeys }; + }) .reduce<{ keys: Set; categories: Array }>( (acc, { key, stringifiedKeys }) => { if (!acc.keys.has(stringifiedKeys)) { diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.tsx index 90c644555d6f6..8fd8b98406e31 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.tsx @@ -115,7 +115,7 @@ export function TableDimensionEditor(props: TableDimensionEditorProps) { }; // need to tell the helper that the colorStops are required to display const displayStops = applyPaletteParams(props.paletteService, activePalette, currentMinMax); - const categories = getColorCategories(currentData?.rows ?? [], accessor, false, [null]); + const categories = getColorCategories(currentData?.rows, accessor, [null]); if (activePalette.name !== CUSTOM_PALETTE && activePalette.params?.stops) { activePalette.params.stops = applyPaletteParams( diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.tsx index dc6f818eb3519..3bbacf124668f 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.tsx @@ -32,7 +32,7 @@ import { ClickTriggerEvent } from '@kbn/charts-plugin/public'; import { IconChartDatatable } from '@kbn/chart-icons'; import useObservable from 'react-use/lib/useObservable'; import { getColorCategories } from '@kbn/chart-expressions-common'; -import { getOriginalId, isTransposeId } from '@kbn/transpose-utils'; +import { getOriginalId } from '@kbn/transpose-utils'; import { CoreTheme } from '@kbn/core/public'; import { getKbnPalettes } from '@kbn/palettes'; import type { LensTableRowContextMenuEvent } from '../../../types'; @@ -405,16 +405,12 @@ export const DatatableComponent = (props: DatatableRenderProps) => { const dataType = getFieldMetaFromDatatable(firstLocalTable, originalId)?.type; const isBucketed = bucketedColumns.some((id) => id === columnId); const colorByTerms = shouldColorByTerms(dataType, isBucketed); - + const categoryRows = (untransposedDataRef.current ?? firstLocalTable)?.rows; const data: ColorMappingInputData = colorByTerms ? { type: 'categories', - categories: getColorCategories( - firstLocalTable.rows, - originalId, - isTransposeId(columnId), - [null] - ), + // Must use non-transposed data here to correctly collate categories across transposed columns + categories: getColorCategories(categoryRows, originalId, [null]), } : { type: 'ranges', diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/partition/dimension_editor.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/partition/dimension_editor.tsx index 21cd550d4f53d..113c4f0d9e015 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/partition/dimension_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/partition/dimension_editor.tsx @@ -130,7 +130,7 @@ export function DimensionEditor(props: DimensionEditorProps) { currentLayer.colorMapping ); const table = props.frame.activeData?.[currentLayer.layerId]; - const splitCategories = getColorCategories(table?.rows ?? [], props.accessor); + const splitCategories = getColorCategories(table?.rows, props.accessor); return ( <> diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx index 5dea1057c3712..2e9b451015a4f 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx @@ -65,7 +65,7 @@ export function TagsDimensionEditor({ state.colorMapping ); const table = frame.activeData?.[state.layerId]; - const splitCategories = getColorCategories(table?.rows ?? [], state.tagAccessor); + const splitCategories = getColorCategories(table?.rows, state.tagAccessor); const setColorMapping = useCallback( (colorMapping?: ColorMapping.Config) => { diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index 994e254bea954..fd31ffcfd1245 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -134,7 +134,7 @@ export function DataDimensionEditor( const table = props.frame.activeData?.[layer.layerId]; const { splitAccessor } = layer; - const splitCategories = getColorCategories(table?.rows ?? [], splitAccessor); + const splitCategories = getColorCategories(table?.rows, splitAccessor); if (props.groupId === 'breakdown') { return !layer.collapseFn ? (