From 5e92d6466079781e66660500604b720b740e06a9 Mon Sep 17 00:00:00 2001 From: ZxBing0066 Date: Mon, 15 Oct 2018 17:50:30 +0800 Subject: [PATCH] feat(Modal): add support of LocaleProvider 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 --- package.json | 4 +- .../LocaleProvider/LocaleProvider.jsx | 12 ++++ .../LocaleProvider/LocaleProvider.md | 7 +++ .../LocaleProvider/__demo__/list.jsx | 19 +++++- .../__tests__/__snapshots__/demo.test.js.snap | 26 +++++++- .../__snapshots__/index.test.js.snap | 60 ++++++++++++++++++- .../LocaleProvider/__tests__/index.test.js | 24 ++++++++ src/components/LocaleProvider/locale/en_US.js | 4 +- .../LocaleProvider/locale/runtime.js | 10 ++++ src/components/LocaleProvider/locale/zh_CN.js | 4 +- .../localeConsumerDecorator.jsx | 11 +++- src/components/Modal/Modal.jsx | 19 ++++-- src/components/Modal/locale/en_US.js | 4 ++ src/components/Modal/locale/zh_CN.js | 4 ++ src/components/Modal/method.jsx | 16 +++-- 15 files changed, 203 insertions(+), 21 deletions(-) create mode 100644 src/components/LocaleProvider/locale/runtime.js create mode 100644 src/components/Modal/locale/en_US.js create mode 100644 src/components/Modal/locale/zh_CN.js diff --git a/package.json b/package.json index 91b20163..7ac52953 100644 --- a/package.json +++ b/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", diff --git a/src/components/LocaleProvider/LocaleProvider.jsx b/src/components/LocaleProvider/LocaleProvider.jsx index 9163847b..85941e29 100644 --- a/src/components/LocaleProvider/LocaleProvider.jsx +++ b/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 @@ -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); } diff --git a/src/components/LocaleProvider/LocaleProvider.md b/src/components/LocaleProvider/LocaleProvider.md index a77e66c2..90fc6359 100644 --- a/src/components/LocaleProvider/LocaleProvider.md +++ b/src/components/LocaleProvider/LocaleProvider.md @@ -3,6 +3,11 @@ * 国际化语言组件,用于外围包裹来支持统一控制组件的语言 * 语言控制分为 3 层,开发者可控制的为 2 层,均为可选,优先级从低到高分别是: `组件默认语言(开发者不可控) -> LocaleProvider.locale -> Component.locale` +### 注意点 + +* 通过 Modal.method 生成的 Modal 组件的语言控制无法通过 context 简单的传输,所以使用了 runtimeLocale,如果一个页面用到了两个传入不同 locale 的 LocaleProvider,那么 runtimeLocale 可能会错乱,请务必注意 +* 如用到 DatePicker、Calendar 等日期相关组件,LocaleProvider 由于生命周期顺序的问题无法实时变更语言,需要手动设置 moment 的语言。 + ### 演示 * 自定义语言 @@ -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 = () => (
diff --git a/src/components/LocaleProvider/__demo__/list.jsx b/src/components/LocaleProvider/__demo__/list.jsx index e03b3728..33653b53 100644 --- a/src/components/LocaleProvider/__demo__/list.jsx +++ b/src/components/LocaleProvider/__demo__/list.jsx @@ -1,6 +1,7 @@ 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'; @@ -8,6 +9,7 @@ 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'; @@ -80,13 +82,28 @@ class Demo extends Component {
+
+
+ +
+
+ +
+
); const { localeStr, locale } = this.state; return (
-
+
+
+
+ +
+
+ +
+
diff --git a/src/components/LocaleProvider/__tests__/__snapshots__/index.test.js.snap b/src/components/LocaleProvider/__tests__/__snapshots__/index.test.js.snap index f93702fc..b2f559d3 100644 --- a/src/components/LocaleProvider/__tests__/__snapshots__/index.test.js.snap +++ b/src/components/LocaleProvider/__tests__/__snapshots__/index.test.js.snap @@ -1,11 +1,19 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`LocaleProvider Modal 1`] = `"
sentinelStart
alert
content
sentinelEnd
"`; + +exports[`LocaleProvider Modal 2`] = `"
sentinelStart
confirm
content
sentinelEnd
"`; + +exports[`LocaleProvider Modal 3`] = `"
sentinelStart
alert
content
sentinelEnd
"`; + +exports[`LocaleProvider Modal 4`] = `"
sentinelStart
confirm
content
sentinelEnd
"`; + exports[`LocaleProvider switch locale 1`] = `
+
+
+ +
+
+ +
+
@@ -1264,7 +1296,7 @@ exports[`LocaleProvider switch locale 2`] = ` class="noeditor-wrap" >
+
+
+ +
+
+ +
+
diff --git a/src/components/LocaleProvider/__tests__/index.test.js b/src/components/LocaleProvider/__tests__/index.test.js index d669fee8..dd33a69a 100644 --- a/src/components/LocaleProvider/__tests__/index.test.js +++ b/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'; @@ -12,4 +13,27 @@ describe('LocaleProvider', () => { wrapper.instance().setLocale('en_US'); expect(renderToJson(wrapper.render())).toMatchSnapshot(); }); + test('Modal', async () => { + const wrapper = mount(); + 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(); + }); }); diff --git a/src/components/LocaleProvider/locale/en_US.js b/src/components/LocaleProvider/locale/en_US.js index ab18f5b1..2ca1933e 100644 --- a/src/components/LocaleProvider/locale/en_US.js +++ b/src/components/LocaleProvider/locale/en_US.js @@ -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 }; diff --git a/src/components/LocaleProvider/locale/runtime.js b/src/components/LocaleProvider/locale/runtime.js new file mode 100644 index 00000000..787453d2 --- /dev/null +++ b/src/components/LocaleProvider/locale/runtime.js @@ -0,0 +1,10 @@ +let runtimeLocale = {}; + +const setRuntimeLocale = _runtimeLocale => { + runtimeLocale = _runtimeLocale; +}; +const getRuntimeLocale = () => { + return runtimeLocale; +}; + +export { setRuntimeLocale, getRuntimeLocale }; diff --git a/src/components/LocaleProvider/locale/zh_CN.js b/src/components/LocaleProvider/locale/zh_CN.js index 2e4a9aa3..4a16bfb9 100644 --- a/src/components/LocaleProvider/locale/zh_CN.js +++ b/src/components/LocaleProvider/locale/zh_CN.js @@ -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 }; diff --git a/src/components/LocaleProvider/localeConsumerDecorator.jsx b/src/components/LocaleProvider/localeConsumerDecorator.jsx index 4a0ea174..0c26c8c3 100644 --- a/src/components/LocaleProvider/localeConsumerDecorator.jsx +++ b/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); @@ -24,7 +26,12 @@ const localeConsumerDecorator = ({ defaultLocale = {}, localeName, publicFn = [] return ( (this.child = ref)} - locale={{ ...defaultLocale, ...context[localeName], ...locale }} + locale={{ + ...defaultLocale, + ...context[localeName], + ...(requireRuntimeLocale ? getRuntimeLocale()[localeName] : {}), + ...locale + }} {...rest} /> ); diff --git a/src/components/Modal/Modal.jsx b/src/components/Modal/Modal.jsx index 8d892651..cb306575 100644 --- a/src/components/Modal/Modal.jsx +++ b/src/components/Modal/Modal.jsx @@ -1,14 +1,18 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import _ from 'lodash'; import Button from 'components/Button'; import Icon from 'components/Icon'; import { animationPrefixCls } from 'src/style/globalAnimation'; +import localeConsumerDecorator from 'src/components/LocaleProvider/localeConsumerDecorator'; import { prefixCls, ModalWrap } from './style'; +import LOCALE from './locale/zh_CN'; const Size = ['sm', 'md', 'lg']; +@localeConsumerDecorator({ defaultLocale: LOCALE, localeName: 'Modal', requireRuntimeLocale: true }) class Modal extends Component { static propTypes = { /** @ignore */ @@ -16,7 +20,7 @@ class Modal extends Component { /** 头部内容 */ title: PropTypes.node, /** 底部内容 */ - footer: PropTypes.node, + footer: PropTypes.oneOfType([PropTypes.node, PropTypes.func]), /** 显示与否 */ visible: PropTypes.bool, /** 弹窗尺寸 */ @@ -52,7 +56,9 @@ class Modal extends Component { /** 弹窗的内容部分的样式 */ bodyStyle: PropTypes.object, /** 遮罩层的样式 */ - maskStyle: PropTypes.object + maskStyle: PropTypes.object, + /** @ignore */ + locale: PropTypes.object }; static defaultProps = { maskAnimation: 'fade', @@ -62,13 +68,13 @@ class Modal extends Component { closable: true }; getDefaultFooter = () => { - const { onOk, onClose } = this.props; + const { onOk, onClose, locale } = this.props; return [ , ]; }; @@ -83,6 +89,7 @@ class Modal extends Component { closable, className, onClose, + locale, ...rest } = this.props; const width = { @@ -111,7 +118,7 @@ class Modal extends Component { ) ]} - footer={footer} + footer={_.isFunction(footer) ? footer({ locale }) : footer} /> ); } diff --git a/src/components/Modal/locale/en_US.js b/src/components/Modal/locale/en_US.js new file mode 100644 index 00000000..868e5023 --- /dev/null +++ b/src/components/Modal/locale/en_US.js @@ -0,0 +1,4 @@ +export default { + confirm: 'confirm', + cancel: 'cancel' +}; diff --git a/src/components/Modal/locale/zh_CN.js b/src/components/Modal/locale/zh_CN.js new file mode 100644 index 00000000..69fc4a73 --- /dev/null +++ b/src/components/Modal/locale/zh_CN.js @@ -0,0 +1,4 @@ +export default { + confirm: '确定', + cancel: '取消' +}; diff --git a/src/components/Modal/method.jsx b/src/components/Modal/method.jsx index 384d4d78..f75e6911 100644 --- a/src/components/Modal/method.jsx +++ b/src/components/Modal/method.jsx @@ -1,5 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import PropTypes from 'prop-types'; import Button from 'components/Button'; import Modal from './Modal'; @@ -21,6 +22,15 @@ const pop = props => { }; const alert = (props, content) => { + const AlertFooter = ({ locale }) => ( + + ); + AlertFooter.propTypes = { + locale: PropTypes.object.isRequired + }; + const options = { children: content, maskClosable: false, @@ -29,11 +39,7 @@ const alert = (props, content) => { onClose: () => { modal.destory(); }, - footer: ( - - ) + footer: AlertFooter }; const modal = pop({