Skip to content

Commit

Permalink
Replaced Table component for html table with Inputs (#7041)
Browse files Browse the repository at this point in the history
* Replaced Tamble component for html table with Inputs

* added missing translation

* fixed e2e

* added error message to inputs

* added key to prevent wrong registration of inputs

---------

Co-authored-by: Santiago <borrajosantiago@gmail.com>
  • Loading branch information
konzz and Zasa-san committed Jul 26, 2024
1 parent da8ca42 commit 8357473
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 158 deletions.
136 changes: 105 additions & 31 deletions app/react/V2/Routes/Settings/Translations/EditTranslations.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable react/jsx-props-no-spreading */
/* eslint-disable max-lines */
import React, { useEffect, useMemo, useRef, useState } from 'react';
import {
Expand All @@ -16,16 +17,17 @@ import { useSetAtom } from 'jotai';
import { Translate } from 'app/I18N';
import { advancedSort } from 'app/utils/advancedSort';
import { ClientTranslationSchema } from 'app/istore';
import { ConfirmNavigationModal } from 'app/V2/Components/Forms';
import { ConfirmNavigationModal, InputField } from 'app/V2/Components/Forms';
import { SettingsContent } from 'app/V2/Components/Layouts/SettingsContent';
import RenderIfVisible from 'react-render-if-visible';
import { Button, ToggleButton } from 'V2/Components/UI';
import * as translationsAPI from 'V2/api/translations';
import * as settingsAPI from 'V2/api/settings';
import { notificationAtom } from 'V2/atoms';
import { availableLanguages } from 'shared/languagesList';
import { Settings } from 'shared/types/settingsType';
import { FetchResponseError } from 'shared/JSONRequest';
import { TranslationsTables } from './components/TableComponents';
import { LanguagePill } from './components/LanguagePill';

const editTranslationsLoader =
(headers?: IncomingHttpHeaders): LoaderFunction =>
Expand Down Expand Up @@ -188,6 +190,10 @@ const EditTranslations = () => {
const { contextTerms, contextLabel, contextId } = getContextInfo(translations);
const defaultLanguage = settings?.languages?.find(language => language.default);
const defaultFormValues = prepareFormValues(translations, defaultLanguage?.key || 'en');
const tablesData = useMemo(
() => calculateTableData(contextTerms, defaultFormValues, hideTranslated),
[contextTerms, defaultFormValues, hideTranslated]
);

const {
register,
Expand Down Expand Up @@ -246,8 +252,6 @@ const EditTranslations = () => {
}
}, [fetcher.data, fetcher.formData, setNotifications]);

const tablesData = calculateTableData(contextTerms, defaultFormValues, hideTranslated);

const formSubmit = async (data: { formValues: formValuesType }) => {
const formData = new FormData();
const values = prepareValuesToSave(data.formValues, translations);
Expand Down Expand Up @@ -281,39 +285,109 @@ const EditTranslations = () => {
title={contextLabel}
contextId={contextId}
/>

<SettingsContent.Body>
<div className="px-5 pt-5">
<ToggleButton className="px-5 pt-5" onToggle={() => setHideTranslated(!hideTranslated)}>
<div className="pl-1 text-sm text-gray-700">
<Translate>Untranslated Terms</Translate>
</div>
</ToggleButton>
<div className="flex items-center w-full gap-4 pt-5 mb-4">
<div>
<ToggleButton
className="px-5 pt-5"
onToggle={() => setHideTranslated(!hideTranslated)}
>
<div className="pl-1 text-sm text-gray-700">
<Translate>Untranslated Terms</Translate>
</div>
</ToggleButton>
</div>
</div>
<div className="flex-grow">
{tablesData.length ? (
<form onSubmit={handleSubmit(formSubmit)} id="edit-translations">
<TranslationsTables
tablesData={tablesData}
submitting={isSubmitting}
register={register}
setValue={setValue}
getFieldState={getFieldState}
/>
</form>
) : (
<div className="flex gap-2 items-center p-4 rounded-md border border-gray-50 bg-primary-50">
<InformationCircleIcon className="w-10 text-primary-800" />
<span className="text-primary-800">
<Translate>There are no untranslated terms</Translate>
</span>
</div>
)}
<form onSubmit={handleSubmit(formSubmit)} id="edit-translations">
{tablesData?.length ? (
tablesData?.map(tableData => {
if (!tableData) return null;
const [title] = Object.keys(tableData);
const values = tableData[title];
return (
<RenderIfVisible key={title}>
<div className="relative w-full mb-4 border rounded-md shadow-sm border-gray-50">
<table className="w-full text-sm text-left" data-testid="table">
{title && (
<caption className="p-4 text-base font-semibold text-left text-gray-900 bg-white">
{title}
</caption>
)}
<thead className="text-xs text-gray-500 uppercase bg-gray-50">
<tr className="border-b">
<th scope="col" className="px-6 py-3">
<div className="inline-flex">
<Translate>Language</Translate>
</div>
</th>
<th scope="col" className="px-6 py-3">
<div className="inline-flex">
<Translate className="sr-only">Language Code</Translate>
</div>
</th>
<th scope="col" className="w-full px-6 py-3">
<div className="inline-flex">
<Translate>Value</Translate>
</div>
</th>
</tr>
</thead>
<tbody>
{values.map(value => {
const hasErrors = Boolean(
getFieldState(value.fieldKey as any)?.error
);
return (
<tr>
<td className="px-6 py-2">{value.language}</td>
<td className="px-6 py-2">
<LanguagePill
languageKey={value.translationStatus.languageKey}
status={value.translationStatus.status}
/>
</td>
<td className="px-6 py-2">
<InputField
id={value.fieldKey}
hideLabel
disabled={isSubmitting}
clearFieldAction={() =>
setValue(value.fieldKey as any, '', { shouldDirty: true })
}
hasErrors={hasErrors}
errorMessage={
hasErrors ? (
<Translate>This field is required</Translate>
) : (
''
)
}
{...register(value.fieldKey as any, { required: true })}
/>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</RenderIfVisible>
);
})
) : (
<div className="flex items-center gap-2 p-4 border rounded-md border-gray-50 bg-primary-50">
<InformationCircleIcon className="w-10 text-primary-800" />
<span className="text-primary-800">
<Translate>There are no untranslated terms</Translate>
</span>
</div>
)}
</form>
</div>
</SettingsContent.Body>

<SettingsContent.Footer>
<div className="flex gap-2 justify-end">
<div className="flex justify-end gap-2">
<div className="flex-1">
{contextId === 'System' && (
<>
Expand Down
38 changes: 0 additions & 38 deletions app/react/V2/Routes/Settings/Translations/components/FormInput.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import { Pill } from 'V2/Components/UI';

const LanguagePill = ({
languageKey,
status,
}: {
languageKey: string | undefined;
status: string;
}) => {
let color: 'gray' | 'primary' | 'yellow' = 'gray';
if (status === 'defaultLanguage') color = 'primary';
if (status === 'untranslated') color = 'yellow';

return <Pill color={color}>{languageKey?.toUpperCase()}</Pill>;
};

export { LanguagePill };
Original file line number Diff line number Diff line change
@@ -1,34 +1,11 @@
/* eslint-disable react/no-multi-comp */
import React, { useMemo } from 'react';
import React from 'react';
import { Link } from 'react-router-dom';
import { UseFormGetFieldState, UseFormRegister, UseFormSetValue } from 'react-hook-form';
import { CellContext, createColumnHelper } from '@tanstack/react-table';
import RenderIfVisible from 'react-render-if-visible';
import { CellContext } from '@tanstack/react-table';
import { Translate } from 'app/I18N';
import { ClientTranslationContextSchema } from 'app/istore';
import { Button, Pill, Table_deprecated as Table } from 'V2/Components/UI';
import { FormInput } from './FormInput';
import { Button, Pill } from 'V2/Components/UI';

type TableData = {
language: string | undefined;
translationStatus: {
languageKey: string | undefined;
status: string;
};
fieldKey: string;
};

type TranslationsTableType = {
tablesData: ({ [contextTerm: string]: TableData[] } | undefined)[];
register: UseFormRegister<any>;
setValue: UseFormSetValue<any>;
getFieldState: UseFormGetFieldState<any>;
submitting: boolean;
};

const LanguageHeader = () => <Translate>Language</Translate>;
const StatusHeader = () => <Translate>Language Code</Translate>;
const FieldKeyHeader = () => <Translate>Value</Translate>;
const LabelHeader = () => <Translate>Name</Translate>;
const TypeHeader = () => <Translate>Type</Translate>;
const ActionHeader = () => <Translate>Action</Translate>;
Expand All @@ -49,66 +26,4 @@ const ContextPill = ({ cell }: CellContext<ClientTranslationContextSchema, any>)
</div>
);

const LanguagePill = ({ cell }: CellContext<TableData, TableData['translationStatus']>) => {
let color: 'gray' | 'primary' | 'yellow' = 'gray';
if (cell.getValue().status === 'defaultLanguage') color = 'primary';
if (cell.getValue().status === 'untranslated') color = 'yellow';

return <Pill color={color}>{cell.getValue().languageKey?.toUpperCase()}</Pill>;
};

const TranslationsTables = ({
tablesData,
register,
setValue,
submitting,
getFieldState,
}: TranslationsTableType) => {
const memoizedInput = useMemo(
() => (data: any) => FormInput(data, { register, setValue, submitting, getFieldState }),
[getFieldState, register, setValue, submitting]
);

const columnHelper = createColumnHelper<TableData>();

const columns = [
columnHelper.accessor('language', {
header: LanguageHeader,
enableSorting: false,
}),

columnHelper.accessor('translationStatus', {
header: StatusHeader,
cell: LanguagePill,
enableSorting: false,
meta: { headerClassName: 'sr-only invisible bg-gray-50' },
}),
columnHelper.accessor('fieldKey', {
header: FieldKeyHeader,
cell: memoizedInput,
enableSorting: false,
meta: { headerClassName: 'w-full' },
}),
];

return (
<>
{tablesData.map(data => {
if (data) {
const [contextTerm] = Object.keys(data);
return (
<div key={contextTerm} className="mt-4">
<RenderIfVisible stayRendered>
<Table<TableData> columns={columns} data={data[contextTerm]} title={contextTerm} />
</RenderIfVisible>
</div>
);
}

return undefined;
})}
</>
);
};

export { RenderButton, ContextPill, TranslationsTables, LabelHeader, TypeHeader, ActionHeader };
export { RenderButton, ContextPill, LabelHeader, TypeHeader, ActionHeader };
1 change: 1 addition & 0 deletions cypress/e2e/settings/translations.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ describe('Translations', () => {
});

it('Should filter translations that have no untranslated terms', () => {
cy.get('[data-testid=settings-translations-edit]').scrollTo('top');
cy.contains('caption', 'Fecha');
cy.get('input[type=text]').eq(0).should('have.value', 'Date');
cy.contains('label', 'Untranslated Terms').click();
Expand Down

0 comments on commit 8357473

Please sign in to comment.