React - InputMask maskPlaceholder is saving as input value - javascript

I'm using InputMask with maskPlaceholder = {"XXX"} and when I check the currentState.value after focusing on the input, the value holds the placeholder value and not null.
I don't want the placeholder to be a part of the value itself, and before I insert any character I want the currentState.value to be null.
Tried to use maskChar={"X"} but this gives me a warning:
Warning: React does not recognize the maskChar prop on a DOM element

Related

Redux Field validation fails after supplying a value

A little context:
I have a form in which the user needs to select a date from a date picker, then submit the form with the selected date and some other pre-populated information. I am using Redux Field to wrap a custom component which includes the React Date Picker.
The problem:
I am using the validate prop for Redux <Field>, which simply enforces that the input value not be undefined. The problem is when I select a date I see the error message that I would normally see when validation fails. In other words the app thinks the date picker input value is undefined even though it clearly has a value.
Troubleshooting
I console logged this.props and checked the React Components Tab in dev tools, and observed the prop in question, "moveInDate", before and after selecting a date. I see an empty string prior to selecting a date (this is expected since I am setting an empty string as an initial value), and I see the expected value after selecting a date.
I also console logged the input value in the wrapped component, and I can see the name of the element and selected value are making down into it.
At this point I'm not sure where to look. Everything is updating as it should from what I can tell, yet the app still sees the input as having a value of undefined some how. I feel I am missing something as it relates to handling the input with Redux. It is useful to note I'm using this Field implementation on another page in the app, and it works without issue. I speculate there must be something elsewhere in the parent component messing with the state, but I'm not sure what to look for.
On a related note, I have a similar issue when selecting a dropdown that is also wrapped in Redux <Field>: the dropdown options don't always display, and I get the same validation bug as described above when I am able to select an option. I don't mean to get off topic with this, but I can't help but feel these issues are related.
I will post code I think will be helpful, but it is quite a lot in its entirety.
Field:
<Field
highlightDates={busyDates}
name="moveInDate"
type="text"
component={DateField}
label="Installation Date"
onChange={moveInDate =>
this.handleMoveInDateChange(
moveInDate,
leaseLength
)
}
validate={[required, this.dateAvailable]}
filterDate={(date) => {
const day = date.day();
return day !== 0 && day !== 6;
}}
minDate={
mode === MODE_RENEW &&
formValues?.currentMoveOutDate !== null
? moment(formValues?.currentMoveOutDate)
: moment().add(settings.blackoutPeriod, 'days')
}
maxDate={
mode === MODE_RENEW &&
formValues?.currentMoveOutDate !== null &&
moment(formValues?.currentMoveOutDate).add(1, 'week')
}
openToDate={
formValues?.moveInDate ||
this.getFirstAvailableDateForMoveIn()
}
Validate Rule
export const required = value => value ? undefined : 'You can\'t leave this empty';
Handler:
handleMoveInDateChange(moveInDate, leaseLength) {
const newMoveOutDate = getMoveOutDateByMoveInDate(
moveInDate,
leaseLength,
this.getBusyMoveOutDates()
);
if (leaseLength && moveInDate) {
this.props.formValues.moveOutDate = newMoveOutDate;
}
}
The handler simply calculates a move out date based on the move in date, then adds it to form values. There isn't a move out date picker in the form. I thought this handler could be causing a problem, but it's not doing anything with the move in date (the problematic input).
Final thought:
My question is admittedly similar to this one, but in my case the issue arises when inputting a new value, and not from dealing with a preloaded value.
Update
I noticed when selecting a date in the datepicker, the value never changes. I refactored the validate rule with braces so I could console log the arg, and there is nothing making it into the function. Conversely when I try this on another page using the same <Field> with datepicker I see the new value each time I change it.
I resolved the issue with keepDirtyOnReinitialize: true in the redux connect code.
I discovered the date picker was working correctly when I commented out the values I was assigning in initialValues in mapStateToProps, so it looks like the problem was the date picker always thought it had a pristine value, which is why the redux field never thought the value changed (from null to the selected value), so the validation logic didn't think anything was in there!
Hope this helps someone who stumbled across a similar problem.

How to programatically set invalid value in input type=number?

I want to set an invalid value to input element with type=number.
I'm writing unit tests for my component which uses input with type=number and it behavior depends on whether the input is empty or not not. Note that I'm treating invalid values like '1.2.3' as not empty, even though it's value property is ''.
While it's possible for user in browser to enter invalid values like '1.2.3' I cannot simulate it for my tests.
// let input be HTMLInputElement
input.value = '1.2.3';
console.log(input.value); // ""
console.log(input.validity.valid); // true
I would like to somehow set invalid value for my input type=number, so I could test if my component's logic is valid.
You can use
input.setCustomValidity("dummy value");
to make input.validity.valid return false.
For an idea of why this works, see this CodePen. What it does is it sets an error message that indicates the input is invalid. To make it appear valid once again, use
input.setCustomValidity("");

