Dashboards: Conserve timestamp on time range copy-paste across timezones (#109769)
* fix(timepicker): preserve UTC timestamp on copy-paste across timezones * test: add UTC copy/paste timezone conversion test * Update packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimeRangeContent.test.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Remove duplicate mockClipboard clear * Extract utility functions for formatting and converting time ranges --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot
parent
a746f6e121
commit
be3fa041a5
@@ -505,3 +505,14 @@ export function relativeToTimeRange(relativeTimeRange: RelativeTimeRange, now: D
|
||||
raw: { from, to },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Returns a RawTimeRange that has been converted so that from and to are strings
|
||||
*/
|
||||
export function formatRawTimeRange(range: RawTimeRange): RawTimeRange {
|
||||
return {
|
||||
from: isDateTime(range.from) ? range.from.toISOString() : range.from,
|
||||
to: isDateTime(range.to) ? range.to.toISOString() : range.to,
|
||||
};
|
||||
}
|
||||
|
||||
+34
-1
@@ -1,4 +1,4 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { dateTimeParse, FeatureToggles, systemDateFormats, TimeRange } from '@grafana/data';
|
||||
@@ -66,6 +66,8 @@ function setup(initial: TimeRange = defaultTimeRange, timeZone = 'utc') {
|
||||
describe('TimeRangeForm', () => {
|
||||
let user: ReturnType<typeof userEvent.setup>;
|
||||
beforeEach(() => {
|
||||
mockClipboard.writeText.mockClear();
|
||||
mockClipboard.readText.mockClear();
|
||||
user = userEvent.setup();
|
||||
Object.defineProperty(global.navigator, 'clipboard', {
|
||||
value: mockClipboard,
|
||||
@@ -117,6 +119,37 @@ describe('TimeRangeForm', () => {
|
||||
expect(getByLabelText('To')).toHaveValue('2021-06-19 19:59:00');
|
||||
});
|
||||
|
||||
it('copy in UTC then paste into different timezone should convert times', async () => {
|
||||
const sourceRange: TimeRange = {
|
||||
from: defaultTimeRange.from,
|
||||
to: defaultTimeRange.to,
|
||||
raw: {
|
||||
from: defaultTimeRange.from,
|
||||
to: defaultTimeRange.to,
|
||||
},
|
||||
};
|
||||
|
||||
const source = setup(sourceRange);
|
||||
|
||||
let written = '';
|
||||
mockClipboard.writeText.mockImplementation((text: string) => {
|
||||
written = text;
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
await user.click(within(source.container).getByTestId('data-testid TimePicker copy button'));
|
||||
|
||||
const target = setup(undefined, 'America/New_York');
|
||||
|
||||
mockClipboard.readText.mockResolvedValue(written);
|
||||
|
||||
const targetPasteButton = within(target.container).getByTestId('data-testid TimePicker paste button');
|
||||
await user.click(targetPasteButton);
|
||||
|
||||
expect(within(target.container).getByLabelText('From')).toHaveValue('2021-06-16 20:00:00');
|
||||
expect(within(target.container).getByLabelText('To')).toHaveValue('2021-06-19 19:59:00');
|
||||
});
|
||||
|
||||
describe('when common format are entered', () => {
|
||||
it('parses those dates in the current timezone', async () => {
|
||||
setup();
|
||||
|
||||
+3
-2
@@ -114,8 +114,9 @@ export const TimeRangeContent = (props: Props) => {
|
||||
};
|
||||
|
||||
const onCopy = () => {
|
||||
const raw: RawTimeRange = { from: from.value, to: to.value };
|
||||
navigator.clipboard.writeText(JSON.stringify(raw));
|
||||
const rawSource: RawTimeRange = value.raw;
|
||||
const clipboardPayload = rangeUtil.formatRawTimeRange(rawSource);
|
||||
navigator.clipboard.writeText(JSON.stringify(clipboardPayload));
|
||||
};
|
||||
|
||||
const onPaste = async () => {
|
||||
|
||||
Reference in New Issue
Block a user