feat: textarea auto resize (#3695)
* feat: improve textarea user experience with autoresize * chore: remove log * chore: update test * chore: update test and cleanup logic useEffect
This commit is contained in:
parent
c62b6e9842
commit
ba3c07eba8
@ -1,9 +1,8 @@
|
||||
import React from 'react'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { render, screen, act } from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
import { TextArea } from './index'
|
||||
|
||||
// Mock the styles import
|
||||
jest.mock('./styles.scss', () => ({}))
|
||||
|
||||
describe('@joi/core/TextArea', () => {
|
||||
@ -31,4 +30,40 @@ describe('@joi/core/TextArea', () => {
|
||||
const textareaElement = screen.getByTestId('custom-textarea')
|
||||
expect(textareaElement).toHaveAttribute('rows', '5')
|
||||
})
|
||||
|
||||
it('should auto resize the textarea based on minResize', () => {
|
||||
render(<TextArea autoResize minResize={10} />)
|
||||
|
||||
const textarea = screen.getByRole('textbox') as HTMLTextAreaElement
|
||||
|
||||
Object.defineProperty(textarea, 'scrollHeight', {
|
||||
value: 20,
|
||||
writable: true,
|
||||
})
|
||||
|
||||
act(() => {
|
||||
textarea.value = 'Short text'
|
||||
textarea.dispatchEvent(new Event('input', { bubbles: true }))
|
||||
})
|
||||
|
||||
expect(textarea.style.height).toBe('10px')
|
||||
})
|
||||
|
||||
it('should auto resize the textarea based on maxResize', () => {
|
||||
render(<TextArea autoResize maxResize={40} />)
|
||||
|
||||
const textarea = screen.getByRole('textbox') as HTMLTextAreaElement
|
||||
|
||||
Object.defineProperty(textarea, 'scrollHeight', {
|
||||
value: 100,
|
||||
writable: true,
|
||||
})
|
||||
|
||||
act(() => {
|
||||
textarea.value = 'A very long text that should exceed max height'
|
||||
textarea.dispatchEvent(new Event('input', { bubbles: true }))
|
||||
})
|
||||
|
||||
expect(textarea.style.height).toBe('40px')
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,19 +1,41 @@
|
||||
import React, { ReactNode, forwardRef } from 'react'
|
||||
import React, { forwardRef, useRef, useEffect } from 'react'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
import './styles.scss'
|
||||
import { ScrollArea } from '../ScrollArea'
|
||||
|
||||
type ResizeProps = {
|
||||
autoResize?: boolean
|
||||
minResize?: number
|
||||
maxResize?: number
|
||||
}
|
||||
|
||||
export interface TextAreaProps
|
||||
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
||||
extends ResizeProps,
|
||||
React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
||||
|
||||
const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
||||
({ className, ...props }, ref) => {
|
||||
(
|
||||
{ autoResize, minResize = 80, maxResize = 250, className, ...props },
|
||||
ref
|
||||
) => {
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (autoResize && textareaRef.current) {
|
||||
const textarea = textareaRef.current
|
||||
textarea.style.height = 'auto'
|
||||
const scrollHeight = textarea.scrollHeight
|
||||
const newHeight = Math.min(maxResize, Math.max(minResize, scrollHeight))
|
||||
textarea.style.height = `${newHeight}px`
|
||||
textarea.style.overflow = newHeight >= maxResize ? 'auto' : 'hidden'
|
||||
}
|
||||
}, [props.value, autoResize, minResize, maxResize])
|
||||
|
||||
return (
|
||||
<div className="textarea__wrapper">
|
||||
<textarea
|
||||
className={twMerge('textarea', className)}
|
||||
ref={ref}
|
||||
ref={autoResize ? textareaRef : ref}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -36,7 +36,7 @@ const ModelConfigInput = ({
|
||||
<TextArea
|
||||
placeholder={placeholder}
|
||||
onChange={(e) => onValueChanged?.(e.target.value)}
|
||||
cols={50}
|
||||
autoResize
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
@ -247,7 +247,7 @@ const ThreadRightPanel = () => {
|
||||
id="assistant-instructions"
|
||||
placeholder="Eg. You are a helpful assistant."
|
||||
value={activeThread?.assistants[0].instructions ?? ''}
|
||||
rows={8}
|
||||
autoResize
|
||||
onChange={onAssistantInstructionChanged}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user