mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-16 05:04:57 +08:00
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
This commit is contained in:
parent
3eb6a55b93
commit
ed88e0cdce
@ -102,6 +102,21 @@ export function formatFullDate(date: Date, showTwelveHour = false, showSeconds =
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats dates to be compatible with attributes of a `<input type="date">`. 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 `<input>`
|
||||||
|
*/
|
||||||
|
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 {
|
export function formatFullTime(date: Date, showTwelveHour = false): string {
|
||||||
if (showTwelveHour) {
|
if (showTwelveHour) {
|
||||||
return twelveHourTime(date, true);
|
return twelveHourTime(date, true);
|
||||||
|
@ -19,6 +19,7 @@ import React, { useState, FormEvent } from "react";
|
|||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import Field from "../elements/Field";
|
import Field from "../elements/Field";
|
||||||
import { RovingAccessibleButton, useRovingTabIndex } from "../../../accessibility/RovingTabIndex";
|
import { RovingAccessibleButton, useRovingTabIndex } from "../../../accessibility/RovingTabIndex";
|
||||||
|
import { formatDateForInput } from "../../../DateUtils";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
ts: number;
|
ts: number;
|
||||||
@ -27,12 +28,9 @@ interface IProps {
|
|||||||
|
|
||||||
const JumpToDatePicker: React.FC<IProps> = ({ ts, onDatePicked }: IProps) => {
|
const JumpToDatePicker: React.FC<IProps> = ({ ts, onDatePicked }: IProps) => {
|
||||||
const date = new Date(ts);
|
const date = new Date(ts);
|
||||||
const year = date.getFullYear();
|
const dateInputDefaultValue = formatDateForInput(date);
|
||||||
const month = `${date.getMonth() + 1}`.padStart(2, "0");
|
|
||||||
const day = `${date.getDate()}`.padStart(2, "0");
|
|
||||||
const dateDefaultValue = `${year}-${month}-${day}`;
|
|
||||||
|
|
||||||
const [dateValue, setDateValue] = useState(dateDefaultValue);
|
const [dateValue, setDateValue] = useState(dateInputDefaultValue);
|
||||||
const [onFocus, isActive, ref] = useRovingTabIndex<HTMLInputElement>();
|
const [onFocus, isActive, ref] = useRovingTabIndex<HTMLInputElement>();
|
||||||
|
|
||||||
const onDateValueInput = (ev: React.ChangeEvent<HTMLInputElement>): void => setDateValue(ev.target.value);
|
const onDateValueInput = (ev: React.ChangeEvent<HTMLInputElement>): void => setDateValue(ev.target.value);
|
||||||
@ -49,6 +47,9 @@ const JumpToDatePicker: React.FC<IProps> = ({ ts, onDatePicked }: IProps) => {
|
|||||||
type="date"
|
type="date"
|
||||||
onInput={onDateValueInput}
|
onInput={onDateValueInput}
|
||||||
value={dateValue}
|
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"
|
className="mx_JumpToDatePicker_datePicker"
|
||||||
label={_t("Pick a date to jump to")}
|
label={_t("Pick a date to jump to")}
|
||||||
onFocus={onFocus}
|
onFocus={onFocus}
|
||||||
|
41
test/components/views/messages/JumpToDatePicker-test.tsx
Normal file
41
test/components/views/messages/JumpToDatePicker-test.tsx
Normal file
@ -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(
|
||||||
|
<JumpToDatePicker ts={new Date("2020-07-04T05:55:00.000Z").getTime()} onDatePicked={() => {}} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,41 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`JumpToDatePicker renders the date picker correctly 1`] = `
|
||||||
|
<DocumentFragment>
|
||||||
|
<form
|
||||||
|
class="mx_JumpToDatePicker_form"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="mx_JumpToDatePicker_label"
|
||||||
|
>
|
||||||
|
Jump to date
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
class="mx_Field mx_Field_input mx_JumpToDatePicker_datePicker"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="mx_Field_1"
|
||||||
|
label="Pick a date to jump to"
|
||||||
|
max="2021-12-17"
|
||||||
|
placeholder="Pick a date to jump to"
|
||||||
|
tabindex="-1"
|
||||||
|
type="date"
|
||||||
|
value="2020-07-04"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
for="mx_Field_1"
|
||||||
|
>
|
||||||
|
Pick a date to jump to
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="mx_AccessibleButton mx_JumpToDatePicker_submitButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Go
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</DocumentFragment>
|
||||||
|
`;
|
@ -19,6 +19,7 @@ import {
|
|||||||
formatRelativeTime,
|
formatRelativeTime,
|
||||||
formatDuration,
|
formatDuration,
|
||||||
formatFullDateNoDayISO,
|
formatFullDateNoDayISO,
|
||||||
|
formatDateForInput,
|
||||||
formatTimeLeft,
|
formatTimeLeft,
|
||||||
formatPreciseDuration,
|
formatPreciseDuration,
|
||||||
formatLocalDateShort,
|
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", () => {
|
describe("formatTimeLeft", () => {
|
||||||
it.each([
|
it.each([
|
||||||
[0, "0s left"],
|
[0, "0s left"],
|
||||||
|
Loading…
Reference in New Issue
Block a user