Skip to content

Commit

Permalink
fix: improve amount UI entry (#2737)
Browse files Browse the repository at this point in the history
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 <tribble@agoric.com>
Co-authored-by: Kate Sills <kate@agoric.com>
  • Loading branch information
3 people authored Mar 28, 2021
1 parent 6b0f22b commit bc7e2ce
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 36 deletions.
2 changes: 1 addition & 1 deletion packages/dapp-svelte-wallet/ui/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
60 changes: 28 additions & 32 deletions packages/ui-components/src/components/NatAmountInput.jsx
Original file line number Diff line number Diff line change
@@ -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/

Expand All @@ -9,76 +8,73 @@ 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();
e.stopPropagation();
}
};

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 (
<TextField
label={label}
type="number"
variant="outlined"
InputProps={inputProps}
onChange={handleOnChange}
onBlur={handleOnBlur}
onKeyPress={preventSubtractChar}
value={displayString}
disabled={disabled}
Expand Down
6 changes: 3 additions & 3 deletions packages/ui-components/test/components/test-NatAmountInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const renderNatAmountInput = ({
disabled = false,
error = false,
} = {}) => {
return render(
const result = render(
<NatAmountInput
label={label}
onChange={onChange}
Expand All @@ -56,6 +56,7 @@ const renderNatAmountInput = ({
error={error}
/>,
);
return result;
};

test('has props', t => {
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit bc7e2ce

Please sign in to comment.