diff --git a/package.json b/package.json index d6695f2..f740fef 100644 --- a/package.json +++ b/package.json @@ -59,15 +59,6 @@ "morgan": "^1.7.0", "node-sass": "^3.12.4", "postcss-loader": "^1.1.1", - "react": "^15.4.0", - "react-addons-test-utils": "^15.4.1", - "react-css-themr": "^1.6.0", - "react-dom": "^15.4.0", - "react-hot-loader": "^1.3.1", - "react-redux": "^4.4.6", - "react-router": "^3.0.0", - "redux": "^3.6.0", - "redux-thunk": "^2.1.0", "rimraf": "^2.5.4", "sass-loader": "^4.0.2", "style-loader": "^0.13.1", @@ -76,5 +67,17 @@ "webpack": "^1.13.3", "webpack-dev-middleware": "^1.8.4", "webpack-hot-middleware": "^2.13.2" + }, + "dependencies": { + "react-intl": "^2.2.3", + "react": "^15.4.0", + "react-addons-test-utils": "^15.4.1", + "react-css-themr": "^1.6.0", + "react-dom": "^15.4.0", + "react-hot-loader": "^1.3.1", + "react-redux": "^4.4.6", + "react-router": "^3.0.0", + "redux": "^3.6.0", + "redux-thunk": "^2.1.0" } } diff --git a/src/Form/Input/index.jsx b/src/Form/Input/index.jsx new file mode 100644 index 0000000..9f25661 --- /dev/null +++ b/src/Form/Input/index.jsx @@ -0,0 +1,49 @@ +import React, { + Component, + PropTypes +} from 'react'; + +import { + intlShape, + injectIntl +} from 'react-intl'; + + + +class Input extends Component { + render() { + const { type, intl, value, required, id, onChange, placeholder } = this.props; + const props = { + type, + value, + required, + id, + onChange + }; + if (placeholder) { + const { formatMessage } = intl; + props.placeholder = formatMessage(placeholder); + } + return ( + + ); + } +} + +Input.propTypes = { + type: PropTypes.string, + intl: intlShape.isRequired, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), + required: PropTypes.bool, + id: PropTypes.string, + onChange: PropTypes.func, + placeholder: PropTypes.object, +}; + +Input.defaultProps = { + inputType: 'text' +}; + +export default injectIntl(Input); diff --git a/src/Login/index.js b/src/Login/index.js deleted file mode 100644 index 2cde355..0000000 --- a/src/Login/index.js +++ /dev/null @@ -1,63 +0,0 @@ -import React, {Component, PropTypes} from 'react'; -import {themr} from 'react-css-themr'; -import {connect} from 'react-redux'; - -import {setUsername, setPassword, toggleRemember, attemptLogin} from './actions'; -import style from './style.scss'; -import constants from '../constants'; - -class Login extends Component { - static propTypes = { - theme: PropTypes.object.isRequired, - username: PropTypes.string, - password: PropTypes.string, - remember: PropTypes.bool, - onUsernameChange: PropTypes.func, - onPasswordChange: PropTypes.func, - onRememberToggle: PropTypes.func, - onLogin: PropTypes.func - } - - render() { - const {theme, username, password, remember, onUsernameChange, onPasswordChange, onRememberToggle, onLogin} = this.props; - return ( -
-
- {constants.LOGIN} -
- - { onUsernameChange(e.target.value); }}/> - { onRememberToggle(e.target.checked); }}/> - -
-
- - { onPasswordChange(e.target.value); }} /> -
-
- -
-
-
- ); - } -} - -const ReduxLogin = connect((state) => { - const {login} = state; - return { - username: login.username, - password: login.password, - remember: login.remember - }; -}, (dispatch) => { - return { - onUsernameChange: (val) => { dispatch(setUsername(val)); }, - onPasswordChange: (val) => { dispatch(setPassword(val)); }, - onRememberToggle: (val) => { dispatch(toggleRemember(val)); }, - onLogin: (uid, password) => { dispatch(attemptLogin(uid, password)); } - }; -})(Login); - -export {Login as LoginBase}; -export default themr('LOGIN', style)(ReduxLogin); diff --git a/src/Login/index.jsx b/src/Login/index.jsx new file mode 100644 index 0000000..6b002f7 --- /dev/null +++ b/src/Login/index.jsx @@ -0,0 +1,148 @@ +import React, { Component, PropTypes } from 'react'; +import { themr } from 'react-css-themr'; +import { connect } from 'react-redux'; +import { + addLocaleData, + IntlProvider, + FormattedMessage, + defineMessages +} from 'react-intl'; +import enLocaleData from 'react-intl/locale-data/en'; +import zhLocaleData from 'react-intl/locale-data/zh'; + +import { setUsername, setPassword, toggleRemember, attemptLogin } from './actions'; +import Input from '../Form/Input'; +import style from './style.scss'; +import constants from '../constants'; +import zhMessage from './l10n/zh_CN'; +import enMessage from './l10n/en_US'; + +addLocaleData(enLocaleData); +addLocaleData(zhLocaleData); + +const getLocaleMessage = (locale) => { + switch (locale) { + case 'en': + return enMessage; + case 'zh': + return zhMessage; + } +} + +const placeholderMessages = defineMessages({ + username: { + id: 'placeholder.username' + }, + password: { + id: 'placeholder.password' + } +}) + +class Login extends Component { + static propTypes = { + theme: PropTypes.object.isRequired, + username: PropTypes.string, + password: PropTypes.string, + remember: PropTypes.bool, + onUsernameChange: PropTypes.func, + onPasswordChange: PropTypes.func, + onRememberToggle: PropTypes.func, + onLogin: PropTypes.func, + locale: PropTypes.string + } + + static defaultProps = { + locale: 'en' + } + + render() { + const {theme, username, password, remember, onUsernameChange, onPasswordChange, onRememberToggle, onLogin, locale} = this.props; + return ( + +
+
+ + + +
+ + { onUsernameChange(e.target.value); }} + /> + { onRememberToggle(e.target.checked); }} + /> + +
+
+ + { onPasswordChange(e.target.value); }} + /> +
+
+ +
+
+
+
+ ); + } +} + +const ReduxLogin = connect((state) => { + const {login} = state; + return { + username: login.username, + password: login.password, + remember: login.remember + }; +}, (dispatch) => { + return { + onUsernameChange: (val) => {dispatch(setUsername(val)); }, + onPasswordChange: (val) => {dispatch(setPassword(val)); }, + onRememberToggle: (val) => {dispatch(toggleRemember(val)); }, + onLogin: (uid, password) => {dispatch(attemptLogin(uid, password)); } + }; +})(Login); + +export {Login as LoginBase}; +export default themr('LOGIN', style)(ReduxLogin); diff --git a/src/Login/l10n/en_US.js b/src/Login/l10n/en_US.js new file mode 100644 index 0000000..9911e5c --- /dev/null +++ b/src/Login/l10n/en_US.js @@ -0,0 +1,11 @@ +export default { + LOGIN: 'Login', + USERNAME: 'Username', + PASSWORD: 'Password', + REMEMBER: 'Remember', + SUBMIT: 'Submit', + FULLNAME: 'Full name', + REGISTER: 'Register', + 'placeholder.username': 'Please input your username', + 'placeholder.password': 'Please input your password' +}; diff --git a/src/Login/l10n/zh_CN.js b/src/Login/l10n/zh_CN.js new file mode 100644 index 0000000..c7f2fe2 --- /dev/null +++ b/src/Login/l10n/zh_CN.js @@ -0,0 +1,11 @@ +export default { + LOGIN: '登录', + USERNAME: '用户名', + PASSWORD: '密码', + REMEMBER: '记住我', + SUBMIT: '确认', + FULLNAME: '全名', + REGISTER: '注册', + 'placeholder.username': '请输入用户名', + 'placeholder.password': '请输入密码' +}; diff --git a/test/login.js b/test/login.js index 6043e5e..ce15fe0 100644 --- a/test/login.js +++ b/test/login.js @@ -8,18 +8,18 @@ describe('', () => { it('should include username input', () => { const wrapper = shallow(); - // console.log(wrapper.debug()); - expect(wrapper.find('input[type="text"]')).to.have.length(1); + // console.log(wrapper.find('[type="text"]').debug()); + expect(wrapper.find('[type="text"]')).to.have.length(1); }); it('should include password input', () => { const wrapper = shallow(); - expect(wrapper.find('input[type="password"]')).to.have.length(1); + expect(wrapper.find('[type="password"]')).to.have.length(1); }); it('should include checkbox input', () => { const wrapper = shallow(); - expect(wrapper.find('input[type="checkbox"]')).to.have.length(1); + expect(wrapper.find('[type="checkbox"]')).to.have.length(1); }); it('should include submit button', () => {