mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DateTimePicker: Add "timeZone" prop (#90031)
* DateTimePicker: Add custom timeZone support * Add test * More tests * Add comment * Tweaks
This commit is contained in:
parent
ff03786909
commit
7c7f09233d
@ -14,7 +14,7 @@ afterAll(() => {
|
||||
return setTimeZoneResolver(() => defaultTimeZone);
|
||||
});
|
||||
|
||||
const renderDatetimePicker = (props?: Props) => {
|
||||
const renderDatetimePicker = (props?: Partial<Props>) => {
|
||||
const combinedProps = Object.assign(
|
||||
{
|
||||
date: dateTimeForTimeZone(getTimeZone(), '2021-05-05 12:00:00'),
|
||||
@ -234,4 +234,20 @@ describe('Date time picker', () => {
|
||||
).not.toBeInTheDocument();
|
||||
}
|
||||
);
|
||||
|
||||
it('should be able to use a custom timeZone', async () => {
|
||||
renderDatetimePicker({
|
||||
timeZone: 'America/New_York',
|
||||
date: dateTimeForTimeZone(getTimeZone({ timeZone: 'utc' }), '2024-07-01 02:00:00'),
|
||||
});
|
||||
|
||||
const dateTimeInput = screen.getByTestId(Components.DateTimePicker.input);
|
||||
expect(dateTimeInput).toHaveDisplayValue('2024-06-30 22:00:00');
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Time picker' }));
|
||||
// Check that calendar date is set correctly
|
||||
expect(screen.getByRole('button', { name: `June 30, 2024` })).toHaveClass('react-calendar__tile--active');
|
||||
// Check that time is set correctly
|
||||
expect(screen.getAllByRole('textbox')[1]).toHaveValue('22:00:00');
|
||||
});
|
||||
});
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
isDateTime,
|
||||
dateTimeForTimeZone,
|
||||
getTimeZone,
|
||||
TimeZone,
|
||||
} from '@grafana/data';
|
||||
import { Components } from '@grafana/e2e-selectors';
|
||||
|
||||
@ -53,6 +54,8 @@ export interface Props {
|
||||
disabledSeconds?: () => number[];
|
||||
/** Can input be cleared/have empty values */
|
||||
clearable?: boolean;
|
||||
/** Custom timezone for the date/time display */
|
||||
timeZone?: TimeZone;
|
||||
}
|
||||
|
||||
export const DateTimePicker = ({
|
||||
@ -64,6 +67,7 @@ export const DateTimePicker = ({
|
||||
disabledHours,
|
||||
disabledMinutes,
|
||||
disabledSeconds,
|
||||
timeZone,
|
||||
showSeconds = true,
|
||||
clearable = false,
|
||||
}: Props) => {
|
||||
@ -136,6 +140,7 @@ export const DateTimePicker = ({
|
||||
ref={refs.setReference}
|
||||
showSeconds={showSeconds}
|
||||
clearable={clearable}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
{isOpen ? (
|
||||
isFullscreen ? (
|
||||
@ -155,6 +160,7 @@ export const DateTimePicker = ({
|
||||
disabledHours={disabledHours}
|
||||
disabledMinutes={disabledMinutes}
|
||||
disabledSeconds={disabledSeconds}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
</div>
|
||||
</FocusScope>
|
||||
@ -176,6 +182,7 @@ export const DateTimePicker = ({
|
||||
disabledHours={disabledHours}
|
||||
disabledMinutes={disabledMinutes}
|
||||
disabledSeconds={disabledSeconds}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -187,21 +194,14 @@ export const DateTimePicker = ({
|
||||
);
|
||||
};
|
||||
|
||||
interface DateTimeCalendarProps {
|
||||
date?: DateTime;
|
||||
interface DateTimeCalendarProps extends Omit<Props, 'label' | 'clearable' | 'onChange'> {
|
||||
onChange: (date: DateTime) => void;
|
||||
onClose: () => void;
|
||||
isFullscreen: boolean;
|
||||
maxDate?: Date;
|
||||
minDate?: Date;
|
||||
style?: React.CSSProperties;
|
||||
showSeconds?: boolean;
|
||||
disabledHours?: () => number[];
|
||||
disabledMinutes?: () => number[];
|
||||
disabledSeconds?: () => number[];
|
||||
}
|
||||
|
||||
type InputProps = Pick<Props, 'onChange' | 'label' | 'date' | 'showSeconds' | 'clearable'> & {
|
||||
type InputProps = Pick<Props, 'onChange' | 'label' | 'date' | 'showSeconds' | 'clearable' | 'timeZone'> & {
|
||||
isFullscreen: boolean;
|
||||
onOpen: (event: FormEvent<HTMLElement>) => void;
|
||||
};
|
||||
@ -212,21 +212,25 @@ type InputState = {
|
||||
};
|
||||
|
||||
const DateTimeInput = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
({ date, label, onChange, onOpen, showSeconds = true, clearable = false }, ref) => {
|
||||
({ date, label, onChange, onOpen, timeZone, showSeconds = true, clearable = false }, ref) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
const format = showSeconds ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD HH:mm';
|
||||
const [internalDate, setInternalDate] = useState<InputState>(() => {
|
||||
return { value: date ? dateTimeFormat(date) : !clearable ? dateTimeFormat(dateTime()) : '', invalid: false };
|
||||
return {
|
||||
value: date ? dateTimeFormat(date, { timeZone }) : !clearable ? dateTimeFormat(dateTime(), { timeZone }) : '',
|
||||
invalid: false,
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (date) {
|
||||
const formattedDate = dateTimeFormat(date, { format, timeZone });
|
||||
setInternalDate({
|
||||
invalid: !isValid(dateTimeFormat(date, { format })),
|
||||
value: isDateTime(date) ? dateTimeFormat(date, { format }) : date,
|
||||
invalid: !isValid(formattedDate),
|
||||
value: isDateTime(date) ? formattedDate : date,
|
||||
});
|
||||
}
|
||||
}, [date, format]);
|
||||
}, [date, format, timeZone]);
|
||||
|
||||
const onChangeDate = useCallback((event: FormEvent<HTMLInputElement>) => {
|
||||
const isInvalid = !isValid(event.currentTarget.value);
|
||||
@ -238,10 +242,10 @@ const DateTimeInput = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
|
||||
const onBlur = useCallback(() => {
|
||||
if (!internalDate.invalid && internalDate.value) {
|
||||
const date = dateTimeForTimeZone(getTimeZone(), internalDate.value);
|
||||
const date = dateTimeForTimeZone(getTimeZone({ timeZone }), internalDate.value);
|
||||
onChange(date);
|
||||
}
|
||||
}, [internalDate, onChange]);
|
||||
}, [internalDate, onChange, timeZone]);
|
||||
|
||||
const clearInternalDate = useCallback(() => {
|
||||
setInternalDate({ value: '', invalid: false });
|
||||
@ -285,6 +289,7 @@ const DateTimeCalendar = React.forwardRef<HTMLDivElement, DateTimeCalendarProps>
|
||||
disabledHours,
|
||||
disabledMinutes,
|
||||
disabledSeconds,
|
||||
timeZone,
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
@ -294,17 +299,17 @@ const DateTimeCalendar = React.forwardRef<HTMLDivElement, DateTimeCalendarProps>
|
||||
// need to keep these 2 separate in state since react-calendar doesn't support different timezones
|
||||
const [timeOfDayDateTime, setTimeOfDayDateTime] = useState(() => {
|
||||
if (date && date.isValid()) {
|
||||
return dateTimeForTimeZone(getTimeZone(), date);
|
||||
return dateTimeForTimeZone(getTimeZone({ timeZone }), date);
|
||||
}
|
||||
|
||||
return dateTimeForTimeZone(getTimeZone(), new Date());
|
||||
return dateTimeForTimeZone(getTimeZone({ timeZone }), new Date());
|
||||
});
|
||||
const [reactCalendarDate, setReactCalendarDate] = useState<Date>(() => {
|
||||
if (date && date.isValid()) {
|
||||
return adjustDateForReactCalendar(date.toDate(), getTimeZone());
|
||||
return adjustDateForReactCalendar(date.toDate(), getTimeZone({ timeZone }));
|
||||
}
|
||||
|
||||
return new Date();
|
||||
return adjustDateForReactCalendar(new Date(), getTimeZone({ timeZone }));
|
||||
});
|
||||
|
||||
const onChangeDate = useCallback<NonNullable<React.ComponentProps<typeof Calendar>['onChange']>>((date) => {
|
||||
|
Loading…
Reference in New Issue
Block a user