[release-12.2.2] Table: Pill and JSON Cells should allow formatting (#113130)
Table: Pill and JSON Cells should allow formatting (#111951)
* Table: PillCell should use formatted text inside pills
* Table: JSONCell should use formatted text
* remove unused imports
(cherry picked from commit 237ab6c1b4)
Co-authored-by: Paul Marbach <paul.marbach@grafana.com>
This commit is contained in:
co-authored by
Paul Marbach
parent
75d12036b8
commit
dc12aeb4ab
@@ -14,6 +14,7 @@ describe('PillCell', () => {
|
|||||||
type: FieldType.string,
|
type: FieldType.string,
|
||||||
values: values,
|
values: values,
|
||||||
config: {},
|
config: {},
|
||||||
|
display: (value: unknown) => ({ text: String(value), color: '#FF780A', numeric: NaN }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const ser = new XMLSerializer();
|
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(
|
||||||
|
<PillCell getTextColorForBackground={getTextColorForBackground} field={field} rowIdx={0} theme={theme} />
|
||||||
|
),
|
||||||
|
`
|
||||||
|
<span style=\"background-color: rgb(207, 250, 255); color: rgb(32, 34, 38);\">value1 lbs</span>
|
||||||
|
<span style=\"background-color: rgb(229, 172, 14); color: rgb(247, 248, 250);\">value2 lbs</span>
|
||||||
|
<span style=\"background-color: rgb(63, 104, 51); color: rgb(247, 248, 250);\">value3 lbs</span>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Color by value mappings', () => {
|
describe('Color by value mappings', () => {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
getColorByStringHash,
|
getColorByStringHash,
|
||||||
FALLBACK_COLOR,
|
FALLBACK_COLOR,
|
||||||
fieldColorModeRegistry,
|
fieldColorModeRegistry,
|
||||||
|
formattedValueToString,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { FieldColorModeId } from '@grafana/schema';
|
import { FieldColorModeId } from '@grafana/schema';
|
||||||
|
|
||||||
@@ -20,10 +21,11 @@ export function PillCell({ rowIdx, field, theme, getTextColorForBackground }: Pi
|
|||||||
const pillValues = inferPills(value);
|
const pillValues = inferPills(value);
|
||||||
return pillValues.length > 0
|
return pillValues.length > 0
|
||||||
? pillValues.map((pill, index) => {
|
? 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);
|
const textColor = getTextColorForBackground(bgColor);
|
||||||
return {
|
return {
|
||||||
value: String(pill),
|
value: renderedValue,
|
||||||
key: `${pill}-${index}`,
|
key: `${pill}-${index}`,
|
||||||
bgColor,
|
bgColor,
|
||||||
color: textColor,
|
color: textColor,
|
||||||
|
|||||||
@@ -438,7 +438,7 @@ export function TableNG(props: TableNGProps) {
|
|||||||
|
|
||||||
// attach JSONCell custom display function to JSONView cell type
|
// attach JSONCell custom display function to JSONView cell type
|
||||||
if (cellType === TableCellDisplayMode.JSONView || field.type === FieldType.other) {
|
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
|
// For some cells, "aligning" the cell will mean aligning the inline contents of the cell with
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import {
|
|||||||
getDisplayName,
|
getDisplayName,
|
||||||
predicateByName,
|
predicateByName,
|
||||||
calculateFooterHeight,
|
calculateFooterHeight,
|
||||||
|
displayJsonValue,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
describe('TableNG utils', () => {
|
describe('TableNG utils', () => {
|
||||||
@@ -1354,10 +1355,35 @@ describe('TableNG utils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('displayJsonValue', () => {
|
describe('displayJsonValue', () => {
|
||||||
it.todo('should parse and then stringify string values');
|
let field: Field;
|
||||||
it.todo('should not throw for non-serializable string values');
|
beforeEach(() => {
|
||||||
it.todo('should stringify non-string values');
|
field = {
|
||||||
it.todo('should not throw for non-serializable non-string values');
|
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', () => {
|
describe('applySort', () => {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
DisplayValueAlignmentFactors,
|
DisplayValueAlignmentFactors,
|
||||||
DataFrame,
|
DataFrame,
|
||||||
DisplayProcessor,
|
DisplayProcessor,
|
||||||
|
DecimalCount,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import {
|
import {
|
||||||
BarGaugeDisplayMode,
|
BarGaugeDisplayMode,
|
||||||
@@ -970,28 +971,24 @@ export function canFieldBeColorized(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const displayJsonValue: DisplayProcessor = (value: unknown): DisplayValue => {
|
export const displayJsonValue: (field: Field) => DisplayProcessor = (field: Field, decimals?: DecimalCount) => {
|
||||||
let displayValue: string;
|
const origDisplay = field.display!;
|
||||||
|
return (value: unknown): DisplayValue => {
|
||||||
|
let jsonText: string;
|
||||||
|
|
||||||
// Handle string values that might be JSON
|
const displayValue = origDisplay(value, decimals);
|
||||||
if (typeof value === 'string') {
|
const formattedValue = formattedValueToString(displayValue);
|
||||||
|
|
||||||
|
// Handle string values that might be JSON
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(value);
|
const parsed = JSON.parse(formattedValue);
|
||||||
displayValue = JSON.stringify(parsed, null, ' ');
|
jsonText = JSON.stringify(parsed, null, ' ');
|
||||||
} catch {
|
} 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 {
|
export function getSummaryCellTextAlign(textAlign: TextAlign, cellType: TableCellDisplayMode): TextAlign {
|
||||||
|
|||||||
Reference in New Issue
Block a user