Table: Fix cell inspect for Sparkline and inferred JSON cells (#113059)
* Table: Sparkline Cell inspect support * update to better support FieldType.other structures * clean up styling a bit for empty case * fix test import * add test for no x case for sparkline * fix merge mistake * fix test import
This commit is contained in:
@@ -927,11 +927,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"packages/grafana-ui/src/components/Table/TableCellInspector.tsx": {
|
||||
"@typescript-eslint/no-explicit-any": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"packages/grafana-ui/src/components/Table/TableNG/TableNG.test.tsx": {
|
||||
"@typescript-eslint/no-explicit-any": {
|
||||
"count": 2
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { isString } from 'lodash';
|
||||
import { css } from '@emotion/css';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
|
||||
import { useStyles2 } from '../../themes/ThemeContext';
|
||||
import { ClipboardButton } from '../ClipboardButton/ClipboardButton';
|
||||
import { Drawer } from '../Drawer/Drawer';
|
||||
import { Stack } from '../Layout/Stack/Stack';
|
||||
@@ -17,34 +19,15 @@ export enum TableCellInspectorMode {
|
||||
|
||||
interface TableCellInspectorProps {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value: any;
|
||||
value: string;
|
||||
onDismiss: () => void;
|
||||
mode: TableCellInspectorMode;
|
||||
}
|
||||
|
||||
export function TableCellInspector({ value, onDismiss, mode }: TableCellInspectorProps) {
|
||||
let displayValue = value;
|
||||
const [currentMode, setMode] = useState(mode);
|
||||
|
||||
if (isString(value)) {
|
||||
const trimmedValue = value.trim();
|
||||
// Exclude numeric strings like '123' from being displayed in code/JSON mode
|
||||
if (trimmedValue[0] === '{' || trimmedValue[0] === '[' || mode === 'code') {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
displayValue = JSON.stringify(value, null, ' ');
|
||||
} catch (error: any) {
|
||||
// Display helpful error to help folks diagnose json errors
|
||||
console.log(
|
||||
'Failed to parse JSON in Table cell inspector (this will cause JSON to not print nicely): ',
|
||||
error.message
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
displayValue = JSON.stringify(value);
|
||||
}
|
||||
let text = displayValue;
|
||||
const text = value.trim();
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
@@ -81,15 +64,23 @@ export function TableCellInspector({ value, onDismiss, mode }: TableCellInspecto
|
||||
height={500}
|
||||
language="json"
|
||||
showLineNumbers={true}
|
||||
showMiniMap={(text && text.length) > 100}
|
||||
showMiniMap={(text ? text.length : 0) > 100}
|
||||
value={text}
|
||||
readOnly={true}
|
||||
wordWrap={true}
|
||||
/>
|
||||
) : (
|
||||
<pre>{text}</pre>
|
||||
<pre className={styles.textContainer}>{text}</pre>
|
||||
)}
|
||||
</Stack>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: should we have different empty styles?
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
textContainer: css({
|
||||
color: theme.colors.text.secondary,
|
||||
minHeight: 42,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
import { css } from '@emotion/css';
|
||||
import * as React from 'react';
|
||||
|
||||
import {
|
||||
FieldType,
|
||||
FieldConfig,
|
||||
getMinMaxAndDelta,
|
||||
FieldSparkline,
|
||||
isDataFrame,
|
||||
Field,
|
||||
isDataFrameWithValue,
|
||||
} from '@grafana/data';
|
||||
import { FieldConfig, getMinMaxAndDelta, Field, isDataFrameWithValue } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import {
|
||||
BarAlignment,
|
||||
@@ -26,7 +18,7 @@ import { measureText } from '../../../../utils/measureText';
|
||||
import { FormattedValueDisplay } from '../../../FormattedValueDisplay/FormattedValueDisplay';
|
||||
import { Sparkline } from '../../../Sparkline/Sparkline';
|
||||
import { SparklineCellProps, TableCellStyles } from '../types';
|
||||
import { getAlignmentFactor, getCellOptions } from '../utils';
|
||||
import { getAlignmentFactor, getCellOptions, prepareSparklineValue } from '../utils';
|
||||
|
||||
export const defaultSparklineCellConfig: TableSparklineCellOptions = {
|
||||
type: TableCellDisplayMode.Sparkline,
|
||||
@@ -43,7 +35,7 @@ export const defaultSparklineCellConfig: TableSparklineCellOptions = {
|
||||
|
||||
export const SparklineCell = (props: SparklineCellProps) => {
|
||||
const { field, value, theme, timeRange, rowIdx, width } = props;
|
||||
const sparkline = getSparkline(value, field);
|
||||
const sparkline = prepareSparklineValue(value, field);
|
||||
|
||||
if (!sparkline) {
|
||||
return <>{field.config.noValue || t('grafana-ui.table.sparkline.no-data', 'no data')}</>;
|
||||
@@ -102,30 +94,6 @@ export const SparklineCell = (props: SparklineCellProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
function getSparkline(value: unknown, field: Field): FieldSparkline | undefined {
|
||||
if (Array.isArray(value)) {
|
||||
return {
|
||||
y: {
|
||||
name: `${field.name}-sparkline`,
|
||||
type: FieldType.number,
|
||||
values: value,
|
||||
config: {},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (isDataFrame(value)) {
|
||||
const timeField = value.fields.find((x) => x.type === FieldType.time);
|
||||
const numberField = value.fields.find((x) => x.type === FieldType.number);
|
||||
|
||||
if (timeField && numberField) {
|
||||
return { x: timeField, y: numberField };
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function getTableSparklineCellOptions(field: Field): TableSparklineCellOptions {
|
||||
let options = getCellOptions(field);
|
||||
if (options.type === TableCellDisplayMode.Auto) {
|
||||
|
||||
@@ -563,7 +563,6 @@ export function TableNG(props: TableNGProps) {
|
||||
<TableCellActions
|
||||
field={field}
|
||||
value={value}
|
||||
cellOptions={cellOptions}
|
||||
displayName={displayName}
|
||||
cellInspect={cellInspect}
|
||||
showFilters={showFilters}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TableNG utils buildInspectValue should handle NaN 1`] = `
|
||||
[
|
||||
"NaN",
|
||||
"text",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`TableNG utils buildInspectValue should handle array 1`] = `
|
||||
[
|
||||
"[
|
||||
"foo",
|
||||
"bar",
|
||||
"baz"
|
||||
]",
|
||||
"code",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`TableNG utils buildInspectValue should handle boolean 1`] = `
|
||||
[
|
||||
"true",
|
||||
"text",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`TableNG utils buildInspectValue should handle geo 1`] = `
|
||||
[
|
||||
"POINT(0 -0.0006656516255105771)",
|
||||
"code",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`TableNG utils buildInspectValue should handle null 1`] = `
|
||||
[
|
||||
"",
|
||||
"text",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`TableNG utils buildInspectValue should handle null w/ JSON 1`] = `
|
||||
[
|
||||
"null",
|
||||
"code",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`TableNG utils buildInspectValue should handle numbers 1`] = `
|
||||
[
|
||||
"0",
|
||||
"text",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`TableNG utils buildInspectValue should handle object 1`] = `
|
||||
[
|
||||
"{
|
||||
"foo": true,
|
||||
"b": "baz"
|
||||
}",
|
||||
"code",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`TableNG utils buildInspectValue should handle sparkline (no x) 1`] = `
|
||||
[
|
||||
"[
|
||||
2,
|
||||
4,
|
||||
6,
|
||||
8,
|
||||
10
|
||||
]",
|
||||
"code",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`TableNG utils buildInspectValue should handle sparkline 1`] = `
|
||||
[
|
||||
"[
|
||||
[0, 10],
|
||||
[1000, 20],
|
||||
[2000, 30]
|
||||
]",
|
||||
"code",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`TableNG utils buildInspectValue should handle string 1`] = `
|
||||
[
|
||||
"foo",
|
||||
"text",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`TableNG utils buildInspectValue should handle string w/ JSON 1`] = `
|
||||
[
|
||||
""baz"",
|
||||
"code",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`TableNG utils buildInspectValue should handle undefined 1`] = `
|
||||
[
|
||||
"",
|
||||
"text",
|
||||
]
|
||||
`;
|
||||
@@ -1,27 +1,13 @@
|
||||
import WKT from 'ol/format/WKT';
|
||||
import Geometry from 'ol/geom/Geometry';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { FieldType } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
|
||||
import { IconButton } from '../../../IconButton/IconButton';
|
||||
import { TableCellInspectorMode } from '../../TableCellInspector';
|
||||
import { TableCellDisplayMode } from '../../types';
|
||||
import { FILTER_FOR_OPERATOR, FILTER_OUT_OPERATOR, TableCellActionsProps } from '../types';
|
||||
import { buildInspectValue } from '../utils';
|
||||
|
||||
export const TableCellActions = memo(
|
||||
({
|
||||
field,
|
||||
value,
|
||||
cellOptions,
|
||||
displayName,
|
||||
setInspectCell,
|
||||
onCellFilterAdded,
|
||||
className,
|
||||
cellInspect,
|
||||
showFilters,
|
||||
}: TableCellActionsProps) => (
|
||||
({ field, value, setInspectCell, onCellFilterAdded, className, cellInspect, showFilters }: TableCellActionsProps) => (
|
||||
// stopping propagation to prevent clicks within the actions menu from triggering the cell click events
|
||||
// for things like the data links tooltip.
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
|
||||
@@ -31,24 +17,8 @@ export const TableCellActions = memo(
|
||||
name="eye"
|
||||
aria-label={t('grafana-ui.table.cell-inspect-tooltip', 'Inspect value')}
|
||||
onClick={() => {
|
||||
let inspectValue = value;
|
||||
let mode = TableCellInspectorMode.text;
|
||||
|
||||
if (field.type === FieldType.geo && value instanceof Geometry) {
|
||||
inspectValue = new WKT().writeGeometry(value, {
|
||||
featureProjection: 'EPSG:3857',
|
||||
dataProjection: 'EPSG:4326',
|
||||
});
|
||||
mode = TableCellInspectorMode.code;
|
||||
}
|
||||
if (cellOptions.type === TableCellDisplayMode.JSONView) {
|
||||
mode = TableCellInspectorMode.code;
|
||||
}
|
||||
|
||||
setInspectCell({
|
||||
value: String(inspectValue ?? ''),
|
||||
mode,
|
||||
});
|
||||
const [inspectValue, mode] = buildInspectValue(value, field);
|
||||
setInspectCell({ value: inspectValue, mode });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -167,12 +167,12 @@ export type InspectCellProps = {
|
||||
rowIdx?: number;
|
||||
value: string;
|
||||
mode?: TableCellInspectorMode.code | TableCellInspectorMode.text;
|
||||
preformatted?: boolean;
|
||||
};
|
||||
|
||||
export interface TableCellActionsProps {
|
||||
field: Field;
|
||||
value: TableCellValue;
|
||||
cellOptions: TableCellOptions;
|
||||
displayName: string;
|
||||
cellInspect: boolean;
|
||||
showFilters: boolean;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Point } from 'ol/geom';
|
||||
import { SortColumn } from 'react-data-grid';
|
||||
|
||||
import {
|
||||
@@ -49,6 +50,8 @@ import {
|
||||
parseStyleJson,
|
||||
calculateFooterHeight,
|
||||
displayJsonValue,
|
||||
prepareSparklineValue,
|
||||
buildInspectValue,
|
||||
} from './utils';
|
||||
|
||||
describe('TableNG utils', () => {
|
||||
@@ -1531,4 +1534,152 @@ describe('TableNG utils', () => {
|
||||
expect(parseStyleJson('{"notARealStyle": "someValue"}')).toEqual({ notARealStyle: 'someValue' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('prepareSparklineValue', () => {
|
||||
it('should return an array of numbers when given an array of numbers', () => {
|
||||
expect(
|
||||
prepareSparklineValue([1, 2, 3, 4, 5], {
|
||||
name: 'test',
|
||||
type: FieldType.number,
|
||||
values: [1, 2, 3, 4, 5],
|
||||
config: {},
|
||||
})
|
||||
).toEqual({
|
||||
y: {
|
||||
name: `test-sparkline`,
|
||||
type: FieldType.number,
|
||||
values: [1, 2, 3, 4, 5],
|
||||
config: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should parse the x and y values from a dataframe', () => {
|
||||
const frame = createDataFrame({
|
||||
fields: [
|
||||
{ name: 'x', type: FieldType.time, values: [0, 1000, 2000, 3000, 4000] },
|
||||
{ name: 'y', type: FieldType.number, values: [10, 20, 30, 40, 50] },
|
||||
],
|
||||
});
|
||||
expect(
|
||||
prepareSparklineValue(frame, {
|
||||
name: 'test',
|
||||
type: FieldType.frame,
|
||||
values: [frame],
|
||||
config: {},
|
||||
})
|
||||
).toEqual({
|
||||
x: {
|
||||
name: 'x',
|
||||
type: FieldType.time,
|
||||
values: [0, 1000, 2000, 3000, 4000],
|
||||
config: {},
|
||||
},
|
||||
y: {
|
||||
name: 'y',
|
||||
type: FieldType.number,
|
||||
values: [10, 20, 30, 40, 50],
|
||||
config: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should return undefined for non-array and non-dataframe values', () => {
|
||||
expect(
|
||||
prepareSparklineValue('not an array or dataframe', {
|
||||
name: 'test',
|
||||
type: FieldType.string,
|
||||
values: ['a', 'b', 'c'],
|
||||
config: {},
|
||||
})
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildInspectValue', () => {
|
||||
const numberFieldWithNulls: Field = {
|
||||
name: 'numbers-with-nulls',
|
||||
type: FieldType.number,
|
||||
values: [0, 1, 2, null, NaN],
|
||||
config: {},
|
||||
};
|
||||
const stringField: Field = {
|
||||
name: 'string',
|
||||
type: FieldType.string,
|
||||
values: ['foo', 'bar', 'baz', null],
|
||||
config: {},
|
||||
};
|
||||
const jsonStringField: Field = {
|
||||
...stringField,
|
||||
config: { custom: { cellOptions: { type: TableCellDisplayMode.JSONView } } },
|
||||
};
|
||||
const booleanField: Field = {
|
||||
name: 'boolean-field',
|
||||
type: FieldType.boolean,
|
||||
values: [true, false, true],
|
||||
config: {},
|
||||
};
|
||||
const sparklineField: Field = {
|
||||
name: 'sparkline-field',
|
||||
type: FieldType.frame,
|
||||
values: [
|
||||
createDataFrame({
|
||||
fields: [
|
||||
{ name: 'x', type: FieldType.time, values: [0, 1000, 2000] },
|
||||
{ name: 'y', type: FieldType.number, values: [10, 20, 30] },
|
||||
],
|
||||
}),
|
||||
],
|
||||
config: {},
|
||||
};
|
||||
const sparklineFieldNoX: Field = {
|
||||
name: 'sparkline-field-no-x',
|
||||
type: FieldType.other,
|
||||
values: [[2, 4, 6, 8, 10]],
|
||||
config: {
|
||||
custom: { cellOptions: { type: TableCellDisplayMode.Sparkline } },
|
||||
},
|
||||
};
|
||||
const arrayField: Field = {
|
||||
name: 'array-field',
|
||||
type: FieldType.other,
|
||||
values: [
|
||||
['foo', 'bar', 'baz'],
|
||||
['one', 'two', 'three'],
|
||||
],
|
||||
config: {},
|
||||
};
|
||||
const objectField: Field = {
|
||||
name: 'array-field',
|
||||
type: FieldType.other,
|
||||
values: [
|
||||
{ foo: true, b: 'baz' },
|
||||
{ foo: false, b: 'qux' },
|
||||
],
|
||||
config: {},
|
||||
};
|
||||
const geoField: Field = {
|
||||
name: 'geo-field',
|
||||
type: FieldType.geo,
|
||||
values: [new Point([0, -74.1])],
|
||||
config: {},
|
||||
};
|
||||
it.each([
|
||||
{ name: 'numbers', input: { valueIdx: 0, field: numberFieldWithNulls } },
|
||||
{ name: 'string', input: { valueIdx: 0, field: stringField } },
|
||||
{ name: 'string w/ JSON', input: { valueIdx: 2, field: jsonStringField } },
|
||||
{ name: 'boolean', input: { valueIdx: 0, field: booleanField } },
|
||||
{ name: 'NaN', input: { valueIdx: 4, field: numberFieldWithNulls } },
|
||||
{ name: 'null', input: { valueIdx: 3, field: numberFieldWithNulls } },
|
||||
{ name: 'null w/ JSON', input: { valueIdx: 3, field: jsonStringField } },
|
||||
{ name: 'undefined', input: { valueIdx: 6, field: numberFieldWithNulls } },
|
||||
{ name: 'sparkline', input: { valueIdx: 0, field: sparklineField } },
|
||||
{ name: 'sparkline (no x)', input: { valueIdx: 0, field: sparklineFieldNoX } },
|
||||
{ name: 'array', input: { valueIdx: 0, field: arrayField } },
|
||||
{ name: 'object', input: { valueIdx: 0, field: objectField } },
|
||||
{ name: 'geo', input: { valueIdx: 0, field: geoField } },
|
||||
])('should handle $name', ({ input: { field, valueIdx = 0 } }) => {
|
||||
expect(buildInspectValue(field.values[valueIdx], field)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Property } from 'csstype';
|
||||
import memoize from 'micro-memoize';
|
||||
import WKT from 'ol/format/WKT';
|
||||
import Geometry from 'ol/geom/Geometry';
|
||||
import { CSSProperties } from 'react';
|
||||
import { SortColumn } from 'react-data-grid';
|
||||
import tinycolor from 'tinycolor2';
|
||||
@@ -15,6 +17,8 @@ import {
|
||||
DisplayValueAlignmentFactors,
|
||||
DataFrame,
|
||||
DisplayProcessor,
|
||||
isDataFrame,
|
||||
FieldSparkline,
|
||||
DecimalCount,
|
||||
} from '@grafana/data';
|
||||
import {
|
||||
@@ -26,10 +30,11 @@ import {
|
||||
} from '@grafana/schema';
|
||||
|
||||
import { getTextColorForAlphaBackground } from '../../../utils/colors';
|
||||
import { TableCellInspectorMode } from '../TableCellInspector';
|
||||
import { TableCellOptions } from '../types';
|
||||
|
||||
import { inferPills } from './Cells/PillCell';
|
||||
import { AutoCellRenderer, getCellRenderer } from './Cells/renderers';
|
||||
import { AutoCellRenderer, getAutoRendererDisplayMode, getCellRenderer } from './Cells/renderers';
|
||||
import { COLUMN, TABLE } from './constants';
|
||||
import {
|
||||
TableRow,
|
||||
@@ -991,6 +996,75 @@ export const displayJsonValue: (field: Field) => DisplayProcessor = (field: Fiel
|
||||
};
|
||||
};
|
||||
|
||||
export function prepareSparklineValue(value: unknown, field: Field): FieldSparkline | undefined {
|
||||
if (Array.isArray(value)) {
|
||||
return {
|
||||
y: {
|
||||
name: `${field.name}-sparkline`,
|
||||
type: FieldType.number,
|
||||
values: value,
|
||||
config: {},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (isDataFrame(value)) {
|
||||
const timeField = value.fields.find((x) => x.type === FieldType.time);
|
||||
const numberField = value.fields.find((x) => x.type === FieldType.number);
|
||||
|
||||
if (timeField && numberField) {
|
||||
return { x: timeField, y: numberField };
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function isPlainObject(value: unknown): value is object {
|
||||
return typeof value === 'object' && value != null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
export function buildInspectValue(value: unknown, field: Field): [string, TableCellInspectorMode] {
|
||||
const cellOptions = getCellOptions(field);
|
||||
|
||||
let inspectValue: string;
|
||||
let mode = TableCellInspectorMode.text;
|
||||
|
||||
if (field.type === FieldType.geo && value instanceof Geometry) {
|
||||
inspectValue = new WKT().writeGeometry(value, {
|
||||
featureProjection: 'EPSG:3857',
|
||||
dataProjection: 'EPSG:4326',
|
||||
});
|
||||
mode = TableCellInspectorMode.code;
|
||||
} else if (
|
||||
cellOptions.type === TableCellDisplayMode.Sparkline ||
|
||||
getAutoRendererDisplayMode(field) === TableCellDisplayMode.Sparkline
|
||||
) {
|
||||
// rather than JSON.stringify this, manually format it to make the coordinate tuples more legible to the user.
|
||||
const fieldSparkline = prepareSparklineValue(value, field);
|
||||
inspectValue = '[';
|
||||
if (fieldSparkline != null) {
|
||||
// if an x value exists, render as a tuple [x,y], otherwise just y
|
||||
const buildValString: (idx: number) => string =
|
||||
fieldSparkline.x != null
|
||||
? (idx) => `[${fieldSparkline.x!.values[idx] ?? 'null'}, ${fieldSparkline.y.values[idx] ?? 'null'}]`
|
||||
: (idx) => `${fieldSparkline.y.values[idx] ?? 'null'}`;
|
||||
for (let i = 0; i < fieldSparkline.y.values.length; i++) {
|
||||
inspectValue += `\n ${buildValString(i)}${i === fieldSparkline.y.values.length - 1 ? '\n' : ','}`;
|
||||
}
|
||||
}
|
||||
inspectValue += ']';
|
||||
mode = TableCellInspectorMode.code;
|
||||
} else if (cellOptions.type === TableCellDisplayMode.JSONView || Array.isArray(value) || isPlainObject(value)) {
|
||||
inspectValue = JSON.stringify(value, null, ' ');
|
||||
mode = TableCellInspectorMode.code;
|
||||
} else {
|
||||
inspectValue = String(value ?? '');
|
||||
}
|
||||
|
||||
return [inspectValue, mode];
|
||||
}
|
||||
|
||||
export function getSummaryCellTextAlign(textAlign: TextAlign, cellType: TableCellDisplayMode): TextAlign {
|
||||
// gauge is weird. left-aligned gauge has the viz on the left and its numbers on the right, and vice-versa.
|
||||
// if you center-aligned your gauge... ok.
|
||||
|
||||
Reference in New Issue
Block a user