diff --git a/packages/grafana-ui/src/components/Table/TableNG/Cells/PillCell.test.tsx b/packages/grafana-ui/src/components/Table/TableNG/Cells/PillCell.test.tsx index 0b54c3ef717..3974ab7cb52 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/Cells/PillCell.test.tsx +++ b/packages/grafana-ui/src/components/Table/TableNG/Cells/PillCell.test.tsx @@ -14,6 +14,7 @@ describe('PillCell', () => { type: FieldType.string, values: values, config: {}, + display: (value: unknown) => ({ text: String(value), color: '#FF780A', numeric: NaN }), }); const ser = new XMLSerializer(); @@ -119,6 +120,29 @@ describe('PillCell', () => { ` ); }); + + it('custom display text', () => { + const mockField = fieldWithValues(['value1,value2,value3']); + const field = { + ...mockField, + display: (value: unknown) => ({ + text: `${value} lbs`, + color: '#FF780A', + numeric: 0, + }), + } satisfies Field; + + expectHTML( + render( + + ), + ` + value1 lbs + value2 lbs + value3 lbs + ` + ); + }); }); describe('Color by value mappings', () => { diff --git a/packages/grafana-ui/src/components/Table/TableNG/Cells/PillCell.tsx b/packages/grafana-ui/src/components/Table/TableNG/Cells/PillCell.tsx index baf3715eb77..92c7aff6f9a 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/Cells/PillCell.tsx +++ b/packages/grafana-ui/src/components/Table/TableNG/Cells/PillCell.tsx @@ -8,6 +8,7 @@ import { getColorByStringHash, FALLBACK_COLOR, fieldColorModeRegistry, + formattedValueToString, } from '@grafana/data'; import { FieldColorModeId } from '@grafana/schema'; @@ -20,10 +21,11 @@ export function PillCell({ rowIdx, field, theme, getTextColorForBackground }: Pi const pillValues = inferPills(value); return pillValues.length > 0 ? pillValues.map((pill, index) => { - const bgColor = getPillColor(pill, field, theme); + const renderedValue = formattedValueToString(field.display!(pill)); + const bgColor = getPillColor(renderedValue, field, theme); const textColor = getTextColorForBackground(bgColor); return { - value: String(pill), + value: renderedValue, key: `${pill}-${index}`, bgColor, color: textColor, diff --git a/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx b/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx index 908642d94a0..06da0b2f5c9 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx +++ b/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx @@ -438,7 +438,7 @@ export function TableNG(props: TableNGProps) { // attach JSONCell custom display function to JSONView cell type if (cellType === TableCellDisplayMode.JSONView || field.type === FieldType.other) { - field.display = displayJsonValue; + field.display = displayJsonValue(field); } // For some cells, "aligning" the cell will mean aligning the inline contents of the cell with diff --git a/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts b/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts index 946ee3fd799..ef295d72652 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts +++ b/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts @@ -47,6 +47,7 @@ import { getDisplayName, predicateByName, calculateFooterHeight, + displayJsonValue, } from './utils'; describe('TableNG utils', () => { @@ -1354,10 +1355,35 @@ describe('TableNG utils', () => { }); describe('displayJsonValue', () => { - it.todo('should parse and then stringify string values'); - it.todo('should not throw for non-serializable string values'); - it.todo('should stringify non-string values'); - it.todo('should not throw for non-serializable non-string values'); + let field: Field; + beforeEach(() => { + field = { + name: 'test', + type: FieldType.string, + config: {}, + state: { displayName: 'Test Display Name' }, + values: [], + display: (val: unknown) => ({ text: String(val), numeric: NaN }), + }; + }); + + it('should parse and then stringify string values', () => { + expect(displayJsonValue(field)('{"valid": "json"}').text).toBe('{\n "valid": "json"\n}'); + }); + + it('should not throw for non-serializable string values', () => { + expect(displayJsonValue(field)('{"invalid": "json').text).toBe('{"invalid": "json'); + }); + + it('should stringify non-string values', () => { + expect(displayJsonValue(field)(42).text).toBe('42'); + }); + + it('should use the underlying field.display method to format values and return numeric values', () => { + field.display = (val: unknown) => ({ text: `**${val}**`, numeric: Number(val), suffix: 'ms' }); + expect(displayJsonValue(field)(42).text).toBe('**42**ms'); + expect(displayJsonValue(field)(42).numeric).toBe(42); + }); }); describe('applySort', () => { diff --git a/packages/grafana-ui/src/components/Table/TableNG/utils.ts b/packages/grafana-ui/src/components/Table/TableNG/utils.ts index 22dcdf6a43f..c754a27200f 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/utils.ts +++ b/packages/grafana-ui/src/components/Table/TableNG/utils.ts @@ -15,6 +15,7 @@ import { DisplayValueAlignmentFactors, DataFrame, DisplayProcessor, + DecimalCount, } from '@grafana/data'; import { BarGaugeDisplayMode, @@ -970,28 +971,24 @@ export function canFieldBeColorized( ); } -export const displayJsonValue: DisplayProcessor = (value: unknown): DisplayValue => { - let displayValue: string; +export const displayJsonValue: (field: Field) => DisplayProcessor = (field: Field, decimals?: DecimalCount) => { + const origDisplay = field.display!; + return (value: unknown): DisplayValue => { + let jsonText: string; - // Handle string values that might be JSON - if (typeof value === 'string') { + const displayValue = origDisplay(value, decimals); + const formattedValue = formattedValueToString(displayValue); + + // Handle string values that might be JSON try { - const parsed = JSON.parse(value); - displayValue = JSON.stringify(parsed, null, ' '); + const parsed = JSON.parse(formattedValue); + jsonText = JSON.stringify(parsed, null, ' '); } catch { - displayValue = value; // Keep original if not valid JSON + jsonText = formattedValue; // Keep original if not valid JSON } - } else { - // For non-string values, stringify them - try { - displayValue = JSON.stringify(value, null, ' '); - } catch (error) { - // Handle circular references or other stringify errors - displayValue = String(value); - } - } - return { text: displayValue, numeric: Number.NaN }; + return { ...displayValue, text: jsonText }; + }; }; export function getSummaryCellTextAlign(textAlign: TextAlign, cellType: TableCellDisplayMode): TextAlign {