Skip to content
This repository was archived by the owner on Apr 4, 2022. It is now read-only.

Search by OrderId and Address account #752

Merged
merged 6 commits into from
Oct 14, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions src/apps/explorer/components/common/Search/Search.styled.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import styled from 'styled-components'
import styled, { css, FlattenSimpleInterpolation } from 'styled-components'
import SVG from 'react-inlinesvg'

export const Wrapper = styled.form`
display: flex;
width: 100%;
max-width: 50rem;
margin: 0 auto;
${({ className }): FlattenSimpleInterpolation =>
className === 'home'
? css`
width: 100%;
max-width: 50rem;
margin: 0 auto;
`
: css`
width: 60rem;
`}
position: relative;
`

Expand Down
6 changes: 4 additions & 2 deletions src/apps/explorer/components/common/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ import { useSearchSubmit } from 'hooks/useSearchSubmit'
// assets
import searchImg from 'assets/img/search2.svg'

export const Search: React.FC = () => {
export const Search: React.FC<React.HTMLAttributes<HTMLDivElement>> = (props) => {
const [query, setQuery] = useState('')
const handleSubmit = useSearchSubmit()
const { className } = props

return (
<Wrapper
onSubmit={(e): void => {
e.preventDefault()
handleSubmit(query)
}}
className={className}
>
<Button type="submit">
<SearchIcon src={searchImg} />
Expand All @@ -25,7 +27,7 @@ export const Search: React.FC = () => {
name="query"
value={query}
onChange={(e): void => setQuery(e.target.value.trim())}
placeholder="Search by order ID"
placeholder="Search by Order ID / Address"
aria-label="Search the GP explorer for orders, batches and transactions"
/>
</Wrapper>
Expand Down
4 changes: 2 additions & 2 deletions src/apps/explorer/pages/Home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ const Wrapper = styled.div`
export const Home: React.FC = () => {
return (
<Wrapper>
<h1>Search Order ID</h1>
<Search />
<h1>Search Order ID / Address</h1>
<Search className="home" />
</Wrapper>
)
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/orders/OrderNotFound/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import { useParams } from 'react-router'
import styled from 'styled-components'
import { Search } from 'components/orders/Search'
import { Search } from 'apps/explorer/components/common/Search'
import SupportIcon from 'assets/img/support.png'
import { MEDIA } from 'const'

Expand Down
72 changes: 0 additions & 72 deletions src/components/orders/Search/Search.styled.ts

This file was deleted.

33 changes: 0 additions & 33 deletions src/components/orders/Search/index.tsx

This file was deleted.

19 changes: 17 additions & 2 deletions src/hooks/useSearchSubmit.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
import { useCallback } from 'react'
import { useHistory } from 'react-router-dom'
import { isAnAddressAccount } from 'utils'
import { usePathPrefix } from 'state/network'

export function pathAccordingTo(query: string): string {
let path = 'orders'
if (isAnAddressAccount(query)) {
path = 'address'
}

return path
}

export function useSearchSubmit(): (query: string) => void {
const history = useHistory()
const prefixNetwork = usePathPrefix()

return useCallback(
(query: string) => {
// For now assumes /orders/ path. Needs logic to try all types for a valid response:
// Orders, transactions, tokens, batches
query && query.length > 0 && history.push(`/orders/${query}`)
const path = pathAccordingTo(query)
const pathPrefix = prefixNetwork ? `${prefixNetwork}/${path}` : `${path}`

query && query.length > 0 && history.push(`/${pathPrefix}/${query}`)
},
[history],
[history, prefixNetwork],
)
}
14 changes: 14 additions & 0 deletions src/utils/miscellaneous.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,17 @@ export async function timeout<T>(params: TimeoutParams<T>): Promise<T | never> {
// no defined result -- throw message
throw new Error(timeoutMsg)
}

/**
* Check if a string is an orderId against regex
*
* @param text Possible OrderId string to check
*/
export const isAnOrderId = (text: string): boolean => text.match(/^0x[a-fA-F0-9]{112}$/)?.input !== undefined

/**
* Check if string is an address account against regex
*
* @param text Possible address string to check
*/
export const isAnAddressAccount = (text: string): boolean => text.match(/^0x[a-fA-F0-9]{40}$/)?.input !== undefined
51 changes: 51 additions & 0 deletions test/hooks/useSearchSubmit.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react'
import { createMemoryHistory, MemoryHistory } from 'history'
import { Router } from 'react-router-dom'
import { renderHook, act } from '@testing-library/react-hooks'

import { useSearchSubmit } from 'hooks/useSearchSubmit'

interface Props {
children?: React.ReactNode
history: MemoryHistory
}

function wrapperMemoryRouter(props: Props): JSX.Element {
return (
<>
<Router history={props.history}>{props.children}</Router>
</>
)
}

describe('useSearchSubmit', () => {
it('should be /orders/... with invalid search', () => {
const query = 'invalid_search'
const history = createMemoryHistory()

const { result } = renderHook(() => useSearchSubmit(), {
wrapper: ({ children }) => wrapperMemoryRouter({ children, history }),
})

act(() => {
result.current(query)
})

expect(history.location.pathname).toBe(`/orders/${query}`)
})

it('should be /address/0x... when address string is valid', () => {
const query = '0xb6BAd41ae76A11D10f7b0E664C5007b908bC77C9'
const history = createMemoryHistory()

const { result } = renderHook(() => useSearchSubmit(), {
wrapper: ({ children }) => wrapperMemoryRouter({ children, history }),
})

act(() => {
result.current(query)
})

expect(history.location.pathname).toBe(`/address/${query}`)
})
})
59 changes: 58 additions & 1 deletion test/utils/miscellaneous.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { tokenList } from '../data'
import { getToken } from 'utils'
import { getToken, isAnAddressAccount, isAnOrderId } from 'utils'
import BN from 'bn.js'
import { pathAccordingTo } from 'hooks/useSearchSubmit'

describe('getToken', () => {
describe('empty cases', () => {
Expand Down Expand Up @@ -89,3 +90,59 @@ describe('getToken', () => {
})
})
})

describe('isAnOrderId', () => {
test('Is orderId', () => {
const text =
'0x405bd0278c11399f84f10e19fb9b45123996e7d0a68a60ddebc3b9581576b484ff714b8b0e2700303ec912bd40496c3997ceea2b614b17d9'

const result = isAnOrderId(text)

expect(result).toBe(true)
})

test('Is not orderId', () => {
const text = '0xb6BAd41ae76A11D10f7b0E664C5007b908bC77C9'

const result = isAnOrderId(text)

expect(result).toBe(false)
})
})

describe('isAnAddressAccount', () => {
test('Is an Address account', () => {
const text = '0xb6BAd41ae76A11D10f7b0E664C5007b908bC77C9'

const result = isAnAddressAccount(text)

expect(result).toBe(true)
})

test('Is not an Address account', () => {
const text =
'0x405bd0278c11399F84f10e19fb9b45123996e7d0a68a60ddebc3b9581576b484ff714b8b0e2700303ec912bd40496c3997ceea2b614b17d9'

const result = isAnAddressAccount(text)

expect(result).toBe(false)
})
})

describe('pathAccordingTo', () => {
it('should return the orders word when it does not match', () => {
const text = 'Invalid Search'

const result = pathAccordingTo(text)

expect(result).toBe('orders')
})

it('should return the address word when it match', () => {
const text = '0xb6BAd41ae76A11D10f7b0E664C5007b908bC77C9'

const result = pathAccordingTo(text)

expect(result).toBe('address')
})
})