Skip to content

Commit

Permalink
feat(@uform/react): actions support clearErrors (#434)
Browse files Browse the repository at this point in the history
  • Loading branch information
janryWang authored Nov 26, 2019
1 parent 9440ac4 commit 551d74c
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 77 deletions.
49 changes: 35 additions & 14 deletions packages/react-shared-components/src/PreviewText.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useContext, createContext } from 'react'
import { isFn } from '@uform/shared'
import { isFn, isEqual } from '@uform/shared'
import { IPreviewTextProps } from './types'

export interface PreviewTextConfigProps {
Expand All @@ -14,20 +14,41 @@ export const PreviewText: React.FC<IPreviewTextProps> & {
const context = useContext(PreviewTextContext) || {}
let value: any
if (props.dataSource && props.dataSource.length) {
let find = props.dataSource.filter(({ value }) =>
Array.isArray(props.value)
? props.value.some(val => val == value)
: props.value == value
)
value = find.reduce((buf, item, index) => {
return buf.concat(item.label, index < find.length - 1 ? ', ' : '')
}, [])
if (Array.isArray(props.value)) {
value = props.value.map((val, index) => {
const finded = props.dataSource.find(item => isEqual(item.value, val))
if (finded) {
return (
<span key={index}>
{finded.label}
{index < props.value.length - 1 ? ' ,' : ''}
</span>
)
}
})
} else {
const fined = props.dataSource.find(item =>
isEqual(item.value, props.value)
)
if (fined) {
value = fined.label
}
}
} else {
value = Array.isArray(props.value)
? props.value.join(' ~ ')
: String(
props.value === undefined || props.value === null ? '' : props.value
if (Array.isArray(props.value)) {
value = props.value.map((val, index) => {
return (
<span key={index}>
{val}
{index < props.value.length - 1 ? '~' : ''}
</span>
)
})
} else {
value = String(
props.value === undefined || props.value === null ? '' : props.value
)
}
}
const placeholder = isFn(context.previewPlaceholder)
? context.previewPlaceholder(props)
Expand All @@ -44,7 +65,7 @@ export const PreviewText: React.FC<IPreviewTextProps> & {
value === undefined ||
(Array.isArray(value) && value.length === 0)
? placeholder || 'N/A'
: String(value)}
: value}
{props.addonTextAfter ? ' ' + props.addonTextAfter : ''}
{props.innerAfter ? ' ' + props.innerAfter : ''}
{props.addonAfter ? ' ' + props.addonAfter : ''}
Expand Down
171 changes: 110 additions & 61 deletions packages/react/src/__tests__/useField.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import React from 'react'
import { act, renderHook } from '@testing-library/react-hooks'
import FormContext from '../context';
import { render } from '@testing-library/react'
import FormContext from '../context'
import useForm from '../hooks/useForm'
import useField from '../hooks/useField'
import { createForm } from '..';
import { FormLifeCycle, LifeCycleTypes } from '@uform/core';
import { createForm } from '..'
import { FormLifeCycle, LifeCycleTypes } from '@uform/core'

describe('useField hook',()=>{
test('form is required', ()=>{
describe('useField hook', () => {
test('form is required', () => {
expect(() => {
useField({})
}).toThrow()
})

test('basic ', ()=>{
test('basic ', () => {
let globalForm
let globalGraph
const formInstance = createForm({
lifecycles: [
new FormLifeCycle(LifeCycleTypes.ON_FORM_GRAPH_CHANGE, (graph) => {
new FormLifeCycle(LifeCycleTypes.ON_FORM_GRAPH_CHANGE, graph => {
globalGraph = graph
})
]
Expand All @@ -28,111 +29,137 @@ describe('useField hook',()=>{
form: formInstance
})
globalForm = form
return <FormContext.Provider value={form}>{children}</FormContext.Provider>
return (
<FormContext.Provider value={form}>{children}</FormContext.Provider>
)
}

const { result } = renderHook(() => useField({ name: 'username' }), { wrapper: formWrapper })
const { result } = renderHook(() => useField({ name: 'username' }), {
wrapper: formWrapper
})
expect(result.current.form).toEqual(globalForm)
expect(result.current.state.props).toEqual({})
expect(result.current.state).toEqual({
...globalGraph.get('username').getState(),
errors: '',
mounted: false,
errors: [],
mounted: false
})
})

test('update', async ()=>{
test('update', async () => {
let globalForm
const formWrapper = ({ children }) => {
const form = useForm({})
globalForm = form
return <FormContext.Provider value={form}>{children}</FormContext.Provider>
return (
<FormContext.Provider value={form}>{children}</FormContext.Provider>
)
}

const { result } = renderHook(() => useField({ name: 'username' }), { wrapper: formWrapper })
const { result } = renderHook(() => useField({ name: 'username' }), {
wrapper: formWrapper
})
expect(result.current.state.value).toEqual(undefined)
act(() => {
globalForm.setFormState(state => state.values.username = 'abcd')
globalForm.setFormState(state => (state.values.username = 'abcd'))
})

expect(result.current.state.value).toEqual('abcd')
})

test('mounted change', async ()=>{
test('mounted change', async () => {
const formWrapper = ({ children }) => {
const form = useForm({})
return <FormContext.Provider value={form}>{children}</FormContext.Provider>
return (
<FormContext.Provider value={form}>{children}</FormContext.Provider>
)
}

const { result, rerender } = renderHook(() => useField({ name: 'username' }), { wrapper: formWrapper })
const { result, rerender } = renderHook(
() => useField({ name: 'username' }),
{ wrapper: formWrapper }
)
expect(result.current.state.mounted).toEqual(false)
rerender()
expect(result.current.state.mounted).toEqual(true)
})

test('dirty', async ()=>{
test('dirty', async () => {
const formWrapper = ({ children }) => {
const formInstance = createForm({
lifecycles: [
new FormLifeCycle(LifeCycleTypes.ON_FIELD_CHANGE, (field) => {

})
new FormLifeCycle(LifeCycleTypes.ON_FIELD_CHANGE, field => {})
]
})
const form = useForm({
form: formInstance
})
return <FormContext.Provider value={form}>{children}</FormContext.Provider>
return (
<FormContext.Provider value={form}>{children}</FormContext.Provider>
)
}

const initialProps = {
name: 'username',
props: { disabled: true },
required: false,
editable: true,
rules: [],
rules: []
}
const { result, rerender } = renderHook(() => useField(initialProps), { wrapper: formWrapper })
const { result, rerender } = renderHook(() => useField(initialProps), {
wrapper: formWrapper
})
expect(result.current.props).toEqual({ disabled: true })
expect(result.current.state.required).toEqual(false)
expect(result.current.state.editable).toEqual(true)
expect(result.current.state.rules).toEqual([])
expect(result.current.state.editable).toEqual(true)
expect(result.current.state.rules).toEqual([])

initialProps.required = true
initialProps.editable = false
initialProps.props = { disabled: false }
initialProps.rules = [() => ({ type: 'warning', message: 'warning msg' })]

expect(result.current.props).toEqual({ disabled: true })
expect(result.current.state.required).toEqual(false)
expect(result.current.state.editable).toEqual(true)
expect(result.current.state.rules).toEqual([])
expect(result.current.state.editable).toEqual(true)
expect(result.current.state.rules).toEqual([])

rerender()

expect(result.current.props).toEqual(initialProps.props)
expect(result.current.state.required).toEqual(initialProps.required)
expect(result.current.state.editable).toEqual(initialProps.editable)
expect(result.current.state.rules).toEqual([...initialProps.rules, { required: true }])
expect(result.current.state.rules).toEqual([
...initialProps.rules,
{ required: true }
])
})

test('extented mutator', ()=>{
test('extented mutator', () => {
const formWrapper = ({ children }) => {
const form = useForm({})
return <FormContext.Provider value={form}>{children}</FormContext.Provider>
return (
<FormContext.Provider value={form}>{children}</FormContext.Provider>
)
}

const getValueFromEvent = (e) => (e.target.value)
const eventValue = { target: { value: 'abc' }}
const { result: result1, rerender: rerender1 } = renderHook(() => useField({ name: 'username' }), { wrapper: formWrapper })
const getValueFromEvent = e => e.target.value
const eventValue = { target: { value: 'abc' } }
const { result: result1, rerender: rerender1 } = renderHook(
() => useField({ name: 'username' }),
{ wrapper: formWrapper }
)
expect(result1.current.state.value).toEqual(undefined)
act(() => {
result1.current.mutators.change(eventValue)
})
rerender1()
expect(result1.current.state.value).toEqual(eventValue)

const { result: result2, rerender: rerender2 } = renderHook(() => useField({ name: 'username', getValueFromEvent }), { wrapper: formWrapper })
const { result: result2, rerender: rerender2 } = renderHook(
() => useField({ name: 'username', getValueFromEvent }),
{ wrapper: formWrapper }
)
expect(result2.current.state.value).toEqual(undefined)
act(() => {
result2.current.mutators.change(eventValue)
Expand All @@ -141,61 +168,83 @@ describe('useField hook',()=>{
expect(result2.current.state.value).toEqual('abc')
})

test('triggerType mutator onChange', async()=>{
test('triggerType mutator onChange', async () => {
const formWrapper = ({ children }) => {
const form = useForm({})
return <FormContext.Provider value={form}>{children}</FormContext.Provider>
return (
<FormContext.Provider value={form}>{children}</FormContext.Provider>
)
}

const fieldProps = { name: 'username', required: true }
const { result: result1, rerender } = renderHook(() => useField(fieldProps), { wrapper: formWrapper })
expect(result1.current.state.errors).toEqual('')
const fieldProps = { name: 'username', required: true }
const { result: result1, rerender } = renderHook(
() => useField(fieldProps),
{ wrapper: formWrapper }
)
expect(result1.current.state.errors).toEqual([])
expect(result1.current.state.value).toEqual(undefined)
rerender()
act(() => {
result1.current.mutators.change('')
})

// await waitForNextUpdate1()
expect(result1.current.state.value).toEqual('')
expect(result1.current.state.errors).toEqual('')
expect(result1.current.state.errors).toEqual([])

const { result: result2, waitForNextUpdate: waitForNextUpdate2 } = renderHook(() => useField({ ...fieldProps, triggerType: 'onChange' }), { wrapper: formWrapper })
expect(result2.current.state.errors).toEqual('')
const {
result: result2,
waitForNextUpdate: waitForNextUpdate2
} = renderHook(() => useField({ ...fieldProps, triggerType: 'onChange' }), {
wrapper: formWrapper
})
expect(result2.current.state.errors).toEqual([])
expect(result2.current.state.value).toEqual(undefined)

act(() => {
result2.current.mutators.change('')
})

await waitForNextUpdate2()
expect(result2.current.state.value).toEqual('')
expect(result2.current.state.errors).toEqual('This field is required')
const { queryByText } = render(<div>{result2.current.state.errors}</div>)
expect(queryByText('This field is required')).toBeVisible()
})

test('triggerType mutator onBlur', async()=>{
test('triggerType mutator onBlur', async () => {
const formWrapper = ({ children }) => {
const form = useForm({})
return <FormContext.Provider value={form}>{children}</FormContext.Provider>
return (
<FormContext.Provider value={form}>{children}</FormContext.Provider>
)
}

const fieldProps = { name: 'username', required: true }
const { result: result1, rerender } = renderHook(() => useField(fieldProps), { wrapper: formWrapper })
expect(result1.current.state.errors).toEqual('')
const fieldProps = { name: 'username', required: true }
const { result: result1, rerender } = renderHook(
() => useField(fieldProps),
{ wrapper: formWrapper }
)
expect(result1.current.state.errors).toEqual([])
rerender()
act(() => {
result1.current.mutators.blur()
})

expect(result1.current.state.errors).toEqual('')

const { result: result2, waitForNextUpdate: waitForNextUpdate2 } = renderHook(() => useField({ ...fieldProps, triggerType: 'onBlur' }), { wrapper: formWrapper })
expect(result2.current.state.errors).toEqual('')
expect(result1.current.state.errors).toEqual([])

const {
result: result2,
waitForNextUpdate: waitForNextUpdate2
} = renderHook(() => useField({ ...fieldProps, triggerType: 'onBlur' }), {
wrapper: formWrapper
})
expect(result2.current.state.errors).toEqual([])

act(() => {
result2.current.mutators.blur()
})
await waitForNextUpdate2()
expect(result2.current.state.errors).toEqual('This field is required')
const { queryByText } = render(<div>{result2.current.state.errors}</div>)
expect(queryByText('This field is required')).toBeVisible()
})
})
})
Loading

0 comments on commit 551d74c

Please sign in to comment.