Skip to content

Commit

Permalink
Textarea Component fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
AlejoYarce committed Dec 9, 2024
1 parent ae55d44 commit cfaee4d
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 23 deletions.
4 changes: 2 additions & 2 deletions src/components/Input/Input.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const meta = {
export default meta
type Story = StoryObj<typeof meta>

export const RequiredtInput: Story = {
export const RequiredInput: Story = {
args: {
label: 'Required Input',
caption: 'caption',
Expand All @@ -25,7 +25,7 @@ export const RequiredtInput: Story = {
},
}

export const OptionaltInput: Story = {
export const OptionalInput: Story = {
args: {
label: 'Optional Input',
caption: 'caption',
Expand Down
1 change: 0 additions & 1 deletion src/components/Textarea/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ type TextareaProps = Omit<
label: string
caption?: string
placeholder?: string
helperText?: string
errorMessage?: string
required?: boolean
disabled?: boolean
Expand Down
27 changes: 23 additions & 4 deletions src/components/Textarea/Textarea.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,16 @@ const meta = {
export default meta
type Story = StoryObj<typeof meta>

export const RequiredtTextarea: Story = {
export const RequiredTextarea: Story = {
args: {
label: 'Required Textarea',
caption: 'caption',
placeholder: 'placeholder',
helperText: 'You have 200 characters remaining',
required: true,
},
}

export const OptionaltTextarea: Story = {
export const OptionalTextarea: Story = {
args: {
label: 'Optional Textarea',
caption: 'caption',
Expand Down Expand Up @@ -54,7 +53,7 @@ export const DefaultValue: Story = {
},
}

export const WithError: Story = {
export const ErrorMessage: Story = {
args: {
label: 'Label',
caption: 'caption',
Expand All @@ -64,6 +63,26 @@ export const WithError: Story = {
},
}

export const MaxLength: Story = {
args: {
label: 'Label',
caption: 'caption',
placeholder: 'placeholder',
maxLength: 200,
required: true,
},
}

export const MinLength: Story = {
args: {
label: 'Label',
caption: 'caption',
placeholder: 'placeholder',
minLength: 5,
required: true,
},
}

export const Disabled: Story = {
args: {
label: 'Label',
Expand Down
3 changes: 2 additions & 1 deletion src/components/Textarea/TextareaDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ const TextareaDemo = () => (
caption='Caption'
placeholder='placeholder'
defaultValue='Default Value'
helperText='You have 200 characters remaining'
minLength={5}
maxLength={200}
required
/>
<Textarea
Expand Down
55 changes: 49 additions & 6 deletions src/components/Textarea/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'
import { Field } from '@chakra-ui/react'
import { TextareaProps } from './types'
import {
Expand All @@ -15,31 +15,64 @@ const Textarea = ({
label,
caption,
placeholder,
helperText,
errorMessage,
required,
disabled,
size = 'default',
defaultValue = '',
onChange,
minLength,
maxLength,
...rest
}: TextareaProps) => {
const [value, setValue] = useState(defaultValue)
const [minLengthError, setMinLengthError] = useState(false)
const [maxLengthError, setMaxLengthError] = useState(false)
const [helperText, setHelperText] = useState('')

useEffect(() => {
const { length } = defaultValue
if (minLength && length < minLength) {
setMinLengthError(length < minLength)
setMaxLengthError(false)
setHelperText(`Enter at least ${minLength - length} characters`)
}
if (maxLength && length > maxLength) {
setMinLengthError(false)
setMaxLengthError(length > maxLength)
setHelperText(`You have ${maxLength - length} characters remaining`)
}
}, [])

const handleOnChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setValue(e.target.value)

const { length } = e.target.value
if (minLength && maxLength) {
setMinLengthError(length < minLength)
setMaxLengthError(length > maxLength)
setHelperText(`You have ${maxLength - length} characters remaining`)
} else if (minLength) {
setMinLengthError(length < minLength)
setMaxLengthError(false)
setHelperText(length < minLength ? `Enter at least ${minLength - length} characters` : '')
} else if (maxLength) {
setMaxLengthError(length > maxLength)
setMinLengthError(false)
setHelperText(`You have ${maxLength - length} characters remaining`)
}

if (onChange) {
onChange(e)
}
}

return (
<TextareaContainer size={size}>
{errorMessage ? (
<ErrorBar size={size} hasHelperText={!!helperText} />
{errorMessage || minLengthError || maxLengthError ? (
<ErrorBar size={size} hasHelperText={!!helperText} hasErrorMessage={!!errorMessage} />
) : null}
<Field.Root required={required} invalid={!!errorMessage} gap='0'>
<Field.Root required={required} invalid={!!errorMessage || minLengthError || maxLengthError} gap='0'>
{label ? (
<StyledFieldLabel size={size} disabled={disabled} aria-label={label}>
<Field.RequiredIndicator aria-label='required' />
Expand Down Expand Up @@ -72,7 +105,17 @@ const Textarea = ({
}}
{...rest}
/>
{helperText ? (
{minLengthError ? (
<StyledFieldErrorMessage size={size} style={{ marginTop: '8px' }}>
Enter at least {minLength} characters
</StyledFieldErrorMessage>
) : null}
{maxLengthError ? (
<StyledFieldErrorMessage size={size} style={{ marginTop: '8px' }}>
You&apos;ve reached the character limit
</StyledFieldErrorMessage>
) : null}
{helperText && !maxLengthError && !minLengthError ? (
<StyledFieldHelperText aria-label={helperText}>
{helperText}
</StyledFieldHelperText>
Expand Down
25 changes: 17 additions & 8 deletions src/components/Textarea/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@ export const TextareaContainer = styled.div<{ size: 'small' | 'default' }>`
gap: ${({ size }) => (size === 'small' ? '12px' : '16px')};
margin-bottom: 20px;
`
const getHeight = (size: 'small' | 'default', hasHelperText: boolean) => {
const getHeight = (size: 'small' | 'default', hasHelperText: boolean, hasErrorMessage: boolean) => {
const isSmall = size === 'small'
if (hasHelperText && hasErrorMessage) {
return isSmall ? '192px' : '208px'
}

if (hasHelperText) {
return isSmall ? '192px' : '204px'
return isSmall ? '174px' : '186px'
}

if (hasErrorMessage) {
return isSmall ? '168px' : '180px'
}

return isSmall ? '169px' : '180px'
Expand All @@ -22,14 +30,15 @@ const getHeight = (size: 'small' | 'default', hasHelperText: boolean) => {
export const ErrorBar = styled.div<{
size: 'small' | 'default'
hasHelperText: boolean
hasErrorMessage: boolean
}>`
width: 3px;
height: 100%;
min-height: ${({ size, hasHelperText }) => getHeight(size, hasHelperText)};
min-height: ${({ size, hasHelperText, hasErrorMessage }) => getHeight(size, hasHelperText, hasErrorMessage)};
background-color: ${getThemedColor('error', 500)};
`

export const StyledFieldLabel = styled(Field.Label)<{
export const StyledFieldLabel = styled(Field.Label) <{
size: 'small' | 'default'
disabled?: boolean
}>`
Expand All @@ -41,11 +50,11 @@ export const StyledFieldLabel = styled(Field.Label)<{
.chakra-field__requiredIndicator {
color: ${({ disabled }) =>
disabled ? getThemedColor('neutral', 600) : getThemedColor('error', 500)};
disabled ? getThemedColor('neutral', 600) : getThemedColor('error', 500)};
}
`

export const StyledFieldCaption = styled(Field.HelperText)<{
export const StyledFieldCaption = styled(Field.HelperText) <{
size: 'small' | 'default'
disabled?: boolean
}>`
Expand All @@ -63,7 +72,7 @@ export const StyledFieldHelperText = styled(Field.HelperText)`
margin-top: 8px;
`

export const StyledFieldErrorMessage = styled(Field.ErrorText)<{
export const StyledFieldErrorMessage = styled(Field.ErrorText) <{
size: 'small' | 'default'
}>`
color: ${getThemedColor('error', 500)};
Expand All @@ -73,7 +82,7 @@ export const StyledFieldErrorMessage = styled(Field.ErrorText)<{
margin-top: 2px;
`

export const StyledTextarea = styled(Textarea)<{ size: 'small' | 'default' }>`
export const StyledTextarea = styled(Textarea) <{ size: 'small' | 'default' }>`
height: 104px;
width: 100%;
border-radius: 4px;
Expand Down
1 change: 0 additions & 1 deletion src/components/Textarea/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export type TextareaProps = Omit<
label: string
caption?: string
placeholder?: string
helperText?: string
errorMessage?: string
required?: boolean
disabled?: boolean
Expand Down

0 comments on commit cfaee4d

Please sign in to comment.