mirror of
https://github.com/zhigang1992/react.git
synced 2026-03-26 22:42:51 +08:00
feat(auto-complete): add control for free solo
test(auto-complete): add testcase for free solo
This commit is contained in:
@@ -4,6 +4,7 @@ exports[`AutoComplete should render correctly 1`] = `
|
||||
<AutoComplete
|
||||
className=""
|
||||
clearable={false}
|
||||
disableFreeSolo={false}
|
||||
disableMatchWidth={false}
|
||||
disabled={false}
|
||||
initialValue=""
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react'
|
||||
import { mount } from 'enzyme'
|
||||
import { AutoComplete } from 'components'
|
||||
import { nativeEvent } from 'tests/utils'
|
||||
import { act } from 'react-dom/test-utils'
|
||||
|
||||
describe('AutoComplete', () => {
|
||||
it('should render correctly', () => {
|
||||
@@ -32,11 +33,13 @@ describe('AutoComplete', () => {
|
||||
expect((input as HTMLInputElement).value).toEqual('value2')
|
||||
})
|
||||
|
||||
it('should render clear icon', () => {
|
||||
it('should render clear icon', async () => {
|
||||
const wrapper = mount(<AutoComplete initialValue="value" />)
|
||||
expect(wrapper.find('svg').length).toBe(0)
|
||||
|
||||
wrapper.setProps({ clearable: true })
|
||||
await act(async () => {
|
||||
wrapper.setProps({ clearable: true })
|
||||
})
|
||||
expect(wrapper.find('svg').length).toBe(1)
|
||||
|
||||
wrapper.find('svg').simulate('click', nativeEvent)
|
||||
@@ -44,11 +47,13 @@ describe('AutoComplete', () => {
|
||||
expect((input as HTMLInputElement).value).toEqual('')
|
||||
})
|
||||
|
||||
it('should reponse width change', () => {
|
||||
it('should reponse width change', async () => {
|
||||
const wrapper = mount(<AutoComplete initialValue="value" width="100px" />)
|
||||
expect(wrapper.prop('width')).toEqual('100px')
|
||||
await act(async () => {
|
||||
wrapper.setProps({ width: '200px' })
|
||||
})
|
||||
|
||||
wrapper.setProps({ width: '200px' })
|
||||
expect(wrapper.prop('width')).toEqual('200px')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from 'react'
|
||||
import { mount, render } from 'enzyme'
|
||||
import { AutoComplete } from 'components'
|
||||
import { nativeEvent } from 'tests/utils'
|
||||
import { nativeEvent, updateWrapper } from 'tests/utils'
|
||||
import { act } from 'react-dom/test-utils'
|
||||
const mockOptions = [{ label: 'London', value: 'london' }]
|
||||
|
||||
describe('AutoComplete Search', () => {
|
||||
@@ -33,9 +34,11 @@ describe('AutoComplete Search', () => {
|
||||
expect(value).not.toEqual('london')
|
||||
})
|
||||
|
||||
it('should render searching component', () => {
|
||||
it('should render searching component', async () => {
|
||||
let wrapper = mount(<AutoComplete searching={false} options={mockOptions} />)
|
||||
wrapper.setProps({ searching: true })
|
||||
await act(async () => {
|
||||
wrapper.setProps({ searching: true })
|
||||
})
|
||||
wrapper.find('input').at(0).simulate('focus')
|
||||
let dropdown = wrapper.find('.auto-complete-dropdown')
|
||||
expect(dropdown.text()).not.toContain('london')
|
||||
@@ -136,4 +139,15 @@ describe('AutoComplete Search', () => {
|
||||
const wrapper = mount(<AutoComplete options={[]} />)
|
||||
expect(() => wrapper.unmount()).not.toThrow()
|
||||
})
|
||||
|
||||
it('value should be reset when freeSolo disabled', async () => {
|
||||
const wrapper = mount(<AutoComplete initialValue="value" disableFreeSolo />)
|
||||
const input = wrapper.find('input').at(0)
|
||||
input.simulate('focus')
|
||||
input.simulate('change', { target: { value: 'test' } })
|
||||
expect((input.getDOMNode() as HTMLInputElement).value).toEqual('test')
|
||||
input.simulate('blur')
|
||||
await updateWrapper(wrapper, 200)
|
||||
expect((input.getDOMNode() as HTMLInputElement).value).toEqual('value')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -28,9 +28,10 @@ const AutoCompleteItem: React.FC<React.PropsWithChildren<AutoCompleteItemProps>>
|
||||
children,
|
||||
}) => {
|
||||
const theme = useTheme()
|
||||
const { value, updateValue, size } = useAutoCompleteContext()
|
||||
const { value, updateValue, size, updateVisible } = useAutoCompleteContext()
|
||||
const selectHandler = () => {
|
||||
updateValue && updateValue(identValue)
|
||||
updateVisible && updateVisible(false)
|
||||
}
|
||||
|
||||
const isActive = useMemo(() => value === identValue, [identValue, value])
|
||||
|
||||
@@ -8,6 +8,7 @@ import { AutoCompleteContext, AutoCompleteConfig } from './auto-complete-context
|
||||
import { NormalSizes, NormalTypes } from '../utils/prop-types'
|
||||
import Loading from '../loading'
|
||||
import { pickChild } from '../utils/collections'
|
||||
import useCurrentState from '../utils/use-current-state'
|
||||
|
||||
export type AutoCompleteOption = {
|
||||
label: string
|
||||
@@ -31,6 +32,7 @@ interface Props {
|
||||
dropdownClassName?: string
|
||||
dropdownStyle?: object
|
||||
disableMatchWidth?: boolean
|
||||
disableFreeSolo?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
@@ -41,6 +43,7 @@ const defaultProps = {
|
||||
clearable: false,
|
||||
size: 'medium' as NormalSizes,
|
||||
disableMatchWidth: false,
|
||||
disableFreeSolo: false,
|
||||
className: '',
|
||||
}
|
||||
|
||||
@@ -83,11 +86,14 @@ const AutoComplete: React.FC<React.PropsWithChildren<AutoCompleteProps>> = ({
|
||||
dropdownClassName,
|
||||
dropdownStyle,
|
||||
disableMatchWidth,
|
||||
disableFreeSolo,
|
||||
...props
|
||||
}) => {
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const [state, setState] = useState<string>(customInitialValue)
|
||||
const resetTimer = useRef<number>()
|
||||
const [state, setState, stateRef] = useCurrentState<string>(customInitialValue)
|
||||
const [selectVal, setSelectVal] = useState<string>(customInitialValue)
|
||||
const [visible, setVisible] = useState<boolean>(false)
|
||||
|
||||
const [, searchChild] = pickChild(children, AutoCompleteSearching)
|
||||
@@ -112,18 +118,24 @@ const AutoComplete: React.FC<React.PropsWithChildren<AutoCompleteProps>> = ({
|
||||
|
||||
const updateValue = (val: string) => {
|
||||
if (disabled) return
|
||||
setSelectVal(val)
|
||||
onSelect && onSelect(val)
|
||||
setState(val)
|
||||
if (inputRef.current) {
|
||||
inputRef.current.focus()
|
||||
setVisible(false)
|
||||
}
|
||||
inputRef.current && inputRef.current.focus()
|
||||
}
|
||||
const updateVisible = (next: boolean) => setVisible(next)
|
||||
const onInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setVisible(true)
|
||||
onSearch && onSearch(event.target.value)
|
||||
setState(event.target.value)
|
||||
}
|
||||
const resetInputValue = () => {
|
||||
if (!disableFreeSolo) return
|
||||
if (!state || state === '') return
|
||||
if (state !== selectVal) {
|
||||
setState(selectVal)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
onChange && onChange(state)
|
||||
@@ -146,9 +158,15 @@ const AutoComplete: React.FC<React.PropsWithChildren<AutoCompleteProps>> = ({
|
||||
)
|
||||
|
||||
const toggleFocusHandler = (next: boolean) => {
|
||||
clearTimeout(resetTimer.current)
|
||||
setVisible(next)
|
||||
if (next) {
|
||||
onSearch && onSearch(state)
|
||||
onSearch && onSearch(stateRef.current)
|
||||
} else {
|
||||
resetTimer.current = window.setTimeout(() => {
|
||||
resetInputValue()
|
||||
clearTimeout(resetTimer.current)
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user