Skip to content

Commit

Permalink
5514 - New translations UI (#5557)
Browse files Browse the repository at this point in the history
* defined loader and tables with data

* dummy edit translations route

* types and some cleanup

* V2 folder path resolution

* updated cypress

* defined primary color

* defined basic button

* define path for component tests

* Added more options to table

* removed console.log

* allow only getting context translations

* updated globals.css

* style adjustments

* enabled tailwind jit mode

* using headers for SSR

* header component

* pill component

* added title to edit translations page

* update header

* small change to how pills work

* form for translations WIP

* storybook viewport addon

* renamed Header component

* more table stories

* fleshed out form fields

* functionality to clear fields

* style adjustments

* form with validation

* updated test

* use keys for every child

* unique keys for table

* Filter empty contexts from translations list

* sort by type

* possible for undefined field error

* remove duplicated import

* sorted translations

* basic paginator

* using the sr-only class

* small fix

* API post for translations

* Form submit

* removed paginator component

* added more color pseudonims to tailwind

* notification component

* removed max-width

* updated notification story

* notifications container wip

* notifications dismiss & self dismissal

* notification stays while user is hovering

* visual and possition adjustments

* notify on save

* ussing correct selectors for test

* bug fixes

* Toggle buttton

* updated selectors and asserts

* disabled input styles

* more style adjustments

* marked untranslated terms

* cleanup

* added translate + style adjustments

* table responsive adjustments

* update table with latest values after submit

* change button based on status

* buttons on edit form WIP

* adjusted table styles

* separted components and tests from stories

* adjusted style of tables

* aligned actions buttons

* disable save button when no changes have been made

* Revert "disable save button when no changes have been made"

This reverts commit 69e6196.

* small fixes

* fixed notifications auto dismissal

* pill colors + cleanup

* more visual adjustments

* styles fix + toggle button

* use memoized table columns and data

* allow long string in eslint

* input field abstraction

* small refactor

* filter by untranslated

* group table

* pill

* Import csv

* fixed Type

* small optimization

* small fixes

* filter after importing file

* return only saved context

* fixed type

* form wyp

* form

* controller and pagination

* Added a translations cy e2e tests

* Added more e2e

* Updated data-cy to data-testid

* Waited for table to populate

* small fixes

* lazy load translations WIP

* emited types

* cleanup

* input filed style adjustments

* remove terms when filtering if translated

* remove use memo

* removes file

* remove attemp to group fields

* Stablizing e2e tests

* Disabled transitions

* improve performance on system translations

* Fixing unpredictability of e2es

* wrapped in Translate

* minor refactor

* fixed cy test failing due to redux

* restored html-webpack-plugin for storybook

* exclude new files from check translations

* update some texts

* message when there are no untranslated terms

* wrapped for translate

* explicit path for storybook compatibility

* delete old translations route and form

* Modal + Cancel confirmation

* small fixes

* removed unused darkmode classes

* added tailwind watch mode in hot

* fixes on cancel btn behavior

* define has errors

* refactor and extracted components

* more refactoring

* adjust input field error style

* go back to previous way of showing errors

* typo fix

* actions at bottom

* removes key in favor of using react table props

* remove obsolete key

* fix eslint errors

* changed error reporting

* memoized input field

* using form submit state

* type fixes

* remove concurrently

* correctly check types on app and cypress

* button at bottom

* more fixes on action buttons

* height of form

* e2e with axe tool

* Cypress tests withe axe errors fixed

* Fixed IX e2e after removed test isolation

* Removed unnecesary snapshot

* Removed snapshot

* fix layout and style of table and buttons

* changed imports

* updated imports

* updated global.css

* correct configuration of ts and eslint

* shoehorned missing types

* updated snapshots

* fix codeclimate issue

* small update to spec

* IX e2e refactor

* Fixed some unit tests after accesibility changes

* Fixed Porgress Bar in specs

* added store to story for translate

* updated imports

* refactored component

* fixed parent selector

* check if parent selector exists

* fixes on cy-e2e

* remove screenshot

* remove whitespaces from translate className

* migrated to cypress

* fixed selectors

* updated fixtures to have default language in english and fixed translations in spanish and arabic

* fixed e2e for new fixtures

* fixed selectors after a11y

* removed in favor of cypress testing

* organized helpers

* updated test and snapshot

* visit login directly

* updated test WIP

* migration for translations

* Fixed e2e after logim refactor

* changes in texts

* fixed yarn.lock

* added row separation line

* renamed props

* prevent all navigation when isDirty

* updated snapshots after adding row separator

* increase delta number

* Small style changes to input and toggle button

* Removed snapshot

* improvements

* reduce border on buttons

* added alert role to notifications

* tested more behavior for the form

* updated fixtures to include a collaborator user

* remove logic to create new user

* wait until search result is in before asserting

* attempt at stabilizing test

* attempt at stabilizing test

---------

Co-authored-by: Kevin <[email protected]>
Co-authored-by: Mercy <[email protected]>
Co-authored-by: A happy cat <[email protected]>
Co-authored-by: Alberto Casado Torres <[email protected]>
  • Loading branch information
5 people authored Apr 14, 2023
1 parent 3170802 commit abf4355
Show file tree
Hide file tree
Showing 124 changed files with 3,869 additions and 1,699 deletions.
3 changes: 1 addition & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ module.exports = {
'implicit-arrow-linebreak': 'off',
'jest/no-focused-tests': 'error',
'object-curly-spacing': ['warn', 'always'],
'max-len': ['error', 150],
'max-len': ['error', 150, { ignoreStrings: true }],
'no-unused-vars': [
'error',
{
Expand Down Expand Up @@ -194,7 +194,6 @@ module.exports = {
},
{
files: ['app/**/specs/*'],
excludedFiles: './**/*.cy.tsx',
rules: {
'max-lines-per-function': 'off',
'max-lines': 'off',
Expand Down
1 change: 1 addition & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = {
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-viewport',
],
framework: '@storybook/react',
core: {
Expand Down
8 changes: 6 additions & 2 deletions app/api/i18n/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ import needsAuthorization from '../auth/authMiddleware';
import translations, { UITranslationNotAvailable } from './translations';

export default (app: Application) => {
app.get('/api/translations', async (_req, res) => {
const response = await translations.get();
app.get('/api/translations', async (req, res) => {
const { context } = req.query;
const response = await translations.get(
{},
context && { contexts: { $elemMatch: { id: context } }, locale: 1 }
);

res.json({ rows: response });
});
Expand Down
82 changes: 82 additions & 0 deletions app/api/i18n/specs/routes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ describe('i18n translations routes', () => {
type: 'Uwazi UI',
values: [{ key: 'Search', value: 'Search' }],
},
{
id: 'contextID',
label: 'Template',
type: 'Entity',
values: [{ key: 'title', value: 'Template 1' }],
},
],
},
{
Expand All @@ -56,6 +62,12 @@ describe('i18n translations routes', () => {
type: 'Uwazi UI',
values: [{ key: 'Search', value: 'Buscar' }],
},
{
id: 'contextID',
label: 'Template',
type: 'Entity',
values: [{ key: 'title', value: 'Plantilla 1' }],
},
],
},
],
Expand Down Expand Up @@ -88,6 +100,12 @@ describe('i18n translations routes', () => {
Search: 'Search',
},
},
{
id: 'contextID',
label: 'Template',
type: 'Entity',
values: { title: 'Template 1' },
},
],
locale: 'en',
},
Expand All @@ -103,6 +121,56 @@ describe('i18n translations routes', () => {
Search: 'Buscar',
},
},
{
id: 'contextID',
label: 'Template',
type: 'Entity',
values: { title: 'Plantilla 1' },
},
],
locale: 'es',
},
],
});
});

