Skip to content

Commit

Permalink
feat(Modal): add support of LocaleProvider
Browse files Browse the repository at this point in the history
Modal support LocaleProvider to control locale, but it's special when use method to create Modal. So add runtimeLocale for cache locale.

re #43
close #48
  • Loading branch information
ZxBing0066 committed Oct 15, 2018
1 parent c891b86 commit 5e92d64
Show file tree
Hide file tree
Showing 15 changed files with 203 additions and 21 deletions.
4 changes: 2 additions & 2 deletions package.json
@@ -1,8 +1,8 @@
{
"name": "@ucloud-fe/react-components",
"version": "0.3.1",
"title": "ucloud react components",
"description": "ucloud react components",
"title": "UCloud react components",
"description": "UCloud react components",
"keywords": [
"react",
"react-component",
Expand Down
12 changes: 12 additions & 0 deletions src/components/LocaleProvider/LocaleProvider.jsx
@@ -1,7 +1,14 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';

import { setRuntimeLocale } from './locale/runtime';

class LocaleProvider extends Component {
constructor(props) {
super(props);
const { locale } = props;
setRuntimeLocale(locale);
}
static propTypes = {
children: PropTypes.node,
locale: PropTypes.object
Expand All @@ -19,6 +26,11 @@ class LocaleProvider extends Component {
}
};
}
componentWillReceiveProps = nextProps => {
if (nextProps.locale !== this.props.locale) {
setRuntimeLocale(nextProps.locale);
}
};
render() {
return React.Children.only(this.props.children);
}
Expand Down
7 changes: 7 additions & 0 deletions src/components/LocaleProvider/LocaleProvider.md
Expand Up @@ -3,6 +3,11 @@
* 国际化语言组件,用于外围包裹来支持统一控制组件的语言
* 语言控制分为 3 层,开发者可控制的为 2 层,均为可选,优先级从低到高分别是: `组件默认语言(开发者不可控) -> LocaleProvider.locale -> Component.locale`

### 注意点

* 通过 Modal.method 生成的 Modal 组件的语言控制无法通过 context 简单的传输,所以使用了 runtimeLocale,如果一个页面用到了两个传入不同 locale 的 LocaleProvider,那么 runtimeLocale 可能会错乱,请务必注意
* 如用到 DatePicker、Calendar 等日期相关组件,LocaleProvider 由于生命周期顺序的问题无法实时变更语言,需要手动设置 moment 的语言。

### 演示

* 自定义语言
Expand All @@ -21,6 +26,8 @@

```js static
import locale from '@ucloud-fe/react-components/lib/components/LocaleProvider/locale/en_US';
// 使用到DatePicker等日期相关组件的情况下需要手动设置moment语言
moment.locale('en');
const Demo = () => (
<div>
<LocaleProvider locale={locale}>
Expand Down
19 changes: 18 additions & 1 deletion src/components/LocaleProvider/__demo__/list.jsx
@@ -1,13 +1,15 @@
import React, { Component } from 'react';
import moment from 'moment';

import Button from 'src/components/Button';
import Radio from 'src/components/Radio';
import Form from 'src/components/Form';
import LocaleProvider from 'src/components/LocaleProvider';
import Pagination from 'src/components/Pagination';
import Calendar from 'src/components/Calendar';
import DatePicker from 'src/components/DatePicker';
import Select from 'src/components/Select';
import Modal from 'src/components/Modal';
import zh_CN from 'src/components/LocaleProvider/locale/zh_CN';
import en_US from 'src/components/LocaleProvider/locale/en_US';

Expand Down Expand Up @@ -80,13 +82,28 @@ class Demo extends Component {
</Select>
</div>
</div>
<div className="demo-wrap">
<div className="demo-block">
<Button className="demo-alert-btn" onClick={() => Modal.alert({ title: 'alert' }, 'content')}>
alert
</Button>
</div>
<div className="demo-block">
<Button
className="demo-confirm-btn"
onClick={() => Modal.confirm({ title: 'confirm' }, 'content')}
>
confirm
</Button>
</div>
</div>
</div>
);
const { localeStr, locale } = this.state;

return (
<div className="noeditor-wrap">
<Form className="demo-wrap">
<Form className="demo-form">
<Form.Item label="locale" {...itemLayout}>
<Radio.Group
styleType="button"
Expand Down
Expand Up @@ -1095,7 +1095,7 @@ exports[`LocaleProvider demo -- list 1`] = `
class="noeditor-wrap"
>
<form
class="demo-wrap"
class="demo-form"
>
<div
class="c0 c1"
Expand Down Expand Up @@ -2196,6 +2196,30 @@ exports[`LocaleProvider demo -- list 1`] = `
</div>
</div>
</div>
<div
class="demo-wrap"
>
<div
class="demo-block"
>
<button
class="demo-alert-btn c9"
type="button"
>
alert
</button>
</div>
<div
class="demo-block"
>
<button
class="demo-confirm-btn c9"
type="button"
>
confirm
</button>
</div>
</div>
</div>
</div>
</div>
Expand Down
@@ -1,11 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`LocaleProvider Modal 1`] = `"<div tabindex=\\"-1\\" class=\\"uc-fe-modal-wrap style__ModalWrap-doQAIf dFDItb\\" role=\\"dialog\\" aria-labelledby=\\"rcDialogTitle0\\" style=\\"z-index: 1010;\\"><div role=\\"document\\" class=\\"uc-fe-modal uc-fe-animation-fade-appear\\" style=\\"width: 300px;\\"><div tabindex=\\"0\\" style=\\"width: 0px; height: 0px; overflow: hidden;\\">sentinelStart</div><div class=\\"uc-fe-modal-content\\"><div class=\\"uc-fe-modal-header\\"><div class=\\"uc-fe-modal-title\\" id=\\"rcDialogTitle0\\"><div class=\\"uc-fe-modal-title-content\\">alert</div><i class=\\"icon__circle-cross uc-fe-modal-close style__IconWrap-hmdRqY gdfCrX\\"></i></div></div><div class=\\"uc-fe-modal-body\\">content</div><div class=\\"uc-fe-modal-footer\\"><button class=\\"style__ButtonWrap-bFwXOZ hljJkM\\" type=\\"button\\"><!-- react-text: 14 -->确定<!-- /react-text --></button></div></div><div tabindex=\\"0\\" style=\\"width: 0px; height: 0px; overflow: hidden;\\">sentinelEnd</div></div></div>"`;
exports[`LocaleProvider Modal 2`] = `"<div tabindex=\\"-1\\" class=\\"uc-fe-modal-wrap style__ModalWrap-doQAIf dFDItb\\" role=\\"dialog\\" aria-labelledby=\\"rcDialogTitle1\\" style=\\"z-index: 1010;\\"><div role=\\"document\\" class=\\"uc-fe-modal uc-fe-animation-fade-appear\\" style=\\"width: 300px;\\"><div tabindex=\\"0\\" style=\\"width: 0px; height: 0px; overflow: hidden;\\">sentinelStart</div><div class=\\"uc-fe-modal-content\\"><div class=\\"uc-fe-modal-header\\"><div class=\\"uc-fe-modal-title\\" id=\\"rcDialogTitle1\\"><div class=\\"uc-fe-modal-title-content\\">confirm</div><i class=\\"icon__circle-cross uc-fe-modal-close style__IconWrap-hmdRqY gdfCrX\\"></i></div></div><div class=\\"uc-fe-modal-body\\">content</div><div class=\\"uc-fe-modal-footer\\"><button class=\\"style__ButtonWrap-bFwXOZ UiDMT\\" type=\\"button\\" style=\\"margin-right: 8px;\\"><!-- react-text: 14 -->取消<!-- /react-text --></button><button class=\\"style__ButtonWrap-bFwXOZ hljJkM\\" type=\\"button\\"><!-- react-text: 16 -->确定<!-- /react-text --></button></div></div><div tabindex=\\"0\\" style=\\"width: 0px; height: 0px; overflow: hidden;\\">sentinelEnd</div></div></div>"`;
exports[`LocaleProvider Modal 3`] = `"<div tabindex=\\"-1\\" class=\\"uc-fe-modal-wrap style__ModalWrap-doQAIf dFDItb\\" role=\\"dialog\\" aria-labelledby=\\"rcDialogTitle2\\" style=\\"z-index: 1010;\\"><div role=\\"document\\" class=\\"uc-fe-modal uc-fe-animation-fade-appear\\" style=\\"width: 300px;\\"><div tabindex=\\"0\\" style=\\"width: 0px; height: 0px; overflow: hidden;\\">sentinelStart</div><div class=\\"uc-fe-modal-content\\"><div class=\\"uc-fe-modal-header\\"><div class=\\"uc-fe-modal-title\\" id=\\"rcDialogTitle2\\"><div class=\\"uc-fe-modal-title-content\\">alert</div><i class=\\"icon__circle-cross uc-fe-modal-close style__IconWrap-hmdRqY gdfCrX\\"></i></div></div><div class=\\"uc-fe-modal-body\\">content</div><div class=\\"uc-fe-modal-footer\\"><button class=\\"style__ButtonWrap-bFwXOZ hljJkM\\" type=\\"button\\"><!-- react-text: 14 -->confirm<!-- /react-text --></button></div></div><div tabindex=\\"0\\" style=\\"width: 0px; height: 0px; overflow: hidden;\\">sentinelEnd</div></div></div>"`;
exports[`LocaleProvider Modal 4`] = `"<div tabindex=\\"-1\\" class=\\"uc-fe-modal-wrap style__ModalWrap-doQAIf dFDItb\\" role=\\"dialog\\" aria-labelledby=\\"rcDialogTitle3\\" style=\\"z-index: 1010;\\"><div role=\\"document\\" class=\\"uc-fe-modal uc-fe-animation-fade-appear\\" style=\\"width: 300px;\\"><div tabindex=\\"0\\" style=\\"width: 0px; height: 0px; overflow: hidden;\\">sentinelStart</div><div class=\\"uc-fe-modal-content\\"><div class=\\"uc-fe-modal-header\\"><div class=\\"uc-fe-modal-title\\" id=\\"rcDialogTitle3\\"><div class=\\"uc-fe-modal-title-content\\">confirm</div><i class=\\"icon__circle-cross uc-fe-modal-close style__IconWrap-hmdRqY gdfCrX\\"></i></div></div><div class=\\"uc-fe-modal-body\\">content</div><div class=\\"uc-fe-modal-footer\\"><button class=\\"style__ButtonWrap-bFwXOZ UiDMT\\" type=\\"button\\" style=\\"margin-right: 8px;\\"><!-- react-text: 14 -->cancel<!-- /react-text --></button><button class=\\"style__ButtonWrap-bFwXOZ hljJkM\\" type=\\"button\\"><!-- react-text: 16 -->confirm<!-- /react-text --></button></div></div><div tabindex=\\"0\\" style=\\"width: 0px; height: 0px; overflow: hidden;\\">sentinelEnd</div></div></div>"`;
exports[`LocaleProvider switch locale 1`] = `
<div
class="noeditor-wrap"
>
<form
class="demo-wrap"
class="demo-form"
>
<div
class="style__ItemWrap-bJHrdv gRAdqA style__RowWrap-bUjzaZ grtDOl"
Expand Down Expand Up @@ -1254,6 +1262,30 @@ exports[`LocaleProvider switch locale 1`] = `
</div>
</div>
</div>
<div
class="demo-wrap"
>
<div
class="demo-block"
>
<button
class="demo-alert-btn style__ButtonWrap-bFwXOZ eAweBM"
type="button"
>
alert
</button>
</div>
<div
class="demo-block"
>
<button
class="demo-confirm-btn style__ButtonWrap-bFwXOZ eAweBM"
type="button"
>
confirm
</button>
</div>
</div>
</div>
</div>
</div>
Expand All @@ -1264,7 +1296,7 @@ exports[`LocaleProvider switch locale 2`] = `
class="noeditor-wrap"
>
<form
class="demo-wrap"
class="demo-form"
>
<div
class="style__ItemWrap-bJHrdv gRAdqA style__RowWrap-bUjzaZ grtDOl"
Expand Down Expand Up @@ -2513,6 +2545,30 @@ exports[`LocaleProvider switch locale 2`] = `
</div>
</div>
</div>
<div
class="demo-wrap"
>
<div
class="demo-block"
>
<button
class="demo-alert-btn style__ButtonWrap-bFwXOZ eAweBM"
type="button"
>
alert
</button>
</div>
<div
class="demo-block"
>
<button
class="demo-confirm-btn style__ButtonWrap-bFwXOZ eAweBM"
type="button"
>
confirm
</button>
</div>
</div>
</div>
</div>
</div>
Expand Down
24 changes: 24 additions & 0 deletions src/components/LocaleProvider/__tests__/index.test.js
@@ -1,6 +1,7 @@
import React from 'react';
import { mount } from 'enzyme';
import { renderToJson } from 'enzyme-to-json';
import sleep from 'tests/shared/sleep';

import Demo from '../__demo__/list';

Expand All @@ -12,4 +13,27 @@ describe('LocaleProvider', () => {
wrapper.instance().setLocale('en_US');
expect(renderToJson(wrapper.render())).toMatchSnapshot();
});
test('Modal', async () => {
const wrapper = mount(<Demo />);
wrapper.find('button.demo-alert-btn').simulate('click');
expect(document.querySelectorAll('.uc-fe-modal-wrap').length).toBe(1);
expect(document.querySelector('.uc-fe-modal-wrap').outerHTML).toMatchSnapshot();
document.querySelector('.uc-fe-modal-close').click();
await sleep(500);
wrapper.find('button.demo-confirm-btn').simulate('click');
expect(document.querySelectorAll('.uc-fe-modal-wrap').length).toBe(1);
expect(document.querySelector('.uc-fe-modal-wrap').outerHTML).toMatchSnapshot();
document.querySelector('.uc-fe-modal-close').click();
await sleep(500);

wrapper.instance().setLocale('en_US');
wrapper.find('button.demo-alert-btn').simulate('click');
expect(document.querySelectorAll('.uc-fe-modal-wrap').length).toBe(1);
expect(document.querySelector('.uc-fe-modal-wrap').outerHTML).toMatchSnapshot();
document.querySelector('.uc-fe-modal-close').click();
await sleep(500);
wrapper.find('button.demo-confirm-btn').simulate('click');
expect(document.querySelectorAll('.uc-fe-modal-wrap').length).toBe(1);
expect(document.querySelector('.uc-fe-modal-wrap').outerHTML).toMatchSnapshot();
});
});
4 changes: 3 additions & 1 deletion src/components/LocaleProvider/locale/en_US.js
Expand Up @@ -2,11 +2,13 @@ import Pagination from 'src/components/Pagination/locale/en_US';
import Calendar from 'src/components/Calendar/locale/en_US';
import DatePicker from 'src/components/DatePicker/locale/en_US';
import Select from 'src/components/Select/locale/en_US';
import Modal from 'src/components/Modal/locale/en_US';

export default {
locale: 'en',
Pagination,
Calendar,
DatePicker,
Select
Select,
Modal
};
10 changes: 10 additions & 0 deletions src/components/LocaleProvider/locale/runtime.js
@@ -0,0 +1,10 @@
let runtimeLocale = {};

const setRuntimeLocale = _runtimeLocale => {
runtimeLocale = _runtimeLocale;
};
const getRuntimeLocale = () => {
return runtimeLocale;
};

export { setRuntimeLocale, getRuntimeLocale };
4 changes: 3 additions & 1 deletion src/components/LocaleProvider/locale/zh_CN.js
Expand Up @@ -2,11 +2,13 @@ import Pagination from 'src/components/Pagination/locale/zh_CN';
import Calendar from 'src/components/Calendar/locale/zh_CN';
import DatePicker from 'src/components/DatePicker/locale/zh_CN';
import Select from 'src/components/Select/locale/zh_CN';
import Modal from 'src/components/Modal/locale/zh_CN';

export default {
locale: 'zh-cn',
Pagination,
Calendar,
DatePicker,
Select
Select,
Modal
};
11 changes: 9 additions & 2 deletions src/components/LocaleProvider/localeConsumerDecorator.jsx
@@ -1,7 +1,9 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';

const localeConsumerDecorator = ({ defaultLocale = {}, localeName, publicFn = [] }) => Child => {
import { getRuntimeLocale } from './locale/runtime';

const localeConsumerDecorator = ({ defaultLocale = {}, localeName, publicFn = [], requireRuntimeLocale }) => Child => {
class LocalConsumerWrappedComponent extends Component {
constructor(...args) {
super(...args);
Expand All @@ -24,7 +26,12 @@ const localeConsumerDecorator = ({ defaultLocale = {}, localeName, publicFn = []
return (
<Child
ref={ref => (this.child = ref)}
locale={{ ...defaultLocale, ...context[localeName], ...locale }}
locale={{
...defaultLocale,
...context[localeName],
...(requireRuntimeLocale ? getRuntimeLocale()[localeName] : {}),
...locale
}}
{...rest}
/>
);
Expand Down

0 comments on commit 5e92d64

Please sign in to comment.