From eb71b6c57924f96e898f984fdfbb4df77748e19b Mon Sep 17 00:00:00 2001 From: Tom Kirkpatrick Date: Wed, 5 Sep 2018 19:07:11 +0200 Subject: [PATCH 1/2] fix(ui): autofocus and clear pay and request forms When the payment and request forms open ensure that the forms are empty and the most relevant form field is selected by default. Fix #389 --- app/components/AmountInput/AmountInput.js | 6 + app/components/Form/Pay.js | 17 ++ app/components/Form/Request.js | 197 ++++++++++++---------- test/unit/components/Form/Pay.spec.js | 8 +- test/unit/components/Form/Request.spec.js | 4 +- 5 files changed, 138 insertions(+), 94 deletions(-) diff --git a/app/components/AmountInput/AmountInput.js b/app/components/AmountInput/AmountInput.js index b9efb05e..c44c3571 100644 --- a/app/components/AmountInput/AmountInput.js +++ b/app/components/AmountInput/AmountInput.js @@ -7,6 +7,7 @@ class AmountInput extends React.Component { this.handleChange = this.handleChange.bind(this) this.handleBlur = this.handleBlur.bind(this) this.handleKeyDown = this.handleKeyDown.bind(this) + this.textInput = React.createRef() } setRules() { @@ -41,6 +42,10 @@ class AmountInput extends React.Component { } } + focusTextInput() { + this.textInput.current.focus() + } + parseNumber(_value) { let value = _value || '' if (typeof _value === 'string') { @@ -143,6 +148,7 @@ class AmountInput extends React.Component { onBlur={this.handleBlur} onKeyDown={this.handleKeyDown} readOnly={readOnly} + ref={this.textInput} type="text" required value={amount} diff --git a/app/components/Form/Pay.js b/app/components/Form/Pay.js index 9e7e726a..1ce3b9a4 100644 --- a/app/components/Form/Pay.js +++ b/app/components/Form/Pay.js @@ -12,6 +12,22 @@ import AmountInput from 'components/AmountInput' import styles from './Pay.scss' class Pay extends Component { + constructor(props) { + super(props) + this.paymentRequestInput = React.createRef() + } + + componentDidMount() { + const { setPayInput, setPayAmount } = this.props + + // Clear the form of any previous data. + setPayInput('') + setPayAmount('') + + // Focus the payment request input field. + this.paymentRequestInput.current.focus() + } + componentDidUpdate(prevProps) { const { isLn, @@ -110,6 +126,7 @@ class Pay extends Component { onBlur={onPayInputBlur} id="paymentRequest" rows="4" + ref={this.paymentRequestInput} />
{ - const onCurrencyFilterClick = currency => { - // change the input amount - setRequestAmount(btc.convert(ticker.currency, currency, amount)) + // Clear the form of any previous data. + setRequestMemo('') + setRequestAmount('') - setCurrency(currency) - setRequestCurrencyFilters(false) + // Focus the amount input field. + this.amountInput.current.focusTextInput() } - return ( -
-
- -

Request Payment

-
- -
-
-
- - -
-
- -
-
setRequestCurrencyFilters(!showCurrencyFilters)} - > - {currencyName} - - - -
-
    - {currentCurrencyFilters.map(filter => ( -
  • onCurrencyFilterClick(filter.key)}> - {filter.name} -
  • - ))} -
+ render() { + const { + requestform: { amount, memo, showCurrencyFilters }, + ticker, + + setRequestAmount, + setRequestMemo, + setCurrency, + setRequestCurrencyFilters, + currencyName, + requestFiatAmount, + + currentCurrencyFilters, + + onRequestSubmit + } = this.props + + const onCurrencyFilterClick = currency => { + // change the input amount + setRequestAmount(btc.convert(ticker.currency, currency, amount)) + + setCurrency(currency) + setRequestCurrencyFilters(false) + } + + return ( +
+
+ +

Request Payment

+
+ +
+
+
+ + +
+
+ +
+
setRequestCurrencyFilters(!showCurrencyFilters)} + > + {currencyName} + + + +
+
    + {currentCurrencyFilters.map(filter => ( +
  • onCurrencyFilterClick(filter.key)}> + {filter.name} +
  • + ))} +
