From 92f5d1a7f5f9e7cc3eeac6a9659ef701bd2c4786 Mon Sep 17 00:00:00 2001 From: Huyen Nguyen <25715018+huyenltnguyen@users.noreply.github.com> Date: Mon, 5 Jul 2021 00:38:41 +0700 Subject: [PATCH] fix(client): set the correct language to the HTML lang attribute (#42729) --- ...{react-i18nextMock.js => react-i18next.js} | 0 .../components/app-mount-notifier.test.tsx | 52 +++++++++++++++++++ client/src/components/app-mount-notifier.ts | 27 ---------- client/src/components/app-mount-notifier.tsx | 38 ++++++++++++++ jest.config.js | 1 - 5 files changed, 90 insertions(+), 28 deletions(-) rename client/src/__mocks__/{react-i18nextMock.js => react-i18next.js} (100%) create mode 100644 client/src/components/app-mount-notifier.test.tsx delete mode 100644 client/src/components/app-mount-notifier.ts create mode 100644 client/src/components/app-mount-notifier.tsx diff --git a/client/src/__mocks__/react-i18nextMock.js b/client/src/__mocks__/react-i18next.js similarity index 100% rename from client/src/__mocks__/react-i18nextMock.js rename to client/src/__mocks__/react-i18next.js diff --git a/client/src/components/app-mount-notifier.test.tsx b/client/src/components/app-mount-notifier.test.tsx new file mode 100644 index 0000000000..ababbe5a39 --- /dev/null +++ b/client/src/components/app-mount-notifier.test.tsx @@ -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( + + +

App content

} /> +
+
+ ); + }; + + 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 + ); + }); + } + ); +}); diff --git a/client/src/components/app-mount-notifier.ts b/client/src/components/app-mount-notifier.ts deleted file mode 100644 index 2bcd979747..0000000000 --- a/client/src/components/app-mount-notifier.ts +++ /dev/null @@ -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) => - bindActionCreators({ appMount }, dispatch); - -type AppMountNotifierProps = { - appMount: () => void; - render: () => ReactNode; -}; - -class AppMountNotifier extends Component { - static displayName = 'AppMountNotifier'; - - componentDidMount() { - return this.props.appMount(); - } - render() { - return this.props.render(); - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(AppMountNotifier); diff --git a/client/src/components/app-mount-notifier.tsx b/client/src/components/app-mount-notifier.tsx new file mode 100644 index 0000000000..f3d6830fec --- /dev/null +++ b/client/src/components/app-mount-notifier.tsx @@ -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 ( + <> + + {render()} + + ); +}; + +AppMountNotifier.displayName = 'AppMountNotifier'; + +export default connect(null, mapDispatchToProps)(AppMountNotifier); diff --git a/jest.config.js b/jest.config.js index ed27610807..6b9bae0cde 100644 --- a/jest.config.js +++ b/jest.config.js @@ -10,7 +10,6 @@ module.exports = { '/client/src/__mocks__/styleMock.js', // CSS Modules - match files that end with 'module.css' '\\.module\\.css$': 'identity-obj-proxy', - 'react-i18next': '/client/src/__mocks__/react-i18nextMock.js', '^lodash-es$': 'lodash' }, globals: {