mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
TimePicker: will display the correct dates when selecting range in calendar with tz. (#35829)
* fixing calendar issue. * reset yarn.lock. * added boilerplate for test. * added tests to verify bugfix.
This commit is contained in:
@@ -4,6 +4,7 @@ export const Components = {
|
|||||||
fromField: 'TimePicker from field',
|
fromField: 'TimePicker from field',
|
||||||
toField: 'TimePicker to field',
|
toField: 'TimePicker to field',
|
||||||
applyTimeRange: 'TimePicker submit button',
|
applyTimeRange: 'TimePicker submit button',
|
||||||
|
calendar: 'TimePicker calendar',
|
||||||
},
|
},
|
||||||
DataSource: {
|
DataSource: {
|
||||||
TestData: {
|
TestData: {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Button } from '../../Button';
|
|||||||
import { Icon } from '../../Icon/Icon';
|
import { Icon } from '../../Icon/Icon';
|
||||||
import { Portal } from '../../Portal/Portal';
|
import { Portal } from '../../Portal/Portal';
|
||||||
import { ClickOutsideWrapper } from '../../ClickOutsideWrapper/ClickOutsideWrapper';
|
import { ClickOutsideWrapper } from '../../ClickOutsideWrapper/ClickOutsideWrapper';
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
export const getStyles = stylesFactory((theme: GrafanaTheme2, isReversed = false) => {
|
export const getStyles = stylesFactory((theme: GrafanaTheme2, isReversed = false) => {
|
||||||
return {
|
return {
|
||||||
@@ -208,7 +209,11 @@ export const TimePickerCalendar = memo<Props>((props) => {
|
|||||||
if (isFullscreen) {
|
if (isFullscreen) {
|
||||||
return (
|
return (
|
||||||
<ClickOutsideWrapper onClick={props.onClose}>
|
<ClickOutsideWrapper onClick={props.onClose}>
|
||||||
<div className={styles.container} onClick={stopPropagation}>
|
<div
|
||||||
|
className={styles.container}
|
||||||
|
onClick={stopPropagation}
|
||||||
|
aria-label={selectors.components.TimePicker.calendar}
|
||||||
|
>
|
||||||
<Body {...props} />
|
<Body {...props} />
|
||||||
</div>
|
</div>
|
||||||
</ClickOutsideWrapper>
|
</ClickOutsideWrapper>
|
||||||
@@ -218,7 +223,7 @@ export const TimePickerCalendar = memo<Props>((props) => {
|
|||||||
return (
|
return (
|
||||||
<Portal>
|
<Portal>
|
||||||
<div className={styles.modal} onClick={stopPropagation}>
|
<div className={styles.modal} onClick={stopPropagation}>
|
||||||
<div className={styles.content}>
|
<div className={styles.content} aria-label={selectors.components.TimePicker.calendar}>
|
||||||
<Header {...props} />
|
<Header {...props} />
|
||||||
<Body {...props} />
|
<Body {...props} />
|
||||||
<Footer {...props} />
|
<Footer {...props} />
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { fireEvent, render, RenderResult } from '@testing-library/react';
|
||||||
|
import { dateTimeParse, TimeRange } from '@grafana/data';
|
||||||
|
import { TimeRangeForm } from './TimeRangeForm';
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
|
type TimeRangeFormRenderResult = RenderResult & {
|
||||||
|
getCalendarDayByLabelText(label: string): HTMLButtonElement;
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultTimeRange: TimeRange = {
|
||||||
|
from: dateTimeParse('2021-06-17 00:00:00', { timeZone: 'utc' }),
|
||||||
|
to: dateTimeParse('2021-06-19 23:59:00', { timeZone: 'utc' }),
|
||||||
|
raw: {
|
||||||
|
from: '2021-06-17 00:00:00',
|
||||||
|
to: '2021-06-19 23:59:00',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function setup(initial: TimeRange = defaultTimeRange, timeZone = 'utc'): TimeRangeFormRenderResult {
|
||||||
|
const result = render(<TimeRangeForm isFullscreen={true} value={initial} onApply={() => {}} timeZone={timeZone} />);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
getCalendarDayByLabelText: (label: string) => {
|
||||||
|
const item = result.getByLabelText(label);
|
||||||
|
return item?.parentElement as HTMLButtonElement;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('TimeRangeForm', () => {
|
||||||
|
it('should render form correcty', () => {
|
||||||
|
const { getByLabelText } = setup();
|
||||||
|
const { TimePicker } = selectors.components;
|
||||||
|
|
||||||
|
expect(getByLabelText(TimePicker.applyTimeRange)).toBeInTheDocument();
|
||||||
|
expect(getByLabelText(TimePicker.fromField)).toBeInTheDocument();
|
||||||
|
expect(getByLabelText(TimePicker.toField)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display calendar when clicking the from input field', () => {
|
||||||
|
const { getByLabelText } = setup();
|
||||||
|
const { TimePicker } = selectors.components;
|
||||||
|
|
||||||
|
fireEvent.focus(getByLabelText(TimePicker.fromField));
|
||||||
|
expect(getByLabelText(TimePicker.calendar)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have passed time range entered in form', () => {
|
||||||
|
const { getByLabelText } = setup();
|
||||||
|
const { TimePicker } = selectors.components;
|
||||||
|
|
||||||
|
const fromValue = defaultTimeRange.raw.from as string;
|
||||||
|
const toValue = defaultTimeRange.raw.to as string;
|
||||||
|
|
||||||
|
expect(getByLabelText(TimePicker.fromField)).toHaveValue(fromValue);
|
||||||
|
expect(getByLabelText(TimePicker.toField)).toHaveValue(toValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display calendar when clicking the to input field', () => {
|
||||||
|
const { getByLabelText } = setup();
|
||||||
|
const { TimePicker } = selectors.components;
|
||||||
|
|
||||||
|
fireEvent.focus(getByLabelText(TimePicker.toField));
|
||||||
|
expect(getByLabelText(TimePicker.calendar)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not display calendar without clicking any input field', () => {
|
||||||
|
const { queryByLabelText } = setup();
|
||||||
|
const { TimePicker } = selectors.components;
|
||||||
|
|
||||||
|
expect(queryByLabelText(TimePicker.calendar)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have passed time range selected in calendar', () => {
|
||||||
|
const { getByLabelText, getCalendarDayByLabelText } = setup();
|
||||||
|
const { TimePicker } = selectors.components;
|
||||||
|
|
||||||
|
fireEvent.focus(getByLabelText(TimePicker.toField));
|
||||||
|
const from = getCalendarDayByLabelText('June 17, 2021');
|
||||||
|
const to = getCalendarDayByLabelText('June 19, 2021');
|
||||||
|
|
||||||
|
expect(from).toHaveClass('react-calendar__tile--rangeStart');
|
||||||
|
expect(to).toHaveClass('react-calendar__tile--rangeEnd');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should select correct time range in calendar when having a custom time zone', () => {
|
||||||
|
const { getByLabelText, getCalendarDayByLabelText } = setup(defaultTimeRange, 'Asia/Tokyo');
|
||||||
|
const { TimePicker } = selectors.components;
|
||||||
|
|
||||||
|
fireEvent.focus(getByLabelText(TimePicker.toField));
|
||||||
|
const from = getCalendarDayByLabelText('June 17, 2021');
|
||||||
|
const to = getCalendarDayByLabelText('June 19, 2021');
|
||||||
|
|
||||||
|
expect(from).toHaveClass('react-calendar__tile--rangeStart');
|
||||||
|
expect(to).toHaveClass('react-calendar__tile--rangeEnd');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -117,8 +117,8 @@ export const TimeRangeForm: React.FC<Props> = (props) => {
|
|||||||
<TimePickerCalendar
|
<TimePickerCalendar
|
||||||
isFullscreen={isFullscreen}
|
isFullscreen={isFullscreen}
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
from={dateTimeParse(from.value, { timeZone })}
|
from={dateTimeParse(from.value)}
|
||||||
to={dateTimeParse(to.value, { timeZone })}
|
to={dateTimeParse(to.value)}
|
||||||
onApply={onApply}
|
onApply={onApply}
|
||||||
onClose={() => setOpen(false)}
|
onClose={() => setOpen(false)}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
|||||||
Reference in New Issue
Block a user