Skip to content

Commit

Permalink
test(search-bar): override
Browse files Browse the repository at this point in the history
  • Loading branch information
guoyunhe committed Apr 25, 2024
1 parent d33fb4b commit 13a899d
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 109 deletions.
214 changes: 109 additions & 105 deletions src/components/search-bar/search-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { ReactNode } from 'react'
import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react'
import { NativeProps, withNativeProps } from '../../utils/native-props'
import { usePropsValue } from '../../utils/use-props-value'
import { mergeProps } from '../../utils/with-default-props'
import { mergeProp, mergeProps } from '../../utils/with-default-props'
import Button from '../button'
import { useConfig } from '../config-provider'
import Input, { InputProps, InputRef } from '../input'
Expand Down Expand Up @@ -48,120 +48,124 @@ const defaultProps = {
showCancelButton: false as NonNullable<SearchBarProps['showCancelButton']>,
defaultValue: '',
clearOnCancel: true,
searchIcon: <SearchOutline />,
}

export const SearchBar = forwardRef<SearchBarRef, SearchBarProps>((p, ref) => {
const { locale, searchBar: componentConfig = {} } = useConfig()
const props = mergeProps(
defaultProps,
componentConfig,
{
cancelText: locale.common.cancel,
},
p
)
const [value, setValue] = usePropsValue(props)
const [hasFocus, setHasFocus] = useState(false)
const inputRef = useRef<InputRef>(null)
const composingRef = useRef(false)
export const SearchBar = forwardRef<SearchBarRef, SearchBarProps>(
(props, ref) => {
const { locale, searchBar: componentConfig = {} } = useConfig()
const mergedProps = mergeProps(
defaultProps,
componentConfig,
{
cancelText: locale.common.cancel,
},
props
)
const searchIcon = mergeProp(
<SearchOutline />,
componentConfig.searchIcon,
props.icon,
props.searchIcon
)
const [value, setValue] = usePropsValue(mergedProps)
const [hasFocus, setHasFocus] = useState(false)
const inputRef = useRef<InputRef>(null)
const composingRef = useRef(false)

useImperativeHandle(ref, () => ({
clear: () => inputRef.current?.clear(),
focus: () => inputRef.current?.focus(),
blur: () => inputRef.current?.blur(),
get nativeElement() {
return inputRef.current?.nativeElement ?? null
},
}))
useImperativeHandle(ref, () => ({
clear: () => inputRef.current?.clear(),
focus: () => inputRef.current?.focus(),
blur: () => inputRef.current?.blur(),
get nativeElement() {
return inputRef.current?.nativeElement ?? null
},
}))

const renderCancelButton = () => {
let isShowCancel: boolean
const renderCancelButton = () => {
let isShowCancel: boolean

if (typeof props.showCancelButton === 'function') {
isShowCancel = props.showCancelButton(hasFocus, value)
} else {
isShowCancel = props.showCancelButton && hasFocus
if (typeof mergedProps.showCancelButton === 'function') {
isShowCancel = mergedProps.showCancelButton(hasFocus, value)
} else {
isShowCancel = mergedProps.showCancelButton && hasFocus
}

return (
isShowCancel && (
<div className={`${classPrefix}-suffix`}>
<Button
fill='none'
className={`${classPrefix}-cancel-button`}
onClick={() => {
if (mergedProps.clearOnCancel) {
inputRef.current?.clear()
}
inputRef.current?.blur()
mergedProps.onCancel?.()
}}
onMouseDown={e => {
e.preventDefault()
}}
>
{mergedProps.cancelText}
</Button>
</div>
)
)
}

return (
isShowCancel && (
<div className={`${classPrefix}-suffix`}>
<Button
fill='none'
className={`${classPrefix}-cancel-button`}
onClick={() => {
if (props.clearOnCancel) {
inputRef.current?.clear()
return withNativeProps(
mergedProps,
<div
className={classNames(classPrefix, {
[`${classPrefix}-active`]: hasFocus,
})}
>
<div className={`${classPrefix}-input-box`}>
{searchIcon && (
<div className={`${classPrefix}-input-box-icon`}>{searchIcon}</div>
)}
<Input
ref={inputRef}
className={classNames(`${classPrefix}-input`, {
[`${classPrefix}-input-without-icon`]: !searchIcon,
})}
value={value}
onChange={setValue}
maxLength={mergedProps.maxLength}
placeholder={mergedProps.placeholder}
clearable={mergedProps.clearable}
onlyShowClearWhenFocus={mergedProps.onlyShowClearWhenFocus}
onFocus={e => {
setHasFocus(true)
mergedProps.onFocus?.(e)
}}
onBlur={e => {
setHasFocus(false)
mergedProps.onBlur?.(e)
}}
onClear={mergedProps.onClear}
type='search'
enterKeyHint='search'
onEnterPress={() => {
if (!composingRef.current) {
inputRef.current?.blur()
mergedProps.onSearch?.(value)
}
inputRef.current?.blur()
props.onCancel?.()
}}
onMouseDown={e => {
e.preventDefault()
aria-label={locale.SearchBar.name}
onCompositionStart={e => {
composingRef.current = true
mergedProps.onCompositionStart?.(e)
}}
>
{props.cancelText}
</Button>
onCompositionEnd={e => {
composingRef.current = false
mergedProps.onCompositionEnd?.(e)
}}
/>
</div>
)
{renderCancelButton()}
</div>
)
}

return withNativeProps(
props,
<div
className={classNames(classPrefix, {
[`${classPrefix}-active`]: hasFocus,
})}
>
<div className={`${classPrefix}-input-box`}>
{(props.searchIcon || props.icon) && (
<div className={`${classPrefix}-input-box-icon`}>
{props.searchIcon || props.icon}
</div>
)}
<Input
ref={inputRef}
className={classNames(`${classPrefix}-input`, {
[`${classPrefix}-input-without-icon`]:
!props.searchIcon && !props.icon,
})}
value={value}
onChange={setValue}
maxLength={props.maxLength}
placeholder={props.placeholder}
clearable={props.clearable}
onlyShowClearWhenFocus={props.onlyShowClearWhenFocus}
onFocus={e => {
setHasFocus(true)
props.onFocus?.(e)
}}
onBlur={e => {
setHasFocus(false)
props.onBlur?.(e)
}}
onClear={props.onClear}
type='search'
enterKeyHint='search'
onEnterPress={() => {
if (!composingRef.current) {
inputRef.current?.blur()
props.onSearch?.(value)
}
}}
aria-label={locale.SearchBar.name}
onCompositionStart={e => {
composingRef.current = true
props.onCompositionStart?.(e)
}}
onCompositionEnd={e => {
composingRef.current = false
props.onCompositionEnd?.(e)
}}
/>
</div>
{renderCancelButton()}
</div>
)
})
)
51 changes: 47 additions & 4 deletions src/components/search-bar/tests/search-bar.test.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import React, { createRef } from 'react'
import {
render,
testA11y,
act,
fireEvent,
render,
screen,
waitForElementToBeRemoved,
testA11y,
userEvent,
act,
waitForElementToBeRemoved,
} from 'testing'
import SearchBar, { SearchBarRef } from '..'
import ConfigProvider from '../../config-provider'

const classPrefix = `adm-search-bar`

Expand Down Expand Up @@ -99,4 +100,46 @@ describe('adm-search-bar', () => {
expect(input).not.toHaveFocus()
expect(onBlur).toBeCalled()
})

describe('searchIcon', () => {
it('default', () => {
const { baseElement } = render(<SearchBar />)
expect(baseElement.querySelector('.antd-mobile-icon')).toBeTruthy()
})

it('legacy', () => {
render(<SearchBar icon='little' />)
expect(screen.getByText('little')).toBeVisible()
})

it('props', () => {
render(<SearchBar searchIcon='bamboo' />)
expect(screen.getByText('bamboo')).toBeVisible()
})

it('props override legacy props', () => {
render(<SearchBar icon='little' searchIcon='bamboo' />)
expect(screen.getByText('bamboo')).toBeVisible()
})

it('context', () => {
render(
<ConfigProvider searchBar={{ searchIcon: 'little' }}>
<SearchBar />
</ConfigProvider>
)

expect(screen.getByText('little')).toBeVisible()
})

it('props override context', () => {
render(
<ConfigProvider searchBar={{ searchIcon: 'little' }}>
<SearchBar searchIcon='bamboo' />
</ConfigProvider>
)

expect(screen.getByText('bamboo')).toBeVisible()
})
})
})

0 comments on commit 13a899d

Please sign in to comment.