+
-
- -
{`≈ ${requestFiatAmount || 0} ${ - ticker.fiatTicker - }`}
-
- -
-
- -
-
- setRequestMemo(event.target.value)} - id="memo" - /> -
-
- -
-
0 ? styles.active : undefined}`} - onClick={onRequestSubmit} - > - Request -
-
+ +
{`≈ ${requestFiatAmount || 0} ${ + ticker.fiatTicker + }`}
+
+ +
+
+ +
+
+ setRequestMemo(event.target.value)} + id="memo" + /> +
+
+ +
+
0 ? styles.active : undefined}`} + onClick={onRequestSubmit} + > + Request +
+
+ - - ) + ) + } } Request.propTypes = { diff --git a/test/unit/components/Form/Pay.spec.js b/test/unit/components/Form/Pay.spec.js index ef1503e1..8cf504e5 100644 --- a/test/unit/components/Form/Pay.spec.js +++ b/test/unit/components/Form/Pay.spec.js @@ -1,5 +1,5 @@ import React from 'react' -import { configure, shallow } from 'enzyme' +import { configure, mount } from 'enzyme' import Adapter from 'enzyme-adapter-react-16' import Pay from 'components/Form/Pay' @@ -45,7 +45,7 @@ const defaultProps = { describe('Form', () => { describe('should show the form without an input', () => { - const el = shallow() + const el = mount() it('should contain Pay', () => { expect(el.find('input#paymentRequest').props.value).toBe(undefined) @@ -54,7 +54,7 @@ describe('Form', () => { describe('should show lightning with a lightning input', () => { const props = { ...defaultProps, isLn: true } - const el = shallow() + const el = mount() it('should contain Pay', () => { expect(el.find('input#paymentRequest').props.value).toBe(undefined) @@ -63,7 +63,7 @@ describe('Form', () => { describe('should show on-chain with an on-chain input', () => { const props = { ...defaultProps, isOnchain: true } - const el = shallow() + const el = mount() it('should contain Pay', () => { expect(el.find('input#paymentRequest').props.value).toBe(undefined) diff --git a/test/unit/components/Form/Request.spec.js b/test/unit/components/Form/Request.spec.js index 7a5976e9..0df1c97b 100644 --- a/test/unit/components/Form/Request.spec.js +++ b/test/unit/components/Form/Request.spec.js @@ -1,5 +1,5 @@ import React from 'react' -import { configure, shallow } from 'enzyme' +import { configure, mount } from 'enzyme' import Adapter from 'enzyme-adapter-react-16' import Request from 'components/Form/Request' @@ -28,7 +28,7 @@ const defaultProps = { describe('Form', () => { describe('should show request form when formType is REQUEST_FORM', () => { const props = { ...defaultProps } - const el = shallow() + const el = mount() it('should contain Request', () => { expect(el.contains('Request')).toBe(true) }) From 4e06f61dacebd3c0046f94eb539dae692170da29 Mon Sep 17 00:00:00 2001 From: Tom Kirkpatrick Date: Wed, 5 Sep 2018 19:29:22 +0200 Subject: [PATCH 2/2] fix(ui): autofocus network forms When the network search and add contact forms open ensure that the most relevant form field is selected by default. Fix #389 --- app/components/AmountInput/AmountInput.js | 4 + app/components/Contacts/AddChannel.js | 249 ++++++++++--------- app/components/Contacts/SubmitChannelForm.js | 12 + 3 files changed, 147 insertions(+), 118 deletions(-) diff --git a/app/components/AmountInput/AmountInput.js b/app/components/AmountInput/AmountInput.js index c44c3571..ed9f91e0 100644 --- a/app/components/AmountInput/AmountInput.js +++ b/app/components/AmountInput/AmountInput.js @@ -46,6 +46,10 @@ class AmountInput extends React.Component { this.textInput.current.focus() } + clearTextInput() { + this.textInput.current.value = '' + } + parseNumber(_value) { let value = _value || '' if (typeof _value === 'string') { diff --git a/app/components/Contacts/AddChannel.js b/app/components/Contacts/AddChannel.js index 351910f4..019e996c 100644 --- a/app/components/Contacts/AddChannel.js +++ b/app/components/Contacts/AddChannel.js @@ -1,4 +1,4 @@ -import React from 'react' +import React, { Component } from 'react' import PropTypes from 'prop-types' import Isvg from 'react-inlinesvg' @@ -6,137 +6,150 @@ import x from 'icons/x.svg' import styles from './AddChannel.scss' -const AddChannel = ({ - contactsform, - closeContactsForm, - openSubmitChannelForm, - updateContactFormSearchQuery, - updateManualFormSearchQuery, - setNode, - activeChannelPubkeys, - nonActiveChannelPubkeys, - pendingOpenChannelPubkeys, - filteredNetworkNodes, - loadingChannelPubkeys, - showManualForm, - openManualForm -}) => { - const renderRightSide = node => { - if (loadingChannelPubkeys.includes(node.pub_key)) { - return ( - -
-
-
- - ) - } +class AddChannel extends Component { + constructor(props) { + super(props) + this.searchInput = React.createRef() + } - if (activeChannelPubkeys.includes(node.pub_key)) { - return ( - - Online - - ) - } + componentDidMount() { + // Focus the search input field. + this.searchInput.current.focus() + } - if (nonActiveChannelPubkeys.includes(node.pub_key)) { - return ( - - Offline - - ) - } + render() { + const { + contactsform, + closeContactsForm, + openSubmitChannelForm, + updateContactFormSearchQuery, + updateManualFormSearchQuery, + setNode, + activeChannelPubkeys, + nonActiveChannelPubkeys, + pendingOpenChannelPubkeys, + filteredNetworkNodes, + loadingChannelPubkeys, + showManualForm, + openManualForm + } = this.props + const renderRightSide = node => { + if (loadingChannelPubkeys.includes(node.pub_key)) { + return ( + +
+
+
+ + ) + } + + if (activeChannelPubkeys.includes(node.pub_key)) { + return ( + + Online + + ) + } + + if (nonActiveChannelPubkeys.includes(node.pub_key)) { + return ( + + Offline + + ) + } + + if (pendingOpenChannelPubkeys.includes(node.pub_key)) { + return ( + + Pending + + ) + } + + if (!node.addresses.length) { + return Private + } - if (pendingOpenChannelPubkeys.includes(node.pub_key)) { return ( - - Pending + { + // set the node public key for the submit form + setNode(node) + // open the submit form + openSubmitChannelForm() + }} + > + Connect ) } - if (!node.addresses.length) { - return Private + const searchUpdated = search => { + updateContactFormSearchQuery(search) + + if (search.includes('@') && search.split('@')[0].length === 66) { + updateManualFormSearchQuery(search) + } } return ( - { - // set the node public key for the submit form - setNode(node) - // open the submit form - openSubmitChannelForm() - }} - > - Connect - - ) - } +
+
+ searchUpdated(event.target.value)} + ref={this.searchInput} + /> + + + +
- const searchUpdated = search => { - updateContactFormSearchQuery(search) +
+
    + {filteredNetworkNodes.map(node => ( +
  • +
    + {node.alias.length > 0 ? ( +

    + {node.alias.trim()} + + ({node.pub_key.substr(0, 10)} + ... + {node.pub_key.substr(node.pub_key.length - 10)}) + +

    + ) : ( +

    + {node.pub_key} +

    + )} +
    +
    {renderRightSide(node)}
    +
  • + ))} +
+
- if (search.includes('@') && search.split('@')[0].length === 66) { - updateManualFormSearchQuery(search) - } + {showManualForm && ( +
+

+ Hm, looks like we can't see that node from here, wanna try to manually connect? +

+
+ Connect Manually +
+
+ )} +
+ ) } - - return ( -
-
- searchUpdated(event.target.value)} - // ref={input => input && input.focus()} - /> - - - -
- -
-
    - {filteredNetworkNodes.map(node => ( -
  • -
    - {node.alias.length > 0 ? ( -

    - {node.alias.trim()} - - ({node.pub_key.substr(0, 10)} - ... - {node.pub_key.substr(node.pub_key.length - 10)}) - -

    - ) : ( -

    - {node.pub_key} -

    - )} -
    -
    {renderRightSide(node)}
    -
  • - ))} -
-
- - {showManualForm && ( -
-

- Hm, looks like we can't see that node from here, wanna try to manually connect? -

-
- Connect Manually -
-
- )} -
- ) } AddChannel.propTypes = { diff --git a/app/components/Contacts/SubmitChannelForm.js b/app/components/Contacts/SubmitChannelForm.js index 63095d63..a0140e0b 100644 --- a/app/components/Contacts/SubmitChannelForm.js +++ b/app/components/Contacts/SubmitChannelForm.js @@ -8,6 +8,17 @@ import AmountInput from 'components/AmountInput' import styles from './SubmitChannelForm.scss' class SubmitChannelForm extends React.Component { + constructor(props) { + super(props) + this.amountInput = React.createRef() + } + + componentDidMount() { + // Clear and Focus the amount input field. + this.amountInput.current.clearTextInput() + this.amountInput.current.focusTextInput() + } + render() { const { closeChannelForm, @@ -107,6 +118,7 @@ class SubmitChannelForm extends React.Component { amount={contactCapacity} currency={ticker.currency} onChangeEvent={updateContactCapacity} + ref={this.amountInput} />