Skip to content

Commit

Permalink
validate form and notify
Browse files Browse the repository at this point in the history
  • Loading branch information
Zasa-san committed Jan 17, 2025
1 parent 1450ff2 commit 445209f
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 10 deletions.
40 changes: 34 additions & 6 deletions app/react/I18N/TranslateModal.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/* eslint-disable react/jsx-props-no-spreading */
import React from 'react';
import { useAtom, useAtomValue } from 'jotai';
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import { useFieldArray, useForm } from 'react-hook-form';
import { FetchResponseError } from 'shared/JSONRequest';
import { Modal } from 'V2/Components/UI';
import { settingsAtom, translationsAtom, inlineEditAtom } from 'V2/atoms';
import { settingsAtom, translationsAtom, inlineEditAtom, notificationAtom } from 'V2/atoms';
import { InputField } from 'app/V2/Components/Forms';
import { Button } from 'V2/Components/UI/Button';
import { TranslationValue } from 'V2/shared/types';
Expand All @@ -13,10 +14,17 @@ import { t } from './translateFunction';
const TranslateModal = () => {
const [inlineEditState, setInlineEditState] = useAtom(inlineEditAtom);
const [translations] = useAtom(translationsAtom);
const setNotifications = useSetAtom(notificationAtom);
const context = translations[0].contexts.find(ctx => ctx.id === inlineEditState.context)!;
const { languages = [] } = useAtomValue(settingsAtom);

const { register, handleSubmit, control, reset } = useForm<{ data: TranslationValue[] }>({
const {
register,
handleSubmit,
control,
reset,
formState: { errors, isDirty },
} = useForm<{ data: TranslationValue[] }>({
mode: 'onSubmit',
});

Expand All @@ -26,7 +34,8 @@ const TranslateModal = () => {
const initialValues = translations.map(translation => {
const language = languages.find(lang => lang.key === translation.locale)!;
const languageContext = translation.contexts.find(c => c.id === context?.id);
const value = languageContext?.values[inlineEditState.translationKey];
const value =
languageContext?.values[inlineEditState.translationKey] || inlineEditState.translationKey;
return {
language: language.key,
value,
Expand All @@ -41,7 +50,25 @@ const TranslateModal = () => {
};

const submit = async ({ data }: { data: TranslationValue[] }) => {
await postV2(data, context);
if (isDirty) {
const response = await postV2(data, context);
if (response === 200) {
setNotifications({
type: 'success',
text: t('System', 'Translations saved', null, false),
});
}
if (response instanceof FetchResponseError) {
const message = response.json?.prettyMessage
? response.json.prettyMessage
: response.message;
setNotifications({
type: 'error',
text: t('System', 'An error occurred', null, false),
details: message,
});
}
}
closeModal();
};

Expand All @@ -67,7 +94,8 @@ const TranslateModal = () => {
}
id={field.id}
key={field.id}
{...register(`data.${index}.value`)}
{...register(`data.${index}.value`, { required: true })}
hasErrors={errors.data && errors.data[index] !== undefined}
/>
))}
</Modal.Body>
Expand Down
58 changes: 54 additions & 4 deletions app/react/I18N/specs/TranslateModal.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@
import React, { act } from 'react';
import { fireEvent, render, RenderResult } from '@testing-library/react';
import { TestAtomStoreProvider } from 'V2/testing';
import { settingsAtom, translationsAtom, inlineEditAtom } from 'V2/atoms';
import { settingsAtom, translationsAtom, inlineEditAtom, notificationAtom } from 'V2/atoms';
import * as translationsAPI from 'V2/api/translations';
import { NotificationsContainer } from 'V2/Components/UI';
import { TranslateModal } from '../TranslateModal';
import { languages, translations } from './fixtures';

describe('TranslateModal', () => {
let renderResult: RenderResult;

beforeAll(() => {
jest.spyOn(translationsAPI, 'postV2').mockImplementationOnce(async () => Promise.resolve([]));
jest.spyOn(translationsAPI, 'postV2').mockImplementation(async () => Promise.resolve(200));
});

afterEach(() => {
Expand All @@ -27,9 +28,11 @@ describe('TranslateModal', () => {
[settingsAtom, { languages }],
[translationsAtom, translations],
[inlineEditAtom, { inlineEdit, context, translationKey }],
[notificationAtom, {}],
]}
>
<TranslateModal />
<NotificationsContainer />
</TestAtomStoreProvider>
);
};
Expand Down Expand Up @@ -72,9 +75,56 @@ describe('TranslateModal', () => {
translations[0].contexts[0]
);
expect(renderResult.queryByText('Translate')).not.toBeInTheDocument();
expect(renderResult.queryByText('Translations saved')).toBeInTheDocument();
});

it('should not allow sending empty fields', () => {});
it('should not allow sending empty fields', async () => {
renderComponent(true, 'System', 'Search');
const inputFields = renderResult.queryAllByRole('textbox');
const saveButton = renderResult.getByTestId('save-button');

await act(() => {
fireEvent.change(inputFields[0], { target: { value: '' } });
fireEvent.click(saveButton);
});

expect(translationsAPI.postV2).not.toHaveBeenCalled();
});

it('should use the default context key if translation does not exist', () => {});
it('should use the default context key if translation does not exist', async () => {
renderComponent(true, 'System', 'This key is not in the database');
const inputFields = renderResult.queryAllByRole('textbox');
expect(inputFields[0]).toHaveValue('This key is not in the database');
expect(inputFields[1]).toHaveValue('This key is not in the database');
const saveButton = renderResult.getByTestId('save-button');

await act(() => {
fireEvent.change(inputFields[0], { target: { value: 'My new key' } });
fireEvent.change(inputFields[1], { target: { value: 'Nueva llave' } });
fireEvent.click(saveButton);
});

expect(translationsAPI.postV2).toHaveBeenCalledWith(
[
{ language: 'en', value: 'My new key', key: 'This key is not in the database' },
{ language: 'es', value: 'Nueva llave', key: 'This key is not in the database' },
],
translations[0].contexts[0]
);
expect(renderResult.queryByText('Translate')).not.toBeInTheDocument();
});

it('should not save if there are no changes', async () => {
renderComponent(true, 'System', 'Search');
const saveButton = renderResult.getByTestId('save-button');
const inputFields = renderResult.queryAllByRole('textbox');

await act(() => {
fireEvent.change(inputFields[1], { target: { value: 'Nueva traducción' } });
fireEvent.change(inputFields[1], { target: { value: 'Buscar' } });
fireEvent.click(saveButton);
});

expect(translationsAPI.postV2).not.toHaveBeenCalled();
});
});

0 comments on commit 445209f

Please sign in to comment.