From 97c99c6aaeda91f77f108b3d856af4dab8f37f52 Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 10 May 2022 12:49:05 -0400 Subject: [PATCH] Only jump to date after pressing the 'go' button (#8548) --- src/components/views/elements/Field.tsx | 10 +-- .../views/elements/NativeOnChangeInput.tsx | 69 ------------------- .../views/messages/JumpToDatePicker.tsx | 37 +--------- src/hooks/useCombinedRefs.ts | 38 ---------- 4 files changed, 4 insertions(+), 150 deletions(-) delete mode 100644 src/components/views/elements/NativeOnChangeInput.tsx delete mode 100644 src/hooks/useCombinedRefs.ts diff --git a/src/components/views/elements/Field.tsx b/src/components/views/elements/Field.tsx index 3942a9eb1d..8e4b896efd 100644 --- a/src/components/views/elements/Field.tsx +++ b/src/components/views/elements/Field.tsx @@ -19,7 +19,6 @@ import classNames from 'classnames'; import { debounce } from "lodash"; import { IFieldState, IValidationResult } from "./Validation"; -import { ComponentClass } from "../../../@types/common"; import Tooltip from "./Tooltip"; // Invoke validation from user input (when typing, etc.) at most once every N ms. @@ -83,7 +82,6 @@ export interface IInputProps extends IProps, InputHTMLAttributes; // The element to create. Defaults to "input". element?: "input"; - componentClass?: undefined; // The input's value. This is a controlled component, so the value is required. value: string; } @@ -93,7 +91,6 @@ interface ISelectProps extends IProps, SelectHTMLAttributes { inputRef?: RefObject; // To define options for a select, use element: "select"; - componentClass?: undefined; // The select's value. This is a controlled component, so the value is required. value: string; } @@ -102,7 +99,6 @@ interface ITextareaProps extends IProps, TextareaHTMLAttributes; element: "textarea"; - componentClass?: undefined; // The textarea's value. This is a controlled component, so the value is required. value: string; } @@ -111,8 +107,6 @@ export interface INativeOnChangeInputProps extends IProps, InputHTMLAttributes; element: "input"; - // The custom component to render - componentClass: ComponentClass; // The input's value. This is a controlled component, so the value is required. value: string; } @@ -248,7 +242,7 @@ export default class Field extends React.PureComponent { public render() { /* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */ - const { element, componentClass, inputRef, prefixComponent, postfixComponent, className, onValidate, children, + const { element, inputRef, prefixComponent, postfixComponent, className, onValidate, children, tooltipContent, forceValidity, tooltipClassName, list, validateOnBlur, validateOnChange, validateOnFocus, usePlaceholderAsHint, forceTooltipVisible, ...inputProps } = this.props; @@ -265,7 +259,7 @@ export default class Field extends React.PureComponent { // Appease typescript's inference const inputProps_ = { ...inputProps, ref: this.inputRef, list }; - const fieldInput = React.createElement(this.props.componentClass || this.props.element, inputProps_, children); + const fieldInput = React.createElement(this.props.element, inputProps_, children); let prefixContainer = null; if (prefixComponent) { diff --git a/src/components/views/elements/NativeOnChangeInput.tsx b/src/components/views/elements/NativeOnChangeInput.tsx deleted file mode 100644 index 0937fd8de9..0000000000 --- a/src/components/views/elements/NativeOnChangeInput.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright 2022 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 { useCombinedRefs } from "../../../hooks/useCombinedRefs"; - -interface IProps extends Omit, 'onChange' | 'onInput'> { - onChange?: (event: Event) => void; - onInput?: (event: Event) => void; -} - -/** -* This component restores the native 'onChange' and 'onInput' behavior of -* JavaScript which have important differences for certain types. This is -* necessary because in React, the `onChange` handler behaves like the native -* `oninput` handler and there is no way to tell the difference between an -* `input` vs `change` event. -* -* via https://stackoverflow.com/a/62383569/796832 and -* https://github.com/facebook/react/issues/9657#issuecomment-643970199 -* -* See: -* - https://reactjs.org/docs/dom-elements.html#onchange -* - https://github.com/facebook/react/issues/3964 -* - https://github.com/facebook/react/issues/9657 -* - https://github.com/facebook/react/issues/14857 -* -* Examples: -* -* We use this for the date picker so we can distinguish from -* a final date picker selection (onChange) vs navigating the months in the date -* picker (onInput). -* -* This is also potentially useful for because the native -* events behave in such a way that moving the slider around triggers an onInput -* event and releasing it triggers onChange. -*/ -const NativeOnChangeInput: React.FC = React.forwardRef((props: IProps, ref) => { - const registerCallbacks = (input: HTMLInputElement | null) => { - if (input) { - input.onchange = props.onChange; - input.oninput = props.onInput; - } - }; - - return {}} - onInput={() => {}} - />; -}); - -export default NativeOnChangeInput; diff --git a/src/components/views/messages/JumpToDatePicker.tsx b/src/components/views/messages/JumpToDatePicker.tsx index 925a741c18..fc23f82a57 100644 --- a/src/components/views/messages/JumpToDatePicker.tsx +++ b/src/components/views/messages/JumpToDatePicker.tsx @@ -18,7 +18,6 @@ import React, { useState, FormEvent } from 'react'; import { _t } from '../../../languageHandler'; import Field from "../elements/Field"; -import NativeOnChangeInput from "../elements/NativeOnChangeInput"; import { RovingAccessibleButton, useRovingTabIndex } from "../../../accessibility/RovingTabIndex"; interface IProps { @@ -34,41 +33,11 @@ const JumpToDatePicker: React.FC = ({ ts, onDatePicked }: IProps) => { const dateDefaultValue = `${year}-${month}-${day}`; const [dateValue, setDateValue] = useState(dateDefaultValue); - // Whether or not to automatically navigate to the given date after someone - // selects a day in the date picker. We want to disable this after someone - // starts manually typing in the input instead of picking. - const [navigateOnDatePickerSelection, setNavigateOnDatePickerSelection] = useState(true); - - // Since we're using NativeOnChangeInput with native JavaScript behavior, this - // tracks the date value changes as they come in. - const onDateValueInput = (e: React.ChangeEvent): void => { - setDateValue(e.target.value); - }; - - // Since we're using NativeOnChangeInput with native JavaScript behavior, the change - // event listener will trigger when a date is picked from the date picker - // or when the text is fully filled out. In order to not trigger early - // as someone is typing out a date, we need to disable when we see keydowns. - const onDateValueChange = (e: React.ChangeEvent): void => { - setDateValue(e.target.value); - - // Don't auto navigate if they were manually typing out a date - if (navigateOnDatePickerSelection) { - onDatePicked(dateValue); - } - }; - const [onFocus, isActive, ref] = useRovingTabIndex(); - const onDateInputKeyDown = (e: React.KeyboardEvent): void => { - // When we see someone manually typing out a date, disable the auto - // submit on change. - setNavigateOnDatePickerSelection(false); - }; - + const onDateValueInput = (ev: React.ChangeEvent) => setDateValue(ev.target.value); const onJumpToDateSubmit = (ev: FormEvent): void => { ev.preventDefault(); - onDatePicked(dateValue); }; @@ -79,11 +48,9 @@ const JumpToDatePicker: React.FC = ({ ts, onDatePicked }: IProps) => { > { _t("Jump to date") } { - const targetRef = useRef(); - - useEffect(() => { - refs.forEach(ref => { - if (!ref) return; - - if (typeof ref === 'function') { - ref(targetRef.current); - } else { - ref.current = targetRef.current; - } - }); - }, [refs]); - - return targetRef; -};