From d2ddcde8e93fcc8ace434978c2dde12d476c501f Mon Sep 17 00:00:00 2001 From: Raj Zalavadia Date: Thu, 25 Jun 2026 18:30:34 +0530 Subject: [PATCH] [BUGFIX] fix cellSettings not applied for columns with no data in any query When a column is defined in columnSettings but no query returns data for it (e.g. cpu_request_hard when no ResourceQuotas exist), the column key is absent from the keys array. This prevents keysAsObj from extending rows with undefined for that column, so the cellSettings null condition is never evaluated and the column shows blank instead of N/A. Fix: include column names from columnSettings in the keys array so that columns with no data are still evaluated for cellSettings conditions. Signed-off-by: Raj Zalavadia --- table/src/components/TablePanel.test.tsx | 40 ++++++++++++++++++++++++ table/src/components/TablePanel.tsx | 10 +++++- table/src/test/mock-query-results.ts | 8 +++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/table/src/components/TablePanel.test.tsx b/table/src/components/TablePanel.test.tsx index 96b731dfa..654cff1da 100644 --- a/table/src/components/TablePanel.test.tsx +++ b/table/src/components/TablePanel.test.tsx @@ -26,6 +26,7 @@ import { VirtuosoMockContext } from 'react-virtuoso'; import { TimeSeriesData } from '@perses-dev/spec'; import { TableOptions, TimeSeriesTableProps } from '../models'; import { + MOCK_MULTI_QUERY_DATA_EMPTY, MOCK_MULTI_QUERY_DATA_Q1, MOCK_MULTI_QUERY_DATA_Q2, MOCK_MULTI_QUERY_DATA_WITH_ZERO, @@ -460,5 +461,44 @@ describe('TablePanel', () => { }, TEST_TIMEOUT ); + + it( + 'should show N/A for columns defined in columnSettings but with no data in any query', + async () => { + // Simulates Request Hard column: defined in columnSettings as value #3, + // but the third query returns no data at all (e.g. no ResourceQuotas on cluster) + const options: TableOptions = { + columnSettings: [ + { name: 'timestamp', hide: true }, + { name: 'namespace', header: 'Namespace' }, + { name: 'value #1', header: 'Value 1' }, + { name: 'value #2', header: 'Value 2' }, + { name: 'value #3', header: 'Value 3 (empty query)' }, + ], + cellSettings: [{ condition: { kind: 'Misc', spec: { value: 'null' } }, text: 'N/A' }], + transforms: [ + { kind: 'MergeSeries', spec: {} }, + { kind: 'JoinByColumnValue', spec: { columns: ['namespace'] } }, + ], + enableFiltering: true, + }; + + // Q1 has data, Q2 has partial data, Q3 is completely empty + renderMultiQueryPanel( + [MOCK_MULTI_QUERY_DATA_Q1, MOCK_MULTI_QUERY_DATA_Q2, MOCK_MULTI_QUERY_DATA_EMPTY], + options + ); + + // Both namespaces should be present + expect(await screen.findByRole('cell', { name: 'ns-a' })).toBeInTheDocument(); + expect(await screen.findByRole('cell', { name: 'ns-b' })).toBeInTheDocument(); + + // Value 3 column should show N/A for all rows since no query produced data for it + const naCells = await screen.findAllByRole('cell', { name: 'N/A' }); + // At minimum: ns-b missing value #2 (1) + both rows missing value #3 (2) = 3 N/A cells + expect(naCells.length).toBeGreaterThanOrEqual(3); + }, + TEST_TIMEOUT + ); }); }); diff --git a/table/src/components/TablePanel.tsx b/table/src/components/TablePanel.tsx index b86f46159..75106f32f 100644 --- a/table/src/components/TablePanel.tsx +++ b/table/src/components/TablePanel.tsx @@ -452,8 +452,16 @@ export function TablePanel({ contentDimensions, spec, queryResults }: TableProps } } + // Include column names from columnSettings so that columns with no data + // are still extended with undefined for cellSettings evaluation (e.g. N/A for null) + for (const col of spec.columnSettings ?? []) { + if (!result.includes(col.name)) { + result.push(col.name); + } + } + return result; - }, [data]); + }, [data, spec.columnSettings]); const columnsFormat = useMemo(() => { const columnsFormat: Record = {}; diff --git a/table/src/test/mock-query-results.ts b/table/src/test/mock-query-results.ts index f07f88e00..433511f3c 100644 --- a/table/src/test/mock-query-results.ts +++ b/table/src/test/mock-query-results.ts @@ -326,6 +326,14 @@ export const MOCK_MULTI_QUERY_DATA_WITH_ZERO: TimeSeriesData = { ], }; +// Query that returns no data at all — simulates a metric like cpu_request_hard +// when no ResourceQuotas exist on the cluster. +export const MOCK_MULTI_QUERY_DATA_EMPTY: TimeSeriesData = { + timeRange: { start: new Date(1666625535000), end: new Date(1666625535000) }, + stepMs: 24379, + series: [], +}; + export const MOCK_TIME_SERIES_QUERY_DEFINITION = { kind: 'TimeSeriesQuery', spec: {