Merge branch 'develop' into forgot_password_captcha

# Conflicts:
#	api/models/authentication/ForgotPasswordForm.php
This commit is contained in:
ErickSkrauch 2017-04-25 21:00:01 +03:00
commit 6dd628cc99
11 changed files with 1348 additions and 933 deletions

2130
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@
"start": "npm run clean && npm run build:dll && webpack-dev-server --progress --colors",
"clean": "rm -rf dist/",
"up": "npm update",
"lockDeps": "npm shrinkwrap --dev",
"test": "npm run build:dll && karma start ./karma.conf.js",
"lint": "eslint ./src",
"i18n:collect": "babel-node ./scripts/i18n-collect.js",
@ -33,7 +34,7 @@
"react": "^15.0.0",
"react-addons-css-transition-group": "^15.1.0",
"react-dom": "^15.0.0",
"react-helmet": "^4.0.0",
"react-helmet": "^5.0.0",
"react-intl": "^2.0.0",
"react-motion": "^0.4.0",
"react-redux": "^5.0.0",
@ -61,14 +62,14 @@
"babel-preset-stage-0": "^6.3.13",
"babel-runtime": "^6.0.0",
"bundle-loader": "^0.5.4",
"circular-dependency-plugin": "^2.0.0",
"css-loader": "^0.26.1",
"circular-dependency-plugin": "^3.0.0",
"css-loader": "^0.28.0",
"enzyme": "^2.2.0",
"eslint": "^3.1.1",
"eslint-plugin-react": "^6.0.0",
"exports-loader": "^0.6.3",
"extract-text-webpack-plugin": "^1.0.0",
"file-loader": "^0.10.0",
"file-loader": "^0.11.0",
"fontgen-loader": "^0.2.1",
"html-loader": "^0.4.3",
"html-webpack-plugin": "^2.0.0",
@ -76,13 +77,13 @@
"jsdom": "^9.8.3",
"json-loader": "^0.5.4",
"karma": "^1.1.0",
"karma-jsdom-launcher": "^5.0.0",
"karma-jsdom-launcher": "^6.0.0",
"karma-mocha": "^1.0.0",
"karma-nyan-reporter": "^0.2.3",
"karma-sinon": "^1.0.4",
"karma-sourcemap-loader": "*",
"karma-webpack": "^2.0.0",
"loader-utils": "^0.2.15",
"loader-utils": "^1.0.0",
"mocha": "^3.0.2",
"node-sass": "^4.0.0",
"postcss-import": "^9.0.0",
@ -92,13 +93,14 @@
"raw-loader": "^0.5.1",
"react-addons-perf": "^15.3.0",
"react-addons-test-utils": "^15.0.2",
"react-test-renderer": "^15.5.4",
"redux-devtools": "^3.3.1",
"redux-devtools-dock-monitor": "^1.1.1",
"redux-devtools-log-monitor": "^1.0.11",
"sass-loader": "^4.0.0",
"scripts": "file:./scripts",
"sinon": "^1.15.3",
"style-loader": "^0.13.0",
"sinon": "^2.0.0",
"style-loader": "^0.16.0",
"unexpected": "^10.15.0",
"unexpected-sinon": "^10.5.1",
"url-loader": "^0.5.7",

View File

