Skip to content

Commit

Permalink
fix(build): i18n npm updates (#648)
Browse files Browse the repository at this point in the history
  • Loading branch information
cdcabrera committed May 12, 2021
1 parent 68ee15c commit e8e520b
Show file tree
Hide file tree
Showing 6 changed files with 262 additions and 79 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
"@redhat-cloud-services/frontend-components-utilities": "3.0.3",
"axios": "^0.21.1",
"classnames": "^2.3.1",
"i18next": "^19.8.7",
"i18next": "^20.2.2",
"i18next-xhr-backend": "^3.2.2",
"js-cookie": "^2.2.1",
"locale-code": "^2.0.2",
Expand All @@ -137,11 +137,12 @@
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-i18next": "^11.8.9",
"react-i18next": "^11.8.14",
"react-redux": "^7.2.4",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"react-use": "^17.2.4",
"redux": "^4.1.0",
"redux-logger": "^3.0.6",
"redux-promise-middleware": "^6.1.2",
Expand Down
34 changes: 24 additions & 10 deletions src/components/i18n/__tests__/i18n.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { readFileSync } from 'fs';
import glob from 'glob';
import React from 'react';
import { act } from 'react-dom/test-utils';
import PropTypes from 'prop-types';
import { mount, shallow } from 'enzyme';
import _get from 'lodash/get';
Expand Down Expand Up @@ -52,25 +53,38 @@ const getTranslationKeys = ({ files = './src/**/!(*.test|*.spec).@(js|jsx)', lis
describe('I18n Component', () => {
const getKeys = getTranslationKeys({});

it('should render a basic component', () => {
const loadHookComponent = async callback => {
let component = null;
await act(async () => {
component = callback();
});
component?.update();
return component;
};

it('should render a basic component', async () => {
const props = {
locale: 'es'
};

const component = mount(
<I18n {...props}>
<React.Fragment>lorem ipsum</React.Fragment>
</I18n>
const component = await loadHookComponent(() =>
mount(
<I18n {...props}>
<React.Fragment>lorem ipsum</React.Fragment>
</I18n>
)
);

expect(component).toMatchSnapshot('basic');
});

it('should pass children', () => {
const component = shallow(
<I18n>
<React.Fragment>lorem ipsum</React.Fragment>
</I18n>
it('should pass children', async () => {
const component = await loadHookComponent(() =>
mount(
<I18n>
<React.Fragment>lorem ipsum</React.Fragment>
</I18n>
)
);

expect(component.html()).toMatchSnapshot('children');
Expand Down
86 changes: 42 additions & 44 deletions src/components/i18n/i18n.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import i18next from 'i18next';
import XHR from 'i18next-xhr-backend';
import { initReactI18next, Trans } from 'react-i18next';
import { useMount } from 'react-use';
import { helpers } from '../../common/helpers';

/**
Expand All @@ -20,10 +21,14 @@ const translate = (translateKey, values = null, components) => {
}

if (components) {
return <Trans i18nKey={translateKey} values={values} components={components} />;
return (
(i18next.store && <Trans i18nKey={translateKey} values={values} components={components} />) || (
<React.Fragment>t({translateKey})</React.Fragment>
)
);
}

return i18next.t(translateKey, values);
return (i18next.store && i18next.t(translateKey, values)) || `t(${translateKey})`;
};

/**
Expand All @@ -33,45 +38,35 @@ const translate = (translateKey, values = null, components) => {
* @returns {Node}
*/
const translateComponent = Component => {
const withTranslation = ({ ...props }) => <Component {...props} t={translate} i18n={i18next} />;
const withTranslation = ({ ...props }) => (
<Component
{...props}
t={(i18next.store && translate) || helpers.noopTranslate}
i18n={(i18next.store && i18next) || helpers.noop}
/>
);

withTranslation.displayName = 'withTranslation';
return withTranslation;
};

/**
* ToDo: reevaluate the "I18nextProvider" on package update.
* Appears to have timing issues, and subsequent firings of the
* ajax/xhr setup. Reverting it to just a function call that populates behind
* the scenes appears more predictable.
*/
/**
* Load I18n.
*
* @augments React.Component
* @param {object} props
* @param {Node} props.children
* @param {string} props.fallbackLng
* @param {string} props.loadPath
* @param {string} props.locale
* @returns {Node}
*/
class I18n extends React.Component {
state = { isLoaded: false };

componentDidMount() {
this.i18nInit();
}

componentDidUpdate(prevProps) {
const { locale } = this.props;

if (locale !== prevProps.locale) {
this.i18nInit();
}
}
const I18n = ({ children, fallbackLng, loadPath, locale }) => {
const [initialized, setInitialized] = useState(false);

/**
* Load i18next.
*
* @returns {Promise<void>}
* Initialize i18next
*/
i18nInit = async () => {
const { fallbackLng, loadPath, locale } = this.props;

useMount(async () => {
try {
await i18next
.use(XHR)
Expand All @@ -81,7 +76,7 @@ class I18n extends React.Component {
loadPath
},
fallbackLng,
lng: locale,
lng: undefined,
debug: !helpers.PROD_MODE,
ns: ['default'],
defaultNS: 'default',
Expand All @@ -90,24 +85,27 @@ class I18n extends React.Component {
}
});
} catch (e) {
// ToDo: eval the need for an isError state ref
//
}

this.setState({ isLoaded: true });
};
setInitialized(true);
});

/**
* Render children after i18next loads.
*
* @returns {Node}
* Update locale.
*/
render() {
const { isLoaded } = this.state;
const { children } = this.props;
useEffect(() => {
if (initialized) {
try {
i18next.changeLanguage(locale);
} catch (e) {
//
}
}
}, [initialized, locale]);

return (isLoaded && <React.Fragment>{children}</React.Fragment>) || <React.Fragment />;
}
}
return (initialized && <React.Fragment>{children}</React.Fragment>) || <React.Fragment />;
};

/**
* Prop types.
Expand Down
10 changes: 9 additions & 1 deletion src/setupTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ configure({ adapter: new Adapter() });
/**
* Emulate for component checks
*/
jest.mock('i18next');
jest.mock('i18next', () => {
const Test = function () { // eslint-disable-line
this.use = () => this;
this.init = () => Promise.resolve();
this.changeLanguage = () => Promise.resolve();
};
return new Test();
});

jest.mock('lodash/debounce', () => jest.fn);

/**
Expand Down
2 changes: 1 addition & 1 deletion tests/__snapshots__/code.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ Array [
"components/inventorySubscriptions/inventorySubscriptions.js:60: console.warn(\`Sorting can only be performed on select fields, confirm field \${id} is allowed.\`);",
"redux/common/reduxHelpers.js:250: console.error(\`Error: Property \${prop} does not exist within the passed state.\`, state);",
"redux/common/reduxHelpers.js:254: console.warn(\`Warning: Property \${prop} does not exist within the passed initialState.\`, initialState);",
"setupTests.js:67: console.error = (message, ...args) => {",
"setupTests.js:75: console.error = (message, ...args) => {",
]
`;
Loading

0 comments on commit e8e520b

Please sign in to comment.