Use page.type on only numeric input fields

I am trying to fill out a form on a webpage using Puppeteer, but there are some input fields that only accept numeric input, like:
<input type="text" inputmode="numeric">
But I can't write to these Input fields with page.type like I do with normal Input fields. When I try it I get the error message:
TypeError: text is not iterable
On all other inputs, page.type works perfectly fine.
This error occurred because you are trying to invoke page.type() function with a number instead of the string type.
page.type(selector, text[, options])
selector <string> A selector of an element to type into. If there are multiple elements satisfying the selector, the first will be used
text <string> A text to type into a focused element.
options <Object>
delay <number> Time to wait between key presses in milliseconds. Defaults to 0.
returns: <Promise>
For instance:
await page.type('#mytextarea', '123')
or you can set value with eval function:
await page.$eval('selector', field => field.value = 12);

String interpolation using react

I am trying to figure out the React way of doing the following:
I have an url that I get from my server with placeholders for url parameters:
http://localhost/{someSlug}/?FirstParameter={FirstParameter}&SecondParamter={SecondParameter}
under the url I have an input field for each parameter, or slug, which should update the url using state.
I solved it quickly by using jquery and regex. I wrapped the Parameter value in a <span data="{FirstParameter}">{FirstParameter}</span>.
http://localhost/<span data="{someSlug}">{someSlug}</span>/?FirstParameter=<span data="{FirstParameter}>{FirstParameter}</span>&SecondParamte‌​r=<span data="{SecondParameter}">{SecondParameter}<span>
Which allowed me to find and replace the value when ever the input changes.
const $el = $(`span[data="{${prop}}"]`);
$el.text(e.target.value);
if ($el.text().trim() === '') {
$el.text(`{${prop}}`)
}
But I am wondering how I would solve this without jQuery.

React.js controlled text cursor focus issue

I have a simple controlled input of type number like below.
<input type="number" value={+value} step={1} onChange={this.updateMyChange} />
My value often returns a decimal number like 123.123 . My problem is, when I try edit the value. The cursor loses focus and shifts to the beginning ignoring the whole numbers as soon as the decimal places are cleared. Like below:
How do I address this? Immediately after the decimal places are cleared, the cursor jumps to the beginning thereby making it impossible to edit the whole numbers. Any help would be appreciated.
Update
Below is the remaining code as requested by the user below.
render() {
const {value} = this.state;
return (
<input type="number" value={+value} step={1} onChange={this.updateMyChange} />
)
}
And my updateMyChange method is simply
updateMyChange(e) {
this.setState({ value: e.target.value });
}
It does nothing much simply sets the new value. The cursor position jumps to the end as soon as decimal places are cleared. It does not set cursor for whole numbers.
This is how React updates an input field's value:
node.setAttribute(attributeName, '' + value);
When you set value attribute using that method, the caret goes to the beginning of the field, regardless of using React. You can see what I am saying in this fiddle - https://jsfiddle.net/5v896g3q/
(Just try and position the cursor in the field, between changes).
According to MDN, setAttribute is unstable when dealing with value. The recommended way of changing value is by accessing the value property of the element, like element.value = newValue. If you use that approach, all seems to go as expected.
This is all I can tell for sure. Now let's speculate a little. When you type anything in that field, you are:
updating the value of the input
reading that value, and sending to React as state
React updates the value of the input, with the new state
When you are typing on the field, step 3 is likely to have no impact, because when the value comes back, the input already got it right. Except on the case with the float number. When your field reads 1., the actual value React updates the field with is 1. And React uses the evil method (setAttribute).
So, a workaround I found was setting the value of the field, using the proper method, before React touches it, on componentWillUpdate:
componentWillUpdate(nProps, nState){
this.refs.input.value = '0' + nState.value
}
The problem there, is that it is "numerizing" the value on every change, meaning I won't be able to have a point (1.). For that reason, I will only edit the input in case new value is 2 characters shorter than the old one (point + digit after point):
componentWillUpdate(nProps, nState){
if(this.state.value.length - nState.value.length === 2){
this.refs.input.value = '0' + nState.value
}
}
Working example - https://jsfiddle.net/bsoku268/3/
note: the fiddle is for demonstration purposes, and not supposed to be a bulletproof solution, as there are many ways of interacting with an input field, such as copy & paste, drag & drop, autofill, etc

Categories

Resources