Skip to content

Commit

Permalink
Fixed invalid additionalInfo validation in enroll offer modal (#2918)
Browse files Browse the repository at this point in the history
* Fixed invalid additionalInfo validation in enroll offer modal

* Fixed code coverage

* Refactored code for better clarity

* Fixed some ternary operators

* Fixed incorrect empty addtionalInfo sending in request
  • Loading branch information
VKormylo authored Dec 3, 2024
1 parent c91b121 commit dc567c7
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 23 deletions.
9 changes: 8 additions & 1 deletion src/components/app-text-area/AppTextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { styles } from '~/components/app-text-area/AppTextArea.styles'
interface AppTextAreaProps
extends Omit<TextFieldProps, 'error' | 'helperText'> {
maxLength?: number
minLength?: number
errorMsg?: string
value?: string
textFieldStyles?: SxProps
Expand All @@ -24,6 +25,7 @@ const AppTextArea: FC<AppTextAreaProps> = ({
minRows = 4,
maxRows = 4,
maxLength,
minLength,
title,
value,
sx,
Expand All @@ -35,6 +37,11 @@ const AppTextArea: FC<AppTextAreaProps> = ({
const textLengthStyle = isRightAligned
? styles.textLengthRight
: styles.textLength
const isLengthTooShort =
minLength && value?.length < minLength && value?.length !== 0
const isLengthTooLong = value?.length === maxLength
const isLengthValid =
isLengthTooShort || isLengthTooLong ? 'error' : 'primary.300'

return (
<Box sx={spliceSx(styles.container, sx)}>
Expand All @@ -50,7 +57,7 @@ const AppTextArea: FC<AppTextAreaProps> = ({
/>
{maxLength && (
<Typography
color={value?.length === maxLength ? 'error' : 'primary.300'}
color={isLengthValid}
sx={textLengthStyle}
variant={TypographyVariantEnum.Body2}
>
Expand Down
5 changes: 4 additions & 1 deletion src/constants/translations/en/offer-details-page.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,8 @@
"offerBanner": {
"saveOfferButton": "Save Offer"
},
"closeOffer": "Are you sure that you want to close this offer? You can't reopen this offer."
"closeOffer": "Are you sure that you want to close this offer? You can't reopen this offer.",
"errors": {
"additionalInfo": "Additional information cannot be shorter than 30 symbols"
}
}
5 changes: 4 additions & 1 deletion src/constants/translations/uk/offer-details-page.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,8 @@
"offerBanner": {
"saveOfferButton": "Зберегти пропозицію"
},
"closeOffer": "Ви впевнені, що бажаєте закрити цю пропозицію? Ви не можете повторно відкрити цю пропозицію."
"closeOffer": "Ви впевнені, що бажаєте закрити цю пропозицію? Ви не можете повторно відкрити цю пропозицію.",
"errors": {
"additionalInfo": "Додаткова інформація не може бути коротшою за 30 символів"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,9 @@ export const styles = {
p: '12px 24px',
width: { xs: '100%', sm: 'fit-content' },
minWidth: '280px'
},
errorText: {
ml: '12.5px',
mt: '3px'
}
}
74 changes: 54 additions & 20 deletions src/containers/offer-details/enroll-offer/EnrollOffer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ import {
ErrorResponse,
Offer,
EnrollOfferForm,
ButtonTypeEnum
ButtonTypeEnum,
TypographyVariantEnum
} from '~/types'
import { openAlert } from '~/redux/features/snackbarSlice'
import { getErrorKey } from '~/utils/get-error-key'
import { textField } from '~/utils/validations/common'
import { Typography } from '@mui/material'

interface EnrollOfferProps {
offer: Offer
Expand Down Expand Up @@ -80,16 +83,35 @@ const EnrollOffer: FC<EnrollOfferProps> = ({ offer, enrollOffer }) => {
onResponseError: handleResponseError
})

const { data, handleInputChange, handleNonInputValueChange, handleSubmit } =
useForm<EnrollOfferForm>({
initialValues: {
proficiencyLevel: offer.proficiencyLevel[0],
price: offer.price,
additionalInfo: '',
title: offer.title
},
onSubmit: fetchData
})
const validateAdditionalInfo = (additionalInfoValue: string) => {
if (additionalInfoValue.length === 0) {
delete data.additionalInfo
}
if (additionalInfoValue.length < 30 && additionalInfoValue.length !== 0) {
return textField(30, 1000)
}
}

const validations = {
additionalInfo: validateAdditionalInfo
}

const {
data,
errors,
handleInputChange,
handleNonInputValueChange,
handleSubmit
} = useForm<EnrollOfferForm>({
initialValues: {
proficiencyLevel: offer.proficiencyLevel[0],
price: offer.price,
additionalInfo: '',
title: offer.title
},
validations: validations,
onSubmit: fetchData
})

const levelOptions = offer.proficiencyLevel.map((level) => ({
title: level,
Expand Down Expand Up @@ -135,15 +157,27 @@ const EnrollOffer: FC<EnrollOfferProps> = ({ offer, enrollOffer }) => {
onChange={handleFieldChange('price')}
title={t('offerDetailsPage.enrollOffer.labels.preferredPrice')}
/>
<AppTextArea
fullWidth
label={t('offerDetailsPage.enrollOffer.labels.info')}
maxLength={1000}
onChange={handleInputChange('additionalInfo')}
sx={styles.textArea}
title={t('offerDetailsPage.enrollOffer.inputs.info')}
value={data.additionalInfo}
/>
<Box>
<AppTextArea
fullWidth
label={t('offerDetailsPage.enrollOffer.labels.info')}
maxLength={1000}
minLength={30}
onChange={handleInputChange('additionalInfo')}
sx={styles.textArea}
title={t('offerDetailsPage.enrollOffer.inputs.info')}
value={data.additionalInfo}
/>
{errors.additionalInfo && (
<Typography
color='error'
sx={styles.errorText}
variant={TypographyVariantEnum.Caption}
>
{t('offerDetailsPage.errors.additionalInfo')}
</Typography>
)}
</Box>
<AppButton
loading={loading}
sx={styles.button}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,25 @@ describe('EnrollOffer', () => {
expect(levelSelect.value).toBe(newLevel)
})

it('should display error message', () => {
const newAdditionalInfo = 'Some text'
const additionalInfoInput = screen.getByLabelText(
'offerDetailsPage.enrollOffer.labels.info'
)
fireEvent.change(additionalInfoInput, {
target: { value: newAdditionalInfo }
})
expect(additionalInfoInput.value).toBe(newAdditionalInfo)

const button = screen.getByText('button.createCooperation')
waitFor(() => fireEvent.click(button))

const errorMessage = screen.getByText(
'offerDetailsPage.errors.additionalInfo'
)
expect(errorMessage).toBeInTheDocument()
})

it('should send form', () => {
const button = screen.getByText('button.createCooperation')

Expand Down

0 comments on commit dc567c7

Please sign in to comment.