mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge pull request #3594 from danjm/i3344-improve-currency-input
Currency input defaults to empty string
This commit is contained in:
commit
424e98f6a8
@ -1,4 +1,4 @@
|
|||||||
const reactTriggerChange = require('react-trigger-change')
|
const reactTriggerChange = require('../../lib/react-trigger-change')
|
||||||
const {
|
const {
|
||||||
timeout,
|
timeout,
|
||||||
queryAsync,
|
queryAsync,
|
||||||
|
161
test/lib/react-trigger-change.js
vendored
Normal file
161
test/lib/react-trigger-change.js
vendored
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -8,8 +8,12 @@ inherits(CurrencyInput, Component)
|
|||||||
function CurrencyInput (props) {
|
function CurrencyInput (props) {
|
||||||
Component.call(this)
|
Component.call(this)
|
||||||
|
|
||||||
|
const sanitizedValue = sanitizeValue(props.value)
|
||||||
|
|
||||||
this.state = {
|
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') {
|
if (value === '0' && newValue[newValueLastIndex] === '0') {
|
||||||
parsedValue = parsedValue.slice(0, newValueLastIndex)
|
parsedValue = parsedValue.slice(0, newValueLastIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
const sanitizedValue = sanitizeValue(parsedValue)
|
const sanitizedValue = sanitizeValue(parsedValue)
|
||||||
this.setState({ value: sanitizedValue })
|
this.setState({
|
||||||
|
value: sanitizedValue,
|
||||||
|
emptyState: newValue === '' && sanitizedValue === '0',
|
||||||
|
})
|
||||||
onInputChange(sanitizedValue)
|
onInputChange(sanitizedValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,17 +92,19 @@ CurrencyInput.prototype.render = function () {
|
|||||||
readOnly,
|
readOnly,
|
||||||
inputRef,
|
inputRef,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
const { emptyState, focused } = this.state
|
||||||
|
|
||||||
const inputSizeMultiplier = readOnly ? 1 : 1.2
|
const inputSizeMultiplier = readOnly ? 1 : 1.2
|
||||||
|
|
||||||
const valueToRender = this.getValueToRender()
|
const valueToRender = this.getValueToRender()
|
||||||
|
|
||||||
return h('input', {
|
return h('input', {
|
||||||
className,
|
className,
|
||||||
value: valueToRender,
|
value: emptyState ? '' : valueToRender,
|
||||||
placeholder,
|
placeholder: focused ? '' : placeholder,
|
||||||
size: valueToRender.length * inputSizeMultiplier,
|
size: valueToRender.length * inputSizeMultiplier,
|
||||||
readOnly,
|
readOnly,
|
||||||
|
onFocus: () => this.setState({ focused: true, emptyState: valueToRender === '0' }),
|
||||||
|
onBlur: () => this.setState({ focused: false, emptyState: false }),
|
||||||
onChange: e => this.handleChange(e.target.value),
|
onChange: e => this.handleChange(e.target.value),
|
||||||
ref: inputRef,
|
ref: inputRef,
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user