From eb6acdae2052931505b2b61b02b88ac284bb2267 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Fri, 23 Apr 2021 12:10:02 -0700 Subject: [PATCH] fix: CMD+F not working on screens with keyboard shortcut guide (#2066) --- app/components/Input.js | 5 +- app/components/InputSearch.js | 119 +++++++++--------------------- app/components/InputSearchPage.js | 95 ++++++++++++++++++++++++ app/scenes/Collection.js | 6 +- app/scenes/Drafts.js | 9 +-- app/scenes/Home.js | 9 +-- app/scenes/KeyboardShortcuts.js | 8 +- app/scenes/Starred.js | 9 +-- 8 files changed, 143 insertions(+), 117 deletions(-) create mode 100644 app/components/InputSearchPage.js diff --git a/app/components/Input.js b/app/components/Input.js index 135c879f..42668c20 100644 --- a/app/components/Input.js +++ b/app/components/Input.js @@ -106,8 +106,9 @@ export type Props = {| onChange?: ( ev: SyntheticInputEvent ) => mixed, - onFocus?: (ev: SyntheticEvent<>) => void, - onBlur?: (ev: SyntheticEvent<>) => void, + onKeyDown?: (ev: SyntheticKeyboardEvent) => mixed, + onFocus?: (ev: SyntheticEvent<>) => mixed, + onBlur?: (ev: SyntheticEvent<>) => mixed, |}; @observer diff --git a/app/components/InputSearch.js b/app/components/InputSearch.js index f36895f6..0d98753f 100644 --- a/app/components/InputSearch.js +++ b/app/components/InputSearch.js @@ -1,98 +1,47 @@ // @flow -import { observable } from "mobx"; -import { observer } from "mobx-react"; import { SearchIcon } from "outline-icons"; import * as React from "react"; -import { withTranslation, type TFunction } from "react-i18next"; -import keydown from "react-keydown"; -import { withRouter, type RouterHistory } from "react-router-dom"; -import styled, { withTheme } from "styled-components"; +import { useTranslation } from "react-i18next"; +import { useTheme } from "styled-components"; import Input from "./Input"; -import { type Theme } from "types"; -import { meta } from "utils/keyboard"; -import { searchUrl } from "utils/routeHelpers"; -type Props = { - history: RouterHistory, - theme: Theme, - source: string, +type Props = {| placeholder?: string, - label?: string, - labelHidden?: boolean, - collectionId?: string, - redirectDisabled?: boolean, - maxWidth?: string, - value: string, + value?: string, onChange: (event: SyntheticInputEvent<>) => mixed, - onKeyDown: (event: SyntheticKeyboardEvent<>) => mixed, - t: TFunction, -}; + onKeyDown: (event: SyntheticKeyboardEvent) => mixed, +|}; -@observer -class InputSearch extends React.Component { - input: ?Input; - @observable focused: boolean = false; +export default function InputSearch(props: Props) { + const { t } = useTranslation(); + const theme = useTheme(); + const [isFocused, setIsFocused] = React.useState(false); - @keydown(`${meta}+f`) - focus(ev: SyntheticEvent<>) { - ev.preventDefault(); + const handleFocus = React.useCallback(() => { + setIsFocused(true); + }, []); - if (this.input) { - this.input.focus(); - } - } + const handleBlur = React.useCallback(() => { + setIsFocused(false); + }, []); - handleSearchInput = (ev: SyntheticInputEvent<>) => { - ev.preventDefault(); - this.props.history.push( - searchUrl(ev.target.value, { - collectionId: this.props.collectionId, - ref: this.props.source, - }) - ); - }; + const { placeholder = `${t("Search")}…`, onKeyDown, ...rest } = props; - handleFocus = () => { - this.focused = true; - }; - - handleBlur = () => { - this.focused = false; - }; - - render() { - const { t, redirectDisabled, value, onChange, onKeyDown } = this.props; - const { theme, placeholder = `${t("Search")}…` } = this.props; - - return ( - (this.input = ref)} - type="search" - placeholder={placeholder} - onInput={redirectDisabled ? undefined : this.handleSearchInput} - value={value} - onChange={onChange} - onKeyDown={onKeyDown} - icon={ - - } - label={this.props.label} - labelHidden={this.props.labelHidden} - maxWidth={this.props.maxWidth} - onFocus={this.handleFocus} - onBlur={this.handleBlur} - margin={0} - /> - ); - } + return ( + + } + onKeyDown={onKeyDown} + onFocus={handleFocus} + onBlur={handleBlur} + margin={0} + labelHidden + {...rest} + /> + ); } - -const InputMaxWidth = styled(Input)` - max-width: ${(props) => props.maxWidth}; -`; - -export default withTranslation()( - withTheme(withRouter(InputSearch)) -); diff --git a/app/components/InputSearchPage.js b/app/components/InputSearchPage.js new file mode 100644 index 00000000..2dec1bff --- /dev/null +++ b/app/components/InputSearchPage.js @@ -0,0 +1,95 @@ +// @flow +import { observable } from "mobx"; +import { observer } from "mobx-react"; +import { SearchIcon } from "outline-icons"; +import * as React from "react"; +import { withTranslation, type TFunction } from "react-i18next"; +import keydown from "react-keydown"; +import { withRouter, type RouterHistory } from "react-router-dom"; +import styled, { withTheme } from "styled-components"; +import Input from "./Input"; +import { type Theme } from "types"; +import { meta } from "utils/keyboard"; +import { searchUrl } from "utils/routeHelpers"; + +type Props = { + history: RouterHistory, + theme: Theme, + source: string, + placeholder?: string, + label?: string, + labelHidden?: boolean, + collectionId?: string, + value: string, + onChange: (event: SyntheticInputEvent<>) => mixed, + onKeyDown: (event: SyntheticKeyboardEvent) => mixed, + t: TFunction, +}; + +@observer +class InputSearchPage extends React.Component { + input: ?Input; + @observable focused: boolean = false; + + @keydown(`${meta}+f`) + focus(ev: SyntheticEvent<>) { + ev.preventDefault(); + + if (this.input) { + this.input.focus(); + } + } + + handleSearchInput = (ev: SyntheticInputEvent<>) => { + ev.preventDefault(); + this.props.history.push( + searchUrl(ev.target.value, { + collectionId: this.props.collectionId, + ref: this.props.source, + }) + ); + }; + + handleFocus = () => { + this.focused = true; + }; + + handleBlur = () => { + this.focused = false; + }; + + render() { + const { t, value, onChange, onKeyDown } = this.props; + const { theme, placeholder = `${t("Search")}…` } = this.props; + + return ( + (this.input = ref)} + type="search" + placeholder={placeholder} + onInput={this.handleSearchInput} + value={value} + onChange={onChange} + onKeyDown={onKeyDown} + icon={ + + } + label={this.props.label} + onFocus={this.handleFocus} + onBlur={this.handleBlur} + margin={0} + labelHidden + /> + ); + } +} + +const InputMaxWidth = styled(Input)` + max-width: 30vw; +`; + +export default withTranslation()( + withTheme(withRouter(InputSearchPage)) +); diff --git a/app/scenes/Collection.js b/app/scenes/Collection.js index d5c354d3..20185b0f 100644 --- a/app/scenes/Collection.js +++ b/app/scenes/Collection.js @@ -18,7 +18,7 @@ import DocumentList from "components/DocumentList"; import Flex from "components/Flex"; import Heading from "components/Heading"; import HelpText from "components/HelpText"; -import InputSearch from "components/InputSearch"; +import InputSearchPage from "components/InputSearchPage"; import LoadingIndicator from "components/LoadingIndicator"; import { ListPlaceholder } from "components/LoadingPlaceholder"; import Mask from "components/Mask"; @@ -120,13 +120,11 @@ function CollectionScene() { actions={ <> - {can.update && ( diff --git a/app/scenes/Drafts.js b/app/scenes/Drafts.js index 81bbd41d..c6af84a5 100644 --- a/app/scenes/Drafts.js +++ b/app/scenes/Drafts.js @@ -14,7 +14,7 @@ import { Action } from "components/Actions"; import Empty from "components/Empty"; import Flex from "components/Flex"; import Heading from "components/Heading"; -import InputSearch from "components/InputSearch"; +import InputSearchPage from "components/InputSearchPage"; import PaginatedDocumentList from "components/PaginatedDocumentList"; import Scene from "components/Scene"; import Subheading from "components/Subheading"; @@ -83,12 +83,7 @@ class Drafts extends React.Component { actions={ <> - + diff --git a/app/scenes/Home.js b/app/scenes/Home.js index 08290506..ff18fae4 100644 --- a/app/scenes/Home.js +++ b/app/scenes/Home.js @@ -6,7 +6,7 @@ import { useTranslation } from "react-i18next"; import { Switch, Route } from "react-router-dom"; import { Action } from "components/Actions"; import Heading from "components/Heading"; -import InputSearch from "components/InputSearch"; +import InputSearchPage from "components/InputSearchPage"; import LanguagePrompt from "components/LanguagePrompt"; import Scene from "components/Scene"; import Tab from "components/Tab"; @@ -29,12 +29,7 @@ function Home() { actions={ <> - + diff --git a/app/scenes/KeyboardShortcuts.js b/app/scenes/KeyboardShortcuts.js index 9b63af1b..09e934ff 100644 --- a/app/scenes/KeyboardShortcuts.js +++ b/app/scenes/KeyboardShortcuts.js @@ -3,7 +3,7 @@ import * as React from "react"; import { useTranslation } from "react-i18next"; import styled from "styled-components"; import Flex from "components/Flex"; -import Input from "components/InputSearch"; +import InputSearch from "components/InputSearch"; import Key from "components/Key"; import { metaDisplay } from "utils/keyboard"; @@ -351,7 +351,7 @@ function KeyboardShortcuts() { }, []); const handleKeyDown = React.useCallback((event) => { - if (event.target.value && event.key === "Escape") { + if (event.currentTarget.value && event.key === "Escape") { event.preventDefault(); event.stopPropagation(); setSearchTerm(""); @@ -360,12 +360,10 @@ function KeyboardShortcuts() { return ( - {categories.map((category, x) => { const filtered = searchTerm diff --git a/app/scenes/Starred.js b/app/scenes/Starred.js index 85bd58d9..c875340a 100644 --- a/app/scenes/Starred.js +++ b/app/scenes/Starred.js @@ -7,7 +7,7 @@ import { type Match } from "react-router-dom"; import { Action } from "components/Actions"; import Empty from "components/Empty"; import Heading from "components/Heading"; -import InputSearch from "components/InputSearch"; +import InputSearchPage from "components/InputSearchPage"; import PaginatedDocumentList from "components/PaginatedDocumentList"; import Scene from "components/Scene"; import Tab from "components/Tab"; @@ -32,12 +32,7 @@ function Starred(props: Props) { actions={ <> - +