From ff4c522045b739968b93e1f71886102791f5a77b Mon Sep 17 00:00:00 2001 From: nebby2105 Date: Tue, 17 Dec 2024 20:53:36 +0200 Subject: [PATCH 1/8] updated AcceptCooperationClosing --- .../translations/en/cooperation-details.json | 8 +- .../translations/uk/cooperation-details.json | 8 +- .../AcceptCooperationClosing.styles.ts | 20 +++++ .../AcceptCooperationClosing.tsx | 79 +++++++++++++++++-- .../CooperationDetails.tsx | 1 + 5 files changed, 108 insertions(+), 8 deletions(-) diff --git a/src/constants/translations/en/cooperation-details.json b/src/constants/translations/en/cooperation-details.json index 83e61f3df..edab6cc07 100644 --- a/src/constants/translations/en/cooperation-details.json +++ b/src/constants/translations/en/cooperation-details.json @@ -33,5 +33,11 @@ "closingMessage1": " started a closing process for the current cooperation. You will have ", "accessDuration": "1 month of access", "closingMessage2": " to study materials after the cooperation has been closed.", - "acceptBtn": "Accept" + "acceptBtn": "Accept", + "declineBtn": "Decline", + "submitBtn": "Submit", + "inputFieldPlaceholder": "Enter your reason for declining", + "InputFieldLabel": "Please provide a reason for declining to close the cooperation.", + "inputError": "This field is required.", + "submitMessage": "Your reason has been successfully submitted to the opposite party." } diff --git a/src/constants/translations/uk/cooperation-details.json b/src/constants/translations/uk/cooperation-details.json index 6bffddeee..f4ebc2aba 100644 --- a/src/constants/translations/uk/cooperation-details.json +++ b/src/constants/translations/uk/cooperation-details.json @@ -33,5 +33,11 @@ "closingMessage1": " ініціював(-ла) процес припинення співпраці. У вас залишиться доступ до матеріалів поточної співпраці протягом ", "accessDuration": "1 місяця", "closingMessage2": " з моменту закриття.", - "acceptBtn": "Прийняти" + "acceptBtn": "Прийняти", + "declineBtn": "Відхилити", + "submitBtn": "Підтвердити", + "inputFieldPlaceholder": "Введіть причину відмови", + "InputFieldLabel": "Будь ласка, вкажіть причину відмови від закриття співпраці.", + "inputError": "Це поле є обов'язковим для заповнення.", + "submitMessage": "Вашу причину було успішно надіслано протилежній стороні." } diff --git a/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.styles.ts b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.styles.ts index 5561f595c..02df2ced2 100644 --- a/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.styles.ts +++ b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.styles.ts @@ -1,5 +1,25 @@ +import palette from '~/styles/app-theme/app.pallete' + export const styles = { boldText: { fontWeight: 500 + }, + inputBox: { + display: 'flex', + flexDirection: 'column', + gap: '10px', + mb: '8px' + }, + inputField: { + display: 'flex', + gap: '16px', + width: '100%', + height: '50px' + }, + input: { + flex: 1 + }, + textGray: { + color: palette.basic.darkGray } } diff --git a/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx index 2bf0bb493..e910d157e 100644 --- a/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx +++ b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx @@ -1,28 +1,68 @@ import { ErrorOutlineRounded } from '@mui/icons-material' -import { Typography } from '@mui/material' +import { Box, Typography } from '@mui/material' import { useTranslation } from 'react-i18next' import CooperationActionBanner from '~/containers/my-cooperations/cooperation-action-banner/CooperationActionBanner' import Button from '~/design-system/components/button/Button' import { styles } from './AcceptCooperationClosing.styles' +import { useState } from 'react' +import InputField from '~/design-system/components/input-field/InputField' +import { InputFieldVariantEnum } from '~/design-system/components/input-field/InputField.constants' interface AcceptCooperationClosureProps { user: string onAccept: () => void + onReasonSubmit: (reason: string) => void } const AcceptCooperationClosing: React.FC = ({ user, - onAccept + onAccept, + onReasonSubmit }) => { const { t } = useTranslation() + const [isInputShown, setIsInputShown] = useState(false) + const [declineReason, setDeclineReason] = useState('') + const [inputError, setInputError] = useState(false) + const [isReasonSubmitted, setIsReasonSubmitted] = useState(false) + + const handleDecline = () => { + setIsInputShown(true) + } + + const handleReasonSubmit = () => { + if (!declineReason.trim()) { + setInputError(true) + } else { + onReasonSubmit(declineReason.trim()) + setDeclineReason('') + setIsInputShown(false) + setIsReasonSubmitted(true) + } + } + + const handleInputChange = ({ + target: { value } + }: React.ChangeEvent) => { + setDeclineReason(value) + if (inputError) setInputError(false) + } + + const isHelperTextShown = inputError + ? t('cooperationDetailsPage.inputError') + : '' return ( - {t('cooperationDetailsPage.acceptBtn')} - + <> + + + } description={ <> @@ -38,7 +78,34 @@ const AcceptCooperationClosing: React.FC = ({ } icon={} title={t('titles.acceptCooperationClosing')} - /> + > + {isInputShown && !isReasonSubmitted ? ( + + + {t('cooperationDetailsPage.InputFieldLabel')}{' '} + + + setDeclineReason('')} + placeholder={t('cooperationDetailsPage.inputFieldPlaceholder')} + sx={styles.input} + value={declineReason} + variant={InputFieldVariantEnum.Outlined} + > + + + + ) : isReasonSubmitted ? ( + + {t('cooperationDetailsPage.submitMessage')} + + ) : null} + ) } diff --git a/src/containers/my-cooperations/cooperation-details/CooperationDetails.tsx b/src/containers/my-cooperations/cooperation-details/CooperationDetails.tsx index f6b7c4d42..5ef00a5c5 100644 --- a/src/containers/my-cooperations/cooperation-details/CooperationDetails.tsx +++ b/src/containers/my-cooperations/cooperation-details/CooperationDetails.tsx @@ -160,6 +160,7 @@ const CooperationDetails = () => { const acceptClosingProcess = !isClosed && ( {}} user={closeCooperationInitiator.firstName} /> ) From 54cde5cf8f8cf14f8cfd0d59695109f3491d84ae Mon Sep 17 00:00:00 2001 From: nebby2105 Date: Tue, 17 Dec 2024 21:04:03 +0200 Subject: [PATCH 2/8] extracted nested ternary op --- .../AcceptCooperationClosing.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx index e910d157e..7493e1cf5 100644 --- a/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx +++ b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx @@ -48,6 +48,12 @@ const AcceptCooperationClosing: React.FC = ({ if (inputError) setInputError(false) } + const isSubmitMessageShown = isReasonSubmitted ? ( + + {t('cooperationDetailsPage.submitMessage')} + + ) : null + const isHelperTextShown = inputError ? t('cooperationDetailsPage.inputError') : '' @@ -100,11 +106,9 @@ const AcceptCooperationClosing: React.FC = ({ - ) : isReasonSubmitted ? ( - - {t('cooperationDetailsPage.submitMessage')} - - ) : null} + ) : ( + isSubmitMessageShown + )} ) } From 772c70ea1165d1605b5c013251a023c1d476127b Mon Sep 17 00:00:00 2001 From: nebby2105 Date: Tue, 17 Dec 2024 23:24:56 +0200 Subject: [PATCH 3/8] added useForm --- .../AcceptCooperationClosing.tsx | 67 ++++++++++--------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx index 7493e1cf5..3780465e4 100644 --- a/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx +++ b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx @@ -8,6 +8,7 @@ import { styles } from './AcceptCooperationClosing.styles' import { useState } from 'react' import InputField from '~/design-system/components/input-field/InputField' import { InputFieldVariantEnum } from '~/design-system/components/input-field/InputField.constants' +import useForm from '~/hooks/use-form' interface AcceptCooperationClosureProps { user: string @@ -22,41 +23,47 @@ const AcceptCooperationClosing: React.FC = ({ }) => { const { t } = useTranslation() const [isInputShown, setIsInputShown] = useState(false) - const [declineReason, setDeclineReason] = useState('') - const [inputError, setInputError] = useState(false) const [isReasonSubmitted, setIsReasonSubmitted] = useState(false) const handleDecline = () => { setIsInputShown(true) } + const { + data, + errors, + trigger, + handleInputChange, + handleNonInputValueChange, + handleSubmit + } = useForm({ + initialValues: { declineReason: '' }, + validations: { + declineReason: (value) => + value ? '' : t('cooperationDetailsPage.inputError') + }, + onSubmit: (formData) => { + if (formData) { + onReasonSubmit(formData.declineReason) + } + } + }) + const handleReasonSubmit = () => { - if (!declineReason.trim()) { - setInputError(true) - } else { - onReasonSubmit(declineReason.trim()) - setDeclineReason('') - setIsInputShown(false) + const isValid = trigger('declineReason') + handleSubmit() + if (isValid && !errors.declineReason) { setIsReasonSubmitted(true) + setIsInputShown(false) } } - const handleInputChange = ({ - target: { value } - }: React.ChangeEvent) => { - setDeclineReason(value) - if (inputError) setInputError(false) - } - - const isSubmitMessageShown = isReasonSubmitted ? ( - - {t('cooperationDetailsPage.submitMessage')} - - ) : null - - const isHelperTextShown = inputError - ? t('cooperationDetailsPage.inputError') - : '' + const isSubmitMessageShown = + isReasonSubmitted && !errors.declineReason ? ( + + {t('cooperationDetailsPage.submitMessage')} + + ) : null return ( = ({ icon={} title={t('titles.acceptCooperationClosing')} > - {isInputShown && !isReasonSubmitted ? ( + {isInputShown ? ( {t('cooperationDetailsPage.InputFieldLabel')}{' '} setDeclineReason('')} + error={!!errors.declineReason} + helperText={errors.declineReason} + onChange={handleInputChange('declineReason')} + onClear={() => handleNonInputValueChange('declineReason', '')} placeholder={t('cooperationDetailsPage.inputFieldPlaceholder')} sx={styles.input} - value={declineReason} + value={data.declineReason} variant={InputFieldVariantEnum.Outlined} > + + + ) + } + return ( + isReasonSubmitted && + !hasErrors && ( + + {t('cooperationDetailsPage.submitMessage')} + + ) + ) + }, [ + isInputShown, + isReasonSubmitted, + data.declineReason, + errors.declineReason, + hasErrors, + resetData, + handleInputChange, + handleReasonSubmit, + t + ]) return ( = ({ - @@ -93,30 +142,7 @@ const AcceptCooperationClosing: React.FC = ({ icon={} title={t('titles.acceptCooperationClosing')} > - {isInputShown ? ( - - - {t('cooperationDetailsPage.InputFieldLabel')}{' '} - - - handleNonInputValueChange('declineReason', '')} - placeholder={t('cooperationDetailsPage.inputFieldPlaceholder')} - sx={styles.input} - value={data.declineReason} - variant={InputFieldVariantEnum.Outlined} - > - - - - ) : ( - isSubmitMessageShown - )} + {renderedInputField} ) } From 92c702a7e884b72cc55298122c28663c293609d9 Mon Sep 17 00:00:00 2001 From: nebby2105 Date: Wed, 18 Dec 2024 02:41:27 +0200 Subject: [PATCH 6/8] removed comments --- .../AcceptCooperationClosing.tsx | 11 ----------- .../AcceptCooperationClosing.spec.jsx | 3 +++ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx index 3a99765ea..c0cb763d0 100644 --- a/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx +++ b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx @@ -26,10 +26,6 @@ const AcceptCooperationClosing: React.FC = ({ const [isInputShown, setIsInputShown] = useState(false) const [isReasonSubmitted, setIsReasonSubmitted] = useState(false) - // const emptyField = (value: string, errorMessage: string) => { - // return value ? '' : errorMessage - // } - const handleDeclineBtnClick = () => { setIsInputShown(true) } @@ -63,13 +59,6 @@ const AcceptCooperationClosing: React.FC = ({ } }, [handleSubmit, setIsInputShown, setIsReasonSubmitted, hasErrors, trigger]) - // const isSubmitMessageShown = - // isReasonSubmitted && !errors.declineReason ? ( - // - // {t('cooperationDetailsPage.submitMessage')} - // - // ) : null - const renderedInputField = useMemo(() => { if (isInputShown) { return ( diff --git a/tests/unit/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.spec.jsx b/tests/unit/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.spec.jsx index 9d0a1ff26..fd2225294 100644 --- a/tests/unit/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.spec.jsx +++ b/tests/unit/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.spec.jsx @@ -30,6 +30,7 @@ describe('AcceptCooperationClosing', () => { const declineBtn = screen.getByText('cooperationDetailsPage.declineBtn') fireEvent.click(declineBtn) expect(declineBtn).toBeInTheDocument() + const inputLabel = screen.getByText( 'cooperationDetailsPage.InputFieldLabel' ) @@ -39,9 +40,11 @@ describe('AcceptCooperationClosing', () => { it('shows error message when submitting empty input', () => { const declineBtn = screen.getByText('cooperationDetailsPage.declineBtn') fireEvent.click(declineBtn) + const submitBtn = screen.getByText('cooperationDetailsPage.submitBtn') expect(submitBtn).toBeInTheDocument() fireEvent.click(submitBtn) + const errorMessage = screen.getByText('cooperationDetailsPage.inputError') expect(errorMessage).toBeInTheDocument() }) From 8b4e161794c1d29659f3def52b9a677c2c3534fa Mon Sep 17 00:00:00 2001 From: nebby2105 Date: Wed, 18 Dec 2024 17:08:33 +0200 Subject: [PATCH 7/8] add ref to input --- .../AcceptCooperationClosing.tsx | 11 +- .../components/input-field/InputField.tsx | 132 ++++++++++-------- 2 files changed, 82 insertions(+), 61 deletions(-) diff --git a/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx index c0cb763d0..9a3cb28f0 100644 --- a/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx +++ b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx @@ -5,7 +5,7 @@ import CooperationActionBanner from '~/containers/my-cooperations/cooperation-ac import Button from '~/design-system/components/button/Button' import { styles } from './AcceptCooperationClosing.styles' -import { useCallback, useMemo, useState } from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import InputField from '~/design-system/components/input-field/InputField' import { InputFieldVariantEnum } from '~/design-system/components/input-field/InputField.constants' import useForm from '~/hooks/use-form' @@ -26,6 +26,14 @@ const AcceptCooperationClosing: React.FC = ({ const [isInputShown, setIsInputShown] = useState(false) const [isReasonSubmitted, setIsReasonSubmitted] = useState(false) + const inputRef = useRef(null) + + useEffect(() => { + if (isInputShown && inputRef.current) { + inputRef.current.focus() + } + }, [isInputShown]) + const handleDeclineBtnClick = () => { setIsInputShown(true) } @@ -73,6 +81,7 @@ const AcceptCooperationClosing: React.FC = ({ onChange={handleInputChange('declineReason')} onClear={() => resetData(['declineReason'])} placeholder={t('cooperationDetailsPage.inputFieldPlaceholder')} + ref={inputRef} sx={styles.inputField} value={data.declineReason} variant={InputFieldVariantEnum.Outlined} diff --git a/src/design-system/components/input-field/InputField.tsx b/src/design-system/components/input-field/InputField.tsx index 89b0c8279..b8b85fd82 100644 --- a/src/design-system/components/input-field/InputField.tsx +++ b/src/design-system/components/input-field/InputField.tsx @@ -10,6 +10,7 @@ import { SxProps } from '@mui/material' import { InputFieldVariantEnum } from './InputField.constants' import '~scss-components/input-field/InputField.scss' +import { forwardRef } from 'react' type BaseInputFieldProps = { variant?: InputFieldVariantEnum @@ -26,66 +27,77 @@ type BaseInputFieldProps = { export type InputFieldProps = BaseInputFieldProps & Omit -const InputField: React.FC = ({ - variant = InputFieldVariantEnum.Small, - label, - disabled, - value, - placeholder, - helperText, - error, - search, - onChange, - onClear, - sx, - ref, - ...props -}) => { - const shouldShowClearIcon = !disabled && value - return ( - -
- {search && } +const InputField = forwardRef( + ( + { + variant = InputFieldVariantEnum.Small, + label, + disabled, + value, + placeholder, + helperText, + error, + search, + onChange, + onClear, + sx, + ...props + }, + ref + ) => { + const shouldShowClearIcon = !disabled && value + return ( + +
+ {search && } - - {label && } - {error ? ( - - ) : ( - shouldShowClearIcon && ( - - ) - )} -
- {helperText && ( -
- - {helperText} - + + {label && } + {error ? ( + + ) : ( + shouldShowClearIcon && ( + + ) + )}
- )} -
- ) -} + {helperText && ( +
+ + {helperText} + +
+ )} + + ) + } +) + +InputField.displayName = 'InputFiled' + export default InputField From 1faef7f6cce17f2f096c4b83e8551a5099b5fcba Mon Sep 17 00:00:00 2001 From: nebby2105 Date: Wed, 18 Dec 2024 21:03:46 +0200 Subject: [PATCH 8/8] fixed tests descriptions and other suggestions --- .../accept-cooperation-close/AcceptCooperationClosing.tsx | 6 ++++-- .../AcceptCooperationClosing.spec.jsx | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx index 9a3cb28f0..66db23bcb 100644 --- a/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx +++ b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx @@ -42,11 +42,12 @@ const AcceptCooperationClosing: React.FC = ({ useForm({ initialValues: { declineReason: '' }, validations: { - declineReason: (value) => - emptyField({ + declineReason: (value) => { + return emptyField({ value, emptyMessage: t('cooperationDetailsPage.inputError') }) + } }, onSubmit: (data) => { if (data) { @@ -93,6 +94,7 @@ const AcceptCooperationClosing: React.FC = ({ ) } + return ( isReasonSubmitted && !hasErrors && ( diff --git a/tests/unit/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.spec.jsx b/tests/unit/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.spec.jsx index fd2225294..36ca1fd47 100644 --- a/tests/unit/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.spec.jsx +++ b/tests/unit/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.spec.jsx @@ -26,7 +26,7 @@ describe('AcceptCooperationClosing', () => { expect(titleText).toBeInTheDocument() }) - it('renders input field when decline button is clicked', () => { + it('should render input field when decline button is clicked', () => { const declineBtn = screen.getByText('cooperationDetailsPage.declineBtn') fireEvent.click(declineBtn) expect(declineBtn).toBeInTheDocument() @@ -37,7 +37,7 @@ describe('AcceptCooperationClosing', () => { expect(inputLabel).toBeInTheDocument() }) - it('shows error message when submitting empty input', () => { + it('should show error message when submitting empty input', () => { const declineBtn = screen.getByText('cooperationDetailsPage.declineBtn') fireEvent.click(declineBtn)