summaryrefslogtreecommitdiff
path: root/web-ui
diff options
context:
space:
mode:
authorSriram Viswanathan <sriramv@thoughtworks.com>2017-03-28 14:48:45 -0300
committerSriram Viswanathan <sriramv@thoughtworks.com>2017-03-28 14:53:12 -0300
commit6f9de40da695e5701104788e3216996b7950087d (patch)
tree9edd99f07d7b4d5bb653284f770425acfbae374c /web-ui
parenta9462a5d761aab4d4bcd89412fb265155c981972 (diff)
[#931] Shows error feedback using Snackbar notification when there's a submit failure on backup account page
Diffstat (limited to 'web-ui')
-rw-r--r--web-ui/app/locales/en_US/translation.json3
-rw-r--r--web-ui/app/locales/pt_BR/translation.json3
-rw-r--r--web-ui/src/backup_account/backup_email/backup_email.js12
-rw-r--r--web-ui/src/backup_account/backup_email/backup_email.spec.js27
-rw-r--r--web-ui/src/backup_account/page.js11
-rw-r--r--web-ui/src/backup_account/page.spec.js25
-rw-r--r--web-ui/src/common/snackbar_notification/snackbar_notification.js62
-rw-r--r--web-ui/src/common/snackbar_notification/snackbar_notification.spec.js31
8 files changed, 168 insertions, 6 deletions
diff --git a/web-ui/app/locales/en_US/translation.json b/web-ui/app/locales/en_US/translation.json
index 7d24a728..c60a705b 100644
--- a/web-ui/app/locales/en_US/translation.json
+++ b/web-ui/app/locales/en_US/translation.json
@@ -103,6 +103,9 @@
"paragraph": "Save this message, it is really important.",
"button": "Got it! I'm ready!",
"retry-button": "Hey, I didn't received it"
+ },
+ "error": {
+ "submit-error": "There was an error. Please try again later."
}
},
"back-to-inbox": "Back to my inbox",
diff --git a/web-ui/app/locales/pt_BR/translation.json b/web-ui/app/locales/pt_BR/translation.json
index ccc0fe5a..d43487a2 100644
--- a/web-ui/app/locales/pt_BR/translation.json
+++ b/web-ui/app/locales/pt_BR/translation.json
@@ -103,6 +103,9 @@
"paragraph": "Salve esse e-mail, ele é bem importante.",
"button": "Recebi! Pronto!",
"retry-button": "Ei, eu não recebi"
+ },
+ "error": {
+ "submit-error": "Ocorreu um erro. Por favor tente novamente mais tarde."
}
},
"back-to-inbox": "Voltar",
diff --git a/web-ui/src/backup_account/backup_email/backup_email.js b/web-ui/src/backup_account/backup_email/backup_email.js
index 09863950..6eeadee8 100644
--- a/web-ui/src/backup_account/backup_email/backup_email.js
+++ b/web-ui/src/backup_account/backup_email/backup_email.js
@@ -40,7 +40,7 @@ export class BackupEmail extends React.Component {
error: !emptyEmail && !validEmail ? t('backup-account.backup-email.error.invalid-email') : '',
submitButtonDisabled: !validEmail || emptyEmail
});
- }
+ };
submitHandler = (event) => {
event.preventDefault();
@@ -54,8 +54,14 @@ export class BackupEmail extends React.Component {
body: JSON.stringify({
csrftoken: [browser.getCookie('XSRF-TOKEN')]
})
- }).then(() => this.props.onSubmit('success'));
- }
+ }).then((response) => {
+ if (response.status === 204) {
+ this.props.onSubmit('success');
+ } else {
+ this.props.onSubmit('error');
+ }
+ });
+ };
render() {
const t = this.props.t;
diff --git a/web-ui/src/backup_account/backup_email/backup_email.spec.js b/web-ui/src/backup_account/backup_email/backup_email.spec.js
index 48199738..ce357bf7 100644
--- a/web-ui/src/backup_account/backup_email/backup_email.spec.js
+++ b/web-ui/src/backup_account/backup_email/backup_email.spec.js
@@ -84,7 +84,7 @@ describe('BackupEmail', () => {
});
});
- describe('Submit', () => {
+ describe('Submit on success', () => {
let preventDefaultSpy;
beforeEach((done) => {
@@ -118,8 +118,31 @@ describe('BackupEmail', () => {
expect(preventDefaultSpy).toHaveBeenCalled();
});
- it('calls onSubmit from props when success', () => {
+ it('calls onSubmit from props with success', () => {
expect(mockOnSubmit).toHaveBeenCalledWith('success');
});
});
+
+ describe('Submit on error', () => {
+ let preventDefaultSpy;
+
+ beforeEach((done) => {
+ mockOnSubmit = expect.createSpy().andCall(() => done());
+ preventDefaultSpy = expect.createSpy();
+ expect.spyOn(browser, 'getCookie').andReturn('abc123');
+
+ backupEmail = shallow(<BackupEmail t={mockTranslations} onSubmit={mockOnSubmit} />);
+
+ fetchMock.post('/backup-account', 500);
+ backupEmail.find('form').simulate('submit', { preventDefault: preventDefaultSpy });
+ });
+
+ it('calls onSubmit from props with error', () => {
+ expect(mockOnSubmit).toHaveBeenCalledWith('error');
+ });
+ });
+
+ afterEach(() => {
+ fetchMock.restore();
+ });
});
diff --git a/web-ui/src/backup_account/page.js b/web-ui/src/backup_account/page.js
index 49e4b316..d5f64add 100644
--- a/web-ui/src/backup_account/page.js
+++ b/web-ui/src/backup_account/page.js
@@ -22,6 +22,7 @@ import Footer from 'src/common/footer/footer';
import Header from 'src/common/header/header';
import BackupEmail from 'src/backup_account/backup_email/backup_email';
import Confirmation from 'src/backup_account/confirmation/confirmation';
+import SnackbarNotification from 'src/common/snackbar_notification/snackbar_notification';
import 'font-awesome/scss/font-awesome.scss';
import './page.scss';
@@ -36,13 +37,20 @@ export class Page extends React.Component {
saveBackupEmail = (status) => {
this.setState({ status });
- }
+ };
mainContent = () => {
if (this.state.status === 'success') return <Confirmation />;
return <BackupEmail onSubmit={this.saveBackupEmail} />;
};
+ showSnackbarOnError = (t) => {
+ if (this.state.status === 'error') {
+ return <SnackbarNotification message={t('backup-account.error.submit-error')} isError />;
+ }
+ return undefined;
+ };
+
render() {
const t = this.props.t;
return (
@@ -52,6 +60,7 @@ export class Page extends React.Component {
<section>
{this.mainContent()}
</section>
+ {this.showSnackbarOnError(t)}
<Footer />
</div>
</DocumentTitle>
diff --git a/web-ui/src/backup_account/page.spec.js b/web-ui/src/backup_account/page.spec.js
index bd7bb884..2933a64e 100644
--- a/web-ui/src/backup_account/page.spec.js
+++ b/web-ui/src/backup_account/page.spec.js
@@ -4,6 +4,7 @@ import React from 'react';
import { Page } from 'src/backup_account/page';
import BackupEmail from 'src/backup_account/backup_email/backup_email';
import Confirmation from 'src/backup_account/confirmation/confirmation';
+import SnackbarNotification from 'src/common/snackbar_notification/snackbar_notification';
describe('BackupAccount', () => {
let page;
@@ -41,5 +42,29 @@ describe('BackupAccount', () => {
pageInstance.saveBackupEmail('success');
expect(page.find(Confirmation).length).toEqual(1);
});
+
+ context('on submit error', () => {
+ beforeEach(() => {
+ pageInstance.saveBackupEmail('error');
+ });
+
+ it('returns snackbar component on error', () => {
+ const snackbar = pageInstance.showSnackbarOnError(pageInstance.props.t);
+ expect(snackbar).toEqual(<SnackbarNotification message='backup-account.error.submit-error' isError />);
+ });
+
+ it('returns nothing when there is no error', () => {
+ pageInstance.saveBackupEmail('success');
+ const snackbar = pageInstance.showSnackbarOnError(pageInstance.props.t);
+ expect(snackbar).toEqual(undefined);
+ });
+
+ it('renders snackbar notification on error', () => {
+ const snackbar = page.find(SnackbarNotification);
+ expect(snackbar).toExist();
+ expect(snackbar.props().message).toEqual('backup-account.error.submit-error');
+ expect(snackbar.props().isError).toEqual(true);
+ });
+ });
});
});
diff --git a/web-ui/src/common/snackbar_notification/snackbar_notification.js b/web-ui/src/common/snackbar_notification/snackbar_notification.js
new file mode 100644
index 00000000..2aee3d67
--- /dev/null
+++ b/web-ui/src/common/snackbar_notification/snackbar_notification.js
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017 ThoughtWorks, Inc.
+ *
+ * Pixelated is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Pixelated is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import React from 'react';
+import Snackbar from 'material-ui/Snackbar';
+import { red500, blue500 } from 'material-ui/styles/colors';
+
+const notificationStyle = () => ({
+ top: 0,
+ bottom: 'auto',
+ left: (window.innerWidth - 288) / 2,
+ transform: 'translate3d(0, 0px, 0)'
+});
+
+const contentStyle = {
+ textAlign: 'center'
+};
+
+const getStyleByType = (isError) => {
+ if (isError) {
+ return { backgroundColor: red500 };
+ }
+ return { backgroundColor: blue500 };
+};
+
+const SnackbarNotification = ({ message, isError = false, autoHideDuration = 5000 }) => (
+ <Snackbar
+ open
+ bodyStyle={getStyleByType(isError)}
+ message={message}
+ autoHideDuration={autoHideDuration}
+ contentStyle={contentStyle}
+ style={notificationStyle()}
+ />
+);
+
+SnackbarNotification.propTypes = {
+ message: React.PropTypes.string.isRequired,
+ isError: React.PropTypes.bool,
+ autoHideDuration: React.PropTypes.number
+};
+
+SnackbarNotification.defaultProps = {
+ isError: false,
+ autoHideDuration: 5000
+};
+
+export default SnackbarNotification;
diff --git a/web-ui/src/common/snackbar_notification/snackbar_notification.spec.js b/web-ui/src/common/snackbar_notification/snackbar_notification.spec.js
new file mode 100644
index 00000000..59461413
--- /dev/null
+++ b/web-ui/src/common/snackbar_notification/snackbar_notification.spec.js
@@ -0,0 +1,31 @@
+import { shallow } from 'enzyme';
+import expect from 'expect';
+import React from 'react';
+import SnackbarNotification from 'src/common/snackbar_notification/snackbar_notification';
+import Snackbar from 'material-ui/Snackbar';
+import { red500 } from 'material-ui/styles/colors';
+
+describe('SnackbarNotification', () => {
+ let snackbarNotification;
+
+ beforeEach(() => {
+ snackbarNotification = shallow(<SnackbarNotification message={'Error Message'} isError />);
+ });
+
+ it('renders snackbar with error message', () => {
+ expect(snackbarNotification.find(Snackbar).props().message).toEqual('Error Message');
+ });
+
+ it('renders snackbar with open as true', () => {
+ expect(snackbarNotification.find(Snackbar).props().open).toEqual(true);
+ });
+
+ it('renders snackbar with error body style', () => {
+ expect(snackbarNotification.find(Snackbar).props().bodyStyle)
+ .toEqual({ backgroundColor: red500 });
+ });
+
+ it('renders snackbar with default auto-hide duration', () => {
+ expect(snackbarNotification.find(Snackbar).props().autoHideDuration).toEqual(5000);
+ });
+});