@ -46,7 +46,10 @@ export default class BaseAuthBody extends Component {
});
bindField = this.form.bindField.bind(this.form);
serialize = this.form.serialize.bind(this.form);
serialize() {
return this.form.serialize();
}
autoFocus() {
const fieldId = this.autoFocusField;

View File

@ -15,14 +15,13 @@ export default class ForgotPasswordBody extends BaseAuthBody {
static hasGoBack = true;
state = {
isLoginEdit: !(this.context.user.email || this.context.user.username)
isLoginEdit: !this.getLogin()
};
autoFocusField = this.state.isLoginEdit ? 'email' : null;
autoFocusField = this.state.isLoginEdit ? 'login' : null;
render() {
const { user } = this.context;
const login = user.email || user.username || '';
const login = this.getLogin();
const isLoginEditShown = this.state.isLoginEdit;
return (
@ -38,7 +37,7 @@ export default class ForgotPasswordBody extends BaseAuthBody {
<p className={styles.descriptionText}>
<Message {...messages.specifyEmail} />
</p>
<Input {...this.bindField('email')}
<Input {...this.bindField('login')}
icon="envelope"
color="lightViolet"
required
@ -63,6 +62,22 @@ export default class ForgotPasswordBody extends BaseAuthBody {
);
}
serialize() {
const data = super.serialize();
if (!data.login) {
data.login = this.getLogin();
}
return data;
}
getLogin() {
const { user, auth } = this.context;
return auth.login || user.username || user.email || '';
}
onClickEdit = () => {
this.setState({
isLoginEdit: true
@ -70,6 +85,6 @@ export default class ForgotPasswordBody extends BaseAuthBody {
this.context.requestRedraw();
// TODO: requestRedraw должен возвращать promise, по которому нужно ставить фокус на поле
// иначе же, если фокус ставить сразу, то форма скачет
setTimeout(() => {this.form.focus('email');}, 300);
setTimeout(() => {this.form.focus('login');}, 300);
};
}

View File

@ -64,7 +64,7 @@ export default class LoggedInPanel extends Component {
onBodyClick = createOnOutsideComponentClickHandler(
() => ReactDOM.findDOMNode(this),
() => this.state.isAccountSwitcherActive,
() => this.state.isAccountSwitcherActive && this._isMounted,
() => this.toggleAccountSwitcher()
);
}
@ -78,6 +78,7 @@ export default class LoggedInPanel extends Component {
* @param {function} getEl - the function, that returns reference to container el
* @param {function} isActive - whether the component is active and callback may be called
* @param {function} callback - the callback to call, when there was a click outside el
*
* @return {function}
*/
function createOnOutsideComponentClickHandler(getEl, isActive, callback) {

View File

@ -10,8 +10,11 @@ export default class ForgotPasswordState extends AbstractState {
}
resolve(context, payload = {}) {
context.run('forgotPassword', {login: payload.email || this.getLogin(context)})
.then(() => context.setState(new RecoverPasswordState()))
context.run('forgotPassword', payload)
.then(() => {
context.run('setLogin', payload.login);
context.setState(new RecoverPasswordState());
})
.catch((err = {}) =>
err.errors || logger.warn('Error requesting password recoverage', err)
);
@ -24,10 +27,4 @@ export default class ForgotPasswordState extends AbstractState {
reject(context) {
context.setState(new RecoverPasswordState());
}
getLogin(context) {
const {auth} = context.getState();
return auth.login;
}
}

View File

@ -6,16 +6,10 @@ import CompleteState from './CompleteState';
export default class RecoverPasswordState extends AbstractState {
enter(context) {
const {auth} = context.getState();
if (auth.login) {
const url = context.getRequest().path.includes('/recover-password')
? context.getRequest().path
: '/recover-password';
context.navigate(url);
} else {
context.setState(new CompleteState());
}
const url = context.getRequest().path.includes('/recover-password')
? context.getRequest().path
: '/recover-password';
context.navigate(url);
}
resolve(context, payload) {

View File

@ -38,7 +38,8 @@ const logger = {
}
logger.info(`Unhandled rejection${message}`, {
error
error,
event
});
});
}

View File

@ -32,13 +32,8 @@ describe('ForgotPasswordState', () => {
});
describe('#resolve', () => {
it('should call forgotPassword with login', () => {
it('should call forgotPassword with email from payload', () => {
const expectedLogin = 'foo@bar.com';
context.getState.returns({
auth: {
login: expectedLogin
}
});
expectRun(
mock,
@ -48,41 +43,30 @@ describe('ForgotPasswordState', () => {
})
).returns(Promise.resolve());
state.resolve(context, {});
});
it('should call forgotPassword with email from payload if any', () => {
const expectedLogin = 'foo@bar.com';
context.getState.returns({
auth: {
login: 'should.not@be.used'
}
});
expectRun(
mock,
'forgotPassword',
sinon.match({
login: expectedLogin
})
).returns(Promise.resolve());
state.resolve(context, {email: expectedLogin});
state.resolve(context, {login: expectedLogin});
});
it('should transition to recoverPassword state on success', () => {
const promise = Promise.resolve();
const expectedLogin = 'foo@bar.com';
context.getState.returns({
auth: {
login: expectedLogin
}
});
mock.expects('run').returns(promise);
mock.expects('run').twice().returns(promise);
expectState(mock, RecoverPasswordState);
state.resolve(context, {});
state.resolve(context, {login: expectedLogin});
return promise;
});
it('should run setLogin on success', () => {
const promise = Promise.resolve();
const expectedLogin = 'foo@bar.com';
mock.expects('run').withArgs('forgotPassword').returns(promise);
expectState(mock, RecoverPasswordState);
mock.expects('run').withArgs('setLogin', expectedLogin);
state.resolve(context, {login: expectedLogin});
return promise;
});

View File

@ -26,9 +26,6 @@ describe('RecoverPasswordState', () => {
describe('#enter', () => {
it('should navigate to /recover-password', () => {
const expectedPath = '/recover-password';
context.getState.returns({
auth: {login: 'foo'}
});
context.getRequest.returns({path: expectedPath});
@ -39,9 +36,6 @@ describe('RecoverPasswordState', () => {
it('should navigate to /recover-password/key', () => {
const expectedPath = '/recover-password/sasx5AS4d61';
context.getState.returns({
auth: {login: 'foo'}
});
context.getRequest.returns({path: expectedPath});
@ -49,16 +43,6 @@ describe('RecoverPasswordState', () => {
state.enter(context);
});
it('should transition to complete if not guest', () => {
context.getState.returns({
auth: {}
});
expectState(mock, CompleteState);
state.enter(context);
});
});
describe('#resolve', () => {

View File

@ -5,6 +5,6 @@
"keywords": [],
"author": "",
"dependencies": {
"loader-utils": "^0.2.12"
"loader-utils": "^1.0.0"
}
}