mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Time Range: Using relative time takes timezone into account (#74013)
* account for timezone when using relative times
Co-authored-by: eledobleefe <laura.fernandez@grafana.com>
Co-authored-by: Joao Silva <joao.silva@grafana.com>
Co-authored-by: L-M-K-B <48948963+L-M-K-B@users.noreply.github.com>
Co-authored-by: joshhunt <josh@trtr.co>
Co-authored-by: tskarhed <1438972+tskarhed@users.noreply.github.com>
* keep as it was originally for now
* add e2e test for relative time zone overrides
* empty commit to add coauthors
Co-authored-by: eledobleefe <laura.fernandez@grafana.com>
Co-authored-by: Joao Silva <joao.silva@grafana.com>
Co-authored-by: L-M-K-B <48948963+L-M-K-B@users.noreply.github.com>
Co-authored-by: joshhunt <josh@trtr.co>
Co-authored-by: tskarhed <1438972+tskarhed@users.noreply.github.com>
* fix types
* switch to using table panel in e2e test
* use regex for partial text match
* actually go to the dashboard...
* use include.text
* check for visibility first
* try waiting on backend request to complete
* CI driven development is fun
* make sure we're waiting for both data query calls
* open dashboard instead
* kick drone
* Revert "open dashboard instead"
This reverts commit bab9c77c4d.
* check timezone second
* refactor to avoid detached elements
---------
Co-authored-by: eledobleefe <laura.fernandez@grafana.com>
Co-authored-by: Joao Silva <joao.silva@grafana.com>
Co-authored-by: L-M-K-B <48948963+L-M-K-B@users.noreply.github.com>
Co-authored-by: joshhunt <josh@trtr.co>
Co-authored-by: tskarhed <1438972+tskarhed@users.noreply.github.com>
This commit is contained in:
179
devenv/dev-dashboards/scenarios/relative_time_zone_support.json
Normal file
179
devenv/dev-dashboards/scenarios/relative_time_zone_support.json
Normal file
@@ -0,0 +1,179 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"id": 7271,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "gdev-testdata"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "auto",
|
||||
"cellOptions": {
|
||||
"type": "auto"
|
||||
},
|
||||
"inspect": false
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"cellHeight": "sm",
|
||||
"footer": {
|
||||
"countRows": false,
|
||||
"fields": "",
|
||||
"reducer": [
|
||||
"sum"
|
||||
],
|
||||
"show": false
|
||||
},
|
||||
"showHeader": true
|
||||
},
|
||||
"pluginVersion": "10.2.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "gdev-testdata"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk",
|
||||
"seriesCount": 1
|
||||
}
|
||||
],
|
||||
"title": "Panel in timezone",
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "gdev-testdata"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "auto",
|
||||
"cellOptions": {
|
||||
"type": "auto"
|
||||
},
|
||||
"inspect": false
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 0
|
||||
},
|
||||
"id": 3,
|
||||
"options": {
|
||||
"cellHeight": "sm",
|
||||
"footer": {
|
||||
"countRows": false,
|
||||
"fields": "",
|
||||
"reducer": [
|
||||
"sum"
|
||||
],
|
||||
"show": false
|
||||
},
|
||||
"showHeader": true
|
||||
},
|
||||
"pluginVersion": "10.2.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "gdev-testdata"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk",
|
||||
"seriesCount": 1
|
||||
}
|
||||
],
|
||||
"timeFrom": "now/d",
|
||||
"title": "Panel with relative time override",
|
||||
"type": "table"
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"schemaVersion": 38,
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-3h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "Asia/Tokyo",
|
||||
"title": "Panel Tests - Relative time zone support",
|
||||
"uid": "d41dbaa2-a39e-4536-ab2b-caca52f1a9c8",
|
||||
"version": 7,
|
||||
"weekStart": ""
|
||||
}
|
||||
@@ -548,6 +548,13 @@ local dashboard = grafana.dashboard;
|
||||
id: 0,
|
||||
}
|
||||
},
|
||||
dashboard.new('relative_time_zone_support', import '../dev-dashboards/scenarios/relative_time_zone_support.json') +
|
||||
resource.addMetadata('folder', 'dev-dashboards') +
|
||||
{
|
||||
spec+: {
|
||||
id: 0,
|
||||
}
|
||||
},
|
||||
dashboard.new('reuse', import '../dev-dashboards/transforms/reuse.json') +
|
||||
resource.addMetadata('folder', 'dev-dashboards') +
|
||||
{
|
||||
|
||||
@@ -85,6 +85,127 @@ e2e.scenario({
|
||||
},
|
||||
});
|
||||
|
||||
e2e.scenario({
|
||||
describeName: 'Dashboard time zone support',
|
||||
itName: 'Tests relative timezone support and overrides',
|
||||
addScenarioDataSource: false,
|
||||
addScenarioDashBoard: false,
|
||||
skipScenario: false,
|
||||
scenario: () => {
|
||||
// Open dashboard
|
||||
e2e.flows.openDashboard({
|
||||
uid: 'd41dbaa2-a39e-4536-ab2b-caca52f1a9c8',
|
||||
});
|
||||
|
||||
e2e().intercept('/api/ds/query*').as('dataQuery');
|
||||
|
||||
// Switch to Browser timezone
|
||||
e2e.flows.setTimeRange({
|
||||
from: 'now-6h',
|
||||
to: 'now',
|
||||
zone: 'Browser',
|
||||
});
|
||||
// Need to wait for 2 calls as there's 2 panels
|
||||
e2e().wait(['@dataQuery', '@dataQuery']);
|
||||
|
||||
e2e.components.Panels.Panel.title('Panel with relative time override')
|
||||
.should('be.visible')
|
||||
.within(() => {
|
||||
e2e().contains('[role="row"]', '00:00:00').should('be.visible');
|
||||
});
|
||||
|
||||
// Today so far, still in Browser timezone
|
||||
e2e.flows.setTimeRange({
|
||||
from: 'now/d',
|
||||
to: 'now',
|
||||
});
|
||||
// Need to wait for 2 calls as there's 2 panels
|
||||
e2e().wait(['@dataQuery', '@dataQuery']);
|
||||
|
||||
e2e.components.Panels.Panel.title('Panel with relative time override')
|
||||
.should('be.visible')
|
||||
.within(() => {
|
||||
e2e().contains('[role="row"]', '00:00:00').should('be.visible');
|
||||
});
|
||||
|
||||
e2e.components.Panels.Panel.title('Panel in timezone')
|
||||
.should('be.visible')
|
||||
.within(() => {
|
||||
e2e().contains('[role="row"]', '00:00:00').should('be.visible');
|
||||
});
|
||||
|
||||
// Test Tokyo timezone
|
||||
e2e.flows.setTimeRange({
|
||||
from: 'now-6h',
|
||||
to: 'now',
|
||||
zone: 'Asia/Tokyo',
|
||||
});
|
||||
// Need to wait for 2 calls as there's 2 panels
|
||||
e2e().wait(['@dataQuery', '@dataQuery']);
|
||||
|
||||
e2e.components.Panels.Panel.title('Panel with relative time override')
|
||||
.should('be.visible')
|
||||
.within(() => {
|
||||
e2e().contains('[role="row"]', '00:00:00').should('be.visible');
|
||||
});
|
||||
|
||||
// Today so far, still in Tokyo timezone
|
||||
e2e.flows.setTimeRange({
|
||||
from: 'now/d',
|
||||
to: 'now',
|
||||
});
|
||||
// Need to wait for 2 calls as there's 2 panels
|
||||
e2e().wait(['@dataQuery', '@dataQuery']);
|
||||
|
||||
e2e.components.Panels.Panel.title('Panel with relative time override')
|
||||
.should('be.visible')
|
||||
.within(() => {
|
||||
e2e().contains('[role="row"]', '00:00:00').should('be.visible');
|
||||
});
|
||||
|
||||
e2e.components.Panels.Panel.title('Panel in timezone')
|
||||
.should('be.visible')
|
||||
.within(() => {
|
||||
e2e().contains('[role="row"]', '00:00:00').should('be.visible');
|
||||
});
|
||||
|
||||
// Test LA timezone
|
||||
e2e.flows.setTimeRange({
|
||||
from: 'now-6h',
|
||||
to: 'now',
|
||||
zone: 'America/Los_Angeles',
|
||||
});
|
||||
// Need to wait for 2 calls as there's 2 panels
|
||||
e2e().wait(['@dataQuery', '@dataQuery']);
|
||||
|
||||
e2e.components.Panels.Panel.title('Panel with relative time override')
|
||||
.should('be.visible')
|
||||
.within(() => {
|
||||
e2e().contains('[role="row"]', '00:00:00').should('be.visible');
|
||||
});
|
||||
|
||||
// Today so far, still in LA timezone
|
||||
e2e.flows.setTimeRange({
|
||||
from: 'now/d',
|
||||
to: 'now',
|
||||
});
|
||||
// Need to wait for 2 calls as there's 2 panels
|
||||
e2e().wait(['@dataQuery', '@dataQuery']);
|
||||
|
||||
e2e.components.Panels.Panel.title('Panel with relative time override')
|
||||
.should('be.visible')
|
||||
.within(() => {
|
||||
e2e().contains('[role="row"]', '00:00:00').should('be.visible');
|
||||
});
|
||||
|
||||
e2e.components.Panels.Panel.title('Panel in timezone')
|
||||
.should('be.visible')
|
||||
.within(() => {
|
||||
e2e().contains('[role="row"]', '00:00:00').should('be.visible');
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const isTimeCorrect = (inUtc: string, inTz: string, offset: number): boolean => {
|
||||
if (inUtc === inTz) {
|
||||
// we need to catch issues when timezone isn't changed for some reason like https://github.com/grafana/grafana/issues/35504
|
||||
|
||||
@@ -130,6 +130,20 @@ export const dateTimeForTimeZone = (
|
||||
return toUtc(input, formatInput);
|
||||
}
|
||||
|
||||
if (timezone && timezone !== 'browser') {
|
||||
let result: moment.Moment;
|
||||
|
||||
if (typeof input === 'string' && formatInput) {
|
||||
result = moment.tz(input, formatInput, timezone);
|
||||
} else {
|
||||
result = moment.tz(input, timezone);
|
||||
}
|
||||
|
||||
if (isDateTime(result)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return dateTime(input, formatInput);
|
||||
};
|
||||
|
||||
|
||||
@@ -112,6 +112,7 @@ export const Components = {
|
||||
Table: {
|
||||
header: 'table header',
|
||||
footer: 'table-footer',
|
||||
body: 'data-testid table body',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -425,4 +426,7 @@ export const Components = {
|
||||
annotationsTypeInput: 'annotations-type-input',
|
||||
annotationsChoosePanelInput: 'choose-panels-input',
|
||||
},
|
||||
Tooltip: {
|
||||
container: 'data-testid tooltip',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ export * from './openPanelMenuItem';
|
||||
export * from './revertAllChanges';
|
||||
export * from './saveDashboard';
|
||||
export * from './selectOption';
|
||||
export * from './setTimeRange';
|
||||
export * from './importDashboard';
|
||||
export * from './importDashboards';
|
||||
export * from './userPreferences';
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
import { VariableSizeList } from 'react-window';
|
||||
|
||||
import { Field, FieldType, ReducerID } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { TableCellHeight } from '@grafana/schema';
|
||||
|
||||
import { useTheme2 } from '../../themes';
|
||||
@@ -330,7 +331,7 @@ export const Table = memo((props: Props) => {
|
||||
<HeaderRow headerGroups={headerGroups} showTypeIcons={showTypeIcons} tableStyles={tableStyles} />
|
||||
)}
|
||||
{itemCount > 0 ? (
|
||||
<div ref={variableSizeListScrollbarRef}>
|
||||
<div data-testid={selectors.components.Panels.Visualization.Table.body} ref={variableSizeListScrollbarRef}>
|
||||
<CustomScrollbar onScroll={handleScroll} hideHorizontalTrack={true}>
|
||||
<VariableSizeList
|
||||
// This component needs an unmount/remount when row height or page changes
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect } from 'react';
|
||||
import { usePopperTooltip } from 'react-popper-tooltip';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { useStyles2 } from '../../themes/ThemeContext';
|
||||
import { buildTooltipTheme } from '../../utils/tooltipUtils';
|
||||
@@ -76,7 +77,11 @@ export const Tooltip = React.forwardRef<HTMLElement, TooltipProps>(
|
||||
})}
|
||||
{visible && (
|
||||
<Portal>
|
||||
<div ref={setTooltipRef} {...getTooltipProps({ className: style.container })}>
|
||||
<div
|
||||
data-testid={selectors.components.Tooltip.container}
|
||||
ref={setTooltipRef}
|
||||
{...getTooltipProps({ className: style.container })}
|
||||
>
|
||||
<div {...getArrowProps({ className: style.arrow })} />
|
||||
{typeof content === 'string' && content}
|
||||
{React.isValidElement(content) && React.cloneElement(content)}
|
||||
|
||||
@@ -129,7 +129,7 @@ describe('TimePickerWithHistory', () => {
|
||||
|
||||
it('Should display handle timezones correctly', async () => {
|
||||
const timeRange = getDefaultTimeRange();
|
||||
render(<TimePickerWithHistory value={timeRange} {...props} {...{ timeZone: 'Eastern/Pacific' }} />);
|
||||
render(<TimePickerWithHistory value={timeRange} {...props} {...{ timeZone: 'Asia/Tokyo' }} />);
|
||||
await userEvent.click(screen.getByLabelText(/Time range selected/));
|
||||
|
||||
await clearAndType(getFromField(), '2022-12-10 00:00:00');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { isString as _isString } from 'lodash';
|
||||
|
||||
import { TimeRange, AppEvents, rangeUtil, dateMath, PanelModel as IPanelModel } from '@grafana/data';
|
||||
import { TimeRange, AppEvents, rangeUtil, dateMath, PanelModel as IPanelModel, dateTimeAsMoment } from '@grafana/data';
|
||||
import { getTemplateSrv } from '@grafana/runtime';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import config from 'app/core/config';
|
||||
@@ -126,11 +126,13 @@ export function applyPanelTimeOverrides(panel: PanelModel, timeRange: TimeRange)
|
||||
}
|
||||
|
||||
if (_isString(timeRange.raw.from)) {
|
||||
const timeFromDate = dateMath.parse(timeFromInfo.from)!;
|
||||
const fromTimezone = dateTimeAsMoment(timeRange.from).tz();
|
||||
const toTimezone = dateTimeAsMoment(timeRange.to).tz();
|
||||
const timeFromDate = dateMath.parse(timeFromInfo.from, undefined, fromTimezone)!;
|
||||
newTimeData.timeInfo = timeFromInfo.display;
|
||||
newTimeData.timeRange = {
|
||||
from: timeFromDate,
|
||||
to: dateMath.parse(timeFromInfo.to)!,
|
||||
to: dateMath.parse(timeFromInfo.to, undefined, toTimezone)!,
|
||||
raw: {
|
||||
from: timeFromInfo.from,
|
||||
to: timeFromInfo.to,
|
||||
|
||||
Reference in New Issue
Block a user