From 3664cdcad31e329b732f19dd9e3d05b00d1ae112 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Wed, 22 Mar 2023 11:45:44 -0500 Subject: [PATCH] Prevent future date selection in jump to date (#10419) You can still type in whatever date you want (native date input behavior) but the UI picker has future dates disabled. Fix https://github.com/vector-im/element-web/issues/20800 --- src/DateUtils.ts | 15 +++++++ .../views/messages/JumpToDatePicker.tsx | 11 ++--- .../views/messages/JumpToDatePicker-test.tsx | 41 +++++++++++++++++++ .../JumpToDatePicker-test.tsx.snap | 41 +++++++++++++++++++ test/utils/DateUtils-test.ts | 10 +++++ 5 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 test/components/views/messages/JumpToDatePicker-test.tsx create mode 100644 test/components/views/messages/__snapshots__/JumpToDatePicker-test.tsx.snap diff --git a/src/DateUtils.ts b/src/DateUtils.ts index a6a8caa946..9116a8047d 100644 --- a/src/DateUtils.ts +++ b/src/DateUtils.ts @@ -102,6 +102,21 @@ export function formatFullDate(date: Date, showTwelveHour = false, showSeconds = }); } +/** + * Formats dates to be compatible with attributes of a ``. Dates + * should be formatted like "2020-06-23" (formatted according to ISO8601) + * + * @param date The date to format. + * @returns The date string in ISO8601 format ready to be used with an `` + */ +export function formatDateForInput(date: Date): string { + const year = `${date.getFullYear()}`.padStart(4, "0"); + const month = `${date.getMonth() + 1}`.padStart(2, "0"); + const day = `${date.getDate()}`.padStart(2, "0"); + const dateInputValue = `${year}-${month}-${day}`; + return dateInputValue; +} + export function formatFullTime(date: Date, showTwelveHour = false): string { if (showTwelveHour) { return twelveHourTime(date, true); diff --git a/src/components/views/messages/JumpToDatePicker.tsx b/src/components/views/messages/JumpToDatePicker.tsx index fa13235809..8bfcebb3ae 100644 --- a/src/components/views/messages/JumpToDatePicker.tsx +++ b/src/components/views/messages/JumpToDatePicker.tsx @@ -19,6 +19,7 @@ import React, { useState, FormEvent } from "react"; import { _t } from "../../../languageHandler"; import Field from "../elements/Field"; import { RovingAccessibleButton, useRovingTabIndex } from "../../../accessibility/RovingTabIndex"; +import { formatDateForInput } from "../../../DateUtils"; interface IProps { ts: number; @@ -27,12 +28,9 @@ interface IProps { const JumpToDatePicker: React.FC = ({ ts, onDatePicked }: IProps) => { const date = new Date(ts); - const year = date.getFullYear(); - const month = `${date.getMonth() + 1}`.padStart(2, "0"); - const day = `${date.getDate()}`.padStart(2, "0"); - const dateDefaultValue = `${year}-${month}-${day}`; + const dateInputDefaultValue = formatDateForInput(date); - const [dateValue, setDateValue] = useState(dateDefaultValue); + const [dateValue, setDateValue] = useState(dateInputDefaultValue); const [onFocus, isActive, ref] = useRovingTabIndex(); const onDateValueInput = (ev: React.ChangeEvent): void => setDateValue(ev.target.value); @@ -49,6 +47,9 @@ const JumpToDatePicker: React.FC = ({ ts, onDatePicked }: IProps) => { type="date" onInput={onDateValueInput} value={dateValue} + // Prevent people from selecting a day in the future (there won't be any + // events there anyway). + max={formatDateForInput(new Date())} className="mx_JumpToDatePicker_datePicker" label={_t("Pick a date to jump to")} onFocus={onFocus} diff --git a/test/components/views/messages/JumpToDatePicker-test.tsx b/test/components/views/messages/JumpToDatePicker-test.tsx new file mode 100644 index 0000000000..c75c6d8ffd --- /dev/null +++ b/test/components/views/messages/JumpToDatePicker-test.tsx @@ -0,0 +1,41 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from "react"; +import { render } from "@testing-library/react"; + +import JumpToDatePicker from "../../../../src/components/views/messages/JumpToDatePicker"; + +describe("JumpToDatePicker", () => { + const nowDate = new Date("2021-12-17T08:09:00.000Z"); + beforeEach(() => { + // Set a stable fake time here so the test is always consistent + jest.useFakeTimers(); + jest.setSystemTime(nowDate.getTime()); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + it("renders the date picker correctly", () => { + const { asFragment } = render( + {}} />, + ); + + expect(asFragment()).toMatchSnapshot(); + }); +}); diff --git a/test/components/views/messages/__snapshots__/JumpToDatePicker-test.tsx.snap b/test/components/views/messages/__snapshots__/JumpToDatePicker-test.tsx.snap new file mode 100644 index 0000000000..02d5d3a337 --- /dev/null +++ b/test/components/views/messages/__snapshots__/JumpToDatePicker-test.tsx.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`JumpToDatePicker renders the date picker correctly 1`] = ` + +
+ + Jump to date + +
+ + +
+ +
+
+`; diff --git a/test/utils/DateUtils-test.ts b/test/utils/DateUtils-test.ts index aa8cf2d994..9b1fe4d870 100644 --- a/test/utils/DateUtils-test.ts +++ b/test/utils/DateUtils-test.ts @@ -19,6 +19,7 @@ import { formatRelativeTime, formatDuration, formatFullDateNoDayISO, + formatDateForInput, formatTimeLeft, formatPreciseDuration, formatLocalDateShort, @@ -126,6 +127,15 @@ describe("formatFullDateNoDayISO", () => { }); }); +describe("formatDateForInput", () => { + it.each([["1993-11-01"], ["1066-10-14"], ["0571-04-22"], ["0062-02-05"]])( + "should format %s", + (dateString: string) => { + expect(formatDateForInput(new Date(dateString))).toBe(dateString); + }, + ); +}); + describe("formatTimeLeft", () => { it.each([ [0, "0s left"],