Page MenuHomePhabricator

[Cashtab] Format XEC amount on input
AbandonedPublic

Authored by kieran709 on Dec 30 2021, 17:35.

Details

Reviewers
bytesofman
Group Reviewers
Restricted Project
Summary

In order to avoid confusion when sending XEC in larger amounts, a user requested that any amount over 3 digits be formatted with spaces. To do this, a function, formatXecOnInput, formats the amount. Because the sendBchInput uses a type of number, conditional formatting has been added. When SendBchInput loses focus, a readonly input with the type text replaces it. On focus, the readonly input will trigger the SendBchInput to reappear. Related to task T2098.

Test Plan

cd web/cashtab
navigate to send tab
input a number in the amount field
remove focus from amount input
if the number is longer than 3 digits, ensure formatting is taking place
select the readonly input
ensure SendBchInput renders
input a number that will trigger sendBchAmountError
ensure that inside the readonly input the text color has changed to reflect that there is an error.

Diff Detail

Repository
rABC Bitcoin ABC
Branch
locale-formatting-for-amounts
Lint
Lint Passed
Unit
No Test Coverage
Build Status
Buildable 17790
Build 35406: Build Diffcashtab-tests
Build 35405: arc lint + arc unit

Event Timeline

corrected mistaken change to line 1036 in Send.js

Tail of the build log:

/work/web/cashtab /work/abc-ci-builds/cashtab-tests
npm notice 
npm notice New major version of npm available! 7.7.6 -> 8.3.0
npm notice Changelog: <https://github.com/npm/cli/releases/tag/v8.3.0>
npm notice Run `npm install -g npm@8.3.0` to update!
npm notice 
npm ERR! code ERR_SOCKET_TIMEOUT
npm ERR! errno ERR_SOCKET_TIMEOUT
npm ERR! request to https://registry.npmjs.org/merge-descriptors failed, reason: Socket timeout

npm ERR! A complete log of this run can be found in:
npm ERR!     /root/.npm/_logs/2021-12-30T17_46_59_608Z-debug.log
Build cashtab-tests failed with exit code 1
bytesofman requested changes to this revision.Jan 3 2022, 13:52

For this approach to be worth it, the displayed field needs to look exactly like the hidden 'real' field (same size, dimensions, drop downs). Also, it needs to render in between keystrokes -- not only when user has clicked away.

If this is impractical, revert to simply displaying the locale formatted amount in XEC above the locale formatted fiat amount.

This revision now requires changes to proceed.Jan 3 2022, 13:52

reverting to showing the formatted amount underneath the amount input. In order to replicate the enhanced input, I think I may need to create a new advanced input which may be too much for one diff. I will create a seperate task for this if given the go-ahead.

Failed tests logs:

====== CashTab Unit Tests:  Wallet without BCH balance ======
Error: expect(received).toMatchSnapshot()

Snapshot name: `Wallet without BCH balance 1`

- Snapshot  - 137
+ Received  + 130

@@ -30,11 +30,11 @@
            className="ant-btn ant-btn-text ant-btn-block"
            onClick={[Function]}
            type="button"
          >
            <div
-             className="sc-iAyFgw ewGzO"
+             className="sc-kEYyzF jiDMYj"
            >
              Switch to multiple recipients
            </div>
          </button>
          <div
@@ -146,185 +146,178 @@
                </div>
              </div>
            </div>
          </div>
          <div
-           className="sc-hMqMXs fyKBWG"
-           name="InputSelectMaxWrapper"
-           onBlur={[Function]}
-           tabIndex="0"
+           className="sc-iwsKbI bwAByd"
          >
            <div
-             className="sc-iwsKbI bwAByd"
+             className="ant-row ant-form-item"
+             style={Object {}}
            >
              <div
-               className="ant-row ant-form-item"
+               className="ant-col ant-form-item-control"
                style={Object {}}
              >
                <div
-                 className="ant-col ant-form-item-control"
-                 style={Object {}}
+                 className="ant-form-item-control-input"
                >
                  <div
-                   className="ant-form-item-control-input"
+                   className="ant-form-item-control-input-content"
                  >
-                   <div
-                     className="ant-form-item-control-input-content"
+                   <span
+                     className="ant-input-group ant-input-group-compact"
                    >
                      <span
-                       className="ant-input-group ant-input-group-compact"
+                       className="ant-input-affix-wrapper"
+                       onMouseUp={[Function]}
+                       style={
+                         Object {
+                           "textAlign": "left",
+                           "width": "60%",
+                         }
+                       }
                      >
                        <span
-                         className="ant-input-affix-wrapper"
-                         onMouseUp={[Function]}
-                         style={
-                           Object {
-                             "textAlign": "left",
-                             "width": "60%",
-                           }
-                         }
+                         className="ant-input-prefix"
                        >
-                         <span
-                           className="ant-input-prefix"
-                         >
-                           <img
-                             alt=""
-                             height={16}
-                             src="logo_primary.png"
-                             width={16}
-                           />
-                         </span>
-                         <input
-                           className="ant-input"
-                           dollar={0}
-                           name="value"
-                           onBlur={[Function]}
-                           onChange={[Function]}
-                           onFocus={[Function]}
-                           onKeyDown={[Function]}
-                           placeholder="Amount"
-                           required={true}
-                           step={0.01}
-                           style={null}
-                           type="number"
-                           value=""
+                         <img
+                           alt=""
+                           height={16}
+                           src="logo_primary.png"
+                           width={16}
                          />
                        </span>
-                       <div
-                         className="ant-select select-after ant-select-single ant-select-show-arrow"
+                       <input
+                         className="ant-input"
+                         dollar={0}
+                         name="value"
                          onBlur={[Function]}
+                         onChange={[Function]}
                          onFocus={[Function]}
                          onKeyDown={[Function]}
-                         onKeyUp={[Function]}
+                         placeholder="Amount"
+                         required={true}
+                         step={0.01}
+                         style={null}
+                         type="number"
+                         value=""
+                       />
+                     </span>
+                     <div
+                       className="ant-select select-after ant-select-single ant-select-show-arrow"
+                       onBlur={[Function]}
+                       onFocus={[Function]}
+                       onKeyDown={[Function]}
+                       onKeyUp={[Function]}
+                       onMouseDown={[Function]}
+                       style={
+                         Object {
+                           "width": "30%",
+                         }
+                       }
+                     >
+                       <div
+                         className="ant-select-selector"
+                         onClick={[Function]}
                          onMouseDown={[Function]}
-                         style={
-                           Object {
-                             "width": "30%",
-                           }
-                         }
                        >
-                         <div
-                           className="ant-select-selector"
-                           onClick={[Function]}
-                           onMouseDown={[Function]}
+                         <span
+                           className="ant-select-selection-search"
                          >
