diff --git a/lib/types/react-types.js b/lib/types/react-types.js new file mode 100644 index 000000000..06df76fae --- /dev/null +++ b/lib/types/react-types.js @@ -0,0 +1,6 @@ +// @flow + +// Flow 0.217 introduces React.RefSetter +export type ReactRefSetter = + | { current: null | I, ... } + | ((null | I) => mixed); diff --git a/web/account/password-input.react.js b/web/account/password-input.react.js index 15f41fda8..453fbe54f 100644 --- a/web/account/password-input.react.js +++ b/web/account/password-input.react.js @@ -1,47 +1,51 @@ // @flow import * as React from 'react'; import SWMansionIcon, { type Icon, } from 'lib/components/SWMansionIcon.react.js'; +import type { ReactRefSetter } from 'lib/types/react-types.js'; import css from './password-input.css'; import Button from '../components/button.react.js'; import Input, { type BaseInputProps } from '../modals/input.react.js'; type PasswordInputProps = BaseInputProps; -function PasswordInput(props: PasswordInputProps, ref): React.Node { +function PasswordInput( + props: PasswordInputProps, + ref: ReactRefSetter, +): React.Node { const [htmlInputType, setHtmlInputType] = React.useState<'password' | 'text'>( 'password', ); const onToggleShowPassword = React.useCallback(() => { setHtmlInputType(oldType => (oldType === 'password' ? 'text' : 'password')); }, []); const icon: Icon = htmlInputType === 'password' ? 'eye-open' : 'eye-closed'; return (
); } const ForwardedPasswordInput: React.AbstractComponent< PasswordInputProps, HTMLInputElement, > = React.forwardRef(PasswordInput); export default ForwardedPasswordInput; diff --git a/web/components/search.react.js b/web/components/search.react.js index 6428cb6a2..27107ada1 100644 --- a/web/components/search.react.js +++ b/web/components/search.react.js @@ -1,58 +1,62 @@ // @flow import * as React from 'react'; import SWMansionIcon from 'lib/components/SWMansionIcon.react.js'; +import type { ReactRefSetter } from 'lib/types/react-types.js'; import ClearSearchButton from './clear-search-button.react.js'; import css from './search.css'; type Props = { ...React.ElementConfig<'input'>, +searchText: string, +onChangeText: (searchText: string) => mixed, +placeholder?: string, +onClearText?: () => mixed, }; -function Search(props: Props, ref): React.Node { +function Search( + props: Props, + ref: ReactRefSetter, +): React.Node { const { searchText, onChangeText, placeholder, onClearText, ...rest } = props; const showClearButton = !!searchText; const onClear = React.useCallback(() => { onChangeText(''); onClearText?.(); }, [onChangeText, onClearText]); const onChange = React.useCallback( event => { onChangeText(event.target.value); }, [onChangeText], ); return (
); } const ForwardedSearch: React.AbstractComponent = React.forwardRef(Search); export default ForwardedSearch; diff --git a/web/modals/input.react.js b/web/modals/input.react.js index a93de01dc..713837162 100644 --- a/web/modals/input.react.js +++ b/web/modals/input.react.js @@ -1,62 +1,67 @@ // @flow import classNames from 'classnames'; import * as React from 'react'; +import type { ReactRefSetter } from 'lib/types/react-types.js'; + import css from './input.css'; export type BaseInputProps = { +value: string, +onChange: (value: SyntheticEvent) => mixed, +onBlur?: (value: SyntheticEvent) => mixed, +disabled?: boolean, +label?: string, +id?: string, +className?: string, }; export type InputProps = { ...BaseInputProps, +type: string, +placeholder?: string, +maxLength?: number, }; -function Input(props: InputProps, ref): React.Node { +function Input( + props: InputProps, + ref: ReactRefSetter, +): React.Node { const { label: labelProp, disabled = false, className = '', id, ...rest } = props; let label; if (labelProp) { label = ( ); } const inputClassName = classNames(css.input, className); return ( <> {label} ); } const ForwardedInput: React.AbstractComponent = React.forwardRef(Input); export default ForwardedInput;