Skip to content
This repository has been archived by the owner on Apr 15, 2019. It is now read-only.

Implement custom alert dialogs in React - Closes #515 #526

Merged
merged 5 commits into from
Jul 28, 2017
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/actions/dialog.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import actionTypes from '../constants/actions';
import Alert from '../components/dialog/alert';

/**
* An action to dispatch to display a dialog
Expand All @@ -9,6 +10,39 @@ export const dialogDisplayed = data => ({
type: actionTypes.dialogDisplayed,
});

/**
* An action to dispatch to display an alert dialog
*
*/
export const alertDialogDisplayed = data => dialogDisplayed({
title: data.title,
type: data.type,
childComponent: Alert,
childComponentProps: {
text: data.text,
},
});

/**
* An action to dispatch to display a success alert dialog
*
*/
export const successAlertDialogDisplayed = data => alertDialogDisplayed({
title: 'Success',
text: data.text,
type: 'success',
});

/**
* An action to dispatch to display a error alert dialog
*
*/
export const errorAlertDialogDisplayed = data => alertDialogDisplayed({
title: 'Error',
text: data.text,
type: 'error',
});

/**
* An action to dispatch to hide a dialog
*
Expand Down
74 changes: 73 additions & 1 deletion src/actions/dialog.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { expect } from 'chai';
import actionTypes from '../constants/actions';
import { dialogDisplayed, dialogHidden } from './dialog';
import {
dialogDisplayed,
alertDialogDisplayed,
successAlertDialogDisplayed,
errorAlertDialogDisplayed,
dialogHidden,
} from './dialog';
import Alert from '../components/dialog/alert';


describe('actions: dialog', () => {
describe('dialogDisplayed', () => {
Expand All @@ -18,6 +26,70 @@ describe('actions: dialog', () => {
});
});

describe('alertDialogDisplayed', () => {
it('should create an action to show alert dialog', () => {
const data = {
title: 'success',
text: 'some text',
};

const expectedAction = {
data: {
title: data.title,
type: undefined,
childComponent: Alert,
childComponentProps: {
text: data.text,
},
},
type: actionTypes.dialogDisplayed,
};
expect(alertDialogDisplayed(data)).to.be.deep.equal(expectedAction);
});
});

describe('successAlertDialogDisplayed', () => {
it('should create an action to show alert dialog', () => {
const data = {
text: 'some text',
};

const expectedAction = {
data: {
title: 'Success',
type: 'success',
childComponent: Alert,
childComponentProps: {
text: data.text,
},
},
type: actionTypes.dialogDisplayed,
};
expect(successAlertDialogDisplayed(data)).to.be.deep.equal(expectedAction);
});
});

describe('errorAlertDialogDisplayed', () => {
it('should create an action to show alert dialog', () => {
const data = {
text: 'some text',
};

const expectedAction = {
data: {
title: 'Error',
type: 'error',
childComponent: Alert,
childComponentProps: {
text: data.text,
},
},
type: actionTypes.dialogDisplayed,
};
expect(errorAlertDialogDisplayed(data)).to.be.deep.equal(expectedAction);
});
});

describe('dialogHidden', () => {
it('should create an action to hide dialog', () => {
const expectedAction = {
Expand Down
17 changes: 17 additions & 0 deletions src/components/dialog/alert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import Button from 'react-toolbox/lib/button';
import grid from '../../../node_modules/flexboxgrid/dist/flexboxgrid.css';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For importing from npm modules, there's not need to use relative path. flexboxgrid/dist/flexboxgrid.css'; would work fine.



const Alert = props => (
<div>
<p>{props.text}</p>
<br />
<section className={`${grid.row} ${grid['between-xs']}`}>
<span />
<Button label='Ok' onClick={props.closeDialog}/>
</section>
</div>
);

export default Alert;
34 changes: 34 additions & 0 deletions src/components/dialog/alert.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import chai, { expect } from 'chai';
import { mount } from 'enzyme';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import chaiEnzyme from 'chai-enzyme';
import Alert from './alert';

chai.use(sinonChai);
chai.use(chaiEnzyme()); // Note the invocation at the end

describe('Alert', () => {
let wrapper;
let closeSpy;
const text = 'some random text';

beforeEach(() => {
closeSpy = sinon.spy();
wrapper = mount(<Alert text={text} closeDialog={closeSpy} />);
});

it('renders paragraph with props.text', () => {
expect(wrapper.find('p').text()).to.equal(text);
});

it('renders "Ok" Button', () => {
expect(wrapper.find('Button').text()).to.equal('Ok');
});

it('renders "Ok" Button that calls props.closeDialog on click', () => {
wrapper.find('Button').simulate('click');
expect(closeSpy).to.have.been.calledWith();
});
});
8 changes: 8 additions & 0 deletions src/components/dialog/dialog.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,11 @@
}
}
}

.error {
background-color: #c62828;
}

.success {
background-color: #7cb342;
}
3 changes: 2 additions & 1 deletion src/components/dialog/dialogElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ class DialogElement extends Component {
<Dialog active={this.props.dialog.childComponent !== undefined && !this.state.hidden}
type='fullscreen'>
<div className={styles.dialog}>
<AppBar title={this.props.dialog.title} flat={true}>
<AppBar title={this.props.dialog.title} flat={true}
className={styles[this.props.dialog.type]}>
<Navigation type='horizontal'>
<IconButton className={`${styles['x-button']} x-button`} onClick={this.closeDialog.bind(this)} icon='close'/>
</Navigation>
Expand Down
27 changes: 27 additions & 0 deletions src/components/dialog/stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import Dialog from './dialogElement';
import Alert from './alert';

const dialogContent = () => (<div>Hello</div>);

Expand All @@ -15,4 +16,30 @@ storiesOf('Dialog', module)
}}
onCancelClick={ action('onCancelClick') }
/>
))
.add('Success alert', () => (
<Dialog
dialog={{
title: 'Success',
type: 'success',
childComponent: Alert,
childComponentProps: {
text: 'Custom success message',
},
}}
onCancelClick={ action('onCancelClick') }
/>
))
.add('Error alert', () => (
<Dialog
dialog={{
title: 'Error',
type: 'error',
childComponent: Alert,
childComponentProps: {
text: 'Custom error message',
},
}}
onCancelClick={ action('onCancelClick') }
/>
));
8 changes: 7 additions & 1 deletion src/components/send/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { connect } from 'react-redux';
import Send from './send';
import { successAlertDialogDisplayed, errorAlertDialogDisplayed } from '../../actions/dialog';

const mapStateToProps = state => ({
account: state.account,
activePeer: state.peers.data,
});

export default connect(mapStateToProps)(Send);
const mapDispatchToProps = dispatch => ({
showSuccessAlert: data => dispatch(successAlertDialogDisplayed(data)),
showErrorAlert: data => dispatch(errorAlertDialogDisplayed(data)),
});

export default connect(mapStateToProps, mapDispatchToProps)(Send);

13 changes: 6 additions & 7 deletions src/components/send/send.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,13 @@ class Send extends React.Component {
this.props.account.passphrase,
this.props.account.sencodPassphrase,
).then(() => {
// TODO implement and use our custom alert dialogs
// eslint-disable-next-line no-alert
alert(`Your transaction of ${this.state.amount.value} LSK to ${this.state.recipient.value} was accepted and will be processed in a few seconds.`);
this.props.closeDialog();
this.props.showSuccessAlert({
text: `Your transaction of ${this.state.amount.value} LSK to ${this.state.recipient.value} was accepted and will be processed in a few seconds.`,
});
}).catch((res) => {
// TODO implement and use our custom alert dialogs
// eslint-disable-next-line no-alert
alert(res && res.message ? res.message : 'An error occurred while creating the transaction.');
this.props.showErrorAlert({
text: res && res.message ? res.message : 'An error occurred while creating the transaction.',
});
});
}

Expand Down