fix(client): set the correct language to the HTML lang attribute (#42729)

This commit is contained in:
Huyen Nguyen
2021-07-05 00:38:41 +07:00
committed by GitHub
parent e19ff14d44
commit 92f5d1a7f5
5 changed files with 90 additions and 28 deletions

View File

@ -0,0 +1,52 @@
import React from 'react';
import { I18nextProvider } from 'react-i18next';
import { render, waitFor } from '@testing-library/react';
import { Provider } from 'react-redux';
import { i18nextCodes } from '../../../config/i18n/all-langs';
import AppMountNotifier from './app-mount-notifier';
import { createStore } from '../redux/createStore';
import i18nTestConfig from '../../i18n/configForTests';
jest.mock('react-ga');
jest.unmock('react-i18next');
type Language = [string, string];
const store = createStore();
// Create a nested array for languages
const languages = Object.keys(i18nextCodes).map(
/* eslint-disable @typescript-eslint/ban-ts-comment, @typescript-eslint/no-unsafe-return */
// @ts-ignore
// TODO: convert `all-langs.js` to TypeScript
(key): Language => [i18nextCodes[key], key]
);
describe('AppMountNotifier', () => {
const setup = (lang: string) => {
i18nTestConfig.language = lang;
render(
<Provider store={store}>
<I18nextProvider i18n={i18nTestConfig}>
<AppMountNotifier render={() => <p>App content</p>} />
</I18nextProvider>
</Provider>
);
};
test.each(languages)(
'should set the lang attribute to %s if the language is %s',
async langCode => {
setup(langCode);
await waitFor(() => {
expect(document.querySelector('html')).toHaveAttribute(
'lang',
langCode
);
});
}
);
});

View File

@ -1,27 +0,0 @@
import { Component, ReactNode } from 'react';
import { AnyAction, bindActionCreators, Dispatch } from 'redux';
import { connect } from 'react-redux';
import { appMount } from '../redux';
const mapStateToProps = () => ({});
const mapDispatchToProps = (dispatch: Dispatch<AnyAction>) =>
bindActionCreators({ appMount }, dispatch);
type AppMountNotifierProps = {
appMount: () => void;
render: () => ReactNode;
};
class AppMountNotifier extends Component<AppMountNotifierProps> {
static displayName = 'AppMountNotifier';
componentDidMount() {
return this.props.appMount();
}
render() {
return this.props.render();
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AppMountNotifier);

View File

@ -0,0 +1,38 @@
import React, { useEffect } from 'react';
import { bindActionCreators, Dispatch } from 'redux';
import { connect } from 'react-redux';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';
import { appMount } from '../redux';
interface AppMountNotifierProps {
render: () => React.ReactNode;
appMount: () => void;
}
const mapDispatchToProps = (dispatch: Dispatch) =>
bindActionCreators({ appMount }, dispatch);
const AppMountNotifier = ({
render,
appMount
}: AppMountNotifierProps): JSX.Element => {
useEffect(() => {
appMount();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const { i18n } = useTranslation();
return (
<>
<Helmet htmlAttributes={{ lang: i18n.language }} />
{render()}
</>
);
};
AppMountNotifier.displayName = 'AppMountNotifier';
export default connect(null, mapDispatchToProps)(AppMountNotifier);

View File

@ -10,7 +10,6 @@ module.exports = {
'<rootDir>/client/src/__mocks__/styleMock.js', '<rootDir>/client/src/__mocks__/styleMock.js',
// CSS Modules - match files that end with 'module.css' // CSS Modules - match files that end with 'module.css'
'\\.module\\.css$': 'identity-obj-proxy', '\\.module\\.css$': 'identity-obj-proxy',
'react-i18next': '<rootDir>/client/src/__mocks__/react-i18nextMock.js',
'^lodash-es$': 'lodash' '^lodash-es$': 'lodash'
}, },
globals: { globals: {