Explore: Format data in Data tab in Query Inspector (#80004)

* Fix betterer

* Improve formatting logic
This commit is contained in:
Haris Rozajac 2024-01-16 07:04:31 -07:00 committed by GitHub
parent e7099eabb3
commit bdbc3f351c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 88 additions and 14 deletions

View File

@ -1,5 +1,6 @@
import { render, screen, fireEvent } from '@testing-library/react';
import React, { ComponentProps } from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
import { Observable } from 'rxjs';
import { LoadingState, InternalTimeZones, getDefaultTimeRange } from '@grafana/data';
@ -10,6 +11,7 @@ type ExploreQueryInspectorProps = ComponentProps<typeof ExploreQueryInspector>;
jest.mock('../inspector/styles', () => ({
getPanelInspectorStyles: () => ({}),
getPanelInspectorStyles2: () => ({}),
}));
jest.mock('app/core/services/backend_srv', () => ({
@ -33,6 +35,15 @@ jest.mock('@grafana/runtime', () => ({
reportInteraction: () => null,
}));
jest.mock('react-virtualized-auto-sizer', () => {
return {
__esModule: true,
default(props: ComponentProps<typeof AutoSizer>) {
return <div>{props.children({ height: 1000, width: 1000 })}</div>;
},
};
});
const setup = (propOverrides = {}) => {
const props: ExploreQueryInspectorProps = {
width: 100,
@ -82,6 +93,54 @@ describe('ExploreQueryInspector', () => {
fireEvent.click(screen.getByText(/expand all/i));
expect(screen.getByText(/very unique test value/i)).toBeInTheDocument();
});
it('should display formatted data', () => {
setup({
queryResponse: {
state: LoadingState.Done,
series: [
{
refId: 'A',
fields: [
{
name: 'time',
type: 'time',
typeInfo: {
frame: 'time.Time',
nullable: true,
},
config: {
interval: 30000,
},
values: [1704285124682, 1704285154682],
entities: {},
},
{
name: 'A-series',
type: 'number',
typeInfo: {
frame: 'float64',
nullable: true,
},
labels: {},
config: {},
values: [71.202732378676928, 72.348839082431916],
entities: {},
},
],
length: 2,
},
],
},
});
fireEvent.click(screen.getByLabelText(/tab data/i));
// assert series values are formatted to 3 digits (xx.x or x.xx)
expect(screen.getByText(/71.2/i)).toBeInTheDocument();
expect(screen.getByText(/72.3/i)).toBeInTheDocument();
// assert timestamps are formatted
expect(screen.getByText(/2024-01-03 12:32:04.682/i)).toBeInTheDocument();
expect(screen.getByText(/2024-01-03 12:32:34.682/i)).toBeInTheDocument();
});
});
const response = (hideFromInspector = false) => ({

View File

@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { CoreApp, LoadingState } from '@grafana/data';
@ -13,6 +13,8 @@ import { InspectStatsTab } from 'app/features/inspector/InspectStatsTab';
import { QueryInspector } from 'app/features/inspector/QueryInspector';
import { StoreState, ExploreItemState } from 'app/types';
import { GetDataOptions } from '../query/state/PanelQueryRunner';
import { runQueries } from './state/query';
interface DispatchProps {
@ -26,6 +28,10 @@ type Props = DispatchProps & ConnectedProps<typeof connector>;
export function ExploreQueryInspector(props: Props) {
const { width, onClose, queryResponse, timeZone } = props;
const [dataOptions, setDataOptions] = useState<GetDataOptions>({
withTransforms: false,
withFieldConfig: true,
});
const dataFrames = queryResponse?.series || [];
let errors = queryResponse?.errors;
if (!errors?.length && queryResponse?.error) {
@ -59,9 +65,11 @@ export function ExploreQueryInspector(props: Props) {
data={dataFrames}
dataName={'Explore'}
isLoading={queryResponse.state === LoadingState.Loading}
options={{ withTransforms: false, withFieldConfig: false }}
options={dataOptions}
timeZone={timeZone}
app={CoreApp.Explore}
formattedDataDescription="Matches the format in the panel"
onOptionsChange={setDataOptions}
/>
),
};

View File

@ -19,6 +19,7 @@ interface Props {
toggleDownloadForExcel: () => void;
data?: DataFrame[];
hasTransformations?: boolean;
formattedDataDescription?: string;
onOptionsChange?: (options: GetDataOptions) => void;
actions?: React.ReactNode;
}
@ -26,6 +27,7 @@ interface Props {
export const InspectDataOptions = ({
options,
actions,
formattedDataDescription,
onOptionsChange,
hasTransformations,
data,
@ -129,10 +131,13 @@ export const InspectDataOptions = ({
{onOptionsChange && (
<Field
label={t('dashboard.inspect-data.formatted-data-label', 'Formatted data')}
description={t(
'dashboard.inspect-data.formatted-data-description',
'Table data is formatted with options defined in the Field and Override tabs.'
)}
description={
formattedDataDescription ||
t(
'dashboard.inspect-data.formatted-data-description',
'Table data is formatted with options defined in the Field and Override tabs.'
)
}
>
<Switch
id="formatted-data-toggle"

View File

@ -37,6 +37,7 @@ interface Props {
panelPluginId?: string;
fieldConfig?: FieldConfigSource;
hasTransformations?: boolean;
formattedDataDescription?: string;
onOptionsChange?: (options: GetDataOptions) => void;
}
@ -176,11 +177,15 @@ export class InspectDataTab extends PureComponent<Props, State> {
const { options, panelPluginId, fieldConfig, timeZone } = this.props;
const data = this.state.transformedData;
if (!options.withFieldConfig || !panelPluginId || !fieldConfig) {
if (!options.withFieldConfig) {
return applyRawFieldOverrides(data);
}
const fieldConfigCleaned = this.cleanTableConfigFromFieldConfig(panelPluginId, fieldConfig);
let fieldConfigCleaned = fieldConfig ?? { defaults: {}, overrides: [] };
// Because we visualize this data in a table we have to remove any custom table display settings
if (panelPluginId === 'table' && fieldConfig) {
fieldConfigCleaned = this.cleanTableConfigFromFieldConfig(fieldConfig);
}
// We need to apply field config as it's not done by PanelQueryRunner (even when withFieldConfig is true).
// It's because transformers create new fields and data frames, and we need to clean field config of any table settings.
@ -194,11 +199,7 @@ export class InspectDataTab extends PureComponent<Props, State> {
}
// Because we visualize this data in a table we have to remove any custom table display settings
cleanTableConfigFromFieldConfig(panelPluginId: string, fieldConfig: FieldConfigSource): FieldConfigSource {
if (panelPluginId !== 'table') {
return fieldConfig;
}
cleanTableConfigFromFieldConfig(fieldConfig: FieldConfigSource): FieldConfigSource {
fieldConfig = cloneDeep(fieldConfig);
// clear all table specific options
fieldConfig.defaults.custom = {};
@ -242,7 +243,7 @@ export class InspectDataTab extends PureComponent<Props, State> {
}
render() {
const { isLoading, options, data, onOptionsChange, hasTransformations } = this.props;
const { isLoading, options, data, formattedDataDescription, onOptionsChange, hasTransformations } = this.props;
const { dataFrameIndex, transformationOptions, selectedDataFrame, downloadForExcel } = this.state;
const styles = getPanelInspectorStyles();
@ -278,6 +279,7 @@ export class InspectDataTab extends PureComponent<Props, State> {
transformationOptions={transformationOptions}
selectedDataFrame={selectedDataFrame}
downloadForExcel={downloadForExcel}
formattedDataDescription={formattedDataDescription}
onOptionsChange={onOptionsChange}
onDataFrameChange={this.onDataFrameChange}
toggleDownloadForExcel={this.onToggleDownloadForExcel}