Dashboards: Fix time range bug when use_browser_locale is enabled 2 (#102750)
* revert * make it work on initial load * add integration test; cleanup * fix test * fix
This commit is contained in:
@@ -32,7 +32,7 @@ describe('dateTimeParse', () => {
|
|||||||
|
|
||||||
const date = dateTimeParse('2025-03-12T07:09:37.253Z', { timeZone: 'browser' });
|
const date = dateTimeParse('2025-03-12T07:09:37.253Z', { timeZone: 'browser' });
|
||||||
expect(date.isValid()).toBe(true);
|
expect(date.isValid()).toBe(true);
|
||||||
expect(date.format()).toEqual('2025-03-12T02:09:37-05:00');
|
expect(date.format()).toEqual('2025-03-12T07:09:37Z');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to parse array formats used by calendar', () => {
|
it('should be able to parse array formats used by calendar', () => {
|
||||||
|
|||||||
@@ -53,29 +53,31 @@ export const dateTimeParse: DateTimeParser<DateTimeOptionsWhenParsing> = (value,
|
|||||||
};
|
};
|
||||||
|
|
||||||
const parseString = (value: string, options?: DateTimeOptionsWhenParsing): DateTime => {
|
const parseString = (value: string, options?: DateTimeOptionsWhenParsing): DateTime => {
|
||||||
const parsed = parse(value, options?.roundUp, options?.timeZone, options?.fiscalYearStartMonth);
|
|
||||||
if (value.indexOf('now') !== -1) {
|
if (value.indexOf('now') !== -1) {
|
||||||
if (!isValid(value)) {
|
if (!isValid(value)) {
|
||||||
return dateTime();
|
return dateTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const parsed = parse(value, options?.roundUp, options?.timeZone, options?.fiscalYearStartMonth);
|
||||||
return parsed || dateTime();
|
return parsed || dateTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeZone = getTimeZone(options);
|
let timeZone = getTimeZone(options);
|
||||||
|
let format = options?.format ?? systemDateFormats.fullDate;
|
||||||
|
if (value.endsWith('Z')) {
|
||||||
|
// This is a special case when we have an ISO date string
|
||||||
|
// In this case we want to force the format to be ISO and the timeZone to be UTC
|
||||||
|
// This logic is needed for initial load when parsing the URL params
|
||||||
|
format = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
|
||||||
|
timeZone = 'utc';
|
||||||
|
}
|
||||||
|
|
||||||
const zone = moment.tz.zone(timeZone);
|
const zone = moment.tz.zone(timeZone);
|
||||||
const format = options?.format ?? systemDateFormats.fullDate;
|
|
||||||
|
|
||||||
if (zone && zone.name) {
|
if (zone && zone.name) {
|
||||||
return dateTimeForTimeZone(zone.name, value, format);
|
return dateTimeForTimeZone(zone.name, value, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format === systemDateFormats.fullDate) {
|
|
||||||
// We use parsed here to handle case when `use_browser_locale` is true
|
|
||||||
// We need to pass the parsed value to handle case when value is an ISO 8601 date string
|
|
||||||
return dateTime(parsed, format);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (lowerCase(timeZone)) {
|
switch (lowerCase(timeZone)) {
|
||||||
case 'utc':
|
case 'utc':
|
||||||
return toUtc(value, format);
|
return toUtc(value, format);
|
||||||
|
|||||||
+1
-1
@@ -269,7 +269,7 @@ function valueAsString(value: DateTime | string, timeZone?: TimeZone): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (value.endsWith('Z')) {
|
if (value.endsWith('Z')) {
|
||||||
const dt = dateTimeParse(value, { timeZone: 'utc', format: 'YYYY-MM-DDTHH:mm:ss.SSSZ' });
|
const dt = dateTimeParse(value);
|
||||||
return dateTimeFormat(dt, { timeZone });
|
return dateTimeFormat(dt, { timeZone });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useParams } from 'react-router-dom-v5-compat';
|
|||||||
import { TestProvider } from 'test/helpers/TestProvider';
|
import { TestProvider } from 'test/helpers/TestProvider';
|
||||||
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
|
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
|
||||||
|
|
||||||
import { PanelProps } from '@grafana/data';
|
import { PanelProps, systemDateFormats, SystemDateFormatsState } from '@grafana/data';
|
||||||
import { getPanelPlugin } from '@grafana/data/test';
|
import { getPanelPlugin } from '@grafana/data/test';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import {
|
import {
|
||||||
@@ -229,6 +229,42 @@ describe('DashboardScenePage', () => {
|
|||||||
expect(await screen.findByTitle('Panel B')).toBeInTheDocument();
|
expect(await screen.findByTitle('Panel B')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('absolute time range', () => {
|
||||||
|
it('should render with absolute time range when use_browser_locale is true', async () => {
|
||||||
|
locationService.push('/d/my-dash-uid?from=2025-03-11T07:09:37.253Z&to=2025-03-12T07:09:37.253Z');
|
||||||
|
systemDateFormats.update({
|
||||||
|
fullDate: 'YYYY-MM-DD HH:mm:ss.SSS',
|
||||||
|
interval: {} as SystemDateFormatsState['interval'],
|
||||||
|
useBrowserLocale: true,
|
||||||
|
});
|
||||||
|
setup();
|
||||||
|
|
||||||
|
await waitForDashboardToRenderWithTimeRange({
|
||||||
|
from: '03/11/2025, 02:09:37 AM',
|
||||||
|
to: '03/12/2025, 02:09:37 AM',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render correct time range when use_browser_locale is true and time range is other than default system date format', async () => {
|
||||||
|
locationService.push('/d/my-dash-uid?from=2025-03-11T07:09:37.253Z&to=2025-03-12T07:09:37.253Z');
|
||||||
|
// mocking navigator.languages to return 'de'
|
||||||
|
// this property configured in the browser settings
|
||||||
|
Object.defineProperty(navigator, 'languages', { value: ['de'] });
|
||||||
|
systemDateFormats.update({
|
||||||
|
// left fullDate empty to show that this should be overridden by the browser locale
|
||||||
|
fullDate: '',
|
||||||
|
interval: {} as SystemDateFormatsState['interval'],
|
||||||
|
useBrowserLocale: true,
|
||||||
|
});
|
||||||
|
setup();
|
||||||
|
|
||||||
|
await waitForDashboardToRenderWithTimeRange({
|
||||||
|
from: '11.03.2025, 02:09:37',
|
||||||
|
to: '12.03.2025, 02:09:37',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('empty state', () => {
|
describe('empty state', () => {
|
||||||
it('Shows empty state when dashboard is empty', async () => {
|
it('Shows empty state when dashboard is empty', async () => {
|
||||||
loadDashboardMock.mockResolvedValue({ dashboard: { uid: 'my-dash-uid', panels: [] }, meta: {} });
|
loadDashboardMock.mockResolvedValue({ dashboard: { uid: 'my-dash-uid', panels: [] }, meta: {} });
|
||||||
@@ -373,3 +409,8 @@ async function waitForDashboardToRender() {
|
|||||||
expect(await screen.findByText('Last 6 hours')).toBeInTheDocument();
|
expect(await screen.findByText('Last 6 hours')).toBeInTheDocument();
|
||||||
expect(await screen.findByTitle('Panel A')).toBeInTheDocument();
|
expect(await screen.findByTitle('Panel A')).toBeInTheDocument();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function waitForDashboardToRenderWithTimeRange(timeRange: { from: string; to: string }) {
|
||||||
|
expect(await screen.findByText(`${timeRange.from} to ${timeRange.to}`)).toBeInTheDocument();
|
||||||
|
expect(await screen.findByTitle('Panel A')).toBeInTheDocument();
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user