From c21a963eb673c73b644800f713fead1a177f0be0 Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Wed, 26 Jun 2019 16:17:01 +0200 Subject: [PATCH 01/25] :seedling: Add addBookmark route and component --- .../bookmarks/addBookmark/addBookmark.css | 72 +++++++ .../bookmarks/addBookmark/addBookmark.js | 176 ++++++++++++++++++ src/components/bookmarks/addBookmark/index.js | 12 ++ src/constants/routes.js | 6 + 4 files changed, 266 insertions(+) create mode 100644 src/components/bookmarks/addBookmark/addBookmark.css create mode 100644 src/components/bookmarks/addBookmark/addBookmark.js create mode 100644 src/components/bookmarks/addBookmark/index.js diff --git a/src/components/bookmarks/addBookmark/addBookmark.css b/src/components/bookmarks/addBookmark/addBookmark.css new file mode 100644 index 0000000000..1b1714b385 --- /dev/null +++ b/src/components/bookmarks/addBookmark/addBookmark.css @@ -0,0 +1,72 @@ +@import '../../app/variablesV2.css'; +@import '../../app/mixins.css'; + +.wrapper { + display: flex; + justify-content: center; + width: 100%; + + & .content { + width: 720px; + + & header { + display: flex; + justify-content: space-between; + align-items: center; + } + + & h1 { + @mixin headingLarge; + } + } +} + +.formHolder { + display: flex; + flex-direction: column; + padding: 20px; + + & > .buttonHolder { + display: flex; + margin: 16px auto 30px; + width: 390px; + + & button { + display: block; + width: 100%; + } + } + + & label { + display: block; + margin-bottom: 24px; + + & > .label { + @mixin contentNormal bold; + + color: var(--color-maastricht-blue); + display: block; + margin-bottom: 12px; + } + + & .input:focus ~ .feedback { + opacity: 1 !important; + } + } + + & .feedback { + margin-top: 5px; + min-height: 44px; + opacity: 0; + transition: none; + + &.error { + opacity: 1; + } + + &:not(:global(.error)) { + padding-left: 0; + padding-right: 0; + } + } +} diff --git a/src/components/bookmarks/addBookmark/addBookmark.js b/src/components/bookmarks/addBookmark/addBookmark.js new file mode 100644 index 0000000000..7a8c3c7bae --- /dev/null +++ b/src/components/bookmarks/addBookmark/addBookmark.js @@ -0,0 +1,176 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { validateAddress } from '../../../utils/validators'; +import networks from '../../../constants/networks'; +import Box from '../../boxV2'; +import { InputV2 } from '../../toolbox/inputsV2'; +import { PrimaryButtonV2 } from '../../toolbox/buttons/button'; +import Feedback from '../../toolbox/feedback/feedback'; +import styles from './addBookmark.css'; + +class AddBookmark extends React.Component { + constructor(props) { + super(props); + + this.fields = [{ + name: 'address', + label: props.t('Address'), + }, { + name: 'label', + label: props.t('Label'), + feedback: props.t('Max. 20 characters'), + }]; + + const fields = this.fields.reduce((acc, field) => ({ + ...acc, + [field.name]: { + value: '', + error: false, + feedback: field.feedback || '', + }, + }), {}); + + this.state = { + fields, + }; + + this.onInputChange = { + address: this.onAddressChange.bind(this), + label: this.onLabelChange.bind(this), + }; + } + + componentDidUpdate(prevProps) { + const { token } = this.props; + const { token: prevToken } = prevProps; + if (token.active === prevToken.active) { + return true; + } + this.revalidate(token.active); + return false; + } + + updateField({ name, data }) { + this.setState(({ fields }) => ({ + fields: { + ...fields, + [name]: { + ...fields[name], + ...data, + }, + }, + })); + } + + revalidate(token) { + const { fields: { label, address } } = this.state; + this.updateField({ name: 'address', data: { ...this.validateAddress(token, address.value) } }); + this.updateField({ name: 'label', data: { ...this.validateLabel(label.value) } }); + } + + onLabelChange({ target: { name, value } }) { + const { error, feedback } = this.validateLabel(value); + this.updateField({ + name, + data: { error, value, feedback }, + }); + } + + validateLabel(value) { + const { t } = this.props; + const maxLength = 20; + const error = value.length > maxLength; + const feedback = !error + ? t('Max. 20 characters') + : t('Nickname is too long.'); + return { feedback, error }; + } + + onAddressChange({ target: { name, value } }) { + const { token: { active } } = this.props; + const { feedback, error } = this.validateAddress(active, value); + this.updateField({ + name, + data: { error, value, feedback }, + }); + } + + validateAddress(token, value) { + const { network } = this.props; + const netCode = network.name === networks.mainnet.name + ? networks.mainnet.code + : networks.testnet.code; + const error = validateAddress(token, value, netCode) === 1; + const feedback = error + ? 'Invalid address.' + : ''; + return { error, feedback }; + } + + render() { + const { t } = this.props; + const { fields } = this.state; + return ( +
+
+
+

{t('Bookmarks')}

+
+ +
+

+ {t('New bookmark')} +

+
+
+ {this.fields.map(field => ( + + ))} +
+ + {t('Add bookmark')} + +
+
+
+
+
+ ); + } +} + +AddBookmark.displayName = 'AddBookmark'; +AddBookmark.propTypes = { + t: PropTypes.func.isRequired, + token: PropTypes.shape({ + active: PropTypes.string.isRequired, + }).isRequired, + bookmarks: PropTypes.shape({ + LSK: PropTypes.arrayOf(PropTypes.shape({ + address: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + })), + }).isRequired, + network: PropTypes.shape({ + name: PropTypes.string.isRequired, + }).isRequired, +}; + +export default AddBookmark; diff --git a/src/components/bookmarks/addBookmark/index.js b/src/components/bookmarks/addBookmark/index.js new file mode 100644 index 0000000000..47a56772ee --- /dev/null +++ b/src/components/bookmarks/addBookmark/index.js @@ -0,0 +1,12 @@ +/* istanbul ignore file */ +import { connect } from 'react-redux'; +import { translate } from 'react-i18next'; +import AddBookmark from './addBookmark'; + +const mapStateToProps = state => ({ + bookmarks: state.bookmarks, + token: state.settings.token, + network: state.network, +}); + +export default connect(mapStateToProps)(translate()(AddBookmark)); diff --git a/src/constants/routes.js b/src/constants/routes.js index ac05f5aec0..1b72f01310 100644 --- a/src/constants/routes.js +++ b/src/constants/routes.js @@ -21,6 +21,7 @@ import Extensions from '../components/extensions'; import TermsOfUse from '../components/termsOfUse'; import ToolboxDemo from '../components/toolbox/demo'; import Dashboard from '../components/dashboard'; +import AddBookmark from '../components/bookmarks/addBookmark'; export default { accountVisualDemo: { @@ -39,6 +40,11 @@ export default { component: Dashboard, isPrivate: false, }, + addBookmark: { + path: '/bookmarks/add-bookmark', + component: AddBookmark, + isPrivate: false, + }, send: { path: '/wallet/send', component: SendV2, From de8bfbd8ae19a1172f03c7b56c30fe3809d62bab Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Wed, 26 Jun 2019 16:18:26 +0200 Subject: [PATCH 02/25] :recycle: Update generated strings --- i18n/locales/en/common.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/i18n/locales/en/common.json b/i18n/locales/en/common.json index ce8a8d94af..ba299f1520 100644 --- a/i18n/locales/en/common.json +++ b/i18n/locales/en/common.json @@ -29,6 +29,7 @@ "Add a Lisk ID to follow": "Add a Lisk ID to follow", "Add a bookmark": "Add a bookmark", "Add address to bookmarks": "Add address to bookmarks", + "Add bookmark": "Add bookmark", "Add some LSK to your Lisk Hub account now!": "Add some LSK to your Lisk Hub account now!", "Add to bookmarks": "Add to bookmarks", "Add to list": "Add to list", @@ -252,6 +253,7 @@ "LSK": "LSK", "LSK forged": "LSK forged", "LSK received": "LSK received", + "Label": "Label", "Last Forged Block": "Last Forged Block", "Later": "Later", "Learn about blockchain with our comprehensive knowledge base.": "Learn about blockchain with our comprehensive knowledge base.", @@ -291,6 +293,7 @@ "Max 20 characters, a-z and 0-1, no special characters except for “!@$&.”": "Max 20 characters, a-z and 0-1, no special characters except for “!@$&.”", "Max amount must be greater than Min amount": "Max amount must be greater than Min amount", "Max amount of delegates in one voting exceeded.": "Max amount of delegates in one voting exceeded.", + "Max. 20 characters": "Max. 20 characters", "Maximum floating point is 8.": "Maximum floating point is 8.", "Maximum length exceeded": "Maximum length exceeded", "Menu": "Menu", @@ -306,8 +309,10 @@ "Name too long": "Name too long", "Names are unique. Once you register a name, it can't be changed.": "Names are unique. Once you register a name, it can't be changed.", "Network switcher": "Network switcher", + "New bookmark": "New bookmark", "New to Lisk? ": "New to Lisk? ", "Next": "Next", + "Nickname is too long.": "Nickname is too long.", "No Bookmarks added yet": "No Bookmarks added yet", "No Public Key": "No Public Key", "No Transactions Yet": "No Transactions Yet", From 66ba7083cd32fd3d82e9e979109a6861ac6acf2b Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Wed, 26 Jun 2019 17:53:51 +0200 Subject: [PATCH 03/25] :recycle: Finish initial logic of add new bookmark --- .../bookmarks/addBookmark/addBookmark.js | 65 ++++++++++++++++--- src/components/bookmarks/addBookmark/index.js | 10 ++- src/store/reducers/bookmarks.js | 2 +- 3 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/components/bookmarks/addBookmark/addBookmark.js b/src/components/bookmarks/addBookmark/addBookmark.js index 7a8c3c7bae..7c39e68969 100644 --- a/src/components/bookmarks/addBookmark/addBookmark.js +++ b/src/components/bookmarks/addBookmark/addBookmark.js @@ -7,6 +7,9 @@ import { InputV2 } from '../../toolbox/inputsV2'; import { PrimaryButtonV2 } from '../../toolbox/buttons/button'; import Feedback from '../../toolbox/feedback/feedback'; import styles from './addBookmark.css'; +import { getIndexOfBookmark } from '../../../utils/bookmarks'; +import { tokenMap } from '../../../constants/tokens'; +import routes from '../../../constants/routes'; class AddBookmark extends React.Component { constructor(props) { @@ -27,6 +30,7 @@ class AddBookmark extends React.Component { value: '', error: false, feedback: field.feedback || '', + readonly: false, }, }), {}); @@ -38,11 +42,22 @@ class AddBookmark extends React.Component { address: this.onAddressChange.bind(this), label: this.onLabelChange.bind(this), }; + this.handleAddBookmark = this.handleAddBookmark.bind(this); } componentDidUpdate(prevProps) { - const { token } = this.props; + const { token, accounts } = this.props; const { token: prevToken } = prevProps; + const { fields } = this.state; + + const account = accounts[fields.address.value] || {}; + if (account.delegate && account.delegate.username !== fields.label.value) { + this.updateField({ + name: 'label', + data: { value: account.delegate.username, readonly: true }, + }); + } + if (token.active === prevToken.active) { return true; } @@ -86,9 +101,18 @@ class AddBookmark extends React.Component { return { feedback, error }; } + checkDelegate(value) { + const { searchAccount } = this.props; + searchAccount({ address: value }); + } + onAddressChange({ target: { name, value } }) { const { token: { active } } = this.props; const { feedback, error } = this.validateAddress(active, value); + if (active === tokenMap.LSK.key && !error) { + this.checkDelegate(value); + } + this.updateField({ name, data: { error, value, feedback }, @@ -96,20 +120,41 @@ class AddBookmark extends React.Component { } validateAddress(token, value) { - const { network } = this.props; + const { network, bookmarks } = this.props; const netCode = network.name === networks.mainnet.name ? networks.mainnet.code : networks.testnet.code; - const error = validateAddress(token, value, netCode) === 1; - const feedback = error - ? 'Invalid address.' - : ''; - return { error, feedback }; + const isInvalid = validateAddress(token, value, netCode) === 1; + const alreadyBookmarked = !isInvalid + && getIndexOfBookmark(bookmarks, { address: value, token }) !== -1; + const feedback = + (isInvalid && 'Invalid address.') + || (alreadyBookmarked && 'Address already bookmarked.') + || ''; + return { error: isInvalid || alreadyBookmarked, feedback }; + } + + handleAddBookmark(e) { + e.preventDefault(); + const { token: { active }, bookmarkAdded, accounts } = this.props; + const { fields: { label, address } } = this.state; + const publicKey = active === tokenMap.LSK.key + ? accounts[address.value].publicKey + : null; + const account = { + title: label.value, + address: address.value, + publicKey, + }; + bookmarkAdded({ token: active, account }); + this.props.history.push(routes.bookmarks.path); } render() { const { t } = this.props; const { fields } = this.state; + const isDisabled = !!Object.keys(fields).find(field => fields[field].error || fields[field].value === ''); + return (
@@ -133,6 +178,7 @@ class AddBookmark extends React.Component { value={fields[field.name].value} onChange={this.onInputChange[field.name]} name={field.name} + readOnly={field.readonly} /> ))}
- + {t('Add bookmark')}
diff --git a/src/components/bookmarks/addBookmark/index.js b/src/components/bookmarks/addBookmark/index.js index 47a56772ee..7ba9b6a4df 100644 --- a/src/components/bookmarks/addBookmark/index.js +++ b/src/components/bookmarks/addBookmark/index.js @@ -1,12 +1,20 @@ /* istanbul ignore file */ import { connect } from 'react-redux'; import { translate } from 'react-i18next'; +import { searchAccount } from '../../../actions/search'; +import { bookmarkAdded } from '../../../actions/bookmarks'; import AddBookmark from './addBookmark'; const mapStateToProps = state => ({ bookmarks: state.bookmarks, token: state.settings.token, network: state.network, + accounts: state.search.accounts, }); -export default connect(mapStateToProps)(translate()(AddBookmark)); +const mapDispatchToProps = { + searchAccount, + bookmarkAdded, +}; + +export default connect(mapStateToProps, mapDispatchToProps)(translate()(AddBookmark)); diff --git a/src/store/reducers/bookmarks.js b/src/store/reducers/bookmarks.js index d257c4fe07..745c7c2a8e 100644 --- a/src/store/reducers/bookmarks.js +++ b/src/store/reducers/bookmarks.js @@ -7,10 +7,10 @@ const bookmarks = (state = getBookmarksFromLocalStorage(), action) => { return { ...state, [action.data.token]: [ - ...state[action.data.token], { ...action.data.account, }, + ...state[action.data.token], ], }; From 4af70c2bb09926617de08d3e6ffd0af25b2cd0fb Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Wed, 26 Jun 2019 22:07:38 +0200 Subject: [PATCH 04/25] :recycle: Refactor add new bookmark component --- .../bookmarks/addBookmark/addBookmark.js | 46 +++++++++---------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/src/components/bookmarks/addBookmark/addBookmark.js b/src/components/bookmarks/addBookmark/addBookmark.js index 7c39e68969..b275350330 100644 --- a/src/components/bookmarks/addBookmark/addBookmark.js +++ b/src/components/bookmarks/addBookmark/addBookmark.js @@ -48,21 +48,22 @@ class AddBookmark extends React.Component { componentDidUpdate(prevProps) { const { token, accounts } = this.props; const { token: prevToken } = prevProps; - const { fields } = this.state; + const { fields: { label, address } } = this.state; - const account = accounts[fields.address.value] || {}; - if (account.delegate && account.delegate.username !== fields.label.value) { + const account = accounts[address.value] || {}; + if (account.delegate && account.delegate.username !== label.value) { this.updateField({ name: 'label', data: { value: account.delegate.username, readonly: true }, }); } - if (token.active === prevToken.active) { - return true; + if (token.active !== prevToken.active) { + this.updateField({ + name: 'address', + data: { ...this.validateAddress(token.active, address.value) }, + }); } - this.revalidate(token.active); - return false; } updateField({ name, data }) { @@ -77,12 +78,6 @@ class AddBookmark extends React.Component { })); } - revalidate(token) { - const { fields: { label, address } } = this.state; - this.updateField({ name: 'address', data: { ...this.validateAddress(token, address.value) } }); - this.updateField({ name: 'label', data: { ...this.validateLabel(label.value) } }); - } - onLabelChange({ target: { name, value } }) { const { error, feedback } = this.validateLabel(value); this.updateField({ @@ -101,7 +96,7 @@ class AddBookmark extends React.Component { return { feedback, error }; } - checkDelegate(value) { + searchAccount(value) { const { searchAccount } = this.props; searchAccount({ address: value }); } @@ -109,8 +104,8 @@ class AddBookmark extends React.Component { onAddressChange({ target: { name, value } }) { const { token: { active } } = this.props; const { feedback, error } = this.validateAddress(active, value); - if (active === tokenMap.LSK.key && !error) { - this.checkDelegate(value); + if (active === tokenMap.LSK.key && !error && value.length) { + this.searchAccount(value); } this.updateField({ @@ -138,15 +133,16 @@ class AddBookmark extends React.Component { e.preventDefault(); const { token: { active }, bookmarkAdded, accounts } = this.props; const { fields: { label, address } } = this.state; - const publicKey = active === tokenMap.LSK.key - ? accounts[address.value].publicKey - : null; - const account = { - title: label.value, - address: address.value, - publicKey, - }; - bookmarkAdded({ token: active, account }); + const { publicKey, delegate } = accounts[address.value]; + bookmarkAdded({ + token: active, + account: { + title: label.value, + address: address.value, + isDelegate: !!(delegate && delegate.username), + publicKey, + }, + }); this.props.history.push(routes.bookmarks.path); } From 13d2f4dd13950bd6cd677844aef1e1be9d2a9240 Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Thu, 27 Jun 2019 12:00:28 +0200 Subject: [PATCH 05/25] :recycle: Add placeholder prop to accountVisual --- src/components/accountVisual/accountVisual.css | 8 ++++++++ src/components/accountVisual/index.js | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/components/accountVisual/accountVisual.css b/src/components/accountVisual/accountVisual.css index 463e3ed40f..4f94f4e060 100644 --- a/src/components/accountVisual/accountVisual.css +++ b/src/components/accountVisual/accountVisual.css @@ -1,3 +1,5 @@ +@import '../app/variablesV2.css'; + .wrapper { display: inline-block; border-radius: 100%; @@ -9,3 +11,9 @@ overflow: hidden; display: inline-block; } + +.placeholder { + background-color: var(--color-platinum); + border-radius: 50%; + display: inline-block; +} diff --git a/src/components/accountVisual/index.js b/src/components/accountVisual/index.js index ee454515ee..f1759de32a 100644 --- a/src/components/accountVisual/index.js +++ b/src/components/accountVisual/index.js @@ -181,7 +181,8 @@ class AccountVisual extends React.Component { shouldComponentUpdate(nextProps, state) { return this.state.isSBreakpoint !== state.isSBreakpoint - || nextProps.address !== this.props.address; + || nextProps.address !== this.props.address + || nextProps.placeholder !== this.props.placeholder; } resizeWindow() { @@ -202,12 +203,21 @@ class AccountVisual extends React.Component { render() { // eslint-disable-line max-statements const { - address, size, sizeS, className, + address, size, sizeS, className, placeholder, } = this.props; const sizeL = size || 200; const newSize = this.state.isSBreakpoint && sizeS ? sizeS : sizeL; + if (placeholder) { + return ( + + ); + } + if (!reg.address.test(address)) { return Date: Thu, 27 Jun 2019 12:01:28 +0200 Subject: [PATCH 06/25] :recycle: Adjust input toolbox component to have error prop in favor of global className --- src/components/toolbox/inputsV2/inputV2.css | 3 ++- src/components/toolbox/inputsV2/inputV2.js | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/components/toolbox/inputsV2/inputV2.css b/src/components/toolbox/inputsV2/inputV2.css index 004cae1b70..f512a29cc9 100644 --- a/src/components/toolbox/inputsV2/inputV2.css +++ b/src/components/toolbox/inputsV2/inputV2.css @@ -50,7 +50,8 @@ border-color: var(--color-primary-standard); } - &:global(.error) { + &:global(.error), /* TODO: Remove after all inputs use props */ + &.error { border-color: var(--color-burnt-sienna); color: var(--color-burnt-sienna); } diff --git a/src/components/toolbox/inputsV2/inputV2.js b/src/components/toolbox/inputsV2/inputV2.js index b6e2fcc827..9da39bea63 100644 --- a/src/components/toolbox/inputsV2/inputV2.js +++ b/src/components/toolbox/inputsV2/inputV2.js @@ -2,11 +2,23 @@ import React from 'react'; import styles from './inputV2.css'; const InputV2 = ({ - className = '', - setRef = null, + className, + setRef, size, + error, ...props }) => - ; + ; + +InputV2.defaultProps = { + className: '', + setRef: null, + error: false, + size: 'l', +}; export default InputV2; From c122e90c349460281486fea9748706dfbe311455 Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Thu, 27 Jun 2019 12:02:02 +0200 Subject: [PATCH 07/25] :recycle: Add avatar to field and improve revalidation logic when updating component --- .../bookmarks/addBookmark/addBookmark.css | 15 +++ .../bookmarks/addBookmark/addBookmark.js | 110 +++++++++++++----- 2 files changed, 99 insertions(+), 26 deletions(-) diff --git a/src/components/bookmarks/addBookmark/addBookmark.css b/src/components/bookmarks/addBookmark/addBookmark.css index 1b1714b385..d9de18b7fb 100644 --- a/src/components/bookmarks/addBookmark/addBookmark.css +++ b/src/components/bookmarks/addBookmark/addBookmark.css @@ -37,6 +37,17 @@ } } + & .fieldGroup { + display: block; + position: relative; + + & .avatar { + left: 16px; + position: absolute; + top: 12px; + } + } + & label { display: block; margin-bottom: 24px; @@ -49,6 +60,10 @@ margin-bottom: 12px; } + & .input[name="address"] { + padding-left: 48px; + } + & .input:focus ~ .feedback { opacity: 1 !important; } diff --git a/src/components/bookmarks/addBookmark/addBookmark.js b/src/components/bookmarks/addBookmark/addBookmark.js index b275350330..2ce046d0e4 100644 --- a/src/components/bookmarks/addBookmark/addBookmark.js +++ b/src/components/bookmarks/addBookmark/addBookmark.js @@ -10,6 +10,7 @@ import styles from './addBookmark.css'; import { getIndexOfBookmark } from '../../../utils/bookmarks'; import { tokenMap } from '../../../constants/tokens'; import routes from '../../../constants/routes'; +import AccountVisual from '../../accountVisual'; class AddBookmark extends React.Component { constructor(props) { @@ -18,10 +19,12 @@ class AddBookmark extends React.Component { this.fields = [{ name: 'address', label: props.t('Address'), + placeholder: props.t('Insert public address'), }, { name: 'label', label: props.t('Label'), feedback: props.t('Max. 20 characters'), + placeholder: props.t('Insert label'), }]; const fields = this.fields.reduce((acc, field) => ({ @@ -46,26 +49,56 @@ class AddBookmark extends React.Component { } componentDidUpdate(prevProps) { - const { token, accounts } = this.props; + const { token } = this.props; const { token: prevToken } = prevProps; + + this.updateLabelIfDelegate(); + + if (token.active !== prevToken.active) { + this.onTokenChange(); + } + } + + updateLabelIfDelegate() { + const { token, accounts } = this.props; const { fields: { label, address } } = this.state; const account = accounts[address.value] || {}; if (account.delegate && account.delegate.username !== label.value) { + const data = token.active === tokenMap.LSK.key + ? { value: account.delegate.username, readonly: true } + : { readonly: false }; this.updateField({ name: 'label', - data: { value: account.delegate.username, readonly: true }, + data, }); - } - - if (token.active !== prevToken.active) { + } else if (!account.delegate && label.readonly) { this.updateField({ - name: 'address', - data: { ...this.validateAddress(token.active, address.value) }, + name: 'label', + data: { readonly: false }, }); } } + onTokenChange() { + const { token, accounts } = this.props; + const { fields: { address } } = this.state; + const account = accounts[address.value] || {}; + + if (token.active === tokenMap.LSK.key && !account.address) { + this.searchAccount(address.value); + } + + this.updateField({ + name: 'address', + data: { ...this.validateAddress(token.active, address.value) }, + }); + this.updateField({ + name: 'label', + data: { readonly: token.active === tokenMap.LSK.key }, + }); + } + updateField({ name, data }) { this.setState(({ fields }) => ({ fields: { @@ -82,7 +115,12 @@ class AddBookmark extends React.Component { const { error, feedback } = this.validateLabel(value); this.updateField({ name, - data: { error, value, feedback }, + data: { + error, + value, + feedback, + readonly: false, + }, }); } @@ -92,7 +130,7 @@ class AddBookmark extends React.Component { const error = value.length > maxLength; const feedback = !error ? t('Max. 20 characters') - : t('Nickname is too long.'); + : t('Label is too long.'); return { feedback, error }; } @@ -103,14 +141,19 @@ class AddBookmark extends React.Component { onAddressChange({ target: { name, value } }) { const { token: { active } } = this.props; - const { feedback, error } = this.validateAddress(active, value); + const { feedback, error, isInvalid } = this.validateAddress(active, value); if (active === tokenMap.LSK.key && !error && value.length) { this.searchAccount(value); } this.updateField({ name, - data: { error, value, feedback }, + data: { + error, + value, + feedback, + isInvalid, + }, }); } @@ -126,7 +169,7 @@ class AddBookmark extends React.Component { (isInvalid && 'Invalid address.') || (alreadyBookmarked && 'Address already bookmarked.') || ''; - return { error: isInvalid || alreadyBookmarked, feedback }; + return { error: isInvalid || alreadyBookmarked, isInvalid, feedback }; } handleAddBookmark(e) { @@ -169,20 +212,35 @@ class AddBookmark extends React.Component { {field.label} - - - {fields[field.name].feedback} - + + {field.name === 'address' + ? ( + + ) : null + } + + + {fields[field.name].feedback} + + ))}
From 2d8650d24d1f8ee13692589fe07dff3c4b7743be Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Thu, 27 Jun 2019 12:02:15 +0200 Subject: [PATCH 08/25] :recycle: Update generated strings --- i18n/locales/en/common.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/i18n/locales/en/common.json b/i18n/locales/en/common.json index ba299f1520..fb28025075 100644 --- a/i18n/locales/en/common.json +++ b/i18n/locales/en/common.json @@ -233,6 +233,8 @@ "Incoming transactions": "Incoming transactions", "Info": "Info", "Initialize Lisk ID": "Initialize Lisk ID", + "Insert label": "Insert label", + "Insert public address": "Insert public address", "Insert public address or a name": "Insert public address or a name", "Insert the amount of transaction": "Insert the amount of transaction", "Install update": "Install update", @@ -254,6 +256,7 @@ "LSK forged": "LSK forged", "LSK received": "LSK received", "Label": "Label", + "Label is too long.": "Label is too long.", "Last Forged Block": "Last Forged Block", "Later": "Later", "Learn about blockchain with our comprehensive knowledge base.": "Learn about blockchain with our comprehensive knowledge base.", @@ -312,7 +315,6 @@ "New bookmark": "New bookmark", "New to Lisk? ": "New to Lisk? ", "Next": "Next", - "Nickname is too long.": "Nickname is too long.", "No Bookmarks added yet": "No Bookmarks added yet", "No Public Key": "No Public Key", "No Transactions Yet": "No Transactions Yet", From ef2f20091d3d430aadccd5fc5d1e0e60e68ac155 Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Thu, 27 Jun 2019 14:12:34 +0200 Subject: [PATCH 09/25] :recycle: Add autocomplete=off to inputs on add Bookmark --- src/components/bookmarks/addBookmark/addBookmark.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/bookmarks/addBookmark/addBookmark.js b/src/components/bookmarks/addBookmark/addBookmark.js index 2ce046d0e4..995d3e36b4 100644 --- a/src/components/bookmarks/addBookmark/addBookmark.js +++ b/src/components/bookmarks/addBookmark/addBookmark.js @@ -232,6 +232,7 @@ class AddBookmark extends React.Component { placeholder={field.placeholder} readOnly={fields[field.name].readonly} size={'l'} + autoComplete={'off'} /> Date: Thu, 27 Jun 2019 15:11:48 +0200 Subject: [PATCH 10/25] :fire: Remove unused component --- src/components/addressInput/addressInput.css | 13 --------- src/components/addressInput/index.js | 29 -------------------- 2 files changed, 42 deletions(-) delete mode 100644 src/components/addressInput/addressInput.css delete mode 100644 src/components/addressInput/index.js diff --git a/src/components/addressInput/addressInput.css b/src/components/addressInput/addressInput.css deleted file mode 100644 index 5f51d996f3..0000000000 --- a/src/components/addressInput/addressInput.css +++ /dev/null @@ -1,13 +0,0 @@ -.input:first-of-type .label { - padding-left: 60px; -} - -.input input:first-of-type { - padding-left: 60px; -} - -.accountVisualInInput { - position: absolute; - left: -40px; - top: -15px; -} diff --git a/src/components/addressInput/index.js b/src/components/addressInput/index.js deleted file mode 100644 index 9457d3af41..0000000000 --- a/src/components/addressInput/index.js +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import ToolBoxInput from '../toolbox/inputs/toolBoxInput'; -import AccountVisual from '../accountVisual'; -import styles from './addressInput.css'; - -const AddressInput = ({ - handleChange, className, address, label, -}) => { - const showAccountVisual = address.value.length && !address.error; - - return handleChange(val)} - theme={showAccountVisual ? styles : {}} - > - {showAccountVisual - ?
- -
- : null - } -
; -}; - -export default AddressInput; From d7477a6dd14cd39f41da27e8f8622d2238870ff8 Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Thu, 27 Jun 2019 15:29:08 +0200 Subject: [PATCH 11/25] :white_check_mark: Adjust inputV2 unit tests --- .../toolbox/inputsV2/inputV2.test.js | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/components/toolbox/inputsV2/inputV2.test.js b/src/components/toolbox/inputsV2/inputV2.test.js index 157b28b0c1..7ab5952613 100644 --- a/src/components/toolbox/inputsV2/inputV2.test.js +++ b/src/components/toolbox/inputsV2/inputV2.test.js @@ -1,23 +1,21 @@ import React from 'react'; -import { expect } from 'chai'; -import { spy } from 'sinon'; import { mount } from 'enzyme'; import InputV2 from './inputV2'; describe('InputV2', () => { let wrapper; const props = { - className: '', + className: 'test', defaultValue: 'test', - onChange: spy(), + onChange: jest.fn(), }; it('should render with passed props', () => { wrapper = mount(); - expect(wrapper).to.have.className(props.className); - expect(wrapper.html()).to.be.contain(props.defaultValue); + expect(wrapper.find('input')).toHaveValue(props.defaultValue); + expect(wrapper.find('input')).toHaveClassName(props.className); wrapper.simulate('change', { target: { value: 'test' } }); - expect(props.onChange).to.have.been.calledWith(); + expect(props.onChange).toBeCalled(); }); it('should pass size as className ', () => { @@ -26,6 +24,14 @@ describe('InputV2', () => { size: 'l', }; wrapper = mount(); - expect(wrapper).to.have.className('l'); + expect(wrapper.find('input')).toHaveClassName('l'); + }); + + it('Should render with error class', () => { + const propWithError = { + error: true, + }; + wrapper = mount(); + expect(wrapper.find('input')).toHaveClassName('error'); }); }); From ef9d2354153fe25a2a7c1a5a748e531a5fbc11c5 Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Thu, 27 Jun 2019 17:04:16 +0200 Subject: [PATCH 12/25] :recycle: Fix issue when adding BTC account --- src/components/bookmarks/addBookmark/addBookmark.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/bookmarks/addBookmark/addBookmark.js b/src/components/bookmarks/addBookmark/addBookmark.js index 995d3e36b4..ababf4b960 100644 --- a/src/components/bookmarks/addBookmark/addBookmark.js +++ b/src/components/bookmarks/addBookmark/addBookmark.js @@ -176,7 +176,7 @@ class AddBookmark extends React.Component { e.preventDefault(); const { token: { active }, bookmarkAdded, accounts } = this.props; const { fields: { label, address } } = this.state; - const { publicKey, delegate } = accounts[address.value]; + const { publicKey, delegate } = accounts[address.value] || {}; bookmarkAdded({ token: active, account: { From 54039392aeabdf9b4780231cf1e27a38ebac43e1 Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Thu, 27 Jun 2019 17:35:20 +0200 Subject: [PATCH 13/25] :white_check_mark: Add unit test for AddBookmark component --- .../bookmarks/addBookmark/addBookmark.test.js | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 src/components/bookmarks/addBookmark/addBookmark.test.js diff --git a/src/components/bookmarks/addBookmark/addBookmark.test.js b/src/components/bookmarks/addBookmark/addBookmark.test.js new file mode 100644 index 0000000000..92d08b2f8b --- /dev/null +++ b/src/components/bookmarks/addBookmark/addBookmark.test.js @@ -0,0 +1,157 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import { tokenMap, tokenKeys } from '../../../constants/tokens'; +import networks from '../../../constants/networks'; +import accounts from '../../../../test/constants/accounts'; +import AddBookmark from './addBookmark'; + +describe('Add a new bookmark component', () => { + const bookmarks = { + LSK: [], + BTC: [], + }; + const props = { + t: v => v, + token: { + active: tokenMap.LSK.key, + }, + bookmarks, + network: networks.testnet, + history: { + push: jest.fn(), + }, + accounts: {}, + bookmarkAdded: jest.fn(), + searchAccount: jest.fn(), + }; + const addresses = { + BTC: 'mkakDp2f31btaXdATtAogoqwXcdx1PqqFo', + LSK: accounts.genesis.address, + }; + + let wrapper; + + beforeEach(() => { + wrapper = mount(); + }); + + + afterEach(() => { + props.history.push.mockClear(); + props.bookmarkAdded.mockClear(); + props.searchAccount.mockReset(); + }); + + it('Should render properly and with pristine state', () => { + expect(wrapper).not.toContainMatchingElement('.error'); + expect(wrapper).toContainMatchingElements(2, 'InputV2'); + expect(wrapper.find('button').at(0)).toBeDisabled(); + }); + + describe('Success scenarios', () => { + tokenKeys.forEach((token) => { + it(`Should add ${token} account`, () => { + wrapper.setProps({ token: { active: token } }); + wrapper.find('input[name="address"]').first().simulate('change', { + target: { + value: addresses[token], + name: 'address', + }, + }); + wrapper.find('input[name="label"]').at(0).simulate('change', { + target: { + value: `label-${token}`, + name: 'label', + }, + }); + expect(wrapper).not.toContainMatchingElement('.error'); + expect(wrapper.find('button').at(0)).not.toBeDisabled(); + wrapper.find('button').at(0).simulate('click'); + expect(props.bookmarkAdded).toBeCalled(); + expect(props.history.push).toBeCalled(); + }); + }); + + it('should not be possible to change delegate label', () => { + props.searchAccount.mockImplementation(({ address }) => { + const account = { address, delegate: { username: accounts.delegate.username } }; + wrapper.setProps({ accounts: { ...props.accounts, [address]: account } }); + }); + wrapper.find('input[name="address"]').first().simulate('change', { + target: { + value: accounts.delegate.address, + name: 'address', + }, + }); + expect(wrapper.find('input[name="label"]')).toHaveValue(accounts.delegate.username); + expect(wrapper.find('input[name="label"]')).toHaveProp('readOnly', true); + expect(wrapper.find('button').at(0)).not.toBeDisabled(); + wrapper.find('button').at(0).simulate('click'); + }); + }); + + describe('Fail scenarios', () => { + beforeEach(() => { + wrapper.setProps({ + bookmarks: { + LSK: [{ address: addresses.LSK, title: 'genesis' }], + BTC: [{ address: addresses.BTC, title: 'btc' }], + }, + }); + }); + + tokenKeys.forEach((token) => { + it(`should not be possible to add already bookmarkd address - ${token}`, () => { + wrapper.find('input[name="address"]').first().simulate('change', { + target: { + value: addresses[token], + name: 'address', + }, + }); + expect(wrapper.find('input[name="address"]')).toHaveClassName('error'); + expect(wrapper).toContainMatchingElement('.error'); + }); + + it(`should show error on invalid address - ${token}`, () => { + wrapper.find('input[name="address"]').first().simulate('change', { + target: { + value: 'invalidAddress', + name: 'address', + }, + }); + expect(wrapper.find('input[name="address"]')).toHaveClassName('error'); + expect(wrapper).toContainMatchingElement('.error'); + }); + + it(`should show error on label too long - ${token}`, () => { + wrapper.find('input[name="label"]').first().simulate('change', { + target: { + value: 'Really long bookmark name', + name: 'label', + }, + }); + expect(wrapper.find('input[name="label"]')).toHaveClassName('error'); + expect(wrapper).toContainMatchingElement('.error'); + }); + }); + }); + + describe('Token switching validation', () => { + it('Should revalidate each time active token is changed', () => { + wrapper.setProps({ token: { active: tokenMap.LSK.key } }); + wrapper.find('input[name="address"]').first().simulate('change', { + target: { + value: accounts.delegate.address, + name: 'address', + }, + }); + expect(wrapper.find('input[name="address"]')).not.toHaveClassName('error'); + wrapper.setProps({ token: { active: tokenMap.BTC.key } }); + wrapper.update(); + expect(wrapper.find('input[name="address"]')).toHaveClassName('error'); + wrapper.setProps({ token: { active: tokenMap.LSK.key } }); + wrapper.update(); + expect(wrapper.find('input[name="address"]')).not.toHaveClassName('error'); + }); + }); +}); From 12cbab2910b3e515214b00d5492f323b35b46c29 Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Fri, 28 Jun 2019 10:22:52 +0200 Subject: [PATCH 14/25] :nail_care: Update button to use variable for width --- src/components/bookmarks/addBookmark/addBookmark.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/bookmarks/addBookmark/addBookmark.css b/src/components/bookmarks/addBookmark/addBookmark.css index d9de18b7fb..aa672d7f1d 100644 --- a/src/components/bookmarks/addBookmark/addBookmark.css +++ b/src/components/bookmarks/addBookmark/addBookmark.css @@ -29,7 +29,7 @@ & > .buttonHolder { display: flex; margin: 16px auto 30px; - width: 390px; + width: var(--footer-button-width); & button { display: block; From 95795b4663b643d252464a1b153c84b46a71eda7 Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Fri, 28 Jun 2019 10:24:36 +0200 Subject: [PATCH 15/25] :recycle: Removed curly braces from jsx where possible --- src/components/bookmarks/addBookmark/addBookmark.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/bookmarks/addBookmark/addBookmark.js b/src/components/bookmarks/addBookmark/addBookmark.js index ababf4b960..6e99fb198e 100644 --- a/src/components/bookmarks/addBookmark/addBookmark.js +++ b/src/components/bookmarks/addBookmark/addBookmark.js @@ -232,11 +232,11 @@ class AddBookmark extends React.Component { placeholder={field.placeholder} readOnly={fields[field.name].readonly} size={'l'} - autoComplete={'off'} + autoComplete="off" /> {fields[field.name].feedback} From 26c2b387fdb17e7362b718404a57ddb854af9203 Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Fri, 28 Jun 2019 11:29:04 +0200 Subject: [PATCH 16/25] :recycle: Change token change behaviour to clear inputs --- .../bookmarks/addBookmark/addBookmark.js | 62 +++++++------------ 1 file changed, 21 insertions(+), 41 deletions(-) diff --git a/src/components/bookmarks/addBookmark/addBookmark.js b/src/components/bookmarks/addBookmark/addBookmark.js index 6e99fb198e..c4d73f765c 100644 --- a/src/components/bookmarks/addBookmark/addBookmark.js +++ b/src/components/bookmarks/addBookmark/addBookmark.js @@ -27,18 +27,8 @@ class AddBookmark extends React.Component { placeholder: props.t('Insert label'), }]; - const fields = this.fields.reduce((acc, field) => ({ - ...acc, - [field.name]: { - value: '', - error: false, - feedback: field.feedback || '', - readonly: false, - }, - }), {}); - this.state = { - fields, + fields: this.setupFields(), }; this.onInputChange = { @@ -48,6 +38,19 @@ class AddBookmark extends React.Component { this.handleAddBookmark = this.handleAddBookmark.bind(this); } + setupFields() { + return this.fields.reduce((acc, field) => ({ + ...acc, + [field.name]: { + value: '', + error: false, + feedback: field.feedback || '', + readonly: false, + }, + }), {}); + } + + componentDidUpdate(prevProps) { const { token } = this.props; const { token: prevToken } = prevProps; @@ -55,7 +58,10 @@ class AddBookmark extends React.Component { this.updateLabelIfDelegate(); if (token.active !== prevToken.active) { - this.onTokenChange(); + this.setState(state => ({ + ...state, + fields: this.setupFields(), + })); } } @@ -63,42 +69,16 @@ class AddBookmark extends React.Component { const { token, accounts } = this.props; const { fields: { label, address } } = this.state; - const account = accounts[address.value] || {}; + const account = (token.active === tokenMap.LSK.key && accounts[address.value]) || {}; if (account.delegate && account.delegate.username !== label.value) { - const data = token.active === tokenMap.LSK.key - ? { value: account.delegate.username, readonly: true } - : { readonly: false }; + const data = { value: account.delegate.username, readonly: true }; this.updateField({ name: 'label', data, }); - } else if (!account.delegate && label.readonly) { - this.updateField({ - name: 'label', - data: { readonly: false }, - }); } } - onTokenChange() { - const { token, accounts } = this.props; - const { fields: { address } } = this.state; - const account = accounts[address.value] || {}; - - if (token.active === tokenMap.LSK.key && !account.address) { - this.searchAccount(address.value); - } - - this.updateField({ - name: 'address', - data: { ...this.validateAddress(token.active, address.value) }, - }); - this.updateField({ - name: 'label', - data: { readonly: token.active === tokenMap.LSK.key }, - }); - } - updateField({ name, data }) { this.setState(({ fields }) => ({ fields: { @@ -235,7 +215,7 @@ class AddBookmark extends React.Component { autoComplete="off" /> From d3d139e09335bf8f8ebb579c7088e1bbf933183b Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Fri, 28 Jun 2019 12:13:14 +0200 Subject: [PATCH 17/25] :nail_care: Fix issue with dropdown on dark mode --- .../toolbox/dropdownV2/dropdownV2.css | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/components/toolbox/dropdownV2/dropdownV2.css b/src/components/toolbox/dropdownV2/dropdownV2.css index 7277edbb80..8c83d8c237 100644 --- a/src/components/toolbox/dropdownV2/dropdownV2.css +++ b/src/components/toolbox/dropdownV2/dropdownV2.css @@ -60,26 +60,28 @@ background: var(--color-maastricht-blue); border: 1px solid var(--light-border-color); - & .option { - background: var(--color-maastricht-blue); - - &.active { - background-color: var(--color-maastricht-blue); - color: #4070f4; - } - - &:hover { - background-color: rgba(64, 112, 244, 0.24); - color: #4070f4; - } - - & input { - @mixin contentNormal; - - border: 1px solid var(--light-border-color); - background-color: var(--color-maastricht-blue); - color: var(--color-white); - padding: 0 20px 0 10px; + & .dropdownContent { + & .option { + background: var(--color-maastricht-blue); + + &.active { + background-color: var(--color-maastricht-blue); + color: #4070f4; + } + + &:hover { + background-color: rgba(64, 112, 244, 0.24); + color: #4070f4; + } + + & input { + @mixin contentNormal; + + border: 1px solid var(--light-border-color); + background-color: var(--color-maastricht-blue); + color: var(--color-white); + padding: 0 20px 0 10px; + } } } } From 190afb68eb73710e78820410d32fa4e781458e3e Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Fri, 28 Jun 2019 12:13:44 +0200 Subject: [PATCH 18/25] :recycle: Adjust feedback component to not use global class --- src/components/toolbox/feedback/feedback.css | 15 +++++++++------ src/components/toolbox/feedback/feedback.js | 11 ++++++++++- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/components/toolbox/feedback/feedback.css b/src/components/toolbox/feedback/feedback.css index a15514889b..3585bbb012 100644 --- a/src/components/toolbox/feedback/feedback.css +++ b/src/components/toolbox/feedback/feedback.css @@ -8,25 +8,28 @@ box-sizing: border-box; color: var(--color-slate-gray); display: flex; + height: auto; justify-content: flex-start; max-height: 0px; opacity: 0; - padding: 0 20px; + padding: 0; transition: max-height var(--animation-speed-fast) linear, - opacity var(--animation-speed-fast) linear; + opacity var(--animation-speed-fast) linear, + padding-bottom var(--animation-speed-fast) linear, + padding-top var(--animation-speed-fast) linear; width: 100%; - &:global(.error) { - color: var(--color-burnt-sienna); + &.error { background: var(--error-background-color); + color: var(--color-burnt-sienna); + padding: 8px 20px; } &.show { max-height: 40px; opacity: 1; - padding-bottom: 8px; - padding-top: 8px; + padding: 8px 20px; } & > img { diff --git a/src/components/toolbox/feedback/feedback.js b/src/components/toolbox/feedback/feedback.js index ebff2dc4e6..12e71baefb 100644 --- a/src/components/toolbox/feedback/feedback.js +++ b/src/components/toolbox/feedback/feedback.js @@ -7,8 +7,17 @@ const Feedback = ({ showIcon, status, children, show, className, dark, }) => { const icon = status === 'error' && svg.alert_icon; + const classNames = [ + dark && styles.dark, + className, + styles.feedback, + show && styles.show, + !!status && styles[status], + ].filter(name => name).join(' '); return ( - + {showIcon && icon && } {children} From f8accafd166890e750d37134dcd72e0936db7925 Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Fri, 28 Jun 2019 12:14:41 +0200 Subject: [PATCH 19/25] :recycle: Remove default props from input so it not show wrongly in other places --- src/components/toolbox/inputsV2/inputV2.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/toolbox/inputsV2/inputV2.js b/src/components/toolbox/inputsV2/inputV2.js index 9da39bea63..50fc8b6267 100644 --- a/src/components/toolbox/inputsV2/inputV2.js +++ b/src/components/toolbox/inputsV2/inputV2.js @@ -18,7 +18,6 @@ InputV2.defaultProps = { className: '', setRef: null, error: false, - size: 'l', }; export default InputV2; From 8f6d17d8cfe30f31df9e6e2168f0488f523197a5 Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Fri, 28 Jun 2019 12:15:10 +0200 Subject: [PATCH 20/25] :recycle: Adjust how feedback component is used on add bookmark --- src/components/bookmarks/addBookmark/addBookmark.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/bookmarks/addBookmark/addBookmark.js b/src/components/bookmarks/addBookmark/addBookmark.js index c4d73f765c..be79d88964 100644 --- a/src/components/bookmarks/addBookmark/addBookmark.js +++ b/src/components/bookmarks/addBookmark/addBookmark.js @@ -215,8 +215,7 @@ class AddBookmark extends React.Component { autoComplete="off" /> {fields[field.name].feedback} From b7799c37e53f7cecc04b97b365ffa67a8c107fca Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Fri, 28 Jun 2019 12:16:36 +0200 Subject: [PATCH 21/25] :nail_care: Adjust css to use variables and fix spacings --- src/components/app/variablesV2.css | 1 + src/components/bookmarks/addBookmark/addBookmark.css | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/app/variablesV2.css b/src/components/app/variablesV2.css index 73250a2b47..b236a30ac9 100644 --- a/src/components/app/variablesV2.css +++ b/src/components/app/variablesV2.css @@ -73,6 +73,7 @@ or "warn/action" ineastd of "red/green" --header-logo-size: 56px; --search-box-width-l: 597px; --footer-button-width: 400px; + --medium-box-width: 720px; /************************* Padding diff --git a/src/components/bookmarks/addBookmark/addBookmark.css b/src/components/bookmarks/addBookmark/addBookmark.css index aa672d7f1d..40997f1c75 100644 --- a/src/components/bookmarks/addBookmark/addBookmark.css +++ b/src/components/bookmarks/addBookmark/addBookmark.css @@ -7,7 +7,7 @@ width: 100%; & .content { - width: 720px; + width: var(--medium-box-width); & header { display: flex; @@ -28,7 +28,7 @@ & > .buttonHolder { display: flex; - margin: 16px auto 30px; + margin: 0 auto 30px; width: var(--footer-button-width); & button { @@ -50,7 +50,7 @@ & label { display: block; - margin-bottom: 24px; + margin-bottom: 12px; & > .label { @mixin contentNormal bold; @@ -71,15 +71,14 @@ & .feedback { margin-top: 5px; - min-height: 44px; + min-height: 40px; opacity: 0; - transition: none; &.error { opacity: 1; } - &:not(:global(.error)) { + &:not(.error) { padding-left: 0; padding-right: 0; } From 8237b5e11602178b10491296a49479e61b2145cf Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Fri, 28 Jun 2019 12:18:51 +0200 Subject: [PATCH 22/25] Revert ":fire: Remove unused component" This reverts commit 0aeb87f5b87507d9d8e09d27790e5da63a9d2bf4. --- src/components/addressInput/addressInput.css | 13 +++++++++ src/components/addressInput/index.js | 29 ++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/components/addressInput/addressInput.css create mode 100644 src/components/addressInput/index.js diff --git a/src/components/addressInput/addressInput.css b/src/components/addressInput/addressInput.css new file mode 100644 index 0000000000..5f51d996f3 --- /dev/null +++ b/src/components/addressInput/addressInput.css @@ -0,0 +1,13 @@ +.input:first-of-type .label { + padding-left: 60px; +} + +.input input:first-of-type { + padding-left: 60px; +} + +.accountVisualInInput { + position: absolute; + left: -40px; + top: -15px; +} diff --git a/src/components/addressInput/index.js b/src/components/addressInput/index.js new file mode 100644 index 0000000000..9457d3af41 --- /dev/null +++ b/src/components/addressInput/index.js @@ -0,0 +1,29 @@ +import React from 'react'; +import ToolBoxInput from '../toolbox/inputs/toolBoxInput'; +import AccountVisual from '../accountVisual'; +import styles from './addressInput.css'; + +const AddressInput = ({ + handleChange, className, address, label, +}) => { + const showAccountVisual = address.value.length && !address.error; + + return handleChange(val)} + theme={showAccountVisual ? styles : {}} + > + {showAccountVisual + ?
+ +
+ : null + } +
; +}; + +export default AddressInput; From 320a072c4b7ebdc2c2698e7d31283384b67f4773 Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Fri, 28 Jun 2019 12:22:15 +0200 Subject: [PATCH 23/25] :white_check_mark: Adjust unit test of add bookmark --- .../bookmarks/addBookmark/addBookmark.test.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/components/bookmarks/addBookmark/addBookmark.test.js b/src/components/bookmarks/addBookmark/addBookmark.test.js index 92d08b2f8b..6aa52242dc 100644 --- a/src/components/bookmarks/addBookmark/addBookmark.test.js +++ b/src/components/bookmarks/addBookmark/addBookmark.test.js @@ -136,8 +136,8 @@ describe('Add a new bookmark component', () => { }); }); - describe('Token switching validation', () => { - it('Should revalidate each time active token is changed', () => { + describe('Token switching', () => { + it('Should clear the fields each time active token is changed', () => { wrapper.setProps({ token: { active: tokenMap.LSK.key } }); wrapper.find('input[name="address"]').first().simulate('change', { target: { @@ -145,13 +145,10 @@ describe('Add a new bookmark component', () => { name: 'address', }, }); - expect(wrapper.find('input[name="address"]')).not.toHaveClassName('error'); + expect(wrapper.find('input[name="address"]')).toHaveValue(accounts.delegate.address); wrapper.setProps({ token: { active: tokenMap.BTC.key } }); wrapper.update(); - expect(wrapper.find('input[name="address"]')).toHaveClassName('error'); - wrapper.setProps({ token: { active: tokenMap.LSK.key } }); - wrapper.update(); - expect(wrapper.find('input[name="address"]')).not.toHaveClassName('error'); + expect(wrapper.find('input[name="address"]')).toHaveValue(''); }); }); }); From 1bf70ffde0f4ca866967071616d50618cadd4ac7 Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Fri, 28 Jun 2019 12:22:34 +0200 Subject: [PATCH 24/25] :recycle: Temporary redirect to dashboard --- src/components/bookmarks/addBookmark/addBookmark.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/bookmarks/addBookmark/addBookmark.js b/src/components/bookmarks/addBookmark/addBookmark.js index be79d88964..2df411e0c1 100644 --- a/src/components/bookmarks/addBookmark/addBookmark.js +++ b/src/components/bookmarks/addBookmark/addBookmark.js @@ -166,7 +166,8 @@ class AddBookmark extends React.Component { publicKey, }, }); - this.props.history.push(routes.bookmarks.path); + this.props.history.push(routes.dashboard.path); + // this.props.history.push(routes.bookmarks.path); // TODO: Update with correct path } render() { From 9710a1bd26be79bd39d1d6b060a5f8308477b336 Mon Sep 17 00:00:00 2001 From: Renato Massao Yonamine Date: Fri, 28 Jun 2019 14:05:40 +0200 Subject: [PATCH 25/25] :bug: Fix issue when changing address after using a delegate one --- .../bookmarks/addBookmark/addBookmark.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/components/bookmarks/addBookmark/addBookmark.js b/src/components/bookmarks/addBookmark/addBookmark.js index 2df411e0c1..d0c61cdc96 100644 --- a/src/components/bookmarks/addBookmark/addBookmark.js +++ b/src/components/bookmarks/addBookmark/addBookmark.js @@ -51,11 +51,12 @@ class AddBookmark extends React.Component { } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps, prevState) { const { token } = this.props; const { token: prevToken } = prevProps; + const { fields: { address } } = prevState; - this.updateLabelIfDelegate(); + this.updateLabelIfDelegate(address.value); if (token.active !== prevToken.active) { this.setState(state => ({ @@ -65,17 +66,23 @@ class AddBookmark extends React.Component { } } - updateLabelIfDelegate() { + updateLabelIfDelegate(prevAddress) { const { token, accounts } = this.props; const { fields: { label, address } } = this.state; - const account = (token.active === tokenMap.LSK.key && accounts[address.value]) || {}; + if (address.value === prevAddress && account.delegate) return; + if (account.delegate && account.delegate.username !== label.value) { const data = { value: account.delegate.username, readonly: true }; this.updateField({ name: 'label', data, }); + } else if (label.readonly) { + this.updateField({ + name: 'label', + data: { value: '', readonly: false }, + }); } }