aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Huang <tmashuang@users.noreply.github.com>2018-03-20 00:20:04 +0800
committerGitHub <noreply@github.com>2018-03-20 00:20:04 +0800
commit424e98f6a896df6a848a92ef318464abc505d83d (patch)
tree4bb99f7ca9d6b924f50c7dcf575962faf185f3c8
parenta1db40047c2a6fdbb8d2de2abacd0484e12e128d (diff)
parent62a95139e6cd9de66941ddb037ec682af568a30b (diff)
downloadtangerine-wallet-browser-424e98f6a896df6a848a92ef318464abc505d83d.tar.gz
tangerine-wallet-browser-424e98f6a896df6a848a92ef318464abc505d83d.tar.zst
tangerine-wallet-browser-424e98f6a896df6a848a92ef318464abc505d83d.zip
Merge pull request #3594 from danjm/i3344-improve-currency-input
Currency input defaults to empty string
-rw-r--r--test/integration/lib/send-new-ui.js2
-rw-r--r--test/lib/react-trigger-change.js161
-rw-r--r--ui/app/components/currency-input.js20
3 files changed, 176 insertions, 7 deletions
diff --git a/test/integration/lib/send-new-ui.js b/test/integration/lib/send-new-ui.js
index 5d21ba2a3..594f5f0b0 100644
--- a/test/integration/lib/send-new-ui.js
+++ b/test/integration/lib/send-new-ui.js
@@ -1,4 +1,4 @@
-const reactTriggerChange = require('react-trigger-change')
+const reactTriggerChange = require('../../lib/react-trigger-change')
const {
timeout,
queryAsync,
diff --git a/test/lib/react-trigger-change.js b/test/lib/react-trigger-change.js
new file mode 100644
index 000000000..a25ddff00
--- /dev/null
+++ b/test/lib/react-trigger-change.js
@@ -0,0 +1,161 @@
+// Trigger React's synthetic change events on input, textarea and select elements
+// https://github.com/vitalyq/react-trigger-change
+
+/******************IMPORTANT NOTE******************/
+/* This file is a modification of the */
+/* 'react-trigger-change' library linked above. */
+/* That library breaks when 'onFocus' events are */
+/* added to components under test because it */
+/* dispatches focus events to ensure changes are */
+/* triggered in some versions of IE. */
+/* This modification removes the accomodations */
+/* 'react-trigger-change' makes for IE to ensure */
+/* our tests can pass in chrome and firefox. */
+/**************************************************/
+
+'use strict';
+
+// Constants and functions are declared inside the closure.
+// In this way, reactTriggerChange can be passed directly to executeScript in Selenium.
+module.exports = function reactTriggerChange(node) {
+ var supportedInputTypes = {
+ color: true,
+ date: true,
+ datetime: true,
+ 'datetime-local': true,
+ email: true,
+ month: true,
+ number: true,
+ password: true,
+ range: true,
+ search: true,
+ tel: true,
+ text: true,
+ time: true,
+ url: true,
+ week: true
+ };
+ var nodeName = node.nodeName.toLowerCase();
+ var type = node.type;
+ var event;
+ var descriptor;
+ var initialValue;
+ var initialChecked;
+ var initialCheckedRadio;
+
+ // Do not try to delete non-configurable properties.
+ // Value and checked properties on DOM elements are non-configurable in PhantomJS.
+ function deletePropertySafe(elem, prop) {
+ var desc = Object.getOwnPropertyDescriptor(elem, prop);
+ if (desc && desc.configurable) {
+ delete elem[prop];
+ }
+ }
+
+ function getCheckedRadio(radio) {
+ var name = radio.name;
+ var radios;
+ var i;
+ if (name) {
+ radios = document.querySelectorAll('input[type="radio"][name="' + name + '"]');
+ for (i = 0; i < radios.length; i += 1) {
+ if (radios[i].checked) {
+ return radios[i] !== radio ? radios[i] : null;
+ }
+ }
+ }
+ return null;
+ }
+
+ function preventChecking(e) {
+ e.preventDefault();
+ if (!initialChecked) {
+ e.target.checked = false;
+ }
+ if (initialCheckedRadio) {
+ initialCheckedRadio.checked = true;
+ }
+ }
+
+ if (nodeName === 'select' ||
+ (nodeName === 'input' && type === 'file')) {
+ // IE9-IE11, non-IE
+ // Dispatch change.
+ event = document.createEvent('HTMLEvents');
+ event.initEvent('change', true, false);
+ node.dispatchEvent(event);
+ } else if ((nodeName === 'input' && supportedInputTypes[type]) ||
+ nodeName === 'textarea') {
+ // React 16
+ // Cache artificial value property descriptor.
+ // Property doesn't exist in React <16, descriptor is undefined.
+ descriptor = Object.getOwnPropertyDescriptor(node, 'value');
+
+ // Update inputValueTracking cached value.
+ // Remove artificial value property.
+ // Restore initial value to trigger event with it.
+ initialValue = node.value;
+ node.value = initialValue + '#';
+ deletePropertySafe(node, 'value');
+ node.value = initialValue;
+
+ // React 0.14: IE10-IE11, non-IE
+ // React 15: non-IE
+ // React 16: IE10-IE11, non-IE
+ event = document.createEvent('HTMLEvents');
+ event.initEvent('input', true, false);
+ node.dispatchEvent(event);
+
+ // React 16
+ // Restore artificial value property descriptor.
+ if (descriptor) {
+ Object.defineProperty(node, 'value', descriptor);
+ }
+ } else if (nodeName === 'input' && type === 'checkbox') {
+ // Invert inputValueTracking cached value.
+ node.checked = !node.checked;
+
+ // Dispatch click.
+ // Click event inverts checked value.
+ event = document.createEvent('MouseEvents');
+ event.initEvent('click', true, true);
+ node.dispatchEvent(event);
+ } else if (nodeName === 'input' && type === 'radio') {
+ // Cache initial checked value.
+ initialChecked = node.checked;
+
+ // Find and cache initially checked radio in the group.
+ initialCheckedRadio = getCheckedRadio(node);
+
+ // React 16
+ // Cache property descriptor.
+ // Invert inputValueTracking cached value.
+ // Remove artificial checked property.
+ // Restore initial value, otherwise preventDefault will eventually revert the value.
+ descriptor = Object.getOwnPropertyDescriptor(node, 'checked');
+ node.checked = !initialChecked;
+ deletePropertySafe(node, 'checked');
+ node.checked = initialChecked;
+
+ // Prevent toggling during event capturing phase.
+ // Set checked value to false if initialChecked is false,
+ // otherwise next listeners will see true.
+ // Restore initially checked radio in the group.
+ node.addEventListener('click', preventChecking, true);
+
+ // Dispatch click.
+ // Click event inverts checked value.
+ event = document.createEvent('MouseEvents');
+ event.initEvent('click', true, true);
+ node.dispatchEvent(event);
+
+ // Remove listener to stop further change prevention.
+ node.removeEventListener('click', preventChecking, true);
+
+ // React 16
+ // Restore artificial checked property descriptor.
+ if (descriptor) {
+ Object.defineProperty(node, 'checked', descriptor);
+ }
+ }
+};
diff --git a/ui/app/components/currency-input.js b/ui/app/components/currency-input.js
index 6f7862e51..940238fa5 100644
--- a/ui/app/components/currency-input.js
+++ b/ui/app/components/currency-input.js
@@ -8,8 +8,12 @@ inherits(CurrencyInput, Component)
function CurrencyInput (props) {
Component.call(this)
+ const sanitizedValue = sanitizeValue(props.value)
+
this.state = {
- value: sanitizeValue(props.value),
+ value: sanitizedValue,
+ emptyState: false,
+ focused: false,
}
}
@@ -58,9 +62,11 @@ CurrencyInput.prototype.handleChange = function (newValue) {
if (value === '0' && newValue[newValueLastIndex] === '0') {
parsedValue = parsedValue.slice(0, newValueLastIndex)
}
-
const sanitizedValue = sanitizeValue(parsedValue)
- this.setState({ value: sanitizedValue })
+ this.setState({
+ value: sanitizedValue,
+ emptyState: newValue === '' && sanitizedValue === '0',
+ })
onInputChange(sanitizedValue)
}
@@ -86,17 +92,19 @@ CurrencyInput.prototype.render = function () {
readOnly,
inputRef,
} = this.props
+ const { emptyState, focused } = this.state
const inputSizeMultiplier = readOnly ? 1 : 1.2
const valueToRender = this.getValueToRender()
-
return h('input', {
className,
- value: valueToRender,
- placeholder,
+ value: emptyState ? '' : valueToRender,
+ placeholder: focused ? '' : placeholder,
size: valueToRender.length * inputSizeMultiplier,
readOnly,
+ onFocus: () => this.setState({ focused: true, emptyState: valueToRender === '0' }),
+ onBlur: () => this.setState({ focused: false, emptyState: false }),
onChange: e => this.handleChange(e.target.value),
ref: inputRef,
})