How to add locale data dynamically using React Intl? - react-intl

Am using React-intl for internationalization of an UI Util Library. The library has a folder say i18n wherein I place json files for different locales.If the user of this library want to add support for additional locales, he/she can place additional json file with key/value pairs for the respective locale.
But React-intl requires to import and addLocaleData for the respective locale in prior.For example,
import fr from 'react-intl/locale-data/fr';
addLocaleData([...fr]);
Is there a way to addLocaleData and import the locale library for the respective locale dynamically in React-intl?

If you are using webpack. You can code-split the different locale data from your app and load dynamically. Webpack 1 supports only require.ensure() and webpack 2 also supports System.import(). System.import returns a promise while require.ensure uses a callback. https://webpack.github.io/docs/code-splitting.html
With System.import()
import { addLocaleData } from 'react-intl';
const reactIntlLocaleData = {
fr: () => System.import('react-intl/locale-data/fr'),
en: () => System.import('react-intl/locale-data/en')
};
function loadLocaleData(locale){
reactIntlLocaleData[locale]()
.then((intlData) => {
addLocaleData(intlData)
}
}
With require.ensure()
import { addLocaleData } from 'react-intl';
const reactIntlLocaleData = {
fr: () => require.ensure([], (require) => {
const frData = require('react-intl/locale-data/fr');
addLocaleData(frData);
}),
en: () => require.ensure([], (require) => {
const enData = require('react-intl/locale-data/en');
addLocaleData(enData);
})
};
function loadLocaleData(locale){
reactIntlLocaleData[locale]();
}
Depending on your development environment the code above may or may not work. It assumes you are using Webpack2 along with Babel to transpile your code.

Hey I have done this now, as described below and its working :-)
const possibleLocale = navigator.language.split('-')[0] || 'en';
addLocaleData(require(`react-intl/locale-data/${possibleLocale}`));
Here, the locale is fetched from the browser through navigator.language.
Hope this helps :-)

Related

_vuex.default.store is not a constructor

I'm trying to test a component that uses vuex inside it, I'm trying to pass the store of the respective component so that it can be assembled, but I'm getting the following error:
_vuex.default.store is not a constructor
I have no idea what's going on and I couldn't find anything on the internet to help me, if anyone can help me, I would be very grateful!
Spec file
import {shallowMount,createLocalVue} from '#vue/test-utils'
import Vuex from 'vuex'
import sateliteComponent from '#/components/satelite/listaSatelite.vue'
import sateliteStore from '#/core/modules/satelite/index.js'
var vueWithVuex = createLocalVue()
vueWithVuex.use(Vuex)
const store = new Vuex.store({
sateliteStore
})
describe('testes componente satelite', () => {
test('instanciado', () => {
const wrapper = shallowMount(sateliteComponent,{
localVue:vueWithVuex,
store
})
expect(wrapper.isVueInstance()).toBeTruthy()
});
});
if necessary, I can post my component that is being rendered
Correct with this:
const store = new Vuex.Store({
sateliteStore
})
It should be Vuex.Store check the capitalization of the word store.
For anyone else visiting:
Even with the right casing, this error can also come up if you try to use
new Vuex.Store()
before running
Vue.use(Vuex)

Unit test a typescript vue component

