Skip to content

Commit

Permalink
Implement TextAreaWithClear
Browse files Browse the repository at this point in the history
  • Loading branch information
kitsuyui committed Dec 4, 2023
1 parent f368620 commit 9db4c23
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 3 deletions.
19 changes: 19 additions & 0 deletions packages/textfield/src/ClearButtonProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from 'react'
import React from 'react'

import { TextArea, WrapperProps as TextAreaWrapperProps } from './TextArea'
import { TextField, WrapperProps as TextFieldWrapperProps } from './TextField'

const TextContext = createContext('')
Expand Down Expand Up @@ -54,6 +55,24 @@ export const TextFieldWithClear = (props: TextFieldWrapperProps) => {
return <TextField {...props} value={text} onInputChunk={handleInputChunk} />
}

export const TextAreaWithClear = (props: TextAreaWrapperProps) => {
const text = useContext(TextContext)
const setText = useContext(SetTextContext)
const onInputChunk = props.onInputChunk
const handleInputChunk = useCallback(
(text: string) => {
setText(text)
onInputChunk?.(text)
},
[setText, onInputChunk]
)
useEffect(() => {
setText(props.value ?? '')
}, [props.value, setText])

return <TextArea {...props} value={text} onInputChunk={handleInputChunk} />
}

export const ClearButton = forwardRef<HTMLButtonElement, WrapperProps>(
(props, ref) => {
const propsExcludedWrapperProps = Object.assign({}, props)
Expand Down
7 changes: 6 additions & 1 deletion packages/textfield/src/TextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
ComponentPropsWithoutRef,
forwardRef,
useCallback,
useEffect,
useRef,
useState,
} from 'react'
Expand All @@ -24,7 +25,7 @@ type excludeProps =
| 'onCompositionEnd'
| 'value'

type WrapperProps = Omit<WrappedProps, excludeProps> & alternateProps
export type WrapperProps = Omit<WrappedProps, excludeProps> & alternateProps

export const TextArea = forwardRef<HTMLTextAreaElement, WrapperProps>(
(props, ref) => {
Expand All @@ -39,6 +40,10 @@ export const TextArea = forwardRef<HTMLTextAreaElement, WrapperProps>(
delete propsExcludedWrapperProps.onInputChunk
delete propsExcludedWrapperProps.onChangeInputting

useEffect(() => {
setInternalValue(props.value ?? '')
}, [props.value])

const handleChange = useCallback(() => {
const text = innerRef.current.value
setInternalValue(text)
Expand Down
4 changes: 2 additions & 2 deletions packages/textfield/src/TextField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type WrappedProps = ComponentPropsWithoutRef<'input'>
type alternateProps = {
onInputChunk?: (value: string) => void
onChangeInputting?: (inputting: boolean) => void
value: string
value?: string
}

type excludeProps =
Expand All @@ -42,7 +42,7 @@ export const TextField = forwardRef<HTMLInputElement, WrapperProps>(
delete propsExcludedWrapperProps.onChangeInputting

useEffect(() => {
setInternalValue(props.value)
setInternalValue(props.value ?? '')
}, [props.value])

const handleChange = useCallback(() => {
Expand Down
32 changes: 32 additions & 0 deletions packages/textfield/src/test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React from 'react'

import {
TextFieldWithClear,
TextAreaWithClear,
ClearButtonProvider,
ClearButton,
} from './ClearButtonProvider'
Expand Down Expand Up @@ -111,3 +112,34 @@ test('render TextField with ClearButton', async () => {
// handleInputChunk is also called when reset button is clicked.
expect(handleInputChunk).toBeCalledTimes(secondMessage.length + 1)
})

test('render TextArea with ClearButton', async () => {
const firstMessage = 'Hello'
const secondMessage = ', World'
const handleInputChunk = jest.fn()

// initial render
render(
<ClearButtonProvider>
<TextAreaWithClear value={firstMessage} onInputChunk={handleInputChunk} />
<ClearButton>Clear</ClearButton>
</ClearButtonProvider>
)

const element = screen.getByText(firstMessage)
expect(element).toBeInstanceOf(HTMLTextAreaElement)
expect(element).toHaveProperty('value', firstMessage)

// click and type
await userEvent.click(element)
await userEvent.type(element, secondMessage)
expect(element).toHaveProperty('value', firstMessage + secondMessage)
expect(handleInputChunk).toBeCalledTimes(secondMessage.length)

// click reset button
const resetButton = screen.getByText('Clear')
await userEvent.click(resetButton)
expect(element).toHaveProperty('value', '')
// handleInputChunk is also called when reset button is clicked.
expect(handleInputChunk).toBeCalledTimes(secondMessage.length + 1)
})

0 comments on commit 9db4c23

Please sign in to comment.