mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Format data in Data tab in Query Inspector (#80004)
* Fix betterer * Improve formatting logic
This commit is contained in:
parent
e7099eabb3
commit
bdbc3f351c
@ -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) => ({
|
||||
|
@ -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}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
@ -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"
|
||||
|
@ -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}
|
||||
|
Loading…
Reference in New Issue
Block a user