Skip to content

Commit

Permalink
fix: login modal validation (#958)
Browse files Browse the repository at this point in the history
  • Loading branch information
ayusharma authored and juanpicado committed Aug 28, 2018
1 parent 431e760 commit 9f78c31
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 66 deletions.
4 changes: 2 additions & 2 deletions src/webui/app.js
Expand Up @@ -64,7 +64,7 @@ export default class App extends Component {
}

/**
* Toogles the login modal
* Toggles the login modal
* Required by: <LoginModal /> <Header />
*/
toggleLoginModal() {
Expand Down Expand Up @@ -94,7 +94,7 @@ export default class App extends Component {
storage.setItem('username', username);
storage.setItem('token', token);
// close login modal after successful login
// set userLoggined to true
// set isUserLoggedin to true
this.setState({
isUserLoggedIn: true,
showLoginModal: false
Expand Down
146 changes: 95 additions & 51 deletions src/webui/components/Login/index.js
@@ -1,4 +1,4 @@
import React, {Component} from 'react';
import React, {Component, createRef} from 'react';
import PropTypes from 'prop-types';
import {Form, Button, Dialog, Input, Alert} from 'element-react';

Expand All @@ -15,15 +15,34 @@ export default class LoginModal extends Component {
error: {},
onCancel: () => {},
onSubmit: () => {}
}
};

state = {
username: '',
password: ''
}
form: {
username: '',
password: ''
},
rules: {
username: [
{
required: true,
message: 'Please input the username',
trigger: 'change'
}
],
password: [
{
required: true,
message: 'Please input the password',
trigger: 'change'
}
]
}
};

constructor(props) {
super(props);
this.formRef = createRef();
this.submitCredentials = this.submitCredentials.bind(this);
this.setCredentials = this.setCredentials.bind(this);
}
Expand All @@ -32,20 +51,34 @@ export default class LoginModal extends Component {
* set login modal's username and password to current state
* Required by: <LoginModal />
*/
setCredentials(name, e) {
this.setState({
[name]: e
});
setCredentials(key, value) {
this.setState(
(prevState) => ({
form: {...prevState.form, [key]: value}
})
);
}

async submitCredentials(event) {
// prevents default submit behaviour
/**
* Clears the username and password field.
*/
handleReset() {
this.formRef.current.resetFields();
}

submitCredentials(event) {
// prevents default submit behavior
event.preventDefault();
const {username, password} = this.state;
await this.props.onSubmit(username, password);
// let's wait for API response and then set
// username and password filed to empty state
this.setState({username: '', password: ''});
this.formRef.current.validate((valid) => {
if (valid) {
const {username, password} = this.state.form;
this.props.onSubmit(username, password);
this.setState({
form: {username}
});
}
return false;
});
}

renderLoginError({type, title, description} = {}) {
Expand All @@ -56,13 +89,16 @@ export default class LoginModal extends Component {
description={description}
showIcon={true}
closable={false}
style={{lineHeight: '10px'}}
/>
) : '';
) : (
''
);
}

render() {
const {visibility, onCancel, error} = this.props;
const {username, password} = this.state;
const {username, password} = this.state.form;
return (
<div className="login-dialog">
<Dialog
Expand All @@ -71,39 +107,47 @@ export default class LoginModal extends Component {
visible={visibility}
onCancel={onCancel}
>
<Form className="login-form">
<Dialog.Body>
{this.renderLoginError(error)}
<br />
<Input
name="username"
placeholder="Username"
value={username}
onChange={this.setCredentials.bind(this, 'username')}
/>
<br />
<br />
<Input
name="password"
type="password"
placeholder="Type your password"
value={password}
onChange={this.setCredentials.bind(this, 'password')}
/>
</Dialog.Body>
<Dialog.Footer className="dialog-footer">
<Button onClick={onCancel} className="cancel-login-button">
Cancel
</Button>
<Button
nativeType="submit"
className="login-button"
onClick={this.submitCredentials}
>
Login
</Button>
</Dialog.Footer>
</Form>
<Dialog.Body>
<Form
className="login-form"
ref={this.formRef}
model={this.state.form}
rules={this.state.rules}
>
<Form.Item>
{this.renderLoginError(error)}
</Form.Item>
<Form.Item prop="username" labelPosition="top">
<Input
name="username"
placeholder="Type your username"
value={username}
onChange={this.setCredentials.bind(this, 'username')}
/>
</Form.Item>
<Form.Item prop="password">
<Input
name="password"
type="password"
placeholder="Type your password"
value={password}
onChange={this.setCredentials.bind(this, 'password')}
/>
</Form.Item>
<Form.Item style={{float: 'right'}}>
<Button onClick={onCancel} className="cancel-login-button">
Cancel
</Button>
<Button
nativeType="submit"
className="login-button"
onClick={this.submitCredentials}
>
Login
</Button>
</Form.Item>
</Form>
</Dialog.Body>
</Dialog>
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions test/unit/webui/components/__snapshots__/login.spec.js.snap
@@ -1,5 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<LoginModal /> should load the component in default state 1`] = `"<div class=\\"login-dialog\\"><div><div style=\\"z-index: 1013;\\" class=\\"el-dialog__wrapper\\"><div style=\\"top: 15%;\\" class=\\"el-dialog el-dialog--tiny\\"><div class=\\"el-dialog__header\\"><span class=\\"el-dialog__title\\">Login</span><button type=\\"button\\" class=\\"el-dialog__headerbtn\\"><i class=\\"el-dialog__close el-icon el-icon-close\\"></i></button></div><form class=\\"el-form el-form--label-right login-form\\"><div class=\\"el-dialog__body\\"><br><div class=\\"el-input\\"><input name=\\"username\\" placeholder=\\"Username\\" type=\\"text\\" class=\\"el-input__inner\\" autocomplete=\\"off\\" value=\\"\\"></div><br><br><div class=\\"el-input\\"><input name=\\"password\\" placeholder=\\"Type your password\\" type=\\"password\\" class=\\"el-input__inner\\" autocomplete=\\"off\\" value=\\"\\"></div></div><div class=\\"el-dialog__footer dialog-footer\\"><button class=\\"el-button el-button--default cancel-login-button\\" type=\\"button\\"><span>Cancel</span></button><button class=\\"el-button el-button--default login-button\\" type=\\"submit\\"><span>Login</span></button></div></form></div></div><div class=\\"v-modal\\" style=\\"z-index: 1012;\\"></div></div></div>"`;
exports[`<LoginModal /> should load the component in default state 1`] = `"<div class=\\"login-dialog\\"><div><div style=\\"z-index: 1013;\\" class=\\"el-dialog__wrapper\\"><div style=\\"top: 15%;\\" class=\\"el-dialog el-dialog--tiny\\"><div class=\\"el-dialog__header\\"><span class=\\"el-dialog__title\\">Login</span><button type=\\"button\\" class=\\"el-dialog__headerbtn\\"><i class=\\"el-dialog__close el-icon el-icon-close\\"></i></button></div><div class=\\"el-dialog__body\\"><form class=\\"el-form el-form--label-right login-form\\"><div class=\\"el-form-item\\"><div class=\\"el-form-item__content\\"></div></div><div class=\\"el-form-item is-required\\"><div class=\\"el-form-item__content\\"><div class=\\"el-input\\"><input name=\\"username\\" placeholder=\\"Type your username\\" type=\\"text\\" class=\\"el-input__inner\\" autocomplete=\\"off\\" value=\\"\\"></div></div></div><div class=\\"el-form-item is-required\\"><div class=\\"el-form-item__content\\"><div class=\\"el-input\\"><input name=\\"password\\" placeholder=\\"Type your password\\" type=\\"password\\" class=\\"el-input__inner\\" autocomplete=\\"off\\" value=\\"\\"></div></div></div><div style=\\"float: right;\\" class=\\"el-form-item\\"><div class=\\"el-form-item__content\\"><button class=\\"el-button el-button--default cancel-login-button\\" type=\\"button\\"><span>Cancel</span></button><button class=\\"el-button el-button--default login-button\\" type=\\"submit\\"><span>Login</span></button></div></div></form></div></div></div><div class=\\"v-modal\\" style=\\"z-index: 1012;\\"></div></div></div>"`;

exports[`<LoginModal /> should load the component with props 1`] = `"<div class=\\"login-dialog\\"><div><div style=\\"z-index: 1013;\\" class=\\"el-dialog__wrapper\\"><div style=\\"top: 15%;\\" class=\\"el-dialog el-dialog--tiny\\"><div class=\\"el-dialog__header\\"><span class=\\"el-dialog__title\\">Login</span><button type=\\"button\\" class=\\"el-dialog__headerbtn\\"><i class=\\"el-dialog__close el-icon el-icon-close\\"></i></button></div><form class=\\"el-form el-form--label-right login-form\\"><div class=\\"el-dialog__body\\"><div class=\\"el-alert el-alert--error\\"><i class=\\"el-alert__icon el-icon-circle-cross is-big\\"></i><div class=\\"el-alert__content\\"><span class=\\"el-alert__title is-bold\\">Error Title</span><p class=\\"el-alert__description\\">Error Description</p><i class=\\"el-alert__closebtn el-icon-close\\" style=\\"display: none;\\"></i></div></div><br><div class=\\"el-input\\"><input name=\\"username\\" placeholder=\\"Username\\" type=\\"text\\" class=\\"el-input__inner\\" autocomplete=\\"off\\" value=\\"\\"></div><br><br><div class=\\"el-input\\"><input name=\\"password\\" placeholder=\\"Type your password\\" type=\\"password\\" class=\\"el-input__inner\\" autocomplete=\\"off\\" value=\\"\\"></div></div><div class=\\"el-dialog__footer dialog-footer\\"><button class=\\"el-button el-button--default cancel-login-button\\" type=\\"button\\"><span>Cancel</span></button><button class=\\"el-button el-button--default login-button\\" type=\\"submit\\"><span>Login</span></button></div></form></div></div><div class=\\"v-modal\\" style=\\"z-index: 1012;\\"></div></div></div>"`;
exports[`<LoginModal /> should load the component with props 1`] = `"<div class=\\"login-dialog\\"><div><div style=\\"z-index: 1013;\\" class=\\"el-dialog__wrapper\\"><div style=\\"top: 15%;\\" class=\\"el-dialog el-dialog--tiny\\"><div class=\\"el-dialog__header\\"><span class=\\"el-dialog__title\\">Login</span><button type=\\"button\\" class=\\"el-dialog__headerbtn\\"><i class=\\"el-dialog__close el-icon el-icon-close\\"></i></button></div><div class=\\"el-dialog__body\\"><form class=\\"el-form el-form--label-right login-form\\"><div class=\\"el-form-item\\"><div class=\\"el-form-item__content\\"><div style=\\"line-height: 10px;\\" class=\\"el-alert el-alert--error\\"><i class=\\"el-alert__icon el-icon-circle-cross is-big\\"></i><div class=\\"el-alert__content\\"><span class=\\"el-alert__title is-bold\\">Error Title</span><p class=\\"el-alert__description\\">Error Description</p><i class=\\"el-alert__closebtn el-icon-close\\" style=\\"display: none;\\"></i></div></div></div></div><div class=\\"el-form-item is-required\\"><div class=\\"el-form-item__content\\"><div class=\\"el-input\\"><input name=\\"username\\" placeholder=\\"Type your username\\" type=\\"text\\" class=\\"el-input__inner\\" autocomplete=\\"off\\" value=\\"\\"></div></div></div><div class=\\"el-form-item is-required\\"><div class=\\"el-form-item__content\\"><div class=\\"el-input\\"><input name=\\"password\\" placeholder=\\"Type your password\\" type=\\"password\\" class=\\"el-input__inner\\" autocomplete=\\"off\\" value=\\"\\"></div></div></div><div style=\\"float: right;\\" class=\\"el-form-item\\"><div class=\\"el-form-item__content\\"><button class=\\"el-button el-button--default cancel-login-button\\" type=\\"button\\"><span>Cancel</span></button><button class=\\"el-button el-button--default login-button\\" type=\\"submit\\"><span>Login</span></button></div></div></form></div></div></div><div class=\\"v-modal\\" style=\\"z-index: 1012;\\"></div></div></div>"`;
27 changes: 16 additions & 11 deletions test/unit/webui/components/login.spec.js
Expand Up @@ -35,7 +35,7 @@ describe('<LoginModal />', () => {
onSubmit: () => {}
};
const wrapper = mount(<LoginModal {...props} />);
wrapper.find('.el-dialog__footer > .cancel-login-button').simulate('click');
wrapper.find('button.cancel-login-button').simulate('click');
expect(props.onCancel).toHaveBeenCalled();
wrapper.find('.el-dialog__headerbtn > .el-dialog__close').simulate('click');
expect(props.onCancel).toHaveBeenCalled();
Expand All @@ -45,35 +45,40 @@ describe('<LoginModal />', () => {
const props = {
visibility: true,
error: {},
onCancel: () => { },
onSubmit: () => { }
onCancel: () => {},
onSubmit: () => {}
};
const wrapper = mount(<LoginModal {...props} />);
const { setCredentials } = wrapper.instance();

expect(setCredentials('username', 'xyz')).toBeUndefined();
expect(wrapper.state('username')).toEqual('xyz');
expect(wrapper.state('form').username).toEqual('xyz');

expect(setCredentials('password', '1234')).toBeUndefined();
expect(wrapper.state('password')).toEqual('1234');
expect(wrapper.state('form').password).toEqual('1234');
});

it('submitCredential: should call the onSubmit', async () => {
it('submitCredential: should call the onSubmit', () => {
const props = {
visibility: true,
error: {},
onCancel: () => { },
onCancel: () => {},
onSubmit: jest.fn()
};

const event = {
preventDefault: jest.fn()
}
};
const wrapper = mount(<LoginModal {...props} />);
const { submitCredentials } = wrapper.instance();
wrapper.setState({username: 'sam', password: 1234})
await submitCredentials(event);
expect(props.onSubmit).toHaveBeenCalledWith('sam', 1234);
wrapper
.find('input[type="text"]')
.simulate('change', { target: { value: 'sam' } });
wrapper
.find('input[type="password"]')
.simulate('change', { target: { value: '1234' } });
submitCredentials(event);
expect(event.preventDefault).toHaveBeenCalled();
expect(props.onSubmit).toHaveBeenCalledWith('sam', '1234');
});
});

0 comments on commit 9f78c31

Please sign in to comment.