From bc7e2ceaee05f3ab2eb362664722323bca62a4c9 Mon Sep 17 00:00:00 2001 From: Dean Tribble Date: Sat, 27 Mar 2021 21:51:03 -0700 Subject: [PATCH] fix: improve amount UI entry (#2737) Improve amount UI entry - preserve user text while editing - rerender when component loses focus - change wallet base color - placeToShow now defautls correctly - all passing - reenabled "failing" test, which now passes Co-authored-by: Dean Tribble Co-authored-by: Kate Sills --- packages/dapp-svelte-wallet/ui/src/App.svelte | 2 +- .../src/components/NatAmountInput.jsx | 60 +++++++++---------- .../test/components/test-NatAmountInput.js | 6 +- 3 files changed, 32 insertions(+), 36 deletions(-) diff --git a/packages/dapp-svelte-wallet/ui/src/App.svelte b/packages/dapp-svelte-wallet/ui/src/App.svelte index c634c7be654..204063b10f2 100644 --- a/packages/dapp-svelte-wallet/ui/src/App.svelte +++ b/packages/dapp-svelte-wallet/ui/src/App.svelte @@ -51,7 +51,7 @@ --text-color-normal: green; --text-color-light: #273242; color: var(--theme-text); - --agoric-bg: #ab2328; + --agoric-bg: rgb(215, 50, 82); --banner-height: 70px; --content-width: 1024px; } diff --git a/packages/ui-components/src/components/NatAmountInput.jsx b/packages/ui-components/src/components/NatAmountInput.jsx index 69f15dce50b..44680d65b81 100644 --- a/packages/ui-components/src/components/NatAmountInput.jsx +++ b/packages/ui-components/src/components/NatAmountInput.jsx @@ -1,6 +1,5 @@ import { parseAsNat } from '../display/natValue/parseAsNat'; import { stringifyNat } from '../display/natValue/stringifyNat'; -import { debounce } from '../helpers'; // https://material-ui.com/api/text-field/ @@ -9,44 +8,34 @@ import { debounce } from '../helpers'; // multiple instances of React and MaterialUI. Thus, we pass the // instances to the component. -const DEBOUNCE_WAIT_MS = 800; - const makeNatAmountInput = ({ React, TextField }) => ({ label = 'Amount', value = 0n, decimalPlaces = 0, - placesToShow = 0, + placesToShow = undefined, disabled = false, error = false, onChange = () => {}, required = false, helperText = null, }) => { - console.log('receiving new value', value); - - // Use react state so it knows to re-render on displayString change - const [displayString, setDisplayString] = React.useState( - value === null ? '0' : stringifyNat(value, decimalPlaces, placesToShow), - ); - - console.log('displayString', displayString); - - // With use effect, the display gets completely disassociated from - // the user's typing. - // React.useEffect(() => { - // setDisplayString( - // value === null ? '0' : stringifyNat(value, decimalPlaces, placesToShow), - // ); - // }, [value, decimalPlaces, placesToShow, displayString]); - const step = 1; - placesToShow = decimalPlaces > 0 ? 2 : 0; + if (typeof placesToShow !== 'number') { + placesToShow = decimalPlaces > 0 ? 2 : 0; + } // No negative values allowed in the input const inputProps = { inputProps: { min: 0, step }, }; + const valueString = stringifyNat(value, decimalPlaces, placesToShow); + + // Use react state so it knows to re-render on fieldString change + const [fieldString, setFieldString] = React.useState( + value === null ? '0' : valueString, + ); + const preventSubtractChar = e => { if (e.key === 'Subtract') { e.preventDefault(); @@ -54,24 +43,30 @@ const makeNatAmountInput = ({ React, TextField }) => ({ } }; - const delayedOnChange = debounce(str => { - console.log('actually parsing'); - const parsed = parseAsNat(str, decimalPlaces); - setDisplayString(stringifyNat(parsed, decimalPlaces, placesToShow)); - onChange(parsed); - }, DEBOUNCE_WAIT_MS); + // Display the rendered version of the value when + // the user stops editing the component. + const handleOnBlur = _ => { + setFieldString(valueString); + }; // We want to delay the input validation so that the user can type // freely, and then it gets formatted appropriately after the user stops. const handleOnChange = ev => { const str = ev.target.value; // Show the user exactly what they are typing - setDisplayString(str); - - // Wait until the user stops typing to parse it - delayedOnChange(str); + setFieldString(str); + const parsed = parseAsNat(str, decimalPlaces); + onChange(parsed); }; + // If what the user is typing parses to the current + // value (though it might have extra punctuation), + // then show that rather than a computed display string + const displayString = + value === parseAsNat(fieldString, decimalPlaces) + ? fieldString + : valueString; + return ( ({ variant="outlined" InputProps={inputProps} onChange={handleOnChange} + onBlur={handleOnBlur} onKeyPress={preventSubtractChar} value={displayString} disabled={disabled} diff --git a/packages/ui-components/test/components/test-NatAmountInput.js b/packages/ui-components/test/components/test-NatAmountInput.js index 3828940f379..026ca38fe99 100644 --- a/packages/ui-components/test/components/test-NatAmountInput.js +++ b/packages/ui-components/test/components/test-NatAmountInput.js @@ -45,7 +45,7 @@ const renderNatAmountInput = ({ disabled = false, error = false, } = {}) => { - return render( + const result = render( , ); + return result; }; test('has props', t => { @@ -116,8 +117,7 @@ test('error=true', t => { t.is(input.attr('aria-invalid'), 'true'); }); -// TODO: change test to account for the delayed validation -test.failing('can simulate input - just calls onChange', async t => { +test('can simulate input - just calls onChange', async t => { let receivedValue; const onChange = newValue => { receivedValue = newValue;