Skip to content

Commit

Permalink
Merge branch 'develop' into update-console
Browse files Browse the repository at this point in the history
  • Loading branch information
raclim authored Nov 18, 2024
2 parents a850d5c + 7652372 commit 0a7c9d9
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 185 deletions.
22 changes: 22 additions & 0 deletions client/common/useSyncFormTranslations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useEffect } from 'react';

// Usage: useSyncFormTranslations(formRef, language)
// This hook ensures that form values are preserved when the language changes.
// Pass a ref to the form instance and the current language as arguments.
const useSyncFormTranslations = (formRef, language) => {
useEffect(() => {
const form = formRef.current;
if (!form) return;

const { values } = form.getState();
form.reset();

Object.keys(values).forEach((field) => {
if (values[field]) {
form.change(field, values[field]);
}
});
}, [language]);
};

export default useSyncFormTranslations;
145 changes: 80 additions & 65 deletions client/modules/User/components/LoginForm.jsx
Original file line number Diff line number Diff line change
@@ -1,99 +1,114 @@
import React, { useState } from 'react';
import React, { useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Form, Field } from 'react-final-form';
import { useDispatch } from 'react-redux';
import { AiOutlineEye, AiOutlineEyeInvisible } from 'react-icons/ai';
import Button from '../../../common/Button';
import { validateLogin } from '../../../utils/reduxFormUtils';
import { validateAndLoginUser } from '../actions';
import { useSyncFormTranslations } from '../../../common/useSyncFormTranslations';

function LoginForm() {
const { t } = useTranslation();
const { t, i18n } = useTranslation();

const dispatch = useDispatch();
function onSubmit(formProps) {
return dispatch(validateAndLoginUser(formProps));
}
const [showPassword, setShowPassword] = useState(false);
const formRef = useRef(null);

const handleVisibility = () => {
setShowPassword(!showPassword);
};

useSyncFormTranslations(formRef, i18n.language);

return (
<Form
fields={['email', 'password']}
validate={validateLogin}
onSubmit={onSubmit}
>
{({ handleSubmit, submitError, submitting, modifiedSinceLastSubmit }) => (
<form className="form" onSubmit={handleSubmit}>
<Field name="email">
{(field) => (
<div className="form__field">
<label htmlFor="email" className="form__label">
{t('LoginForm.UsernameOrEmail')}
</label>
<input
className="form__input"
aria-label={t('LoginForm.UsernameOrEmailARIA')}
type="text"
id="email"
autoComplete="username"
autoCapitalize="none"
{...field.input}
/>
{field.meta.touched && field.meta.error && (
<span className="form-error" aria-live="polite">
{field.meta.error}
</span>
)}
</div>
)}
</Field>
<Field name="password">
{(field) => (
<div className="form__field">
<label htmlFor="password" className="form__label">
{t('LoginForm.Password')}
</label>
<div className="form__field__password">
{({
handleSubmit,
submitError,
submitting,
modifiedSinceLastSubmit,
form
}) => {
formRef.current = form;

return (
<form className="form" onSubmit={handleSubmit}>
<Field name="email">
{(field) => (
<div className="form__field">
<label htmlFor="email" className="form__label">
{t('LoginForm.UsernameOrEmail')}
</label>
<input
className="form__input"
aria-label={t('LoginForm.PasswordARIA')}
type={showPassword ? 'text' : 'password'}
id="password"
autoComplete="current-password"
aria-label={t('LoginForm.UsernameOrEmailARIA')}
type="text"
id="email"
autoComplete="username"
autoCapitalize="none"
{...field.input}
/>
<button
className="form__eye__icon"
type="button"
onClick={handleVisibility}
aria-hidden="true"
>
{showPassword ? (
<AiOutlineEyeInvisible />
) : (
<AiOutlineEye />
)}
</button>
{field.meta.touched && field.meta.error && (
<span className="form-error" aria-live="polite">
{field.meta.error}
</span>
)}
</div>
)}
</Field>
<Field name="password">
{(field) => (
<div className="form__field">
<label htmlFor="password" className="form__label">
{t('LoginForm.Password')}
</label>
<div className="form__field__password">
<input
className="form__input"
aria-label={t('LoginForm.PasswordARIA')}
type={showPassword ? 'text' : 'password'}
id="password"
autoComplete="current-password"
{...field.input}
/>
<button
className="form__eye__icon"
type="button"
onClick={handleVisibility}
aria-hidden="true"
>
{showPassword ? (
<AiOutlineEyeInvisible />
) : (
<AiOutlineEye />
)}
</button>
</div>
{field.meta.touched && field.meta.error && (
<span className="form-error" aria-live="polite">
{field.meta.error}
</span>
)}
</div>
{field.meta.touched && field.meta.error && (
<span className="form-error" aria-live="polite">
{field.meta.error}
</span>
)}
</div>
)}
</Field>
{submitError && !modifiedSinceLastSubmit && (
<span className="form-error">{submitError}</span>
)}
</Field>
{submitError && !modifiedSinceLastSubmit && (
<span className="form-error">{submitError}</span>
)}
<Button type="submit" disabled={submitting}>
{t('LoginForm.Submit')}
</Button>
</form>
)}
<Button type="submit" disabled={submitting}>
{t('LoginForm.Submit')}
</Button>
</form>
);
}}
</Form>
);
}
Expand Down
4 changes: 4 additions & 0 deletions client/modules/User/components/LoginForm.unit.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ jest.mock('../actions', () => ({
)
}));

jest.mock('../../../common/useSyncFormTranslations', () => ({
useSyncFormTranslations: jest.fn()
}));

const subject = () => {
reduxRender(<LoginForm />, {
store
Expand Down
Loading

0 comments on commit 0a7c9d9

Please sign in to comment.