Skip to content

Commit

Permalink
feat: textarea auto resize (janhq#3695)
Browse files Browse the repository at this point in the history
* feat: improve textarea user experience with autoresize

* chore: remove log

* chore: update test

* chore: update test and cleanup logic useEffect
  • Loading branch information
urmauur authored Sep 19, 2024
1 parent c62b6e9 commit ba3c07e
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 9 deletions.
39 changes: 37 additions & 2 deletions joi/src/core/TextArea/TextArea.test.tsx
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand Down Expand Up @@ -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')
})
})
32 changes: 27 additions & 5 deletions joi/src/core/TextArea/index.tsx
Original file line number Diff line number Diff line change
@@ -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>
Expand Down
2 changes: 1 addition & 1 deletion web/containers/ModelConfigInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const ModelConfigInput = ({
<TextArea
placeholder={placeholder}
onChange={(e) => onValueChanged?.(e.target.value)}
cols={50}
autoResize
value={value}
disabled={disabled}
/>
Expand Down
2 changes: 1 addition & 1 deletion web/screens/Thread/ThreadRightPanel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down

0 comments on commit ba3c07e

Please sign in to comment.