Commit bba47705 by Hugo Häggmark Committed by GitHub

PanelInspector: Adds a Raw display mode but defaults to Formatted display mode (#27306)

* PanelInspector: Fields with overrides are formatted correct in CSV

* Refactor: adds raw format

* Refactor: changes switch to Formatted values

* Tests: adds tests for applyRawFieldOverrides and getRawDisplayProcessor

* Test: change to utc timeZone

* Refactor: changes after PR comments
parent ae385983
import { getDisplayProcessor } from './displayProcessor'; import { getDisplayProcessor, getRawDisplayProcessor } from './displayProcessor';
import { DisplayProcessor, DisplayValue } from '../types/displayValue'; import { DisplayProcessor, DisplayValue } from '../types/displayValue';
import { MappingType, ValueMapping } from '../types/valueMapping'; import { MappingType, ValueMapping } from '../types/valueMapping';
import { Field, FieldConfig, FieldType, GrafanaTheme, Threshold, ThresholdsMode } from '../types'; import { Field, FieldConfig, FieldType, GrafanaTheme, Threshold, ThresholdsMode } from '../types';
...@@ -326,3 +326,27 @@ describe('Date display options', () => { ...@@ -326,3 +326,27 @@ describe('Date display options', () => {
}); });
}); });
}); });
describe('getRawDisplayProcessor', () => {
const processor = getRawDisplayProcessor();
const date = new Date('2020-01-01T00:00:00.000Z');
const timestamp = date.valueOf();
it.each`
value | expected
${0} | ${'0'}
${13.37} | ${'13.37'}
${true} | ${'true'}
${false} | ${'false'}
${date} | ${`${date}`}
${timestamp} | ${'1577836800000'}
${'a string'} | ${'a string'}
${null} | ${'null'}
${undefined} | ${'undefined'}
${{ value: 0, label: 'a label' }} | ${'[object Object]'}
`('when called with value:{$value}', ({ value, expected }) => {
const result = processor(value);
expect(result).toEqual({ text: expected, numeric: null });
});
});
...@@ -4,7 +4,7 @@ import _ from 'lodash'; ...@@ -4,7 +4,7 @@ import _ from 'lodash';
// Types // Types
import { Field, FieldType } from '../types/dataFrame'; import { Field, FieldType } from '../types/dataFrame';
import { GrafanaTheme } from '../types/theme'; import { GrafanaTheme } from '../types/theme';
import { DisplayProcessor, DisplayValue, DecimalCount, DecimalInfo } from '../types/displayValue'; import { DecimalCount, DecimalInfo, DisplayProcessor, DisplayValue } from '../types/displayValue';
import { getValueFormat } from '../valueFormats/valueFormats'; import { getValueFormat } from '../valueFormats/valueFormats';
import { getMappedValue } from '../utils/valueMappings'; import { getMappedValue } from '../utils/valueMappings';
import { dateTime } from '../datetime'; import { dateTime } from '../datetime';
...@@ -166,3 +166,10 @@ export function getDecimalsForValue(value: number, decimalOverride?: DecimalCoun ...@@ -166,3 +166,10 @@ export function getDecimalsForValue(value: number, decimalOverride?: DecimalCoun
return { decimals, scaledDecimals }; return { decimals, scaledDecimals };
} }
export function getRawDisplayProcessor(): DisplayProcessor {
return (value: any) => ({
text: `${value}`,
numeric: (null as unknown) as number,
});
}
import { import {
applyFieldOverrides,
applyRawFieldOverrides,
FieldOverrideEnv, FieldOverrideEnv,
findNumericFieldMinMax, findNumericFieldMinMax,
setFieldConfigDefaults,
setDynamicConfigValue,
applyFieldOverrides,
getLinksSupplier, getLinksSupplier,
setDynamicConfigValue,
setFieldConfigDefaults,
} from './fieldOverrides'; } from './fieldOverrides';
import { MutableDataFrame, toDataFrame } from '../dataframe'; import { MutableDataFrame, toDataFrame } from '../dataframe';
import { import {
DataFrame,
Field,
FieldColorMode,
FieldConfig, FieldConfig,
FieldConfigPropertyItem, FieldConfigPropertyItem,
GrafanaTheme,
FieldType,
DataFrame,
FieldConfigSource, FieldConfigSource,
FieldType,
GrafanaTheme,
InterpolateFunction, InterpolateFunction,
ThresholdsMode,
} from '../types'; } from '../types';
import { Registry } from '../utils'; import { locationUtil, Registry } from '../utils';
import { mockStandardProperties } from '../utils/tests/mockStandardProperties'; import { mockStandardProperties } from '../utils/tests/mockStandardProperties';
import { FieldMatcherID } from '../transformations'; import { FieldMatcherID } from '../transformations';
import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry'; import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry';
import { getFieldDisplayName } from './fieldState'; import { getFieldDisplayName } from './fieldState';
import { locationUtil } from '../utils'; import { ArrayVector } from '../vector';
import { getDisplayProcessor } from './displayProcessor';
const property1: any = { const property1: any = {
id: 'custom.property1', // Match field properties id: 'custom.property1', // Match field properties
...@@ -543,3 +548,191 @@ describe('getLinksSupplier', () => { ...@@ -543,3 +548,191 @@ describe('getLinksSupplier', () => {
); );
}); });
}); });
describe('applyRawFieldOverrides', () => {
const getNumberFieldConfig = () => ({
custom: {},
thresholds: {
mode: ThresholdsMode.Absolute,
steps: [
{
color: 'green',
value: (null as unknown) as number,
},
{
color: 'red',
value: 80,
},
],
},
mappings: [],
color: {
mode: FieldColorMode.Thresholds,
},
min: 0,
max: 1599124316808,
});
const getEmptyConfig = () => ({
custom: {},
mappings: [],
});
const getDisplayValue = (frames: DataFrame[], frameIndex: number, fieldIndex: number) => {
const field = frames[frameIndex].fields[fieldIndex];
const value = field.values.get(0);
return field.display!(value);
};
const expectRawDataDisplayValue = (frames: DataFrame[], frameIndex: number) => {
expect(getDisplayValue(frames, frameIndex, 0)).toEqual({ text: '1599045551050', numeric: null });
expect(getDisplayValue(frames, frameIndex, 1)).toEqual({ text: '3.14159265359', numeric: null });
expect(getDisplayValue(frames, frameIndex, 2)).toEqual({ text: '0', numeric: null });
expect(getDisplayValue(frames, frameIndex, 3)).toEqual({ text: '0', numeric: null });
expect(getDisplayValue(frames, frameIndex, 4)).toEqual({ text: 'A - string', numeric: null });
expect(getDisplayValue(frames, frameIndex, 5)).toEqual({ text: '1599045551050', numeric: null });
};
const expectFormattedDataDisplayValue = (frames: DataFrame[], frameIndex: number) => {
expect(getDisplayValue(frames, frameIndex, 0)).toEqual({
color: '#F2495C',
numeric: 1599045551050,
prefix: undefined,
suffix: undefined,
text: '1599045551050',
threshold: {
color: 'red',
value: 80,
},
});
expect(getDisplayValue(frames, frameIndex, 1)).toEqual({
color: '#73BF69',
numeric: 3.14159265359,
prefix: undefined,
suffix: undefined,
text: '3.142',
threshold: {
color: 'green',
value: null,
},
});
expect(getDisplayValue(frames, frameIndex, 2)).toEqual({
color: '#73BF69',
numeric: 0,
prefix: undefined,
suffix: undefined,
text: '0',
threshold: {
color: 'green',
value: null,
},
});
expect(getDisplayValue(frames, frameIndex, 3)).toEqual({
numeric: 0,
prefix: undefined,
suffix: undefined,
text: '0',
});
expect(getDisplayValue(frames, frameIndex, 4)).toEqual({
numeric: NaN,
prefix: undefined,
suffix: undefined,
text: 'A - string',
});
expect(getDisplayValue(frames, frameIndex, 5)).toEqual({
numeric: 1599045551050,
prefix: undefined,
suffix: undefined,
text: '2020-09-02 11:19:11',
});
};
describe('when called', () => {
it('then all fields should have their display processor replaced with the raw display processor', () => {
const numberAsEpoc: Field = {
name: 'numberAsEpoc',
type: FieldType.number,
values: new ArrayVector([1599045551050]),
config: getNumberFieldConfig(),
};
const numberWithDecimals: Field = {
name: 'numberWithDecimals',
type: FieldType.number,
values: new ArrayVector([3.14159265359]),
config: {
...getNumberFieldConfig(),
decimals: 3,
},
};
const numberAsBoolean: Field = {
name: 'numberAsBoolean',
type: FieldType.number,
values: new ArrayVector([0]),
config: getNumberFieldConfig(),
};
const boolean: Field = {
name: 'boolean',
type: FieldType.boolean,
values: new ArrayVector([0]),
config: getEmptyConfig(),
};
const string: Field = {
name: 'string',
type: FieldType.boolean,
values: new ArrayVector(['A - string']),
config: getEmptyConfig(),
};
const datetime: Field = {
name: 'datetime',
type: FieldType.time,
values: new ArrayVector([1599045551050]),
config: {
unit: 'dateTimeAsIso',
},
};
const dataFrameA: DataFrame = toDataFrame({
fields: [numberAsEpoc, numberWithDecimals, numberAsBoolean, boolean, string, datetime],
});
dataFrameA.fields[0].display = getDisplayProcessor({ field: dataFrameA.fields[0] });
dataFrameA.fields[1].display = getDisplayProcessor({ field: dataFrameA.fields[1] });
dataFrameA.fields[2].display = getDisplayProcessor({ field: dataFrameA.fields[2] });
dataFrameA.fields[3].display = getDisplayProcessor({ field: dataFrameA.fields[3] });
dataFrameA.fields[4].display = getDisplayProcessor({ field: dataFrameA.fields[4] });
dataFrameA.fields[5].display = getDisplayProcessor({ field: dataFrameA.fields[5], timeZone: 'utc' });
const dataFrameB: DataFrame = toDataFrame({
fields: [numberAsEpoc, numberWithDecimals, numberAsBoolean, boolean, string, datetime],
});
dataFrameB.fields[0].display = getDisplayProcessor({ field: dataFrameB.fields[0] });
dataFrameB.fields[1].display = getDisplayProcessor({ field: dataFrameB.fields[1] });
dataFrameB.fields[2].display = getDisplayProcessor({ field: dataFrameB.fields[2] });
dataFrameB.fields[3].display = getDisplayProcessor({ field: dataFrameB.fields[3] });
dataFrameB.fields[4].display = getDisplayProcessor({ field: dataFrameB.fields[4] });
dataFrameB.fields[5].display = getDisplayProcessor({ field: dataFrameB.fields[5], timeZone: 'utc' });
const data = [dataFrameA, dataFrameB];
const rawData = applyRawFieldOverrides(data);
// expect raw data is correct
expectRawDataDisplayValue(rawData, 0);
expectRawDataDisplayValue(rawData, 1);
// expect the original data is still the same
expectFormattedDataDisplayValue(data, 0);
expectFormattedDataDisplayValue(data, 1);
});
});
});
import { import {
DynamicConfigValue, ApplyFieldOverrideOptions,
FieldConfig, ColorScheme,
DataFrame, DataFrame,
DataLink,
DataSourceInstanceSettings,
DynamicConfigValue,
Field, Field,
FieldType,
FieldColorMode, FieldColorMode,
ColorScheme, FieldConfig,
FieldOverrideContext,
ScopedVars,
ApplyFieldOverrideOptions,
FieldConfigPropertyItem, FieldConfigPropertyItem,
LinkModel, FieldOverrideContext,
InterpolateFunction, FieldType,
ValueLinkConfig,
GrafanaTheme, GrafanaTheme,
InterpolateFunction,
LinkModel,
ScopedVars,
TimeZone, TimeZone,
DataLink, ValueLinkConfig,
DataSourceInstanceSettings,
} from '../types'; } from '../types';
import { fieldMatchers, ReducerID, reduceField } from '../transformations'; import { fieldMatchers, reduceField, ReducerID } from '../transformations';
import { FieldMatcher } from '../types/transformations'; import { FieldMatcher } from '../types/transformations';
import isNumber from 'lodash/isNumber'; import isNumber from 'lodash/isNumber';
import set from 'lodash/set'; import set from 'lodash/set';
import unset from 'lodash/unset'; import unset from 'lodash/unset';
import get from 'lodash/get'; import get from 'lodash/get';
import { getDisplayProcessor } from './displayProcessor'; import { getDisplayProcessor, getRawDisplayProcessor } from './displayProcessor';
import { guessFieldTypeForField } from '../dataframe'; import { guessFieldTypeForField } from '../dataframe';
import { standardFieldConfigEditorRegistry } from './standardFieldConfigEditorRegistry'; import { standardFieldConfigEditorRegistry } from './standardFieldConfigEditorRegistry';
import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry'; import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry';
import { DataLinkBuiltInVars, locationUtil } from '../utils'; import { DataLinkBuiltInVars, locationUtil } from '../utils';
import { formattedValueToString } from '../valueFormats'; import { formattedValueToString } from '../valueFormats';
import { getFieldDisplayValuesProxy } from './getFieldDisplayValuesProxy'; import { getFieldDisplayValuesProxy } from './getFieldDisplayValuesProxy';
import { getFrameDisplayName, getFieldDisplayName } from './fieldState'; import { getFieldDisplayName, getFrameDisplayName } from './fieldState';
import { getTimeField } from '../dataframe/processDataFrame'; import { getTimeField } from '../dataframe/processDataFrame';
import { mapInternalLinkToExplore } from '../utils/dataLinks'; import { mapInternalLinkToExplore } from '../utils/dataLinks';
import { getTemplateProxyForField } from './templateProxies'; import { getTemplateProxyForField } from './templateProxies';
...@@ -427,3 +427,34 @@ export const getLinksSupplier = ( ...@@ -427,3 +427,34 @@ export const getLinksSupplier = (
} }
}); });
}; };
/**
* Return a copy of the DataFrame with raw data
*/
export function applyRawFieldOverrides(data: DataFrame[]): DataFrame[] {
if (!data || data.length === 0) {
return [];
}
const newData = [...data];
const processor = getRawDisplayProcessor();
for (let frameIndex = 0; frameIndex < newData.length; frameIndex++) {
const newFrame = { ...newData[frameIndex] };
const newFields = [...newFrame.fields];
for (let fieldIndex = 0; fieldIndex < newFields.length; fieldIndex++) {
newFields[fieldIndex] = {
...newFields[fieldIndex],
display: processor,
};
}
newData[frameIndex] = {
...newFrame,
fields: newFields,
};
}
return newData;
}
...@@ -5,6 +5,6 @@ export * from './standardFieldConfigEditorRegistry'; ...@@ -5,6 +5,6 @@ export * from './standardFieldConfigEditorRegistry';
export * from './overrides/processors'; export * from './overrides/processors';
export { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry'; export { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry';
export { applyFieldOverrides, validateFieldConfig } from './fieldOverrides'; export { applyFieldOverrides, validateFieldConfig, applyRawFieldOverrides } from './fieldOverrides';
export { getFieldDisplayValuesProxy } from './getFieldDisplayValuesProxy'; export { getFieldDisplayValuesProxy } from './getFieldDisplayValuesProxy';
export { getFieldDisplayName, getFrameDisplayName } from './fieldState'; export { getFieldDisplayName, getFrameDisplayName } from './fieldState';
import { readCSV, toCSV, CSVHeaderStyle } from './csv'; import { CSVHeaderStyle, readCSV, toCSV } from './csv';
import { getDataFrameRow } from '../dataframe/processDataFrame'; import { getDataFrameRow, toDataFrameDTO } from '../dataframe/processDataFrame';
// Test with local CSV files // Test with local CSV files
import fs from 'fs'; import fs from 'fs';
import { toDataFrameDTO } from '../dataframe/processDataFrame';
import { MutableDataFrame } from '../dataframe'; import { MutableDataFrame } from '../dataframe';
import { getDisplayProcessor } from '../field';
describe('read csv', () => { describe('read csv', () => {
it('should get X and y', () => { it('should get X and y', () => {
...@@ -115,4 +115,29 @@ describe('DataFrame to CSV', () => { ...@@ -115,4 +115,29 @@ describe('DataFrame to CSV', () => {
" "
`); `);
}); });
it('should use field display processor if exists', () => {
const dataFrame = new MutableDataFrame({
fields: [
{ name: 'Time', values: [1589455688623] },
{
name: 'Value',
values: [1589455688623],
config: {
unit: 'dateTimeAsIso',
},
},
],
});
dataFrame.fields[1].display = getDisplayProcessor({ field: dataFrame.fields[1], timeZone: 'utc' });
const csv = toCSV([dataFrame]);
expect(csv).toMatchInlineSnapshot(`
"\\"Time\\",\\"Value\\"
1589455688623,2020-05-14 11:28:08
"
`);
});
}); });
// Libraries // Libraries
import Papa, { ParseResult, ParseConfig, Parser } from 'papaparse'; import Papa, { ParseConfig, Parser, ParseResult } from 'papaparse';
import defaults from 'lodash/defaults'; import defaults from 'lodash/defaults';
import isNumber from 'lodash/isNumber';
// Types // Types
import { DataFrame, Field, FieldType, FieldConfig } from '../types'; import { DataFrame, Field, FieldConfig, FieldType } from '../types';
import { guessFieldTypeFromValue } from '../dataframe/processDataFrame'; import { guessFieldTypeFromValue } from '../dataframe/processDataFrame';
import { MutableDataFrame } from '../dataframe/MutableDataFrame'; import { MutableDataFrame } from '../dataframe/MutableDataFrame';
import { getFieldDisplayName } from '../field'; import { getFieldDisplayName } from '../field';
import { formattedValueToString } from '../valueFormats';
export enum CSVHeaderStyle { export enum CSVHeaderStyle {
full, full,
...@@ -205,21 +205,11 @@ function writeValue(value: any, config: CSVConfig): string { ...@@ -205,21 +205,11 @@ function writeValue(value: any, config: CSVConfig): string {
} }
function makeFieldWriter(field: Field, config: CSVConfig): FieldWriter { function makeFieldWriter(field: Field, config: CSVConfig): FieldWriter {
if (field.type) { if (field.display) {
if (field.type === FieldType.boolean) { return (value: any) => {
return (value: any) => { const displayValue = field.display!(value);
return value ? 'true' : 'false'; return writeValue(formattedValueToString(displayValue), config);
}; };
}
if (field.type === FieldType.number) {
return (value: any) => {
if (isNumber(value)) {
return value.toString();
}
return writeValue(value, config);
};
}
} }
return (value: any) => writeValue(value, config); return (value: any) => writeValue(value, config);
......
import React, { FC } from 'react'; import React, { FC } from 'react';
import { TableCellProps } from './types';
import { formattedValueToString, LinkModel } from '@grafana/data'; import { formattedValueToString, LinkModel } from '@grafana/data';
import { TableCellProps } from './types';
export const DefaultCell: FC<TableCellProps> = props => { export const DefaultCell: FC<TableCellProps> = props => {
const { field, cell, tableStyles, row } = props; const { field, cell, tableStyles, row } = props;
let link: LinkModel<any> | undefined; let link: LinkModel<any> | undefined;
...@@ -13,33 +14,33 @@ export const DefaultCell: FC<TableCellProps> = props => { ...@@ -13,33 +14,33 @@ export const DefaultCell: FC<TableCellProps> = props => {
valueRowIndex: row.index, valueRowIndex: row.index,
})[0]; })[0];
} }
const value = field.display ? formattedValueToString(displayValue) : displayValue; const value = field.display ? formattedValueToString(displayValue) : `${displayValue}`;
if (!link) {
return <div className={tableStyles.tableCell}>{value}</div>;
}
return ( return (
<div className={tableStyles.tableCell}> <div className={tableStyles.tableCell}>
{link ? ( <a
<a href={link.href}
href={link.href} onClick={
onClick={ link.onClick
link.onClick ? event => {
? event => { // Allow opening in new tab
// Allow opening in new tab if (!(event.ctrlKey || event.metaKey || event.shiftKey) && link!.onClick) {
if (!(event.ctrlKey || event.metaKey || event.shiftKey) && link!.onClick) { event.preventDefault();
event.preventDefault(); link!.onClick(event);
link!.onClick(event);
}
} }
: undefined }
} : undefined
target={link.target} }
title={link.title} target={link.target}
className={tableStyles.tableCellLink} title={link.title}
> className={tableStyles.tableCellLink}
{value} >
</a> {value}
) : ( </a>
value
)}
</div> </div>
); );
}; };
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { import {
applyFieldOverrides, applyFieldOverrides,
applyRawFieldOverrides,
DataFrame, DataFrame,
DataTransformerID, DataTransformerID,
dateTimeFormat, dateTimeFormat,
...@@ -8,13 +9,8 @@ import { ...@@ -8,13 +9,8 @@ import {
SelectableValue, SelectableValue,
toCSV, toCSV,
transformDataFrame, transformDataFrame,
getTimeField,
FieldType,
FormattedVector,
DisplayProcessor,
getDisplayProcessor,
} from '@grafana/data'; } from '@grafana/data';
import { Button, Field, Icon, Switch, Select, Table, VerticalGroup, Container, HorizontalGroup } from '@grafana/ui'; import { Button, Container, Field, HorizontalGroup, Icon, Select, Switch, Table, VerticalGroup } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import AutoSizer from 'react-virtualized-auto-sizer'; import AutoSizer from 'react-virtualized-auto-sizer';
...@@ -60,33 +56,6 @@ export class InspectDataTab extends PureComponent<Props, State> { ...@@ -60,33 +56,6 @@ export class InspectDataTab extends PureComponent<Props, State> {
const { panel } = this.props; const { panel } = this.props;
const { transformId } = this.state; const { transformId } = this.state;
// Replace the time field with a formatted time
const { timeIndex, timeField } = getTimeField(dataFrame);
if (timeField) {
// Use the configured date or standard time display
let processor: DisplayProcessor | undefined = timeField.display;
if (!processor) {
processor = getDisplayProcessor({
field: timeField,
});
}
const formattedDateField = {
...timeField,
type: FieldType.string,
values: new FormattedVector(timeField.values, processor),
};
const fields = [...dataFrame.fields];
fields[timeIndex!] = formattedDateField;
dataFrame = {
...dataFrame,
fields,
};
}
const dataFrameCsv = toCSV([dataFrame]); const dataFrameCsv = toCSV([dataFrame]);
const blob = new Blob([String.fromCharCode(0xfeff), dataFrameCsv], { const blob = new Blob([String.fromCharCode(0xfeff), dataFrameCsv], {
...@@ -146,12 +115,16 @@ export class InspectDataTab extends PureComponent<Props, State> { ...@@ -146,12 +115,16 @@ export class InspectDataTab extends PureComponent<Props, State> {
}); });
} }
if (!options.withFieldConfig) {
return applyRawFieldOverrides(data);
}
// We need to apply field config even though it was already applied in the PanelQueryRunner. // We need to apply field config even though it was already applied in the PanelQueryRunner.
// That's because transformers create new fields and data frames, so i.e. display processor is no longer there // That's because transformers create new fields and data frames, so i.e. display processor is no longer there
return applyFieldOverrides({ return applyFieldOverrides({
data, data,
theme: config.theme, theme: config.theme,
fieldConfig: options.withFieldConfig ? this.props.panel.fieldConfig : { defaults: {}, overrides: [] }, fieldConfig: this.props.panel.fieldConfig,
replaceVariables: (value: string) => { replaceVariables: (value: string) => {
return value; return value;
}, },
...@@ -185,7 +158,7 @@ export class InspectDataTab extends PureComponent<Props, State> { ...@@ -185,7 +158,7 @@ export class InspectDataTab extends PureComponent<Props, State> {
} }
if (options.withFieldConfig) { if (options.withFieldConfig) {
activeString += 'field configuration'; activeString += 'formatted data';
} }
} }
...@@ -261,8 +234,8 @@ export class InspectDataTab extends PureComponent<Props, State> { ...@@ -261,8 +234,8 @@ export class InspectDataTab extends PureComponent<Props, State> {
)} )}
{showFieldConfigsOption && ( {showFieldConfigsOption && (
<Field <Field
label="Apply field configuration" label="Formatted data"
description="Table data is displayed with options defined in the Field and Override tabs." description="Table data is formatted with options defined in the Field and Override tabs."
> >
<Switch <Switch
value={!!options.withFieldConfig} value={!!options.withFieldConfig}
......
...@@ -32,7 +32,7 @@ const PanelInspectorUnconnected: React.FC<Props> = ({ panel, dashboard, defaultT ...@@ -32,7 +32,7 @@ const PanelInspectorUnconnected: React.FC<Props> = ({ panel, dashboard, defaultT
const dispatch = useDispatch(); const dispatch = useDispatch();
const [dataOptions, setDataOptions] = useState<GetDataOptions>({ const [dataOptions, setDataOptions] = useState<GetDataOptions>({
withTransforms: false, withTransforms: false,
withFieldConfig: false, withFieldConfig: true,
}); });
const { data, isLoading, error } = usePanelLatestData(panel, dataOptions); const { data, isLoading, error } = usePanelLatestData(panel, dataOptions);
const metaDs = useDatasourceMetadata(data); const metaDs = useDatasourceMetadata(data);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment