From 7cabf42ecc78017a4a9f4804249fa4ae2067171e Mon Sep 17 00:00:00 2001 From: htranho <34069728+htranho@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:53:01 -0500 Subject: [PATCH 1/4] MMT-3960: Resetting millisecond part only when date time is selected. --- .../CustomDateTimeWidget.jsx | 20 +++++++++++++++---- .../__tests__/CustomDateTimeWidget.test.jsx | 14 ++++++------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/static/src/js/components/CustomDateTimeWidget/CustomDateTimeWidget.jsx b/static/src/js/components/CustomDateTimeWidget/CustomDateTimeWidget.jsx index f406a762c..b45f949a4 100644 --- a/static/src/js/components/CustomDateTimeWidget/CustomDateTimeWidget.jsx +++ b/static/src/js/components/CustomDateTimeWidget/CustomDateTimeWidget.jsx @@ -46,6 +46,10 @@ const CustomDateTimeWidget = ({ }) => { const [showCalender, setShowCalender] = useState(false) const datetimeScrollRef = useRef(null) + // Variable to flag method of input: selected or typed. + // We can't store as state variable because it gets updated + // only after some delay. + let dateInputMethod = 'selected' const { description } = schema @@ -89,14 +93,21 @@ const CustomDateTimeWidget = ({ } const handleChange = (newDate) => { - // The picket widget has a bug where it is not setting millis to 0 when selecting a time. - newDate.setMilliseconds(0) + // The picket widget has a bug where it is not setting millis to 0 when selecting a time, + // so we need to reset the millisecond part. When the date time string is pasted in or a value + // exists, do not reset. + if (!value && dateInputMethod !== 'typed') newDate.setMilliseconds(0) const formattedDateTime = fromZonedTime(newDate, 'GMT').toISOString() onChange(formattedDateTime) handleBlur() } + const handleDateInput = (event) => { + // Saves the input method for handleChange + dateInputMethod = 'typed' + } + return ( handleDateInput(event)} onFocus={handleFocus} open={showCalender} peekNextMonth - placeholderText="YYYY-MM-DDTHH:MM:SSZ" + placeholderText="YYYY-MM-DDTHH:MM:SS.SSSZ" wrapperClassName="d-block" selected={fieldValue} showMonthDropdown diff --git a/static/src/js/components/CustomDateTimeWidget/__tests__/CustomDateTimeWidget.test.jsx b/static/src/js/components/CustomDateTimeWidget/__tests__/CustomDateTimeWidget.test.jsx index 1cc2eb2ed..eb58f737a 100644 --- a/static/src/js/components/CustomDateTimeWidget/__tests__/CustomDateTimeWidget.test.jsx +++ b/static/src/js/components/CustomDateTimeWidget/__tests__/CustomDateTimeWidget.test.jsx @@ -77,7 +77,7 @@ describe('CustomDateTimeWidget', () => { test('shows the field description', async () => { setup() - const field = await screen.findByPlaceholderText('YYYY-MM-DDTHH:MM:SSZ') + const field = await screen.findByPlaceholderText('YYYY-MM-DDTHH:MM:SS.SSSZ') await act(async () => { field.focus() @@ -96,7 +96,7 @@ describe('CustomDateTimeWidget', () => { test('blurs the field', async () => { const { props } = setup() - const field = await screen.findByPlaceholderText('YYYY-MM-DDTHH:MM:SSZ') + const field = await screen.findByPlaceholderText('YYYY-MM-DDTHH:MM:SS.SSSZ') await act(async () => { field.focus() @@ -115,7 +115,7 @@ describe('CustomDateTimeWidget', () => { test('calls onChange', async () => { const { props, user } = setup() - const field = await screen.findByPlaceholderText('YYYY-MM-DDTHH:MM:SSZ') + const field = await screen.findByPlaceholderText('YYYY-MM-DDTHH:MM:SS.SSSZ') await user.type(field, '1') @@ -165,9 +165,9 @@ describe('CustomDateTimeWidget', () => { value: '2023-12-05T00:00:00.000Z' }) - const field = await screen.findByPlaceholderText('YYYY-MM-DDTHH:MM:SSZ') + const field = await screen.findByPlaceholderText('YYYY-MM-DDTHH:MM:SS.SSSZ') - expect(field).toHaveValue('2023-12-05T00:00:00Z') + expect(field).toHaveValue('2023-12-05T00:00:00.000Z') }) }) @@ -177,9 +177,9 @@ describe('CustomDateTimeWidget', () => { value: '2023-12-05T16:05:59.000Z' }) - const field = await screen.findByPlaceholderText('YYYY-MM-DDTHH:MM:SSZ') + const field = await screen.findByPlaceholderText('YYYY-MM-DDTHH:MM:SS.SSSZ') - expect(field).toHaveValue('2023-12-05T16:05:59Z') + expect(field).toHaveValue('2023-12-05T16:05:59.000Z') }) }) }) From b76c05cd594b1fae31766b52eb637574b36f3d21 Mon Sep 17 00:00:00 2001 From: htranho <34069728+htranho@users.noreply.github.com> Date: Thu, 2 Jan 2025 15:41:21 -0500 Subject: [PATCH 2/4] MMT-3960: Add input test --- .../__tests__/CustomDateTimeWidget.test.jsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/static/src/js/components/CustomDateTimeWidget/__tests__/CustomDateTimeWidget.test.jsx b/static/src/js/components/CustomDateTimeWidget/__tests__/CustomDateTimeWidget.test.jsx index eb58f737a..c48a1af54 100644 --- a/static/src/js/components/CustomDateTimeWidget/__tests__/CustomDateTimeWidget.test.jsx +++ b/static/src/js/components/CustomDateTimeWidget/__tests__/CustomDateTimeWidget.test.jsx @@ -174,12 +174,24 @@ describe('CustomDateTimeWidget', () => { describe('When a date has a specific time in the form', () => { test('shows the date and time', async () => { setup({ - value: '2023-12-05T16:05:59.000Z' + value: '2023-12-05T16:05:59.090Z' }) const field = await screen.findByPlaceholderText('YYYY-MM-DDTHH:MM:SS.SSSZ') - expect(field).toHaveValue('2023-12-05T16:05:59.000Z') + expect(field).toHaveValue('2023-12-05T16:05:59.090Z') + }) + }) + + describe('when the field is typed in', () => { + test('calls onChange', async () => { + const { props, user } = setup() + + const field = await screen.findByPlaceholderText('YYYY-MM-DDTHH:MM:SS.SSSZ') + + await user.type(field, '2025-01-02T03:20:15.999Z') + + expect(props.onChange).toHaveBeenCalledWith('2025-01-02T03:20:15.999Z') }) }) }) From 78ce695503f21f781970c4f5ca4c9245b1d2c8b5 Mon Sep 17 00:00:00 2001 From: htranho <34069728+htranho@users.noreply.github.com> Date: Fri, 3 Jan 2025 13:53:06 -0500 Subject: [PATCH 3/4] MMT-3960: Update comment --- .../CustomDateTimeWidget/CustomDateTimeWidget.jsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/static/src/js/components/CustomDateTimeWidget/CustomDateTimeWidget.jsx b/static/src/js/components/CustomDateTimeWidget/CustomDateTimeWidget.jsx index b45f949a4..b862fffd6 100644 --- a/static/src/js/components/CustomDateTimeWidget/CustomDateTimeWidget.jsx +++ b/static/src/js/components/CustomDateTimeWidget/CustomDateTimeWidget.jsx @@ -93,9 +93,10 @@ const CustomDateTimeWidget = ({ } const handleChange = (newDate) => { - // The picket widget has a bug where it is not setting millis to 0 when selecting a time, - // so we need to reset the millisecond part. When the date time string is pasted in or a value - // exists, do not reset. + // The picker widget has a bug where it is not setting the ms. to 0 when a user selects a time. + // We are working around this bug by setting the ms to 0 only if the user selects/chooses + // a time from the widget. If they type in a date/time or paste in a date/time should , + // we should not perform this reset as we should keep whatever ms they user enters. if (!value && dateInputMethod !== 'typed') newDate.setMilliseconds(0) const formattedDateTime = fromZonedTime(newDate, 'GMT').toISOString() onChange(formattedDateTime) From ee0ed1ed5ff208a878d16f2013012622ffd99fbc Mon Sep 17 00:00:00 2001 From: htranho <34069728+htranho@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:02:03 -0500 Subject: [PATCH 4/4] MMT-3960: Fix lint error --- .../components/CustomDateTimeWidget/CustomDateTimeWidget.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/src/js/components/CustomDateTimeWidget/CustomDateTimeWidget.jsx b/static/src/js/components/CustomDateTimeWidget/CustomDateTimeWidget.jsx index b862fffd6..ac595ee95 100644 --- a/static/src/js/components/CustomDateTimeWidget/CustomDateTimeWidget.jsx +++ b/static/src/js/components/CustomDateTimeWidget/CustomDateTimeWidget.jsx @@ -104,7 +104,7 @@ const CustomDateTimeWidget = ({ handleBlur() } - const handleDateInput = (event) => { + const handleDateInput = () => { // Saves the input method for handleChange dateInputMethod = 'typed' } @@ -127,7 +127,7 @@ const CustomDateTimeWidget = ({ locale="en-GB" // Use the UK locale, located in the Greenwich Mean Time (GMT) zone, onBlur={handleBlur} onChange={handleChange} - onChangeRaw={(event) => handleDateInput(event)} + onChangeRaw={() => handleDateInput()} onFocus={handleFocus} open={showCalender} peekNextMonth