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:
Ivana Huckova 2021-12-02 12:07:23 +01:00 committed by GitHub
parent feea6e456c
commit d28e7b6185
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 124 additions and 60 deletions

View File

@ -82,6 +82,7 @@ export const InspectContent: React.FC<Props> = ({
isLoading={isDataLoading}
options={dataOptions}
onOptionsChange={onDataOptionsChange}
timeZone={dashboard.timezone}
/>
)}
<CustomScrollbar autoHeightMin="100%">

View File

@ -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>

View File

@ -162,6 +162,7 @@ export function ExploreGraph({
width={width}
height={height}
onChangeTimeRange={onChangeTime}
timeZone={timeZone}
options={
{
tooltip: { mode: tooltipDisplayMode },

View File

@ -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: [],

View File

@ -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}
/>
),
};

View File

@ -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' },
]);
});
});

View File

@ -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}

View File

@ -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>
`;

View File

@ -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;
},