mirror of
https://github.com/grafana/grafana.git
synced 2024-11-27 03:11:01 -06:00
Explore: Fix timezone not changing for graph and table (#42430)
* Pass timezone to graph in Explore * Fix timezone issues for table * Fix type error * Update public/app/features/explore/TableContainer.test.tsx Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Replace UTC with InternalTimeZones * Update CEST to cest Co-authored-by: Giordano Ricci <me@giordanoricci.com>
This commit is contained in:
parent
feea6e456c
commit
d28e7b6185
@ -82,6 +82,7 @@ export const InspectContent: React.FC<Props> = ({
|
||||
isLoading={isDataLoading}
|
||||
options={dataOptions}
|
||||
onOptionsChange={onDataOptionsChange}
|
||||
timeZone={dashboard.timezone}
|
||||
/>
|
||||
)}
|
||||
<CustomScrollbar autoHeightMin="100%">
|
||||
|
@ -232,13 +232,14 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
}
|
||||
|
||||
renderTablePanel(width: number) {
|
||||
const { exploreId, datasourceInstance } = this.props;
|
||||
const { exploreId, datasourceInstance, timeZone } = this.props;
|
||||
return (
|
||||
<TableContainer
|
||||
ariaLabel={selectors.pages.Explore.General.table}
|
||||
width={width}
|
||||
exploreId={exploreId}
|
||||
onCellFilterAdded={datasourceInstance?.modifyQuery ? this.onCellFilterAdded : undefined}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -303,6 +304,7 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
showLogs,
|
||||
showTrace,
|
||||
showNodeGraph,
|
||||
timeZone,
|
||||
} = this.props;
|
||||
const { openDrawer } = this.state;
|
||||
const styles = getStyles(theme);
|
||||
@ -364,6 +366,7 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
exploreId={exploreId}
|
||||
width={width}
|
||||
onClose={this.toggleShowQueryInspector}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
)}
|
||||
</ErrorBoundaryAlert>
|
||||
|
@ -162,6 +162,7 @@ export function ExploreGraph({
|
||||
width={width}
|
||||
height={height}
|
||||
onChangeTimeRange={onChangeTime}
|
||||
timeZone={timeZone}
|
||||
options={
|
||||
{
|
||||
tooltip: { mode: tooltipDisplayMode },
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { ComponentProps } from 'react';
|
||||
import { Observable } from 'rxjs';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { TimeRange, LoadingState } from '@grafana/data';
|
||||
import { TimeRange, LoadingState, InternalTimeZones } from '@grafana/data';
|
||||
import { ExploreId } from 'app/types';
|
||||
import { ExploreQueryInspector } from './ExploreQueryInspector';
|
||||
|
||||
@ -33,6 +33,7 @@ const setup = (propOverrides = {}) => {
|
||||
width: 100,
|
||||
exploreId: ExploreId.left,
|
||||
onClose: jest.fn(),
|
||||
timeZone: InternalTimeZones.utc,
|
||||
queryResponse: {
|
||||
state: LoadingState.Done,
|
||||
series: [],
|
||||
|
@ -14,13 +14,14 @@ import { InspectErrorTab } from 'app/features/inspector/InspectErrorTab';
|
||||
interface DispatchProps {
|
||||
width: number;
|
||||
exploreId: ExploreId;
|
||||
timeZone: TimeZone;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
type Props = DispatchProps & ConnectedProps<typeof connector>;
|
||||
|
||||
export function ExploreQueryInspector(props: Props) {
|
||||
const { loading, width, onClose, queryResponse } = props;
|
||||
const { loading, width, onClose, queryResponse, timeZone } = props;
|
||||
const dataFrames = queryResponse?.series || [];
|
||||
const error = queryResponse?.error;
|
||||
|
||||
@ -47,6 +48,7 @@ export function ExploreQueryInspector(props: Props) {
|
||||
data={dataFrames}
|
||||
isLoading={loading}
|
||||
options={{ withTransforms: false, withFieldConfig: false }}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
@ -1,41 +1,102 @@
|
||||
import React from 'react';
|
||||
import { render, shallow } from 'enzyme';
|
||||
import { screen, render, within } from '@testing-library/react';
|
||||
import { TableContainer } from './TableContainer';
|
||||
import { DataFrame } from '@grafana/data';
|
||||
import { DataFrame, toDataFrame, FieldType, InternalTimeZones } from '@grafana/data';
|
||||
import { ExploreId } from 'app/types/explore';
|
||||
|
||||
function getTable(): HTMLElement {
|
||||
return screen.getAllByRole('table')[0];
|
||||
}
|
||||
|
||||
function getRowsData(rows: HTMLElement[]): Object[] {
|
||||
let content = [];
|
||||
for (let i = 1; i < rows.length; i++) {
|
||||
content.push({
|
||||
time: within(rows[i]).getByText(/2021*/).textContent,
|
||||
text: within(rows[i]).getByText(/test_string_*/).textContent,
|
||||
});
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
const dataFrame = toDataFrame({
|
||||
name: 'A',
|
||||
fields: [
|
||||
{
|
||||
name: 'time',
|
||||
type: FieldType.time,
|
||||
values: [1609459200000, 1609470000000, 1609462800000, 1609466400000],
|
||||
config: {
|
||||
custom: {
|
||||
filterable: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'text',
|
||||
type: FieldType.string,
|
||||
values: ['test_string_1', 'test_string_2', 'test_string_3', 'test_string_4'],
|
||||
config: {
|
||||
custom: {
|
||||
filterable: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const defaultProps = {
|
||||
exploreId: ExploreId.left as ExploreId,
|
||||
loading: false,
|
||||
width: 800,
|
||||
onCellFilterAdded: jest.fn(),
|
||||
tableResult: dataFrame,
|
||||
splitOpen: (() => {}) as any,
|
||||
range: {} as any,
|
||||
timeZone: InternalTimeZones.utc,
|
||||
};
|
||||
|
||||
describe('TableContainer', () => {
|
||||
it('should render component', () => {
|
||||
const props = {
|
||||
exploreId: ExploreId.left as ExploreId,
|
||||
loading: false,
|
||||
width: 800,
|
||||
onCellFilterAdded: jest.fn(),
|
||||
tableResult: {} as DataFrame,
|
||||
splitOpen: (() => {}) as any,
|
||||
range: {} as any,
|
||||
};
|
||||
|
||||
const wrapper = shallow(<TableContainer {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
render(<TableContainer {...defaultProps} />);
|
||||
expect(getTable()).toBeInTheDocument();
|
||||
const rows = within(getTable()).getAllByRole('row');
|
||||
expect(rows).toHaveLength(5);
|
||||
expect(getRowsData(rows)).toEqual([
|
||||
{ time: '2021-01-01 00:00:00', text: 'test_string_1' },
|
||||
{ time: '2021-01-01 03:00:00', text: 'test_string_2' },
|
||||
{ time: '2021-01-01 01:00:00', text: 'test_string_3' },
|
||||
{ time: '2021-01-01 02:00:00', text: 'test_string_4' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should render 0 series returned on no items', () => {
|
||||
const props = {
|
||||
exploreId: ExploreId.left as ExploreId,
|
||||
loading: false,
|
||||
width: 800,
|
||||
onCellFilterAdded: jest.fn(),
|
||||
tableResult: {
|
||||
name: 'TableResultName',
|
||||
fields: [],
|
||||
length: 0,
|
||||
} as DataFrame,
|
||||
splitOpen: (() => {}) as any,
|
||||
range: {} as any,
|
||||
};
|
||||
const emptyFrames = {
|
||||
name: 'TableResultName',
|
||||
fields: [],
|
||||
length: 0,
|
||||
} as DataFrame;
|
||||
render(<TableContainer {...defaultProps} tableResult={emptyFrames} />);
|
||||
expect(screen.getByText('0 series returned')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const wrapper = render(<TableContainer {...props} />);
|
||||
expect(wrapper.find('0 series returned')).toBeTruthy();
|
||||
it('should update time when timezone changes', () => {
|
||||
const { rerender } = render(<TableContainer {...defaultProps} />);
|
||||
const rowsBeforeChange = within(getTable()).getAllByRole('row');
|
||||
expect(getRowsData(rowsBeforeChange)).toEqual([
|
||||
{ time: '2021-01-01 00:00:00', text: 'test_string_1' },
|
||||
{ time: '2021-01-01 03:00:00', text: 'test_string_2' },
|
||||
{ time: '2021-01-01 01:00:00', text: 'test_string_3' },
|
||||
{ time: '2021-01-01 02:00:00', text: 'test_string_4' },
|
||||
]);
|
||||
|
||||
rerender(<TableContainer {...defaultProps} timeZone="cest" />);
|
||||
const rowsAfterChange = within(getTable()).getAllByRole('row');
|
||||
expect(getRowsData(rowsAfterChange)).toEqual([
|
||||
{ time: '2020-12-31 19:00:00', text: 'test_string_1' },
|
||||
{ time: '2020-12-31 22:00:00', text: 'test_string_2' },
|
||||
{ time: '2020-12-31 20:00:00', text: 'test_string_3' },
|
||||
{ time: '2020-12-31 21:00:00', text: 'test_string_4' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
import { ValueLinkConfig } from '@grafana/data';
|
||||
import { ValueLinkConfig, applyFieldOverrides, TimeZone } from '@grafana/data';
|
||||
import { Collapse, Table } from '@grafana/ui';
|
||||
import { ExploreId, ExploreItemState } from 'app/types/explore';
|
||||
import { StoreState } from 'app/types';
|
||||
@ -15,6 +15,7 @@ interface TableContainerProps {
|
||||
ariaLabel?: string;
|
||||
exploreId: ExploreId;
|
||||
width: number;
|
||||
timeZone: TimeZone;
|
||||
onCellFilterAdded?: (filter: FilterItem) => void;
|
||||
}
|
||||
|
||||
@ -48,17 +49,27 @@ export class TableContainer extends PureComponent<Props> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loading, onCellFilterAdded, tableResult, width, splitOpen, range, ariaLabel } = this.props;
|
||||
|
||||
const { loading, onCellFilterAdded, tableResult, width, splitOpen, range, ariaLabel, timeZone } = this.props;
|
||||
const height = this.getTableHeight();
|
||||
const tableWidth = width - config.theme.panelPadding * 2 - PANEL_BORDER;
|
||||
const hasTableResult = tableResult?.length;
|
||||
|
||||
if (tableResult && tableResult.length) {
|
||||
let dataFrame = tableResult;
|
||||
|
||||
if (dataFrame?.length) {
|
||||
dataFrame = applyFieldOverrides({
|
||||
data: [dataFrame],
|
||||
timeZone,
|
||||
theme: config.theme2,
|
||||
replaceVariables: (v: string) => v,
|
||||
fieldConfig: {
|
||||
defaults: {},
|
||||
overrides: [],
|
||||
},
|
||||
})[0];
|
||||
// Bit of code smell here. We need to add links here to the frame modifying the frame on every render.
|
||||
// Should work fine in essence but still not the ideal way to pass props. In logs container we do this
|
||||
// differently and sidestep this getLinks API on a dataframe
|
||||
for (const field of tableResult.fields) {
|
||||
for (const field of dataFrame.fields) {
|
||||
field.getLinks = (config: ValueLinkConfig) => {
|
||||
return getFieldLinksForExplore({ field, rowIndex: config.valueRowIndex!, splitOpenFn: splitOpen, range });
|
||||
};
|
||||
@ -67,10 +78,10 @@ export class TableContainer extends PureComponent<Props> {
|
||||
|
||||
return (
|
||||
<Collapse label="Table" loading={loading} isOpen>
|
||||
{hasTableResult ? (
|
||||
{dataFrame?.length ? (
|
||||
<Table
|
||||
ariaLabel={ariaLabel}
|
||||
data={tableResult!}
|
||||
data={dataFrame}
|
||||
width={tableWidth}
|
||||
height={height}
|
||||
onCellFilterAdded={onCellFilterAdded}
|
||||
|
@ -1,19 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TableContainer should render component 1`] = `
|
||||
<Collapse
|
||||
isOpen={true}
|
||||
label="Table"
|
||||
loading={false}
|
||||
>
|
||||
<Memo(MetaInfoText)
|
||||
metaItems={
|
||||
Array [
|
||||
Object {
|
||||
"value": "0 series returned",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</Collapse>
|
||||
`;
|
@ -12,6 +12,7 @@ import {
|
||||
SelectableValue,
|
||||
toCSV,
|
||||
transformDataFrame,
|
||||
TimeZone,
|
||||
} from '@grafana/data';
|
||||
import { Button, Container, Spinner, Table } from '@grafana/ui';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
@ -30,6 +31,7 @@ import { transformToOTLP } from 'app/plugins/datasource/tempo/resultTransformer'
|
||||
interface Props {
|
||||
isLoading: boolean;
|
||||
options: GetDataOptions;
|
||||
timeZone: TimeZone;
|
||||
data?: DataFrame[];
|
||||
panel?: PanelModel;
|
||||
onOptionsChange?: (options: GetDataOptions) => void;
|
||||
@ -184,7 +186,7 @@ export class InspectDataTab extends PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
getProcessedData(): DataFrame[] {
|
||||
const { options, panel } = this.props;
|
||||
const { options, panel, timeZone } = this.props;
|
||||
const data = this.state.transformedData;
|
||||
|
||||
if (!options.withFieldConfig || !panel) {
|
||||
@ -197,6 +199,7 @@ export class InspectDataTab extends PureComponent<Props, State> {
|
||||
data,
|
||||
theme: config.theme2,
|
||||
fieldConfig: panel.fieldConfig,
|
||||
timeZone,
|
||||
replaceVariables: (value: string) => {
|
||||
return value;
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user