-                           <span
-                             className="ant-select-selection-search"
-                           >
-                             <input
-                               aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
-                               aria-autocomplete="list"
-                               aria-controls="rc_select_TEST_OR_SSR_list"
-                               aria-haspopup="listbox"
-                               aria-owns="rc_select_TEST_OR_SSR_list"
-                               autoComplete="off"
-                               className="ant-select-selection-search-input"
-                               disabled={false}
-                               id="rc_select_TEST_OR_SSR"
-                               onChange={[Function]}
-                               onCompositionEnd={[Function]}
-                               onCompositionStart={[Function]}
-                               onKeyDown={[Function]}
-                               onMouseDown={[Function]}
-                               onPaste={[Function]}
-                               readOnly={true}
-                               role="combobox"
-                               style={
-                                 Object {
-                                   "opacity": 0,
-                                 }
+                           <input
+                             aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
+                             aria-autocomplete="list"
+                             aria-controls="rc_select_TEST_OR_SSR_list"
+                             aria-haspopup="listbox"
+                             aria-owns="rc_select_TEST_OR_SSR_list"
+                             autoComplete="off"
+                             className="ant-select-selection-search-input"
+                             disabled={false}
+                             id="rc_select_TEST_OR_SSR"
+                             onChange={[Function]}
+                             onCompositionEnd={[Function]}
+                             onCompositionStart={[Function]}
+                             onKeyDown={[Function]}
+                             onMouseDown={[Function]}
+                             onPaste={[Function]}
+                             readOnly={true}
+                             role="combobox"
+                             style={
+                               Object {
+                                 "opacity": 0,
                                }
-                               type="search"
-                               unselectable="on"
-                               value=""
-                             />
-                           </span>
-                           <span
-                             className="ant-select-selection-item"
-                             title="XEC"
-                           >
-                             XEC
-                           </span>
-                         </div>
+                             }
+                             type="search"
+                             unselectable="on"
+                             value=""
+                           />
+                         </span>
                          <span
-                           aria-hidden={true}
-                           className="ant-select-arrow"
-                           onMouseDown={[Function]}
-                           style={
-                             Object {
-                               "WebkitUserSelect": "none",
-                               "userSelect": "none",
-                             }
-                           }
-                           unselectable="on"
+                           className="ant-select-selection-item"
+                           title="XEC"
                          >
-                           <span
-                             aria-label="down"
-                             className="anticon anticon-down ant-select-suffix"
-                             role="img"
-                           >
-                             <svg
-                               aria-hidden="true"
-                               data-icon="down"
-                               fill="currentColor"
-                               focusable="false"
-                               height="1em"
-                               viewBox="64 64 896 896"
-                               width="1em"
-                             >
-                               <path
-                                 d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
-                               />
-                             </svg>
-                           </span>
+                           XEC
                          </span>
                        </div>
                        <span
-                         className="sc-gqjmRU cFeeUJ"
-                         disabled={false}
-                         onClick={[Function]}
+                         aria-hidden={true}
+                         className="ant-select-arrow"
+                         onMouseDown={[Function]}
                          style={
                            Object {
-                             "height": "60px",
-                             "lineHeight": "60px",
-                             "width": "10%",
+                             "WebkitUserSelect": "none",
+                             "userSelect": "none",
                            }
                          }
+                         unselectable="on"
                        >
-                         max
+                         <span
+                           aria-label="down"
+                           className="anticon anticon-down ant-select-suffix"
+                           role="img"
+                         >
+                           <svg
+                             aria-hidden="true"
+                             data-icon="down"
+                             fill="currentColor"
+                             focusable="false"
+                             height="1em"
+                             viewBox="64 64 896 896"
+                             width="1em"
+                           >
+                             <path
+                               d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
+                             />
+                           </svg>
+                         </span>
                        </span>
+                     </div>
+                     <span
+                       className="sc-gqjmRU cFeeUJ"
+                       disabled={false}
+                       onClick={[Function]}
+                       style={
+                         Object {
+                           "height": "60px",
+                           "lineHeight": "60px",
+                           "width": "10%",
+                         }
+                       }
+                     >
+                       max
                      </span>
-                   </div>
+                   </span>
                  </div>
+               </div>
+               <div
+                 className="ant-form-item-explain"
+               >
                  <div
-                   className="ant-form-item-explain"
+                   role="alert"
                  >
-                   <div
-                     role="alert"
-                   >
-                     
-                   </div>
+                   
                  </div>
                </div>
              </div>
            </div>
          </div>
    at Object.<anonymous> (/work/web/cashtab/src/components/Send/__tests__/Send.test.js:54:18)
    at Object.asyncJestTest (/work/web/cashtab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
    at new Promise (<anonymous>)
    at mapper (/work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
====== CashTab Unit Tests:  Wallet with BCH balances ======
Error: expect(received).toMatchSnapshot()

Snapshot name: `Wallet with BCH balances 1`

- Snapshot  - 137
+ Received  + 130

@@ -30,11 +30,11 @@
            className="ant-btn ant-btn-text ant-btn-block"
            onClick={[Function]}
            type="button"
          >
            <div
-             className="sc-iAyFgw ewGzO"
+             className="sc-kEYyzF jiDMYj"
            >
              Switch to multiple recipients
            </div>
          </button>
          <div
@@ -146,185 +146,178 @@
                </div>
              </div>
            </div>
          </div>
          <div
-           className="sc-hMqMXs fyKBWG"
-           name="InputSelectMaxWrapper"
-           onBlur={[Function]}
-           tabIndex="0"
+           className="sc-iwsKbI bwAByd"
          >
            <div
-             className="sc-iwsKbI bwAByd"
+             className="ant-row ant-form-item"
+             style={Object {}}
            >
              <div
-               className="ant-row ant-form-item"
+               className="ant-col ant-form-item-control"
                style={Object {}}
              >
                <div
-                 className="ant-col ant-form-item-control"
-                 style={Object {}}
+                 className="ant-form-item-control-input"
                >
                  <div
-                   className="ant-form-item-control-input"
+                   className="ant-form-item-control-input-content"
                  >
-                   <div
-                     className="ant-form-item-control-input-content"
+                   <span
+                     className="ant-input-group ant-input-group-compact"
                    >
                      <span
-                       className="ant-input-group ant-input-group-compact"
+                       className="ant-input-affix-wrapper"
+                       onMouseUp={[Function]}
+                       style={
+                         Object {
+                           "textAlign": "left",
+                           "width": "60%",
+                         }
+                       }
                      >
                        <span
-                         className="ant-input-affix-wrapper"
-                         onMouseUp={[Function]}
-                         style={
-                           Object {
-                             "textAlign": "left",
-                             "width": "60%",
-                           }
-                         }
+                         className="ant-input-prefix"
                        >
-                         <span
-                           className="ant-input-prefix"
-                         >
-                           <img
-                             alt=""
-                             height={16}
-                             src="logo_primary.png"
-                             width={16}
-                           />
-                         </span>
-                         <input
-                           className="ant-input"
-                           dollar={0}
-                           name="value"
-                           onBlur={[Function]}
-                           onChange={[Function]}
-                           onFocus={[Function]}
-                           onKeyDown={[Function]}
-                           placeholder="Amount"
-                           required={true}
-                           step={0.01}
-                           style={null}
-                           type="number"
-                           value=""
+                         <img
+                           alt=""
+                           height={16}
+                           src="logo_primary.png"
+                           width={16}
                          />
                        </span>
-                       <div
-                         className="ant-select select-after ant-select-single ant-select-show-arrow"
+                       <input
+                         className="ant-input"
+                         dollar={0}
+                         name="value"
                          onBlur={[Function]}
+                         onChange={[Function]}
                          onFocus={[Function]}
                          onKeyDown={[Function]}
-                         onKeyUp={[Function]}
+                         placeholder="Amount"
+                         required={true}
+                         step={0.01}
+                         style={null}
+                         type="number"
+                         value=""
+                       />
+                     </span>
+                     <div
+                       className="ant-select select-after ant-select-single ant-select-show-arrow"
+                       onBlur={[Function]}
+                       onFocus={[Function]}
+                       onKeyDown={[Function]}
+                       onKeyUp={[Function]}
+                       onMouseDown={[Function]}
+                       style={
+                         Object {
+                           "width": "30%",
+                         }
+                       }
+                     >
+                       <div
+                         className="ant-select-selector"
+                         onClick={[Function]}
                          onMouseDown={[Function]}
-                         style={
-                           Object {
-                             "width": "30%",
-                           }
-                         }
                        >
-                         <div
-                           className="ant-select-selector"
-                           onClick={[Function]}
-                           onMouseDown={[Function]}
+                         <span
+                           className="ant-select-selection-search"
                          >
-                           <span
-                             className="ant-select-selection-search"
-                           >
-                             <input
-                               aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
-                               aria-autocomplete="list"
-                               aria-controls="rc_select_TEST_OR_SSR_list"
-                               aria-haspopup="listbox"
-                               aria-owns="rc_select_TEST_OR_SSR_list"
-                               autoComplete="off"
-                               className="ant-select-selection-search-input"
-                               disabled={false}
-                               id="rc_select_TEST_OR_SSR"
-                               onChange={[Function]}
-                               onCompositionEnd={[Function]}
-                               onCompositionStart={[Function]}
-                               onKeyDown={[Function]}
-                               onMouseDown={[Function]}
-                               onPaste={[Function]}
-                               readOnly={true}
-                               role="combobox"
-                               style={
-                                 Object {
-                                   "opacity": 0,
-                                 }
+                           <input
+                             aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
+                             aria-autocomplete="list"
+                             aria-controls="rc_select_TEST_OR_SSR_list"
+                             aria-haspopup="listbox"
+                             aria-owns="rc_select_TEST_OR_SSR_list"
+                             autoComplete="off"
+                             className="ant-select-selection-search-input"
+                             disabled={false}
+                             id="rc_select_TEST_OR_SSR"
+                             onChange={[Function]}
+                             onCompositionEnd={[Function]}
+                             onCompositionStart={[Function]}
+                             onKeyDown={[Function]}
+                             onMouseDown={[Function]}
+                             onPaste={[Function]}
+                             readOnly={true}
+                             role="combobox"
+                             style={
+                               Object {
+                                 "opacity": 0,
                                }
-                               type="search"
-                               unselectable="on"
-                               value=""
-                             />
-                           </span>
-                           <span
-                             className="ant-select-selection-item"
-                             title="XEC"
-                           >
-                             XEC
-                           </span>
-                         </div>
+                             }
+                             type="search"
+                             unselectable="on"
+                             value=""
+                           />
+                         </span>
                          <span
-                           aria-hidden={true}
-                           className="ant-select-arrow"
-                           onMouseDown={[Function]}
-                           style={
-                             Object {
-                               "WebkitUserSelect": "none",
-                               "userSelect": "none",
-                             }
-                           }
-                           unselectable="on"
+                           className="ant-select-selection-item"
+                           title="XEC"
                          >
-                           <span
-                             aria-label="down"
-                             className="anticon anticon-down ant-select-suffix"
-                             role="img"
-                           >
-                             <svg
-                               aria-hidden="true"
-                               data-icon="down"
-                               fill="currentColor"
-                               focusable="false"
-                               height="1em"
-                               viewBox="64 64 896 896"
-                               width="1em"
-                             >
-                               <path
-                                 d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
-                               />
-                             </svg>
-                           </span>
+                           XEC
                          </span>
                        </div>
                        <span
-                         className="sc-gqjmRU cFeeUJ"
-                         disabled={false}
-                         onClick={[Function]}
+                         aria-hidden={true}
+                         className="ant-select-arrow"
+                         onMouseDown={[Function]}
                          style={
                            Object {
-                             "height": "60px",
-                             "lineHeight": "60px",
-                             "width": "10%",
+                             "WebkitUserSelect": "none",
+                             "userSelect": "none",
                            }
                          }
+                         unselectable="on"
                        >
-                         max
+                         <span
+                           aria-label="down"
+                           className="anticon anticon-down ant-select-suffix"
+                           role="img"
+                         >
+                           <svg
+                             aria-hidden="true"
+                             data-icon="down"
+                             fill="currentColor"
+                             focusable="false"
+                             height="1em"
+                             viewBox="64 64 896 896"
+                             width="1em"
+                           >
+                             <path
+                               d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
+                             />
+                           </svg>
+                         </span>
                        </span>
+                     </div>
+                     <span
+                       className="sc-gqjmRU cFeeUJ"
+                       disabled={false}
+                       onClick={[Function]}
+                       style={
+                         Object {
+                           "height": "60px",
+                           "lineHeight": "60px",
+                           "width": "10%",
+                         }
+                       }
+                     >
+                       max
                      </span>
-                   </div>
+                   </span>
                  </div>
+               </div>
+               <div
+                 className="ant-form-item-explain"
+               >
                  <div
-                   className="ant-form-item-explain"
+                   role="alert"
                  >
-                   <div
-                     role="alert"
-                   >
-                     
-                   </div>
+                   
                  </div>
                </div>
              </div>
            </div>
          </div>
    at Object.<anonymous> (/work/web/cashtab/src/components/Send/__tests__/Send.test.js:68:18)
    at Object.asyncJestTest (/work/web/cashtab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
    at new Promise (<anonymous>)
    at mapper (/work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
====== CashTab Unit Tests:  Wallet with BCH balances and tokens ======
Error: expect(received).toMatchSnapshot()

Snapshot name: `Wallet with BCH balances and tokens 1`

- Snapshot  - 137
+ Received  + 130

@@ -30,11 +30,11 @@
            className="ant-btn ant-btn-text ant-btn-block"
            onClick={[Function]}
            type="button"
          >
            <div
-             className="sc-iAyFgw ewGzO"
+             className="sc-kEYyzF jiDMYj"
            >
              Switch to multiple recipients
            </div>
          </button>
          <div
@@ -146,185 +146,178 @@
                </div>
              </div>
            </div>
          </div>
          <div
-           className="sc-hMqMXs fyKBWG"
-           name="InputSelectMaxWrapper"
-           onBlur={[Function]}
-           tabIndex="0"
+           className="sc-iwsKbI bwAByd"
          >
            <div
-             className="sc-iwsKbI bwAByd"
+             className="ant-row ant-form-item"
+             style={Object {}}
            >
              <div
-               className="ant-row ant-form-item"
+               className="ant-col ant-form-item-control"
                style={Object {}}
              >
                <div
-                 className="ant-col ant-form-item-control"
-                 style={Object {}}
+                 className="ant-form-item-control-input"
                >
                  <div
-                   className="ant-form-item-control-input"
+                   className="ant-form-item-control-input-content"
                  >
-                   <div
-                     className="ant-form-item-control-input-content"
+                   <span
+                     className="ant-input-group ant-input-group-compact"
                    >
                      <span
-                       className="ant-input-group ant-input-group-compact"
+                       className="ant-input-affix-wrapper"
+                       onMouseUp={[Function]}
+                       style={
+                         Object {
+                           "textAlign": "left",
+                           "width": "60%",
+                         }
+                       }
                      >
                        <span
-                         className="ant-input-affix-wrapper"
-                         onMouseUp={[Function]}
-                         style={
-                           Object {
-                             "textAlign": "left",
-                             "width": "60%",
-                           }
-                         }
+                         className="ant-input-prefix"
                        >
-                         <span
-                           className="ant-input-prefix"
-                         >
-                           <img
-                             alt=""
-                             height={16}
-                             src="logo_primary.png"
-                             width={16}
-                           />
-                         </span>
-                         <input
-                           className="ant-input"
-                           dollar={0}
-                           name="value"
-                           onBlur={[Function]}
-                           onChange={[Function]}
-                           onFocus={[Function]}
-                           onKeyDown={[Function]}
-                           placeholder="Amount"
-                           required={true}
-                           step={0.01}
-                           style={null}
-                           type="number"
-                           value=""
+                         <img
+                           alt=""
+                           height={16}
+                           src="logo_primary.png"
+                           width={16}
                          />
                        </span>
-                       <div
-                         className="ant-select select-after ant-select-single ant-select-show-arrow"
+                       <input
+                         className="ant-input"
+                         dollar={0}
+                         name="value"
                          onBlur={[Function]}
+                         onChange={[Function]}
                          onFocus={[Function]}
                          onKeyDown={[Function]}
-                         onKeyUp={[Function]}
+                         placeholder="Amount"
+                         required={true}
+                         step={0.01}
+                         style={null}
+                         type="number"
+                         value=""
+                       />
+                     </span>
+                     <div
+                       className="ant-select select-after ant-select-single ant-select-show-arrow"
+                       onBlur={[Function]}
+                       onFocus={[Function]}
+                       onKeyDown={[Function]}
+                       onKeyUp={[Function]}
+                       onMouseDown={[Function]}
+                       style={
+                         Object {
+                           "width": "30%",
+                         }
+                       }
+                     >
+                       <div
+                         className="ant-select-selector"
+                         onClick={[Function]}
                          onMouseDown={[Function]}
-                         style={
-                           Object {
-                             "width": "30%",
-                           }
-                         }
                        >
-                         <div
-                           className="ant-select-selector"
-                           onClick={[Function]}
-                           onMouseDown={[Function]}
+                         <span
+                           className="ant-select-selection-search"
                          >
-                           <span
-                             className="ant-select-selection-search"
-                           >
-                             <input
-                               aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
-                               aria-autocomplete="list"
-                               aria-controls="rc_select_TEST_OR_SSR_list"
-                               aria-haspopup="listbox"
-                               aria-owns="rc_select_TEST_OR_SSR_list"
-                               autoComplete="off"
-                               className="ant-select-selection-search-input"
-                               disabled={false}
-                               id="rc_select_TEST_OR_SSR"
-                               onChange={[Function]}
-                               onCompositionEnd={[Function]}
-                               onCompositionStart={[Function]}
-                               onKeyDown={[Function]}
-                               onMouseDown={[Function]}
-                               onPaste={[Function]}
-                               readOnly={true}
-                               role="combobox"
-                               style={
-                                 Object {
-                                   "opacity": 0,
-                                 }
+                           <input
+                             aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
+                             aria-autocomplete="list"
+                             aria-controls="rc_select_TEST_OR_SSR_list"
+                             aria-haspopup="listbox"
+                             aria-owns="rc_select_TEST_OR_SSR_list"
+                             autoComplete="off"
+                             className="ant-select-selection-search-input"
+                             disabled={false}
+                             id="rc_select_TEST_OR_SSR"
+                             onChange={[Function]}
+                             onCompositionEnd={[Function]}
+                             onCompositionStart={[Function]}
+                             onKeyDown={[Function]}
+                             onMouseDown={[Function]}
+                             onPaste={[Function]}
+                             readOnly={true}
+                             role="combobox"
+                             style={
+                               Object {
+                                 "opacity": 0,
                                }
-                               type="search"
-                               unselectable="on"
-                               value=""
-                             />
-                           </span>
-                           <span
-                             className="ant-select-selection-item"
-                             title="XEC"
-                           >
-                             XEC
-                           </span>
-                         </div>
+                             }
+                             type="search"
+                             unselectable="on"
+                             value=""
+                           />
+                         </span>
                          <span
-                           aria-hidden={true}
-                           className="ant-select-arrow"
-                           onMouseDown={[Function]}
-                           style={
-                             Object {
-                               "WebkitUserSelect": "none",
-                               "userSelect": "none",
-                             }
-                           }
-                           unselectable="on"
+                           className="ant-select-selection-item"
+                           title="XEC"
                          >
-                           <span
-                             aria-label="down"
-                             className="anticon anticon-down ant-select-suffix"
-                             role="img"
-                           >
-                             <svg
-                               aria-hidden="true"
-                               data-icon="down"
-                               fill="currentColor"
-                               focusable="false"
-                               height="1em"
-                               viewBox="64 64 896 896"
-                               width="1em"
-                             >
-                               <path
-                                 d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
-                               />
-                             </svg>
-                           </span>
+                           XEC
                          </span>
                        </div>
                        <span
-                         className="sc-gqjmRU cFeeUJ"
-                         disabled={false}
-                         onClick={[Function]}
+                         aria-hidden={true}
+                         className="ant-select-arrow"
+                         onMouseDown={[Function]}
                          style={
                            Object {
-                             "height": "60px",
-                             "lineHeight": "60px",
-                             "width": "10%",
+                             "WebkitUserSelect": "none",
+                             "userSelect": "none",
                            }
                          }
+                         unselectable="on"
                        >
-                         max
+                         <span
+                           aria-label="down"
+                           className="anticon anticon-down ant-select-suffix"
+                           role="img"
+                         >
+                           <svg
+                             aria-hidden="true"
+                             data-icon="down"
+                             fill="currentColor"
+                             focusable="false"
+                             height="1em"
+                             viewBox="64 64 896 896"
+                             width="1em"
+                           >
+                             <path
+                               d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
+                             />
+                           </svg>
+                         </span>
                        </span>
+                     </div>
+                     <span
+                       className="sc-gqjmRU cFeeUJ"
+                       disabled={false}
+                       onClick={[Function]}
+                       style={
+                         Object {
+                           "height": "60px",
+                           "lineHeight": "60px",
+                           "width": "10%",
+                         }
+                       }
+                     >
+                       max
                      </span>
-                   </div>
+                   </span>
                  </div>
+               </div>
+               <div
+                 className="ant-form-item-explain"
+               >
                  <div
-                   className="ant-form-item-explain"
+                   role="alert"
                  >
-                   <div
-                     role="alert"
-                   >
-                     
-                   </div>
+                   
                  </div>
                </div>
              </div>
            </div>
          </div>
    at Object.<anonymous> (/work/web/cashtab/src/components/Send/__tests__/Send.test.js:82:18)
    at Object.asyncJestTest (/work/web/cashtab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
    at new Promise (<anonymous>)
    at mapper (/work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
====== CashTab Unit Tests:  Wallet with BCH balances and tokens and state field ======
Error: expect(received).toMatchSnapshot()

Snapshot name: `Wallet with BCH balances and tokens and state field 1`

- Snapshot  - 137
+ Received  + 130

@@ -37,11 +37,11 @@
            className="ant-btn ant-btn-text ant-btn-block"
            onClick={[Function]}
            type="button"
          >
            <div
-             className="sc-iAyFgw ewGzO"
+             className="sc-kEYyzF jiDMYj"
            >
              Switch to multiple recipients
            </div>
          </button>
          <div
@@ -153,185 +153,178 @@
                </div>
              </div>
            </div>
          </div>
          <div
-           className="sc-hMqMXs fyKBWG"
-           name="InputSelectMaxWrapper"
-           onBlur={[Function]}
-           tabIndex="0"
+           className="sc-iwsKbI bwAByd"
          >
            <div
-             className="sc-iwsKbI bwAByd"
+             className="ant-row ant-form-item"
+             style={Object {}}
            >
              <div
-               className="ant-row ant-form-item"
+               className="ant-col ant-form-item-control"
                style={Object {}}
              >
                <div
-                 className="ant-col ant-form-item-control"
-                 style={Object {}}
+                 className="ant-form-item-control-input"
                >
                  <div
-                   className="ant-form-item-control-input"
+                   className="ant-form-item-control-input-content"
                  >
-                   <div
-                     className="ant-form-item-control-input-content"
+                   <span
+                     className="ant-input-group ant-input-group-compact"
                    >
                      <span
-                       className="ant-input-group ant-input-group-compact"
+                       className="ant-input-affix-wrapper"
+                       onMouseUp={[Function]}
+                       style={
+                         Object {
+                           "textAlign": "left",
+                           "width": "60%",
+                         }
+                       }
                      >
                        <span
-                         className="ant-input-affix-wrapper"
-                         onMouseUp={[Function]}
-                         style={
-                           Object {
-                             "textAlign": "left",
-                             "width": "60%",
-                           }
-                         }
+                         className="ant-input-prefix"
                        >
-                         <span
-                           className="ant-input-prefix"
-                         >
-                           <img
-                             alt=""
-                             height={16}
-                             src="logo_primary.png"
-                             width={16}
-                           />
-                         </span>
-                         <input
-                           className="ant-input"
-                           dollar={0}
-                           name="value"
-                           onBlur={[Function]}
-                           onChange={[Function]}
-                           onFocus={[Function]}
-                           onKeyDown={[Function]}
-                           placeholder="Amount"
-                           required={true}
-                           step={0.01}
-                           style={null}
-                           type="number"
-                           value=""
+                         <img
+                           alt=""
+                           height={16}
+                           src="logo_primary.png"
+                           width={16}
                          />
                        </span>
-                       <div
-                         className="ant-select select-after ant-select-single ant-select-show-arrow"
+                       <input
+                         className="ant-input"
+                         dollar={0}
+                         name="value"
                          onBlur={[Function]}
+                         onChange={[Function]}
                          onFocus={[Function]}
                          onKeyDown={[Function]}
-                         onKeyUp={[Function]}
+                         placeholder="Amount"
+                         required={true}
+                         step={0.01}
+                         style={null}
+                         type="number"
+                         value=""
+                       />
+                     </span>
+                     <div
+                       className="ant-select select-after ant-select-single ant-select-show-arrow"
+                       onBlur={[Function]}
+                       onFocus={[Function]}
+                       onKeyDown={[Function]}
+                       onKeyUp={[Function]}
+                       onMouseDown={[Function]}
+                       style={
+                         Object {
+                           "width": "30%",
+                         }
+                       }
+                     >
+                       <div
+                         className="ant-select-selector"
+                         onClick={[Function]}
                          onMouseDown={[Function]}
-                         style={
-                           Object {
-                             "width": "30%",
-                           }
-                         }
                        >
-                         <div
-                           className="ant-select-selector"
-                           onClick={[Function]}
-                           onMouseDown={[Function]}
+                         <span
+                           className="ant-select-selection-search"
                          >
-                           <span
-                             className="ant-select-selection-search"
-                           >
-                             <input
-                               aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
-                               aria-autocomplete="list"
-                               aria-controls="rc_select_TEST_OR_SSR_list"
-                               aria-haspopup="listbox"
-                               aria-owns="rc_select_TEST_OR_SSR_list"
-                               autoComplete="off"
-                               className="ant-select-selection-search-input"
-                               disabled={false}
-                               id="rc_select_TEST_OR_SSR"
-                               onChange={[Function]}
-                               onCompositionEnd={[Function]}
-                               onCompositionStart={[Function]}
-                               onKeyDown={[Function]}
-                               onMouseDown={[Function]}
-                               onPaste={[Function]}
-                               readOnly={true}
-                               role="combobox"
-                               style={
-                                 Object {
-                                   "opacity": 0,
-                                 }
+                           <input
+                             aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
+                             aria-autocomplete="list"
+                             aria-controls="rc_select_TEST_OR_SSR_list"
+                             aria-haspopup="listbox"
+                             aria-owns="rc_select_TEST_OR_SSR_list"
+                             autoComplete="off"
+                             className="ant-select-selection-search-input"
+                             disabled={false}
+                             id="rc_select_TEST_OR_SSR"
+                             onChange={[Function]}
+                             onCompositionEnd={[Function]}
+                             onCompositionStart={[Function]}
+                             onKeyDown={[Function]}
+                             onMouseDown={[Function]}
+                             onPaste={[Function]}
+                             readOnly={true}
+                             role="combobox"
+                             style={
+                               Object {
+                                 "opacity": 0,
                                }
-                               type="search"
-                               unselectable="on"
-                               value=""
-                             />
-                           </span>
-                           <span
-                             className="ant-select-selection-item"
-                             title="XEC"
-                           >
-                             XEC
-                           </span>
-                         </div>
+                             }
+                             type="search"
+                             unselectable="on"
+                             value=""
+                           />
+                         </span>
                          <span
-                           aria-hidden={true}
-                           className="ant-select-arrow"
-                           onMouseDown={[Function]}
-                           style={
-                             Object {
-                               "WebkitUserSelect": "none",
-                               "userSelect": "none",
-                             }
-                           }
-                           unselectable="on"
+                           className="ant-select-selection-item"
+                           title="XEC"
                          >
-                           <span
-                             aria-label="down"
-                             className="anticon anticon-down ant-select-suffix"
-                             role="img"
-                           >
-                             <svg
-                               aria-hidden="true"
-                               data-icon="down"
-                               fill="currentColor"
-                               focusable="false"
-                               height="1em"
-                               viewBox="64 64 896 896"
-                               width="1em"
-                             >
-                               <path
-                                 d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
-                               />
-                             </svg>
-                           </span>
+                           XEC
                          </span>
                        </div>
                        <span
-                         className="sc-gqjmRU cFeeUJ"
-                         disabled={false}
-                         onClick={[Function]}
+                         aria-hidden={true}
+                         className="ant-select-arrow"
+                         onMouseDown={[Function]}
                          style={
                            Object {
-                             "height": "60px",
-                             "lineHeight": "60px",
-                             "width": "10%",
+                             "WebkitUserSelect": "none",
+                             "userSelect": "none",
                            }
                          }
+                         unselectable="on"
                        >
-                         max
+                         <span
+                           aria-label="down"
+                           className="anticon anticon-down ant-select-suffix"
+                           role="img"
+                         >
+                           <svg
+                             aria-hidden="true"
+                             data-icon="down"
+                             fill="currentColor"
+                             focusable="false"
+                             height="1em"
+                             viewBox="64 64 896 896"
+                             width="1em"
+                           >
+                             <path
+                               d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
+                             />
+                           </svg>
+                         </span>
                        </span>
+                     </div>
+                     <span
+                       className="sc-gqjmRU cFeeUJ"
+                       disabled={false}
+                       onClick={[Function]}
+                       style={
+                         Object {
+                           "height": "60px",
+                           "lineHeight": "60px",
+                           "width": "10%",
+                         }
+                       }
+                     >
+                       max
                      </span>
-                   </div>
+                   </span>
                  </div>
+               </div>
+               <div
+                 className="ant-form-item-explain"
+               >
                  <div
-                   className="ant-form-item-explain"
+                   role="alert"
                  >
-                   <div
-                     role="alert"
-                   >
-                     
-                   </div>
+                   
                  </div>
                </div>
              </div>
            </div>
          </div>
    at Object.<anonymous> (/work/web/cashtab/src/components/Send/__tests__/Send.test.js:96:18)
    at Object.asyncJestTest (/work/web/cashtab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
    at new Promise (<anonymous>)
    at mapper (/work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
====== CashTab Unit Tests:  Without wallet defined ======
Error: expect(received).toMatchSnapshot()

Snapshot name: `Without wallet defined 1`

- Snapshot  - 137
+ Received  + 130

@@ -30,11 +30,11 @@
            className="ant-btn ant-btn-text ant-btn-block"
            onClick={[Function]}
            type="button"
          >
            <div
-             className="sc-iAyFgw ewGzO"
+             className="sc-kEYyzF jiDMYj"
            >
              Switch to multiple recipients
            </div>
          </button>
          <div
@@ -146,185 +146,178 @@
                </div>
              </div>
            </div>
          </div>
          <div
-           className="sc-hMqMXs fyKBWG"
-           name="InputSelectMaxWrapper"
-           onBlur={[Function]}
-           tabIndex="0"
+           className="sc-iwsKbI bwAByd"
          >
            <div
-             className="sc-iwsKbI bwAByd"
+             className="ant-row ant-form-item"
+             style={Object {}}
            >
              <div
-               className="ant-row ant-form-item"
+               className="ant-col ant-form-item-control"
                style={Object {}}
              >
                <div
-                 className="ant-col ant-form-item-control"
-                 style={Object {}}
+                 className="ant-form-item-control-input"
                >
                  <div
-                   className="ant-form-item-control-input"
+                   className="ant-form-item-control-input-content"
                  >
-                   <div
-                     className="ant-form-item-control-input-content"
+                   <span
+                     className="ant-input-group ant-input-group-compact"
                    >
                      <span
-                       className="ant-input-group ant-input-group-compact"
+                       className="ant-input-affix-wrapper"
+                       onMouseUp={[Function]}
+                       style={
+                         Object {
+                           "textAlign": "left",
+                           "width": "60%",
+                         }
+                       }
                      >
                        <span
-                         className="ant-input-affix-wrapper"
-                         onMouseUp={[Function]}
-                         style={
-                           Object {
-                             "textAlign": "left",
-                             "width": "60%",
-                           }
-                         }
+                         className="ant-input-prefix"
                        >
-                         <span
-                           className="ant-input-prefix"
-                         >
-                           <img
-                             alt=""
-                             height={16}
-                             src="logo_primary.png"
-                             width={16}
-                           />
-                         </span>
-                         <input
-                           className="ant-input"
-                           dollar={0}
-                           name="value"
-                           onBlur={[Function]}
-                           onChange={[Function]}
-                           onFocus={[Function]}
-                           onKeyDown={[Function]}
-                           placeholder="Amount"
-                           required={true}
-                           step={0.01}
-                           style={null}
-                           type="number"
-                           value=""
+                         <img
+                           alt=""
+                           height={16}
+                           src="logo_primary.png"
+                           width={16}
                          />
                        </span>
-                       <div
-                         className="ant-select select-after ant-select-single ant-select-show-arrow"
+                       <input
+                         className="ant-input"
+                         dollar={0}
+                         name="value"
                          onBlur={[Function]}
+                         onChange={[Function]}
                          onFocus={[Function]}
                          onKeyDown={[Function]}
-                         onKeyUp={[Function]}
+                         placeholder="Amount"
+                         required={true}
+                         step={0.01}
+                         style={null}
+                         type="number"
+                         value=""
+                       />
+                     </span>
+                     <div
+                       className="ant-select select-after ant-select-single ant-select-show-arrow"
+                       onBlur={[Function]}
+                       onFocus={[Function]}
+                       onKeyDown={[Function]}
+                       onKeyUp={[Function]}
+                       onMouseDown={[Function]}
+                       style={
+                         Object {
+                           "width": "30%",
+                         }
+                       }
+                     >
+                       <div
+                         className="ant-select-selector"
+                         onClick={[Function]}
                          onMouseDown={[Function]}
-                         style={
-                           Object {
-                             "width": "30%",
-                           }
-                         }
                        >
-                         <div
-                           className="ant-select-selector"
-                           onClick={[Function]}
-                           onMouseDown={[Function]}
+                         <span
+                           className="ant-select-selection-search"
                          >
-                           <span
-                             className="ant-select-selection-search"
-                           >
-                             <input
-                               aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
-                               aria-autocomplete="list"
-                               aria-controls="rc_select_TEST_OR_SSR_list"
-                               aria-haspopup="listbox"
-                               aria-owns="rc_select_TEST_OR_SSR_list"
-                               autoComplete="off"
-                               className="ant-select-selection-search-input"
-                               disabled={false}
-                               id="rc_select_TEST_OR_SSR"
-                               onChange={[Function]}
-                               onCompositionEnd={[Function]}
-                               onCompositionStart={[Function]}
-                               onKeyDown={[Function]}
-                               onMouseDown={[Function]}
-                               onPaste={[Function]}
-                               readOnly={true}
-                               role="combobox"
-                               style={
-                                 Object {
-                                   "opacity": 0,
-                                 }
+                           <input
+                             aria-activedescendant="rc_select_TEST_OR_SSR_list_0"
+                             aria-autocomplete="list"
+                             aria-controls="rc_select_TEST_OR_SSR_list"
+                             aria-haspopup="listbox"
+                             aria-owns="rc_select_TEST_OR_SSR_list"
+                             autoComplete="off"
+                             className="ant-select-selection-search-input"
+                             disabled={false}
+                             id="rc_select_TEST_OR_SSR"
+                             onChange={[Function]}
+                             onCompositionEnd={[Function]}
+                             onCompositionStart={[Function]}
+                             onKeyDown={[Function]}
+                             onMouseDown={[Function]}
+                             onPaste={[Function]}
+                             readOnly={true}
+                             role="combobox"
+                             style={
+                               Object {
+                                 "opacity": 0,
                                }
-                               type="search"
-                               unselectable="on"
-                               value=""
-                             />
-                           </span>
-                           <span
-                             className="ant-select-selection-item"
-                             title="XEC"
-                           >
-                             XEC
-                           </span>
-                         </div>
+                             }
+                             type="search"
+                             unselectable="on"
+                             value=""
+                           />
+                         </span>
                          <span
-                           aria-hidden={true}
-                           className="ant-select-arrow"
-                           onMouseDown={[Function]}
-                           style={
-                             Object {
-                               "WebkitUserSelect": "none",
-                               "userSelect": "none",
-                             }
-                           }
-                           unselectable="on"
+                           className="ant-select-selection-item"
+                           title="XEC"
                          >
-                           <span
-                             aria-label="down"
-                             className="anticon anticon-down ant-select-suffix"
-                             role="img"
-                           >
-                             <svg
-                               aria-hidden="true"
-                               data-icon="down"
-                               fill="currentColor"
-                               focusable="false"
-                               height="1em"
-                               viewBox="64 64 896 896"
-                               width="1em"
-                             >
-                               <path
-                                 d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
-                               />
-                             </svg>
-                           </span>
+                           XEC
                          </span>
                        </div>
                        <span
-                         className="sc-gqjmRU cFeeUJ"
-                         disabled={false}
-                         onClick={[Function]}
+                         aria-hidden={true}
+                         className="ant-select-arrow"
+                         onMouseDown={[Function]}
                          style={
                            Object {
-                             "height": "60px",
-                             "lineHeight": "60px",
-                             "width": "10%",
+                             "WebkitUserSelect": "none",
+                             "userSelect": "none",
                            }
                          }
+                         unselectable="on"
                        >
-                         max
+                         <span
+                           aria-label="down"
+                           className="anticon anticon-down ant-select-suffix"
+                           role="img"
+                         >
+                           <svg
+                             aria-hidden="true"
+                             data-icon="down"
+                             fill="currentColor"
+                             focusable="false"
+                             height="1em"
+                             viewBox="64 64 896 896"
+                             width="1em"
+                           >
+                             <path
+                               d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
+                             />
+                           </svg>
+                         </span>
                        </span>
+                     </div>
+                     <span
+                       className="sc-gqjmRU cFeeUJ"
+                       disabled={false}
+                       onClick={[Function]}
+                       style={
+                         Object {
+                           "height": "60px",
+                           "lineHeight": "60px",
+                           "width": "10%",
+                         }
+                       }
+                     >
+                       max
                      </span>
-                   </div>
+                   </span>
                  </div>
+               </div>
+               <div
+                 className="ant-form-item-explain"
+               >
                  <div
-                   className="ant-form-item-explain"
+                   role="alert"
                  >
-                   <div
-                     role="alert"
-                   >
-                     
-                   </div>
+                   
                  </div>
                </div>
              </div>
            </div>
          </div>
    at Object.<anonymous> (/work/web/cashtab/src/components/Send/__tests__/Send.test.js:114:18)
    at Object.asyncJestTest (/work/web/cashtab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
    at new Promise (<anonymous>)
    at mapper (/work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
    at processTicksAndRejections (node:internal/process/task_queues:94:5)

Each failure log is accessible here:
CashTab Unit Tests: Wallet without BCH balance
CashTab Unit Tests: Wallet with BCH balances
CashTab Unit Tests: Wallet with BCH balances and tokens
CashTab Unit Tests: Wallet with BCH balances and tokens and state field
CashTab Unit Tests: Without wallet defined

bytesofman requested changes to this revision.Jan 5 2022, 15:41

Good diff. In general, I tend to really like this type of "first, make it work" approach -- solve the user problem as best as we practically can in a stable way, then build a fancier long term solution on top of that.

Some things can be simplified and cleaned up -- see inline comments. I also created T2130 and assigned it to you for cleaning up some Cashtab format function organization to prevent future rewrites of existing functions.

web/cashtab/src/components/Send/Send.js
127 ↗(On Diff #31638)

It's okay to display this all the time, the same way the fiat string is displayed. Remove this conditional.

488 ↗(On Diff #31638)

I think the formatBalance function from cashMethods.js will work fine here. If for some reason you need a new function, then make sure to add it to a util file and include unit tests for it with this diff.

This revision now requires changes to proceed.Jan 5 2022, 15:41

responding to review feedback

added function I mistakenly removed

Failed tests logs:

====== CashTab Unit Tests:  Wallet without BCH balance ======
Error: expect(received).toMatchSnapshot()

Snapshot name: `Wallet without BCH balance 1`

- Snapshot  - 0
+ Received  + 7

@@ -319,10 +319,17 @@
                  </div>
                </div>
              </div>
            </div>
          </div>
+         <h3
+           className="sc-iAyFgw irwSQk"
+         >
+           0
+            
+           XEC
+         </h3>
          <div
            className="sc-jKJlTe hccxef"
          >
            =
             
    at Object.<anonymous> (/work/web/cashtab/src/components/Send/__tests__/Send.test.js:54:18)
    at Object.asyncJestTest (/work/web/cashtab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
    at new Promise (<anonymous>)
    at mapper (/work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
====== CashTab Unit Tests:  Wallet with BCH balances ======
Error: expect(received).toMatchSnapshot()

Snapshot name: `Wallet with BCH balances 1`

- Snapshot  - 0
+ Received  + 7

@@ -319,10 +319,17 @@
                  </div>
                </div>
              </div>
            </div>
          </div>
+         <h3
+           className="sc-iAyFgw irwSQk"
+         >
+           0
+            
+           XEC
+         </h3>
          <div
            className="sc-jKJlTe hccxef"
          >
            =
             
    at Object.<anonymous> (/work/web/cashtab/src/components/Send/__tests__/Send.test.js:68:18)
    at Object.asyncJestTest (/work/web/cashtab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
    at new Promise (<anonymous>)
    at mapper (/work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
====== CashTab Unit Tests:  Wallet with BCH balances and tokens ======
Error: expect(received).toMatchSnapshot()

Snapshot name: `Wallet with BCH balances and tokens 1`

- Snapshot  - 0
+ Received  + 7

@@ -319,10 +319,17 @@
                  </div>
                </div>
              </div>
            </div>
          </div>
+         <h3
+           className="sc-iAyFgw irwSQk"
+         >
+           0
+            
+           XEC
+         </h3>
          <div
            className="sc-jKJlTe hccxef"
          >
            =
             
    at Object.<anonymous> (/work/web/cashtab/src/components/Send/__tests__/Send.test.js:82:18)
    at Object.asyncJestTest (/work/web/cashtab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
    at new Promise (<anonymous>)
    at mapper (/work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
====== CashTab Unit Tests:  Wallet with BCH balances and tokens and state field ======
Error: expect(received).toMatchSnapshot()

Snapshot name: `Wallet with BCH balances and tokens and state field 1`

- Snapshot  - 0
+ Received  + 7

@@ -326,10 +326,17 @@
                  </div>
                </div>
              </div>
            </div>
          </div>
+         <h3
+           className="sc-iAyFgw irwSQk"
+         >
+           0
+            
+           XEC
+         </h3>
          <div
            className="sc-jKJlTe hccxef"
          >
            =
             
    at Object.<anonymous> (/work/web/cashtab/src/components/Send/__tests__/Send.test.js:96:18)
    at Object.asyncJestTest (/work/web/cashtab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
    at new Promise (<anonymous>)
    at mapper (/work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
====== CashTab Unit Tests:  Without wallet defined ======
Error: expect(received).toMatchSnapshot()

Snapshot name: `Without wallet defined 1`

- Snapshot  - 0
+ Received  + 7

@@ -319,10 +319,17 @@
                  </div>
                </div>
              </div>
            </div>
          </div>
+         <h3
+           className="sc-iAyFgw irwSQk"
+         >
+           0
+            
+           XEC
+         </h3>
          <div
            className="sc-jKJlTe hccxef"
          >
            =
             
    at Object.<anonymous> (/work/web/cashtab/src/components/Send/__tests__/Send.test.js:114:18)
    at Object.asyncJestTest (/work/web/cashtab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
    at new Promise (<anonymous>)
    at mapper (/work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
    at processTicksAndRejections (node:internal/process/task_queues:94:5)

Each failure log is accessible here:
CashTab Unit Tests: Wallet without BCH balance
CashTab Unit Tests: Wallet with BCH balances
CashTab Unit Tests: Wallet with BCH balances and tokens
CashTab Unit Tests: Wallet with BCH balances and tokens and state field
CashTab Unit Tests: Without wallet defined

Failed tests logs:

====== CashTab Unit Tests:  Wallet without BCH balance ======
Error: expect(received).toMatchSnapshot()

Snapshot name: `Wallet without BCH balance 1`

- Snapshot  - 0
+ Received  + 7

@@ -319,10 +319,17 @@
                  </div>
                </div>
              </div>
            </div>
          </div>
+         <h3
+           className="sc-iAyFgw irwSQk"
+         >
+           0
+            
+           XEC
+         </h3>
          <div
            className="sc-jKJlTe hccxef"
          >
            =
             
    at Object.<anonymous> (/work/web/cashtab/src/components/Send/__tests__/Send.test.js:54:18)
    at Object.asyncJestTest (/work/web/cashtab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
    at new Promise (<anonymous>)
    at mapper (/work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
====== CashTab Unit Tests:  Wallet with BCH balances ======
Error: expect(received).toMatchSnapshot()

Snapshot name: `Wallet with BCH balances 1`

- Snapshot  - 0
+ Received  + 7

@@ -319,10 +319,17 @@
                  </div>
                </div>
              </div>
            </div>
          </div>
+         <h3
+           className="sc-iAyFgw irwSQk"
+         >
+           0
+            
+           XEC
+         </h3>
          <div
            className="sc-jKJlTe hccxef"
          >
            =
             
    at Object.<anonymous> (/work/web/cashtab/src/components/Send/__tests__/Send.test.js:68:18)
    at Object.asyncJestTest (/work/web/cashtab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
    at new Promise (<anonymous>)
    at mapper (/work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
====== CashTab Unit Tests:  Wallet with BCH balances and tokens ======
Error: expect(received).toMatchSnapshot()

Snapshot name: `Wallet with BCH balances and tokens 1`

- Snapshot  - 0
+ Received  + 7

@@ -319,10 +319,17 @@
                  </div>
                </div>
              </div>
            </div>
          </div>
+         <h3
+           className="sc-iAyFgw irwSQk"
+         >
+           0
+            
+           XEC
+         </h3>
          <div
            className="sc-jKJlTe hccxef"
          >
            =
             
    at Object.<anonymous> (/work/web/cashtab/src/components/Send/__tests__/Send.test.js:82:18)
    at Object.asyncJestTest (/work/web/cashtab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
    at new Promise (<anonymous>)
    at mapper (/work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
====== CashTab Unit Tests:  Wallet with BCH balances and tokens and state field ======
Error: expect(received).toMatchSnapshot()

Snapshot name: `Wallet with BCH balances and tokens and state field 1`

- Snapshot  - 0
+ Received  + 7

@@ -326,10 +326,17 @@
                  </div>
                </div>
              </div>
            </div>
          </div>
+         <h3
+           className="sc-iAyFgw irwSQk"
+         >
+           0
+            
+           XEC
+         </h3>
          <div
            className="sc-jKJlTe hccxef"
          >
            =
             
    at Object.<anonymous> (/work/web/cashtab/src/components/Send/__tests__/Send.test.js:96:18)
    at Object.asyncJestTest (/work/web/cashtab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
    at new Promise (<anonymous>)
    at mapper (/work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
====== CashTab Unit Tests:  Without wallet defined ======
Error: expect(received).toMatchSnapshot()

Snapshot name: `Without wallet defined 1`

- Snapshot  - 0
+ Received  + 7

@@ -319,10 +319,17 @@
                  </div>
                </div>
              </div>
            </div>
          </div>
+         <h3
+           className="sc-iAyFgw irwSQk"
+         >
+           0
+            
+           XEC
+         </h3>
          <div
            className="sc-jKJlTe hccxef"
          >
            =
             
    at Object.<anonymous> (/work/web/cashtab/src/components/Send/__tests__/Send.test.js:114:18)
    at Object.asyncJestTest (/work/web/cashtab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
    at new Promise (<anonymous>)
    at mapper (/work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
    at /work/web/cashtab/node_modules/jest-jasmine2/build/queueRunner.js:75:41
    at processTicksAndRejections (node:internal/process/task_queues:94:5)

Each failure log is accessible here:
CashTab Unit Tests: Wallet without BCH balance
CashTab Unit Tests: Wallet with BCH balances
CashTab Unit Tests: Wallet with BCH balances and tokens
CashTab Unit Tests: Wallet with BCH balances and tokens and state field
CashTab Unit Tests: Without wallet defined