#26: incapsulate auth flow rejection logic into a separate component

This commit is contained in:
SleepWalker 2016-05-14 15:38:00 +03:00
parent b0a6740751
commit 4733b79d75
11 changed files with 50 additions and 91 deletions

View File

@ -11,7 +11,6 @@ export default class BaseAuthBody extends Component {
static contextTypes = {
clearErrors: PropTypes.func.isRequired,
resolve: PropTypes.func.isRequired,
reject: PropTypes.func.isRequired,
auth: PropTypes.shape({
error: PropTypes.string,
scopes: PropTypes.array

View File

@ -0,0 +1,25 @@
import React, { PropTypes } from 'react';
import { FormattedMessage as Message } from 'react-intl';
export default function RejectionLink(props, context) {
return (
<a href="#" onClick={(event) => {
event.preventDefault();
context.reject();
}}>
<Message {...props.label} />
</a>
);
}
RejectionLink.displayName = 'RejectionLink';
RejectionLink.propTypes = {
label: PropTypes.shape({
id: PropTypes.string
}).isRequired
};
RejectionLink.contextTypes = {
reject: PropTypes.func.isRequired
};

View File

@ -1,8 +1,7 @@
import React from 'react';
import { FormattedMessage as Message } from 'react-intl';
import { Button } from 'components/ui/form';
import RejectionLink from 'components/auth/RejectionLink';
import AuthTitle from 'components/auth/AuthTitle';
import messages from './Activation.intl.json';
@ -13,10 +12,6 @@ export default function Activation() {
Title: () => <AuthTitle title={messages.accountActivationTitle} />,
Body,
Footer: () => <Button color="blue" label={messages.confirmEmail} />,
Links: () => (
<a href="#">
<Message {...messages.didNotReceivedEmail} />
</a>
)
Links: () => <RejectionLink label={messages.didNotReceivedEmail} />
};
}

View File

@ -1,32 +1,17 @@
import React, { PropTypes } from 'react';
import { FormattedMessage as Message } from 'react-intl';
import React from 'react';
import { Button } from 'components/ui/form';
import RejectionLink from 'components/auth/RejectionLink';
import AuthTitle from 'components/auth/AuthTitle';
import Body from './ChangePasswordBody';
import messages from './ChangePassword.intl.json';
export default function ChangePassword() {
const componentsMap = {
return {
Title: () => <AuthTitle title={messages.changePasswordTitle} />,
Body,
Footer: () => <Button color="darkBlue" label={messages.change} />,
Links: (props, context) => (
<a href="#" onClick={(event) => {
event.preventDefault();
context.reject();
}}>
<Message {...messages.skipThisStep} />
</a>
)
Links: () => <RejectionLink label={messages.skipThisStep} />
};
componentsMap.Links.contextTypes = {
reject: PropTypes.func.isRequired
};
return componentsMap;
}

View File

@ -1,8 +1,7 @@
import React from 'react';
import { FormattedMessage as Message } from 'react-intl';
import { Button } from 'components/ui/form';
import RejectionLink from 'components/auth/RejectionLink';
import AuthTitle from 'components/auth/AuthTitle';
import messages from './ForgotPassword.intl.json';
@ -13,10 +12,6 @@ export default function ForgotPassword() {
Title: () => <AuthTitle title={messages.forgotPasswordTitle} />,
Body,
Footer: () => <Button color="lightViolet" label={messages.sendMail} />,
Links: () => (
<a href="/send-message">
<Message {...messages.contactSupport} />
</a>
)
Links: () => <RejectionLink label={messages.contactSupport} />
};
}

View File

@ -1,9 +1,7 @@
import React from 'react';
import { FormattedMessage as Message } from 'react-intl';
import { Link } from 'react-router';
import { Button } from 'components/ui/form';
import RejectionLink from 'components/auth/RejectionLink';
import AuthTitle from 'components/auth/AuthTitle';
import Body from './PasswordBody';
@ -14,10 +12,6 @@ export default function Password() {
Title: () => <AuthTitle title={messages.passwordTitle} />,
Body,
Footer: () => <Button color="green" label={messages.signInButton} />,
Links: () => (
<Link to="/forgot-password">
<Message {...messages.forgotPassword} />
</Link>
)
Links: () => <RejectionLink label={messages.forgotPassword} />
};
}

View File

@ -1,32 +1,17 @@
import React, { PropTypes } from 'react';
import { FormattedMessage as Message } from 'react-intl';
import React from 'react';
import { Button } from 'components/ui/form';
import RejectionLink from 'components/auth/RejectionLink';
import AuthTitle from 'components/auth/AuthTitle';
import messages from './Permissions.intl.json';
import Body from './PermissionsBody';
export default function Permissions() {
const componentsMap = {
return {
Title: () => <AuthTitle title={messages.permissionsTitle} />,
Body,
Footer: () => <Button color="orange" autoFocus label={messages.approve} />,
Links: (props, context) => (
<a href="#" onClick={(event) => {
event.preventDefault();
context.reject();
}}>
<Message {...messages.decline} />
</a>
)
Links: () => <RejectionLink label={messages.decline} />
};
componentsMap.Links.contextTypes = {
reject: PropTypes.func.isRequired
};
return componentsMap;
}

View File

@ -1,8 +1,7 @@
import React from 'react';
import { FormattedMessage as Message } from 'react-intl';
import { Button } from 'components/ui/form';
import RejectionLink from 'components/auth/RejectionLink';
import AuthTitle from 'components/auth/AuthTitle';
import activationMessages from 'components/auth/activation/Activation.intl.json';
@ -14,10 +13,6 @@ export default function Register() {
Title: () => <AuthTitle title={messages.registerTitle} />,
Body,
Footer: () => <Button color="blue" label={messages.signUpButton} />,
Links: () => (
<a href="#">
<Message {...activationMessages.didNotReceivedEmail} />
</a>
)
Links: () => <RejectionLink label={activationMessages.didNotReceivedEmail} />
};
}

View File

@ -20,7 +20,6 @@ import ChangePassword from 'components/auth/changePassword/ChangePassword';
import ForgotPassword from 'components/auth/forgotPassword/ForgotPassword';
import Finish from 'components/auth/finish/Finish';
import authFlow from 'services/authFlow';
export default function routesFactory(store) {
@ -46,15 +45,15 @@ export default function routesFactory(store) {
<Route path="oauth" component={OAuthInit} {...startAuthFlow} />
<Route path="auth" component={AuthPage} {...startAuthFlow}>
<Route path="/login" components={new Login()} />
<Route path="/password" components={new Password()} />
<Route path="/register" components={new Register()} />
<Route path="/activation" components={new Activation()} />
<Route path="/oauth/permissions" components={new Permissions()} />
<Route path="/oauth/finish" component={Finish} />
<Route path="/change-password" components={new ChangePassword()} />
<Route path="/forgot-password" components={new ForgotPassword()} />
<Route path="auth" component={AuthPage}>
<Route path="/login" components={new Login()} {...startAuthFlow} />
<Route path="/password" components={new Password()} {...startAuthFlow} />
<Route path="/register" components={new Register()} {...startAuthFlow} />
<Route path="/activation" components={new Activation()} {...startAuthFlow} />
<Route path="/oauth/permissions" components={new Permissions()} {...startAuthFlow} />
<Route path="/oauth/finish" component={Finish} {...startAuthFlow} />
<Route path="/change-password" components={new ChangePassword()} {...startAuthFlow} />
<Route path="/forgot-password" components={new ForgotPassword()} {...startAuthFlow} />
</Route>
<Route path="profile" component={ProfilePage} {...userOnly}>

View File

@ -1,6 +1,5 @@
import AbstractState from './AbstractState';
import PasswordState from './PasswordState';
import ForgotPasswordState from './ForgotPasswordState';
export default class LoginState extends AbstractState {
enter(context) {
@ -17,8 +16,4 @@ export default class LoginState extends AbstractState {
context.run('login', payload)
.then(() => context.setState(new PasswordState()));
}
reject(context) {
context.setState(new ForgotPasswordState());
}
}

View File

@ -86,12 +86,4 @@ describe('LoginState', () => {
return promise.catch(mock.verify.bind(mock));
});
});
describe('#reject', () => {
it('should transition to forgot password state', () => {
expectState(mock, ForgotPasswordState);
state.reject(context);
});
});
});