From fc8fb28469f949c1fd27db14b49f4997237f5610 Mon Sep 17 00:00:00 2001 From: Pascal Mugnier Date: Wed, 21 Mar 2018 07:36:46 +0100 Subject: [PATCH] SONAR-10475 Add indicator for login in progress (#19) --- .../js/apps/sessions/components/LoginForm.tsx | 20 +++- .../components/LoginFormContainer.tsx | 2 +- .../components/__tests__/LoginForm-test.tsx | 25 ++++- .../__snapshots__/LoginForm-test.tsx.snap | 103 +++++++++++++++++- 4 files changed, 139 insertions(+), 11 deletions(-) diff --git a/server/sonar-web/src/main/js/apps/sessions/components/LoginForm.tsx b/server/sonar-web/src/main/js/apps/sessions/components/LoginForm.tsx index b793c09a985..6cdc0087e02 100644 --- a/server/sonar-web/src/main/js/apps/sessions/components/LoginForm.tsx +++ b/server/sonar-web/src/main/js/apps/sessions/components/LoginForm.tsx @@ -19,22 +19,25 @@ */ import * as React from 'react'; import { Link } from 'react-router'; +import * as classNames from 'classnames'; import OAuthProviders from './OAuthProviders'; import GlobalMessagesContainer from '../../../app/components/GlobalMessagesContainer'; import { IdentityProvider } from '../../../app/types'; import { SubmitButton } from '../../../components/ui/buttons'; import { translate } from '../../../helpers/l10n'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; import './LoginForm.css'; interface Props { onSonarCloud: boolean; identityProviders: IdentityProvider[]; - onSubmit: (login: string, password: string) => void; + onSubmit: (login: string, password: string) => Promise; returnTo: string; } interface State { collapsed: boolean; + loading: boolean; login: string; password: string; } @@ -44,14 +47,22 @@ export default class LoginForm extends React.PureComponent { super(props); this.state = { collapsed: props.identityProviders.length > 0, + loading: false, login: '', password: '' }; } + stopLoading = () => { + this.setState({ loading: false }); + }; + handleSubmit = (event: React.SyntheticEvent) => { event.preventDefault(); - this.props.onSubmit(this.state.login, this.state.password); + this.setState({ loading: true }); + this.props + .onSubmit(this.state.login, this.state.password) + .then(this.stopLoading, this.stopLoading); }; handleMoreOptionsClick = (event: React.SyntheticEvent) => { @@ -130,7 +141,10 @@ export default class LoginForm extends React.PureComponent {
- {translate('sessions.log_in')} + + + {translate('sessions.log_in')} + {translate('cancel')} diff --git a/server/sonar-web/src/main/js/apps/sessions/components/LoginFormContainer.tsx b/server/sonar-web/src/main/js/apps/sessions/components/LoginFormContainer.tsx index f8056a41dd5..63c3f7b998a 100644 --- a/server/sonar-web/src/main/js/apps/sessions/components/LoginFormContainer.tsx +++ b/server/sonar-web/src/main/js/apps/sessions/components/LoginFormContainer.tsx @@ -73,7 +73,7 @@ class LoginFormContainer extends React.PureComponent { }; handleSubmit = (login: string, password: string) => { - this.props.doLogin(login, password).then(this.handleSuccessfulLogin, () => {}); + return this.props.doLogin(login, password).then(this.handleSuccessfulLogin, () => {}); }; render() { diff --git a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/LoginForm-test.tsx b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/LoginForm-test.tsx index 3451d0e923d..8ba3c8e1d8f 100644 --- a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/LoginForm-test.tsx +++ b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/LoginForm-test.tsx @@ -20,7 +20,7 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import LoginForm from '../LoginForm'; -import { change, click, submit } from '../../../../helpers/testUtils'; +import { change, click, submit, waitAndUpdate } from '../../../../helpers/testUtils'; const identityProvider = { backgroundColor: '#000', @@ -30,9 +30,9 @@ const identityProvider = { }; it('logs in with simple credentials', () => { - const onSubmit = jest.fn(); + const onSubmit = jest.fn(() => Promise.resolve()); const wrapper = shallow( - + ); expect(wrapper).toMatchSnapshot(); @@ -43,11 +43,26 @@ it('logs in with simple credentials', () => { expect(onSubmit).toBeCalledWith('admin', 'admin'); }); +it('should display a spinner and disabled button while loading', async () => { + const onSubmit = jest.fn(() => Promise.resolve()); + const wrapper = shallow( + + ); + + change(wrapper.find('#login'), 'admin'); + change(wrapper.find('#password'), 'admin'); + submit(wrapper.find('form')); + wrapper.update(); + expect(wrapper).toMatchSnapshot(); + + await waitAndUpdate(wrapper); +}); + it('logs in with identity provider', () => { const wrapper = shallow( @@ -58,8 +73,8 @@ it('logs in with identity provider', () => { it('expands more options', () => { const wrapper = shallow( diff --git a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/LoginForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/LoginForm-test.tsx.snap index 744c0b93b09..c2082daeaff 100644 --- a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/LoginForm-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/LoginForm-test.tsx.snap @@ -111,7 +111,14 @@ exports[`expands more options 2`] = `
- + + sessions.log_in - + + + sessions.log_in + + + cancel + +
+
+ +
+`; + +exports[`should display a spinner and disabled button while loading 1`] = ` +
+

+ login.login_to_sonarqube +

+
+ +
+ + +
+
+ + +
+
+
+ + sessions.log_in