Add back timezone when not using utc picker

This commit is contained in:
Daniel Lautzenheiser 2023-04-04 22:22:28 -04:00
parent 7413483266
commit 9e22385e8d
5 changed files with 69 additions and 58 deletions

View File

@ -12,7 +12,12 @@ import Field from '@staticcms/core/components/common/field/Field';
import TextField from '@staticcms/core/components/common/text-field/TextField';
import { isNotEmpty } from '@staticcms/core/lib/util/string.util';
import NowButton from './components/NowButton';
import { DEFAULT_DATETIME_FORMAT, DEFAULT_DATE_FORMAT, DEFAULT_TIME_FORMAT } from './constants';
import {
DEFAULT_DATETIME_FORMAT,
DEFAULT_DATE_FORMAT,
DEFAULT_TIMEZONE_FORMAT,
DEFAULT_TIME_FORMAT,
} from './constants';
import { localToUTC } from './utc.util';
import type { TextFieldProps as MuiTextFieldProps } from '@mui/material/TextField';
@ -58,6 +63,11 @@ const DateTimeControl: FC<WidgetControlProps<string, DateTimeField>> = ({
setOpen(false);
}, []);
const timezoneExtra = useMemo(
() => (field.picker_utc ? '' : DEFAULT_TIMEZONE_FORMAT),
[field.picker_utc],
);
const { format, dateFormat, timeFormat } = useMemo(() => {
// dateFormat and timeFormat are strictly for modifying input field with the date/time pickers
const dateFormat: string | boolean = field.date_format ?? true;
@ -68,9 +78,9 @@ const DateTimeControl: FC<WidgetControlProps<string, DateTimeField>> = ({
if (timeFormat === false) {
finalFormat = field.format ?? DEFAULT_DATE_FORMAT;
} else if (dateFormat === false) {
finalFormat = field.format ?? DEFAULT_TIME_FORMAT;
finalFormat = field.format ?? `${DEFAULT_TIME_FORMAT}${timezoneExtra}`;
} else {
finalFormat = field.format ?? DEFAULT_DATETIME_FORMAT;
finalFormat = field.format ?? `${DEFAULT_DATETIME_FORMAT}${timezoneExtra}`;
}
return {
@ -78,7 +88,7 @@ const DateTimeControl: FC<WidgetControlProps<string, DateTimeField>> = ({
dateFormat,
timeFormat,
};
}, [field.date_format, field.format, field.time_format]);
}, [field.date_format, field.format, field.time_format, timezoneExtra]);
const inputFormat = useMemo(() => {
if (typeof dateFormat === 'string' || typeof timeFormat === 'string') {
@ -92,7 +102,7 @@ const DateTimeControl: FC<WidgetControlProps<string, DateTimeField>> = ({
if (typeof timeFormat === 'string' && isNotEmpty(timeFormat)) {
formatParts.push(timeFormat);
} else if (timeFormat !== false) {
formatParts.push(DEFAULT_TIME_FORMAT);
formatParts.push(`${DEFAULT_TIME_FORMAT}${timezoneExtra}`);
}
if (formatParts.length > 0) {
@ -105,11 +115,11 @@ const DateTimeControl: FC<WidgetControlProps<string, DateTimeField>> = ({
}
if (dateFormat === false) {
return format ?? DEFAULT_TIME_FORMAT;
return format ?? `${DEFAULT_TIME_FORMAT}${timezoneExtra}`;
}
return format ?? DEFAULT_DATETIME_FORMAT;
}, [dateFormat, format, timeFormat]);
return format ?? `${DEFAULT_DATETIME_FORMAT}${timezoneExtra}`;
}, [dateFormat, format, timeFormat, timezoneExtra]);
const defaultValue = useMemo(() => {
const today = field.picker_utc ? localToUTC(new Date()) : new Date();

View File

@ -202,7 +202,7 @@ describe(DateTimeControl.name, () => {
const inputWrapper = getByTestId('date-time-input');
const input = inputWrapper.getElementsByTagName('input')[0];
expect(input).toHaveValue('2023-02-12T10:15:35.000');
expect(input).toHaveValue('2023-02-12T10:15:35.000-10:00');
});
it('should use default if provided', () => {
@ -210,39 +210,39 @@ describe(DateTimeControl.name, () => {
label: 'I am a label',
field: {
...mockDateTimeField,
default: '2023-01-10T06:23:15.000',
default: '2023-01-10T06:23:15.000-10:00',
},
});
const inputWrapper = getByTestId('date-time-input');
const input = inputWrapper.getElementsByTagName('input')[0];
expect(input).toHaveValue('2023-01-10T06:23:15.000');
expect(input).toHaveValue('2023-01-10T06:23:15.000-10:00');
});
it('should only use prop value as initial value', async () => {
const { rerender, getByTestId } = renderControl({ value: '2023-02-12T10:15:35.000' });
const { rerender, getByTestId } = renderControl({ value: '2023-02-12T10:15:35.000-10:00' });
const inputWrapper = getByTestId('date-time-input');
const input = inputWrapper.getElementsByTagName('input')[0];
expect(input).toHaveValue('2023-02-12T10:15:35.000');
expect(input).toHaveValue('2023-02-12T10:15:35.000-10:00');
rerender({ value: '2023-02-18T14:37:02.000' });
expect(input).toHaveValue('2023-02-12T10:15:35.000');
rerender({ value: '2023-02-18T14:37:02.000-10:00' });
expect(input).toHaveValue('2023-02-12T10:15:35.000-10:00');
});
it('should use prop value exclusively if field is i18n duplicate', async () => {
const { rerender, getByTestId } = renderControl({
field: { ...mockDateTimeField, i18n: 'duplicate' },
duplicate: true,
value: '2023-02-12T10:15:35.000',
value: '2023-02-12T10:15:35.000-10:00',
});
const inputWrapper = getByTestId('date-time-input');
const input = inputWrapper.getElementsByTagName('input')[0];
expect(input).toHaveValue('2023-02-12T10:15:35.000');
expect(input).toHaveValue('2023-02-12T10:15:35.000-10:00');
rerender({ value: '2023-02-18T14:37:02.000' });
expect(input).toHaveValue('2023-02-18T14:37:02.000');
rerender({ value: '2023-02-18T14:37:02.000-10:00' });
expect(input).toHaveValue('2023-02-18T14:37:02.000-10:00');
});
it('should disable input and now button if disabled', () => {
@ -297,7 +297,7 @@ describe(DateTimeControl.name, () => {
await userEventActions.click(days[0]);
});
expect(onChange).toHaveBeenLastCalledWith('2023-02-01T10:15:35.000');
expect(onChange).toHaveBeenLastCalledWith('2023-02-01T10:15:35.000-10:00');
const hours = document.querySelectorAll('.MuiClockNumber-root');
expect(hours.length).toBe(12);
@ -312,7 +312,7 @@ describe(DateTimeControl.name, () => {
fireEvent.touchEnd(square!, hourClockEvent);
});
expect(onChange).toHaveBeenLastCalledWith('2023-02-01T01:15:35.000');
expect(onChange).toHaveBeenLastCalledWith('2023-02-01T01:15:35.000-10:00');
const minutes = document.querySelectorAll('.MuiClockNumber-root');
expect(minutes.length).toBe(12);
@ -324,7 +324,7 @@ describe(DateTimeControl.name, () => {
fireEvent.touchEnd(square!, minuteClockEvent);
});
expect(onChange).toHaveBeenLastCalledWith('2023-02-01T01:05:35.000');
expect(onChange).toHaveBeenLastCalledWith('2023-02-01T01:05:35.000-10:00');
});
it('should set value to current date and time when now button is clicked', async () => {
@ -337,20 +337,20 @@ describe(DateTimeControl.name, () => {
const inputWrapper = getByTestId('date-time-input');
const input = inputWrapper.getElementsByTagName('input')[0];
expect(input).toHaveValue('2023-02-12T10:15:35.000');
expect(input).toHaveValue('2023-02-12T10:15:35.000-10:00');
await selectDateTime(getByTestId, 1, 2, 20, 'am');
expect(onChange).toHaveBeenLastCalledWith('2023-02-01T02:20:35.000');
expect(input).toHaveValue('2023-02-01T02:20:35.000');
expect(onChange).toHaveBeenLastCalledWith('2023-02-01T02:20:35.000-10:00');
expect(input).toHaveValue('2023-02-01T02:20:35.000-10:00');
await act(async () => {
const nowButton = getByTestId('datetime-now');
await userEventActions.click(nowButton);
});
expect(onChange).toHaveBeenLastCalledWith('2023-02-12T10:15:36.000'); // Testing framework moves the time forward by a second by this point
expect(input).toHaveValue('2023-02-12T10:15:36.000');
expect(onChange).toHaveBeenLastCalledWith('2023-02-12T10:15:36.000-10:00'); // Testing framework moves the time forward by a second by this point
expect(input).toHaveValue('2023-02-12T10:15:36.000-10:00');
});
describe('format', () => {
@ -368,12 +368,12 @@ describe(DateTimeControl.name, () => {
const inputWrapper = getByTestId('date-time-input');
const input = inputWrapper.getElementsByTagName('input')[0];
expect(input).toHaveValue('02/12/2023 10:15:35.000');
expect(input).toHaveValue('02/12/2023 10:15:35.000-10:00');
await selectDateTime(getByTestId, 1, 2, 20, 'am');
expect(onChange).toHaveBeenLastCalledWith('2023-02-01T02:20:35.000');
expect(input).toHaveValue('02/01/2023 02:20:35.000');
expect(onChange).toHaveBeenLastCalledWith('2023-02-01T02:20:35.000-10:00');
expect(input).toHaveValue('02/01/2023 02:20:35.000-10:00');
});
it('uses custom time display format', async () => {
@ -394,7 +394,7 @@ describe(DateTimeControl.name, () => {
await selectDateTime(getByTestId, 1, 2, 20, 'am');
expect(onChange).toHaveBeenLastCalledWith('2023-02-01T02:20:35.000');
expect(onChange).toHaveBeenLastCalledWith('2023-02-01T02:20:35.000-10:00');
expect(input).toHaveValue('2023-02-01 02:20 am');
});
@ -417,7 +417,7 @@ describe(DateTimeControl.name, () => {
await selectDateTime(getByTestId, 1, 2, 20, 'am');
expect(onChange).toHaveBeenLastCalledWith('2023-02-01T02:20:35.000');
expect(onChange).toHaveBeenLastCalledWith('2023-02-01T02:20:35.000-10:00');
expect(input).toHaveValue('02/01/2023 02:20 am');
});
@ -479,7 +479,7 @@ describe(DateTimeControl.name, () => {
const inputWrapper = getByTestId('date-time-input');
const input = inputWrapper.getElementsByTagName('input')[0];
expect(input).toHaveValue('2023-02-12T15:15:35.000');
expect(input).toHaveValue('2023-02-12T20:15:35.000');
});
it('should use default if provided (assuming default is already in UTC)', () => {
@ -741,7 +741,7 @@ describe(DateTimeControl.name, () => {
const inputWrapper = getByTestId('time-input');
const input = inputWrapper.getElementsByTagName('input')[0];
expect(input).toHaveValue('10:15:35.000');
expect(input).toHaveValue('10:15:35.000-10:00');
});
it('should use default if provided', () => {
@ -749,43 +749,43 @@ describe(DateTimeControl.name, () => {
label: 'I am a label',
field: {
...mockTimeField,
default: '06:23:15.000',
default: '06:23:15.000-10:00',
},
});
const inputWrapper = getByTestId('time-input');
const input = inputWrapper.getElementsByTagName('input')[0];
expect(input).toHaveValue('06:23:15.000');
expect(input).toHaveValue('06:23:15.000-10:00');
});
it('should only use prop value as initial value', async () => {
const { rerender, getByTestId } = renderControl({
field: mockTimeField,
value: '10:15:35.000',
value: '10:15:35.000-10:00',
});
const inputWrapper = getByTestId('time-input');
const input = inputWrapper.getElementsByTagName('input')[0];
expect(input).toHaveValue('10:15:35.000');
expect(input).toHaveValue('10:15:35.000-10:00');
rerender({ value: '14:37:02.000' });
expect(input).toHaveValue('10:15:35.000');
rerender({ value: '14:37:02.000-10:00' });
expect(input).toHaveValue('10:15:35.000-10:00');
});
it('should use prop value exclusively if field is i18n duplicate', async () => {
const { rerender, getByTestId } = renderControl({
field: { ...mockTimeField, i18n: 'duplicate' },
duplicate: true,
value: '10:15:35.000',
value: '10:15:35.000-10:00',
});
const inputWrapper = getByTestId('time-input');
const input = inputWrapper.getElementsByTagName('input')[0];
expect(input).toHaveValue('10:15:35.000');
expect(input).toHaveValue('10:15:35.000-10:00');
rerender({ value: '14:37:02.000' });
expect(input).toHaveValue('14:37:02.000');
rerender({ value: '14:37:02.000-10:00' });
expect(input).toHaveValue('14:37:02.000-10:00');
});
it('should disable input and now button if disabled', () => {
@ -850,7 +850,7 @@ describe(DateTimeControl.name, () => {
fireEvent.touchEnd(square!, hourClockEvent);
});
expect(onChange).toHaveBeenLastCalledWith('01:15:35.000');
expect(onChange).toHaveBeenLastCalledWith('01:15:35.000-10:00');
const minutes = document.querySelectorAll('.MuiClockNumber-root');
expect(minutes.length).toBe(12);
@ -862,7 +862,7 @@ describe(DateTimeControl.name, () => {
fireEvent.touchEnd(square!, minuteClockEvent);
});
expect(onChange).toHaveBeenLastCalledWith('01:05:35.000');
expect(onChange).toHaveBeenLastCalledWith('01:05:35.000-10:00');
});
it('should set value to current time when now button is clicked', async () => {
@ -876,20 +876,20 @@ describe(DateTimeControl.name, () => {
const inputWrapper = getByTestId('time-input');
const input = inputWrapper.getElementsByTagName('input')[0];
expect(input).toHaveValue('10:15:35.000');
expect(input).toHaveValue('10:15:35.000-10:00');
await selectTime(getByTestId, 2, 20, 'am');
expect(onChange).toHaveBeenLastCalledWith('02:20:35.000');
expect(input).toHaveValue('02:20:35.000');
expect(onChange).toHaveBeenLastCalledWith('02:20:35.000-10:00');
expect(input).toHaveValue('02:20:35.000-10:00');
await act(async () => {
const nowButton = getByTestId('datetime-now');
await userEventActions.click(nowButton);
});
expect(onChange).toHaveBeenLastCalledWith('10:15:36.000'); // Testing framework moves the time forward by a second by this point
expect(input).toHaveValue('10:15:36.000');
expect(onChange).toHaveBeenLastCalledWith('10:15:36.000-10:00'); // Testing framework moves the time forward by a second by this point
expect(input).toHaveValue('10:15:36.000-10:00');
});
describe('format', () => {
@ -911,7 +911,7 @@ describe(DateTimeControl.name, () => {
await selectTime(getByTestId, 2, 20, 'am');
expect(onChange).toHaveBeenLastCalledWith('02:20:35.000');
expect(onChange).toHaveBeenLastCalledWith('02:20:35.000-10:00');
expect(input).toHaveValue('02:20 am');
});
@ -972,7 +972,7 @@ describe(DateTimeControl.name, () => {
const inputWrapper = getByTestId('time-input');
const input = inputWrapper.getElementsByTagName('input')[0];
expect(input).toHaveValue('15:15:35.000');
expect(input).toHaveValue('20:15:35.000');
});
it('should use default if provided (assuming default is already in UTC)', () => {

View File

@ -1,15 +1,15 @@
import { localToUTC, utcToLocal } from '../utc.util';
describe('utc util', () => {
it('converts local (EST) to UTC', () => {
it('converts local (Hawaii time) to UTC', () => {
expect(localToUTC(new Date(2023, 1, 12, 10, 5, 35)).toString()).toEqual(
'Sun Feb 12 2023 15:05:35 GMT-0500 (Eastern Standard Time)',
'Sun Feb 12 2023 20:05:35 GMT-1000 (Hawaii-Aleutian Standard Time)',
);
});
it('converts UTC to local (EST)', () => {
it('converts UTC to local (Hawaii time)', () => {
expect(utcToLocal(new Date(2023, 1, 12, 15, 5, 35)).toString()).toEqual(
'Sun Feb 12 2023 10:05:35 GMT-0500 (Eastern Standard Time)',
'Sun Feb 12 2023 05:05:35 GMT-1000 (Hawaii-Aleutian Standard Time)',
);
});
});

View File

@ -1,3 +1,4 @@
export const DEFAULT_DATE_FORMAT = 'yyyy-MM-dd';
export const DEFAULT_TIME_FORMAT = 'HH:mm:ss.SSS';
export const DEFAULT_DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS";
export const DEFAULT_TIMEZONE_FORMAT = "XXX";

View File

@ -1,3 +1,3 @@
module.exports = async () => {
process.env.TZ = 'America/New_York';
process.env.TZ = 'US/Hawaii';
};