Skip to content

Commit

Permalink
Add modal infrastructure + confirm modal for deleting a wallet (#316)
Browse files Browse the repository at this point in the history
* Add modal infrastructure + confirm modal for deleting a wallet

* Remove unused const
  • Loading branch information
evgenyboxer authored and shawnmclean committed Nov 9, 2017
1 parent 44537ae commit 52f8e04
Show file tree
Hide file tree
Showing 21 changed files with 442 additions and 32 deletions.
49 changes: 49 additions & 0 deletions __tests__/components/Modals/ConfirmModal.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react'
import { shallow } from 'enzyme'
import ConfirmModal from '../../../app/components/Modals/ConfirmModal/ConfirmModal'

describe('ConfirmModal', () => {
const props = {
title: 'test modal',
text: 'text',
hideModal: jest.fn(),
onClick: jest.fn(),
onCancel: jest.fn()
}

test('should render without crashing', () => {
const wrapper = shallow(<ConfirmModal {...props} />)

expect(wrapper).toMatchSnapshot()
})

test('should render the title correctly', () => {
const wrapper = shallow(<ConfirmModal {...props} />)

expect(wrapper.dive().find('.modalHeaderTitle').text()).toEqual(props.title)
})

test('should render the text correctly', () => {
const wrapper = shallow(<ConfirmModal {...props} />)

expect(wrapper.dive().find('.text').text()).toEqual(props.text)
})

test('should trigger the onCancel function followed by hideModal', () => {
const wrapper = shallow(<ConfirmModal {...props} />)

wrapper.dive().find('.cancelButton').simulate('click')

expect(props.onCancel).toHaveBeenCalled()
expect(props.hideModal).toHaveBeenCalled()
})

test('should trigger the onClick function followed by hideModal', () => {
const wrapper = shallow(<ConfirmModal {...props} />)

wrapper.dive().find('.actionButton').simulate('click')

expect(props.onClick).toHaveBeenCalled()
expect(props.hideModal).toHaveBeenCalled()
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ConfirmModal should render without crashing 1`] = `
<BaseModal
height="450px"
hideModal={[Function]}
style={
Object {
"content": Object {
"height": "175px",
"width": "450px",
},
}
}
title="test modal"
width="450px"
>
<div
className="textContainer"
>
<strong
className="text"
>
text
</strong>
</div>
<div
className="modalFooter"
>
<button
className="actionButton"
onClick={[Function]}
>
Confirm
</button>
<button
className="cancelButton"
onClick={[Function]}
>
Cancel
</button>
</div>
</BaseModal>
`;
3 changes: 2 additions & 1 deletion __tests__/store/reducers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ describe('root reducer', () => {
metadata: expect.any(Object),
notification: expect.any(Object),
claim: expect.any(Object),
rpx: expect.any(Object)
rpx: expect.any(Object),
modal: expect.any(Object)
})
})
})
61 changes: 61 additions & 0 deletions app/components/Modals/BaseModal/BaseModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// @flow
import React from 'react'
import type { Children } from 'react'
import ReactModal from 'react-modal'
import Close from 'react-icons/lib/md/close'
import styles from './BaseModal.scss'

type Props = {
title: string,
children: Children,
hideModal: Function,
width?: string,
height?: string,
className?: string,
style: {
content: Object,
overlay: Object
}
}

const BaseModal = ({ hideModal, title, children, width, height, className, style }: Props) => (
<ReactModal
isOpen
onRequestClose={hideModal}
style={{
content: {
width,
height,
margin: 'auto',
padding: 0,
boxShadow: 'rgba(0, 0, 0, 0.1) 0px 0px 4px',
border: 'none',
borderRadius: '4px',
...style.content
},
overlay: {
backgroundColor: 'rgba(26, 54, 80, 0.25)',
margin: 'auto',
...style.overlay
}
}}
className={className}
>
<div className={styles.modalHeader}>
<div className={styles.modalHeaderTitle}>{title}</div>
<div className={styles.modalHeaderCloseButton} onClick={hideModal}><Close /></div>
</div>
<div className={styles.modalBody}>{children}</div>
</ReactModal>
)

BaseModal.defaultProps = {
width: '450px',
height: '450px',
style: {
content: {},
overlay: {}
}
}

export default BaseModal
25 changes: 25 additions & 0 deletions app/components/Modals/BaseModal/BaseModal.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.modalHeader {
display: flex;
height: 45px;
padding: 0px 24px;
border-bottom: 1px solid #DAE1E9;
position: relative;
justify-content: center;
align-items: center;
}

.modalHeaderTitle {
font-size: 18px;
}

.modalHeaderCloseButton {
width: 18px;
height: 18px;
margin-left: auto;
fill: #9BA6B2;
cursor: pointer;
}

.modalBody {
padding: 0px 16px;
}
1 change: 1 addition & 0 deletions app/components/Modals/BaseModal/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './BaseModal'
54 changes: 54 additions & 0 deletions app/components/Modals/ConfirmModal/ConfirmModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// @flow
import React from 'react'
import BaseModal from '../BaseModal'
import styles from './ConfirmModal.scss'

type Props = {
title: string,
text: string,
onClick: Function,
onCancel: Function,
hideModal: Function,
width: string,
height: string,
}

const ConfirmModal = ({ hideModal, title, onClick, onCancel, text, width, height }: Props) => (
<BaseModal
title={title}
hideModal={hideModal}
style={{
content: {
width,
height
}
}}
>
<div className={styles.textContainer}>
<strong className={styles.text}>{text}</strong>
</div>
<div className={styles.modalFooter}>
<button
className={styles.actionButton}
onClick={() => {
onClick()
hideModal()
}}>Confirm</button>
<button
className={styles.cancelButton}
onClick={() => {
hideModal()
if (onCancel) {
onCancel()
}
}}>Cancel</button>
</div>
</BaseModal>
)

ConfirmModal.defaultProps = {
width: '450px',
height: '175px'
}

export default ConfirmModal
24 changes: 24 additions & 0 deletions app/components/Modals/ConfirmModal/ConfirmModal.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.textContainer {
padding: 20px 0;
}

.modalFooter {
padding-top: 20px;
text-align: right;
}

.actionButton {
background-color: red;
&:hover {
background-color: red;
}
}

.cancelButton {
background: none;
border: 1px solid #999;
color: #000;
&:hover {
background: none
}
}
1 change: 1 addition & 0 deletions app/components/Modals/ConfirmModal/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ConfirmModal'
2 changes: 2 additions & 0 deletions app/containers/App/App.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow
import React, { Component } from 'react'
import ModalRenderer from '../ModalRenderer'
import Notification from '../../components/Notification'
import classNames from 'classnames'
import { NOTIFICATION_POSITIONS } from '../../core/constants'
Expand Down Expand Up @@ -29,6 +30,7 @@ class App extends Component<Props> {
[styles.pushTop]: shouldPushTop,
[styles.noAnimation]: noAnimation
})}>{children}</div>
<ModalRenderer />
</div>
)
}
Expand Down
32 changes: 32 additions & 0 deletions app/containers/ModalRenderer/ModalRenderer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// @flow
import React from 'react'

import { MODAL_TYPES } from '../../core/constants'
import ConfirmModal from '../../components/Modals/ConfirmModal'

const {
CONFIRM
} = MODAL_TYPES

const MODAL_COMPONENTS = {
[CONFIRM]: ConfirmModal
}

type Props = {
modalType?: ModalType,
modalProps: Object,
hideModal: Function
}

const ModalRenderer = (props: Props) => {
const { modalType, modalProps, hideModal } = props

if (modalType) {
const Modal = MODAL_COMPONENTS[modalType]
return <Modal {...modalProps} hideModal={hideModal} />
}

return null
}

export default ModalRenderer
18 changes: 18 additions & 0 deletions app/containers/ModalRenderer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// @flow
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import ModalRenderer from './ModalRenderer'
import { getModalType, getModalProps, hideModal } from '../../modules/modal'

const mapStateToProps = (state) => ({
modalType: getModalType(state),
modalProps: getModalProps(state)
})

const actionCreators = {
hideModal
}

const mapDispatchToProps = dispatch => bindActionCreators(actionCreators, dispatch)

export default connect(mapStateToProps, mapDispatchToProps)(ModalRenderer)
28 changes: 17 additions & 11 deletions app/containers/Settings/Settings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import storage from 'electron-json-storage'
import Delete from 'react-icons/lib/md/delete'
import Page from '../../components/Page'
import HomeButtonLink from '../../components/HomeButtonLink'
import { EXPLORER } from '../../core/constants'
import { EXPLORER, MODAL_TYPES } from '../../core/constants'

const { dialog } = require('electron').remote

type Props = {
setKeys: Function,
setBlockExplorer: Function,
explorer: string,
wallets: any
wallets: any,
showModal: Function
}

type State = {
Expand Down Expand Up @@ -105,15 +107,19 @@ export default class Settings extends Component<Props, State> {
}

deleteWallet = (key: string) => {
const { setKeys } = this.props
if (window.confirm(`Please confirm deleting saved wallet - ${key}`)) {
// eslint-disable-next-line
storage.get('keys', (error, data) => {
delete data[key]
storage.set('keys', data)
setKeys(data)
})
}
const { setKeys, showModal } = this.props
showModal(MODAL_TYPES.CONFIRM, {
title: 'Confirm Delete',
text: `Please confirm deleting saved wallet - ${key}`,
onClick: () => {
// eslint-disable-next-line
storage.get('keys', (error, data) => {
delete data[key]
storage.set('keys', data)
setKeys(data)
})
}
})
}

render () {
Expand Down
Loading

0 comments on commit 52f8e04

Please sign in to comment.