I need to be able test my component (methods, computed properties, data, ...). However, when I import my vue component in my unit test:
import Pagination from 'src/components/shared/pagination.vue'
import { newComponent } from '../component-factory'
describe('pagination.vue', () => {
const propsData = {
metadata: {
page: 2,
records: 155,
total: 11,
},
perPage: 15,
}
it('should have correct number of records', () => {
const ctor = Vue.extend(Pagination)
const vm = new ctor({propsData}).$mount()
expect(vm.firstRecord).toBe(16)
expect(vm.lastRecord).toBe(30)
})
...
vm is of type Vue, and thus does not have the firstRecord/lastRecord properties. Running the test with karma shows a success, but the typescript compiler spits out Errors:
ERROR in ./tests/shared/pagination.spec.ts
(16,19): error TS2339: Property 'firstRecord' does not exist on type 'Vue'.
ERROR in ./tests/shared/pagination.spec.ts
(17,19): error TS2339: Property 'lastRecord' does not exist on type 'Vue'.
I tried casting:
...
const vm = new ctor({propsData}).$mount() as Pagination
...
But that results in a warning in VSCode:
[ts] Cannot find name 'Pagination'.
And has the effect of treating vm as type any which is totally counterproductive.
I think this all stems from the fact that when using .vue files you have to add the declaration:
declare module '*.vue' {
import Vue from 'vue'
export default typeof Vue
}
Which clearly sets the type of all .vue files to Vue, which isn't exactly a lie, but isn't helpful either... Any suggestions? What am I doing wrong?
For future reference, I have attempted to use vuetype which generates .d.ts files for each .vue file, but ran into this issue. Also, there is a request to make .vue a first class citizen in the typescript ecosystem, which would eliminate this problem. And, I just added a request for a vue language service extension
Up until Vue 2.5, their TypeScript documentation page recommended exporting an interface that extends Vue if you were not going to use vue-class-component. You can export this interface to use in your tests, to cast your component instance. The recommendation has been removed from the docs, but I have not been able to figure out how to change my tests to not need the interface.
It looks like vuetype could generate these interfaces for you, but I've just been creating them manually.
Here is a greatly simplified example, but you can define anything in your interface that you would reference on vm, ie data, props, methods:
// NOTE: Make sure your interface extends `Vue`!
export interface PaginationComponent extends Vue {
firstRecord: number,
lastRecord: number
}
export default {
name: 'Pagination',
data: function() {
return {
firstRecord: 16,
lastRecord: 30,
}
}
}
For your test, you can cast the component instance to the type of your exported interface:
import Pagination, {PaginationComponent} from 'src/components/shared/pagination.vue'
describe('pagination', () => {
it('should know about component data fields', () => {
const ctor = Vue.extend(Pagination)
const vm : PaginationComponent = new ctor().$mount()
expect(vm.firstRecord).toBe(16)
expect(vm.lastRecord).toBe(30)
})
})

Django React with i18n

Wondering what is the best practices to use django with React when i18n needed.
Currently I'm loading javascript_catalog on global scope.
all the translations controlled by django i18n.
From react components I'm using gettext django javascript catalog function to translate all the necessary texts.
For localization i'm also using django i10n support provided by javascript_catalog.
anyone have better practices for this using i18n in both django and react
I do not claim this is best practice but it works for me. Separating the frontend and backend a bit.
Think about it, what part translation is related to ? UI/UX right ?
Therefore, why have it on the backend, why not just have it in your react project ?
Steps: install i18next for react:
npm install react-i18next#legacy i18next --save
Notice, you will not get a > v9 but this tutorial :
https://react.i18next.com/legacy-v9/step-by-step-guide
is still valid,
step 1:
create translation folder in your src folder like this:
-src
-translations
-ar
transilationsAR.js
-en
transilationEN.js
- i18n.js
- App.js
The way I do it, is manually to insure amazing User experience.
This code will turn any keywords From ENGLISH-ENGLISH to any language:
data_pre.py
en_en= {"kw": "kw"}
en_kw = []
for kw in en_en:
en_kw.append(kw)
# translate the list of english kw to desired language via google translate
ar_kw = """ [en_to_desired_langauge] """
ar_kw = ar_kw.replace("،", ",")
ar_kw = eval(ar_kw)
en_ar = dict()
for i in range(0, len(ar_kw)):
en_ar[en_kw[i]] = ar_kw[i]
# en_ar key words are in en_ar variable.
transilationEN.js : Here we will have for example ENGLISH-ENGLISH keywords dictionary,
there are some tools help you fetch all key words, phrases from your website.
export const TRANSLATIONS_EN = {
'Hello': 'Hello',
}
transilationAR.js, here we have new dictionary out of data preprocessing.
export const TRANSLATIONS_AR = {
'Hello': 'مرحبا',
}
run
npm install --save i18next-browser-languagedetector
i18n.js
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import {TRANSLATIONS_AR } from "./ar/translationsAR";
import { TRANSLATIONS_EN } from "./en/translationsEN";
// the translations
// (tip move them in a JSON file and import them)
const resources = {
en: {
translation: TRANSLATIONS_EN
},
ar: {
translation: TRANSLATIONS_AR
},
};
i18n
.use(initReactI18next)
.use(LanguageDetector)
.init({
resources,
lng:[ "en", 'ar'],
keySeparator: false,
interpolation: {
escapeValue: false
},
react: {
useSuspense: false
}
});
export default i18n;
App.js
import React, { Component } from "react";
import { withTranslation } from 'react-i18next';
import i18n from "./translations/i18n";
class App extends Component {
handleChangeLangToAR = () => {
i18n.changeLanguage("ar")
};
handleChangeLangToEN = () => {
i18n.changeLanguage("en")
};
render() {
return (
<SomeComponent
{...this.props}
handleChangeLangToAR={() => this.handleChangeLangToAR()}
handleChangeLangToEN={ () => this.handleChangeLangToEN()}
/>
);
}
}
export default (withTranslation()(App));
now in someComponent.js we have acess to t() via props, which can be use to translate any keywords, phrases on our website.
someComponent.js
import React from "react";
class someComponent extends React.Component {
render() {
const { handleChangeLangToEN, handleChangeLangToAR, t} = this.props;
return (
<h1>{t('Hello')}</h1>
)
}
};
export default someComponent();
handleChangeLangToEN: can be set to onClick on a button to switch site language to English.
handleChangeLangToAR : can be set to onClick a button to switch site language to Arabic.
Both sould be in layout component, so we do not have to pass them every where in our project.
for example:
<Button OnClick={ () => handleChangeLangToEN }> English </Button>
Or if we have another component we want to translate, simply we export the component with WithTranslation , then we have access to t():
anotherComponent.js
import React from "react";
import { withTranslation } from 'react-i18next';
class anotherComponent extends React.Component {
render() {
const {t} = this.props;
return (
<h1>{t('Hello')}</h1>
)
}
};
export default (withTransilation()anotherComponent)
if you are connecting your props using redux store an still would like to use withTransilation(), do not get confused, you do it like this.
const mapStateToProps = state => {
return {
isAuthenticated: state.auth.token !== null
};
};
const mapDispatchToProps = dispatch => {
return {
onTryAutoSignup: () => dispatch(actions.authCheckState())
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(withTranslation()(App));
'''

Jest with React - How do you render an entire component and test it?

Let's say I have a simple App component and I am running a test for it to see if it simply returns a div that prints Hello World to the screen. What is the function helper in order to render App inside my testing file? Furthermore, what is the function to call in the expect call in order to test the HTML being rendered?
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
describe("App", () => {
it('prints "HELLO WORLD" to the screen', () => {
expect(**App??**).**toRENDER??**("HELLO WORLD")
}
}
Thanks
It's a good practice to use enzyme library from airbnb.
There is an example of using both together
Your code can looks like the next:
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import {shallow} from 'enzyme'
describe("App", () => {
it('prints "HELLO WORLD" to the screen', () => {
const wrapper = shallow(<App />)
expect(wrapper.text()).toBe("HELLO WORLD")
}
}
You would want to look at Jest's snapshot testing for this. This is as simple as calling:
import renderer from 'react-test-renderer';
describe('snapshot testing', () => {
it('should render App', () => {
const component = renderer.create(<App />);
const tree = component.toJSON();
expect(component).toMatchSnapshot();
});
});
A snapshot file will be stored in a __tests__/__snapshots__ folder. Make sure you commit these snapshot files into your Git repository so other contributors can have a snapshot to their components against.
For more information on how to setup Jest snapshot testing with React, refer to this docs here as you will need to install some npm dependencies.

How to Unit Test React-Redux Connected Components?

I am using Mocha, Chai, Karma, Sinon, Webpack for Unit tests.
I followed this link to configure my testing environment for React-Redux Code.
How to implement testing + code coverage on React with Karma, Babel, and Webpack
I can successfully test my action and reducers javascript code, but when it comes to testing my components it always throw some error.
import React from 'react';
import TestUtils from 'react/lib/ReactTestUtils'; //I like using the Test Utils, but you can just use the DOM API instead.
import chai from 'chai';
// import sinon from 'sinon';
import spies from 'chai-spies';
chai.use(spies);
let should = chai.should()
, expect = chai.expect;
import { PhoneVerification } from '../PhoneVerification';
let fakeStore = {
'isFetching': false,
'usernameSettings': {
'errors': {},
'username': 'sahil',
'isEditable': false
},
'emailSettings': {
'email': 'test#test.com',
'isEmailVerified': false,
'isEditable': false
},
'passwordSettings': {
'errors': {},
'password': 'showsomestarz',
'isEditable': false
},
'phoneSettings': {
'isEditable': false,
'errors': {},
'otp': null,
'isOTPSent': false,
'isOTPReSent': false,
'isShowMissedCallNumber': false,
'isShowMissedCallVerificationLink': false,
'missedCallNumber': null,
'timeLeftToVerify': null,
'_verifiedNumber': null,
'timers': [],
'phone': '',
'isPhoneVerified': false
}
}
function setup () {
console.log(PhoneVerification);
// PhoneVerification.componentDidMount = chai.spy();
let output = TestUtils.renderIntoDocument(<PhoneVerification {...fakeStore}/>);
return {
output
}
}
describe('PhoneVerificationComponent', () => {
it('should render properly', (done) => {
const { output } = setup();
expect(PhoneVerification.prototype.componentDidMount).to.have.been.called;
done();
})
});
This following error comes up with above code.
FAILED TESTS:
PhoneVerificationComponent
✖ should render properly
Chrome 48.0.2564 (Mac OS X 10.11.3)
Error: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.
Tried switching from sinon spies to chai-spies.
How should I unit test my React-Redux Connected Components(Smart Components)?
A prettier way to do this, is to export both your plain component, and the component wrapped in connect. The named export would be the component, the default is the wrapped component:
export class Sample extends Component {
render() {
let { verification } = this.props;
return (
<h3>This is my awesome component.</h3>
);
}
}
const select = (state) => {
return {
verification: state.verification
}
}
export default connect(select)(Sample);
In this way you can import normally in your app, but when it comes to testing you can import your named export using import { Sample } from 'component'.
The problem with the accepted answer is that we are exporting something unnecessarily just to be able to test it. And exporting a class just to test it is not a good idea in my opinion.
Here is a neater solution without the need of exporting anything but the connected component:
If you are using jest, you can mock connect method to return three things:
mapStateToProps
mapDispatchToProps
ReactComponent
Doing so is pretty simple. There are 2 ways: Inline mocks or global mocks.
1. Using inline mock
Add the following snippet before the test's describe function.
jest.mock('react-redux', () => {
return {
connect: (mapStateToProps, mapDispatchToProps) => (ReactComponent) => ({
mapStateToProps,
mapDispatchToProps,
ReactComponent
}),
Provider: ({ children }) => children
}
})
2. Using file mock
Create a file __mocks__/react-redux.js in the root (where package.json is located)
Add the following snippet in the file.
module.exports = {
connect: (mapStateToProps, mapDispatchToProps) => (ReactComponent) => ({
mapStateToProps,
mapDispatchToProps,
ReactComponent,
}),
Provider: ({children}) => children
};
After mocking, you would be able to access all the above three using Container.mapStateToProps,Container.mapDispatchToProps and Container.ReactComponent.
Container can be imported by simply doing
import Container from '<path>/<fileName>.container.js'
Hope it helps.
Note that if you use file mock. The mocked file will be used globally for all the test cases(unless you do jest.unmock('react-redux')) before the test case.
Edit: I have written a detailed blog explaining the above in detail:
http://rahulgaba.com/front-end/2018/10/19/unit-testing-redux-containers-the-better-way-using-jest.html
You can test your connected component and I think you should do so. You may want to test the unconnected component first, but I suggest that you will not have complete test coverage without also testing the connected component.
Below is an untested extract of what I do with Redux and Enzyme. The central idea is to use Provider to connect the state in test to the connected component in test.
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import SongForm from '../SongForm'; // import the CONNECTED component
// Use the same middlewares you use with Redux's applyMiddleware
const mockStore = configureMockStore([ /* middlewares */ ]);
// Setup the entire state, not just the part Redux passes to the connected component.
const mockStoreInitialized = mockStore({
songs: {
songsList: {
songs: {
songTags: { /* ... */ }
}
}
}
});
const nullFcn1 = () => null;
const nullFcn2 = () => null;
const nullFcn3 = () => null;
const wrapper = mount( // enzyme
<Provider store={store}>
<SongForm
screen="add"
disabled={false}
handleFormSubmit={nullFcn1}
handleModifySong={nullFcn2}
handleDeleteSong={nullFcn3}
/>
</Provider>
);
const formPropsFromReduxForm = wrapper.find(SongForm).props(); // enzyme
expect(
formPropsFromReduxForm
).to.be.deep.equal({
screen: 'add',
songTags: initialSongTags,
disabled: false,
handleFormSubmit: nullFcn1,
handleModifySong: nullFcn2,
handleDeleteSong: nullFcn3,
});
===== ../SongForm.js
import React from 'react';
import { connect } from 'react-redux';
const SongForm = (/* object */ props) /* ReactNode */ => {
/* ... */
return (
<form onSubmit={handleSubmit(handleFormSubmit)}>
....
</form>
};
const mapStateToProps = (/* object */ state) /* object */ => ({
songTags: state.songs.songTags
});
const mapDispatchToProps = () /* object..function */ => ({ /* ... */ });
export default connect(mapStateToProps, mapDispatchToProps)(SongForm)
You may want to create a store with pure Redux. redux-mock-store is just a light-weight version of it meant for testing.
You may want to use react-addons-test-utils instead of airbnb's Enzyme.
I use airbnb's chai-enzyme to have React-aware expect options. It was not needed in this example.
redux-mock-store is an awesome tool to test redux connected components in react
const containerElement = shallow((<Provider store={store}><ContainerElement /></Provider>));
Create fake store and mount the component
You may refer to this article Testing redux store connected React Components using Jest and Enzyme | TDD | REACT | REACT NATIVE
Try creating 2 files, one with component itself, being not aware of any store or anything (PhoneVerification-component.js). Then second one (PhoneVerification.js), which you will use in your application and which only returns the first component subscribed to store via connect function, something like
import PhoneVerificationComponent from './PhoneVerification-component.js'
import {connect} from 'react-redux'
...
export default connect(mapStateToProps, mapDispatchToProps)(PhoneVerificationComponent)
Then you can test your "dumb" component by requiring PhoneVerification-component.js in your test and providing it with necessary mocked props. There is no point of testing already tested (connect decorator, mapStateToProps, mapDispatchToProps etc...)