Skip to content

Commit

Permalink
Merge pull request #6994 from SNikhill/refactor/#6392-ts-search
Browse files Browse the repository at this point in the history
TypeScript: Search Component
  • Loading branch information
pettinarip authored Jul 16, 2022
2 parents 69be560 + 19f237e commit 69237c4
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 49 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"@types/node": "^17.0.23",
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11",
"@types/react-instantsearch-dom": "^6.12.3",
"@types/styled-components": "^5.1.25",
"@types/styled-system": "^5.1.15",
"babel-preset-gatsby": "^2.14.0",
Expand Down
2 changes: 1 addition & 1 deletion src/components/Nav/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ const Nav: React.FC<IProps> = ({ handleThemeChange, isDarkTheme, path }) => {
<Menu path={path} sections={linkSections} />
</LeftItems>
<RightItems>
<Search useKeyboardShortcuts />
<Search useKeyboardShortcut />
<ThemeToggle
onClick={handleThemeChange}
aria-label={
Expand Down
22 changes: 18 additions & 4 deletions src/components/Search/Input.js → src/components/Search/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react"
import React, { ChangeEvent, FormEvent } from "react"
import { useIntl } from "react-intl"
import styled from "styled-components"
import { connectSearchBox } from "react-instantsearch-dom"
Expand Down Expand Up @@ -57,17 +57,31 @@ const SearchSlash = styled.p`
}
`

const Input = ({ query, setQuery, refine, inputRef, ...rest }) => {
interface IInputProps
extends React.ComponentPropsWithoutRef<typeof StyledInput> {
query: string
setQuery: (query: string) => void
refine: (query: string) => void
inputRef: React.MutableRefObject<HTMLInputElement>
}

const Input: React.FC<IInputProps> = ({
query,
setQuery,
refine,
inputRef,
...rest
}) => {
const intl = useIntl()
const searchString = translateMessageId("search", intl)

const handleInputChange = (event) => {
const handleInputChange = (event: ChangeEvent<HTMLInputElement>): void => {
const value = event.target.value
refine(value)
setQuery(value)
}

const handleSubmit = (event) => {
const handleSubmit = (event: FormEvent<HTMLFormElement>): void => {
event.preventDefault()
}

Expand Down
103 changes: 59 additions & 44 deletions src/components/Search/index.js → src/components/Search/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useRef } from "react"
import React, { useState, useRef, MouseEventHandler } from "react"
import { Link as GatsbyLink } from "gatsby"
import { useIntl } from "react-intl"
import {
Expand All @@ -10,7 +10,9 @@ import {
Snippet,
connectStateResults,
} from "react-instantsearch-dom"
import type { StateResultsProvided } from "react-instantsearch-core"
import algoliasearch from "algoliasearch/lite"
import { Hit } from "@algolia/client-search"
import styled from "styled-components"

import Input from "./Input"
Expand All @@ -25,7 +27,7 @@ const Root = styled.div`
grid-gap: 1em;
`

const HitsWrapper = styled.div`
const HitsWrapper = styled.div<{ show: boolean }>`
display: ${(props) => (props.show ? `grid` : `none`)};
max-height: 80vh;
overflow: scroll;
Expand Down Expand Up @@ -113,9 +115,10 @@ const StyledHighlight = styled(Highlight)`
}
`

//FIXME: Add a strict type for `hit` prop
const PageHit =
(clickHandler) =>
({ hit }) => {
(clickHandler: MouseEventHandler) =>
({ hit }: { hit: Hit<Record<string, any>> }) => {
// Make url relative, so `handleSelect` is triggered
const url = hit.url.replace("https://ethereum.org", "")
return (
Expand Down Expand Up @@ -158,49 +161,61 @@ const indices = [
]

// Validate against basic requirements of an ETH address
const isValidAddress = (address) => {
const isValidAddress = (address: string): boolean => {
return /^(0x)?[0-9a-f]{40}$/i.test(address)
}

const Results = connectStateResults(
({ searchState: state, searchResults: res, children }) => {
if (res && res.nbHits > 0) {
return children
}
if (isValidAddress(state.query)) {
return (
<div>
<p>
<strong>
<Translation id="search-no-results" />
</strong>{" "}
"{state.query}"
</p>
<p>
<Translation id="search-eth-address" />{" "}
<Link to={`https://etherscan.io/address/${state.query}`}>
Etherscan
</Link>
.
</p>
</div>
)
}
const Results: React.FC<StateResultsProvided> = ({
searchState: state,
searchResults: res,
children,
}) => {
if (res && res.nbHits > 0) {
return <>{children}</>
}
if (state.query && isValidAddress(state.query)) {
return (
<div>
<strong>
<Translation id="search-no-results" />
</strong>{" "}
"{state.query}"
<p>
<strong>
<Translation id="search-no-results" />
</strong>{" "}
"{state.query}"
</p>
<p>
<Translation id="search-eth-address" />{" "}
<Link to={`https://etherscan.io/address/${state.query}`}>
Etherscan
</Link>
.
</p>
</div>
)
}
)
return (
<div>
<strong>
<Translation id="search-no-results" />
</strong>{" "}
"{state.query}"
</div>
)
}

const ConnectedResults = connectStateResults(Results)

interface ISearchProps {
handleSearchSelect?: () => void
useKeyboardShortcut?: boolean
}

const Search = ({ handleSearchSelect, useKeyboardShortcuts }) => {
const Search: React.FC<ISearchProps> = ({
handleSearchSelect,
useKeyboardShortcut = false,
}) => {
const intl = useIntl()
const containerRef = useRef()
const inputRef = useRef()
const containerRef = useRef<HTMLDivElement>(null)
const inputRef = useRef<HTMLInputElement>(null)
const [query, setQuery] = useState(``)
const [focus, setFocus] = useState(false)
const algoliaClient = algoliasearch(
Expand All @@ -227,23 +242,23 @@ const Search = ({ handleSearchSelect, useKeyboardShortcuts }) => {
}
useOnClickOutside(containerRef, () => setFocus(false))

const handleSelect = () => {
const handleSelect = (): void => {
setQuery(``)
setFocus(false)
if (handleSearchSelect) {
handleSearchSelect()
}
}

const focusSearch = (event) => {
if (!useKeyboardShortcuts) {
const focusSearch = (event: KeyboardEvent): void => {
if (!useKeyboardShortcut) {
return
}

const searchInput = inputRef.current
if (document.activeElement !== searchInput) {
event.preventDefault()
searchInput.focus()
searchInput?.focus()
}
}

Expand All @@ -263,12 +278,12 @@ const Search = ({ handleSearchSelect, useKeyboardShortcuts }) => {
setQuery={setQuery}
onFocus={() => setFocus(true)}
/>
<HitsWrapper show={query && query.length > 0 && focus}>
<HitsWrapper show={query?.length > 0 && focus}>
{indices.map(({ name, hitComp }) => (
<Index key={name} indexName={name}>
<Results>
<ConnectedResults>
<Hits hitComponent={PageHit(() => handleSelect())} />
</Results>
</ConnectedResults>
</Index>
))}
</HitsWrapper>
Expand Down
Loading

0 comments on commit 69237c4

Please sign in to comment.