it('should only return the requested context', async () => {
const appWithQuery = setUpApp(i18nRoutes, (req, _res, next) => {
req.user = {
username: 'admin',
role: UserRole.ADMIN,
email: '[email protected]',
};
req.query = { context: 'contextID' };
next();
});

const response = await request(appWithQuery).get('/api/translations').expect(200);

expect(response.body).toEqual({
rows: [
{
_id: expect.any(String),
contexts: [
{
id: 'contextID',
label: 'Template',
type: 'Entity',
values: { title: 'Template 1' },
},
],
locale: 'en',
},

{
_id: expect.any(String),
contexts: [
{
id: 'contextID',
label: 'Template',
type: 'Entity',
values: { title: 'Plantilla 1' },
},
],
locale: 'es',
},
Expand Down Expand Up @@ -267,6 +335,13 @@ describe('i18n translations routes', () => {
],
_id: expect.anything(),
},
{
id: 'contextID',
label: 'Template',
type: 'Entity',
values: [{ key: 'title', value: 'Template 1', _id: expect.anything() }],
_id: expect.anything(),
},
],
_id: expect.anything(),
__v: 0,
Expand Down Expand Up @@ -316,6 +391,13 @@ describe('i18n translations routes', () => {
Search: 'Buscar traducida',
},
},
{
_id: expect.any(String),
id: 'contextID',
label: 'Template',
type: 'Entity',
values: { title: 'Plantilla 1' },
},
],
locale: 'es',
},
Expand Down
4 changes: 2 additions & 2 deletions app/api/i18n/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,8 @@ const update = async (translation: TranslationType | IndexedTranslations) => {

export default {
prepareContexts,
async get(query = {}) {
const translations = await model.get(query);
async get(query = {}, selector = {}) {
const translations = await model.get(query, selector);
return translations.map(
translation =>
({
Expand Down
75 changes: 75 additions & 0 deletions app/api/migrations/migrations/129-update_translations/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const newKeys = [
{ key: 'Discard changes?' },
{ key: 'You have unsaved changes. Do you want to continue?' },
{ key: 'Language Code' },
{ key: 'Clear' },
{ key: 'Navigate back' },
{ key: 'Dismiss' },
{ key: 'View less' },
{ key: 'View more' },
];

const deletedKeys = [{ key: 'User Interface' }];

const updateTranslation = (currentTranslation, keysToUpdate, loc) => {
const translation = { ...currentTranslation };
const newTranslation = keysToUpdate.find(row => row.key === currentTranslation.key);
if (newTranslation) {
translation.key = newTranslation.newKey;
if (loc === 'en' || currentTranslation.value === newTranslation.oldValue) {
translation.value = newTranslation.newValue;
}
}
return translation;
};

export default {
delta: 129,

reindex: false,

name: 'update_translations',

description: 'Updates translations for new Translatinos UI',

async up(db) {
const keysToInsert = newKeys;
const keysToDelete = deletedKeys;
const translations = await db.collection('translations').find().toArray();
const locToSystemContext = {};
translations.forEach(tr => {
locToSystemContext[tr.locale] = tr.contexts.find(c => c.id === 'System');
});

const alreadyInDB = [];
Object.entries(locToSystemContext).forEach(([loc, context]) => {
const contextValues = context.values.reduce((newValues, currentTranslation) => {
const deleted = keysToDelete.find(
deletedTranslation => deletedTranslation.key === currentTranslation.key
);
if (!deleted) {
const translation = updateTranslation(currentTranslation, [], loc);
newValues.push(translation);
}
keysToInsert.forEach(newEntry => {
if (newEntry.key === currentTranslation.key) {
alreadyInDB.push(currentTranslation.key);
}
});
return newValues;
}, []);
keysToInsert
.filter(k => !alreadyInDB.includes(k.key))
.forEach(newEntry => {
contextValues.push({ key: newEntry.key, value: newEntry.key });
});
context.values = contextValues;
});

await Promise.all(
translations.map(tr => db.collection('translations').replaceOne({ _id: tr._id }, tr))
);

process.stdout.write(`${this.name}...\r\n`);
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import testingDB from 'api/utils/testing_db';
import migration from '../index.js';
import fixtures, { templateContext } from './fixtures.js';

describe('migration update translations of new Translations UI', () => {
beforeEach(async () => {
jest.spyOn(process.stdout, 'write').mockImplementation(() => {});
await testingDB.setupFixturesAndContext(fixtures);
});

afterAll(async () => {
await testingDB.disconnect();
});

it('should have a delta number', () => {
expect(migration.delta).toBe(129);
});

it('should update the keys that have changed', async () => {
await migration.up(testingDB.mongodb);
const allTranslations = await testingDB.mongodb.collection('translations').find().toArray();

const uwaziUI = allTranslations.filter(tr =>
tr.contexts.filter(ctx => ctx.type === 'Uwazi UI')
);

const previousSystemValues = {
key: 'existing-key-in-system',
value: 'existing-key-in-system',
};

const addedKeys = [
{ key: 'Discard changes?' },
{ key: 'You have unsaved changes. Do you want to continue?' },
{ key: 'Language Code' },
{ key: 'Clear' },
{ key: 'Navigate back' },
{ key: 'Dismiss' },
{ key: 'View less' },
{ key: 'View more' },
].map(key => ({ ...key, value: key.key }));

const defaultContextContent = expect.objectContaining({
type: 'Uwazi UI',
values: expect.arrayContaining([previousSystemValues, ...addedKeys]),
});
expect(uwaziUI).toMatchObject([
expect.objectContaining({
locale: 'en',
contexts: [defaultContextContent, templateContext],
}),
expect.objectContaining({
locale: 'es',
contexts: [
expect.objectContaining({
type: 'Uwazi UI',
values: [previousSystemValues, ...addedKeys],
}),
templateContext,
],
}),
expect.objectContaining({
locale: 'pt',
contexts: [defaultContextContent, templateContext],
}),
]);
});
});
Loading

0 comments on commit abf4355

Please sign in to comment.