React input - cursor jump to the end - reactjs

I'm using this package react-phone-number-input
Context
I've this phone number field
<InputPhone
{...input}
value={input.value || ''}
placeholder={intl.formatMessage(
placeholder ? messages[placeholder] : messages.placeholder,
)}
style={{
borderColor: meta && meta.touched && meta.error && 'red',
}}
tabIndex={index}
size={size}
/>
This is inside a functional component, I cannot use another textInput because we use this little component in multiple forms plus it has special function inside it, like the style and the placeholder.
Problem
The problem is linked to this package react-phone-number-input: the caret jump to the end if i try to edit a number in the firsts two portions.
Someoneknow how can i fix it since the function SmartCaret of react-phone-number-input was disabled ?

Take a look at this issue - sounds like your problem.
You could try out the Smart Caret feature - the last item on the docs page. Keep in mind it might have limitations on some Android devices.
import SmartInput from 'react-phone-number-input/smart-input'
<PhoneInput
inputComponent={ SmartInput }
placeholder="Enter phone number"
value={ this.state.value }
onChange={ value => this.setState({ value }) }/>
Hope it helps!
If this doesn't work, a minimal, reproducible example would be helpful, like Dupocas suggested.

Related

Is there a way to clear a MUI TextField Input from the form.restart() method in React-Final-Form and capture the event?

The form.restart() in my reset button resets all fields states and values as per my understanding of this Final-Form.
The method fires and resets all fields in my form and I can capture the event in the autocomplete, but I am unable to capture the clear event in the textfield - I have a state (not related to the value of the field) I need tor reset.
My form reset button
<Button
type={"button"}
disabled={submitting || pristine}
variant={"outlined"}
onClick={() => {
form.getRegisteredFields().forEach((field) => form.resetFieldState(field));
form.restart();
if (clearActionHandler) {
clearActionHandler();
}
setFormSubmittedOnce(false);
}}
>
Clear
</Button>;
My textfieldadapter
const [shrink, setShrink] = useState < boolean > false;
const countCharacters: (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => boolean = (e) => {
setCount(e.target.value.length);
return maxCharacterCount === 0 || e.target.value.length < maxCharacterCount;
};
return (
<TextField
{...input}
{...rest}
onChange={(e) => {
if (countCharacters(e)) {
input.onChange(e);
}
}}
value={input.value}
onBlur={(e) => {
!input.value && input.onBlur(e);
!input.value && setShrink(false);
}}
error={meta.error && meta.touched}
helperText={
meta.touched ? (
<React.Fragment>
{maxCharacterCount > 0 ? (
<React.Fragment>
<Typography variant={"body1"} textAlign={"end"}>
{count}/{maxCharacterCount}
</Typography>
<br />
</React.Fragment>
) : null}{" "}
{meta.error}
</React.Fragment>
) : maxCharacterCount > 0 ? (
<React.Fragment>
<Typography variant={"body1"} textAlign={"end"}>
{count}/{maxCharacterCount}
</Typography>
<br />
</React.Fragment>
) : (
""
)
}
placeholder={placeholder}
fullWidth={true}
margin={"dense"}
multiline={multiline > 1}
rows={multiline}
inputProps={inputProps}
InputProps={{
startAdornment: (
<InputAdornment position={"start"} sx={{ width: "24px" }}>
{startAdornment}
</InputAdornment>
),
}}
InputLabelProps={{
shrink: shrink,
}}
onFocus={() => setShrink(true)}
sx={{
"& .MuiInputLabel-root:not(.MuiInputLabel-shrink)": {
transform: "translate(50px, 17px)",
},
}}
/>
);
Versions of packages:
"#mui/material": "^5.11.1",
"react": "^18.2.0",
"react-final-form": "^6.5.9"
I have tried to capture the onChange event with bluring all elements before the reset method is called, that doesn't call the textfield onblur method. I am just not sure how to clear it away.
Make sure that the field in question has an entry in initialValues and that that entry is initialised to an empty string. This is a long shot, so take everything I say as theory, but it looks like what would happen if the initial value was not defined or was explicitly set to undefined.
That would mean when you reset the form, the value prop of the TextField would be set to undefined. This is not a valid controlled value of a TextField, so the behavior of MUI could well be to switch to uncontrolled mode, where the value is kept internally and not actually controlled by the value prop anymore. It depends on the library what happens next as this is generally unexpected and leads to non-deterministic behavior, but it's likely that MUI just keeps the previous controlled value around internally, or it's sitting in a transient DOM state on the element itself since value is judged to be no longer the source of truth and so it was never written to the actual DOM element. When the user types again, onChange would be called, the state in your form set to a string, and it would become controlled again. Base react <input> gives warnings in the console when you transition like this but MUI might not.
This might explain why it does not capture the transition to the original form state.
Note null is usually not allowed either but there's some final form magic that protects you from that one.
I verified MUI does this on a codesandbox (excuse the old class style I just used an old TextField sandbox to build on). If my long shot is correct, the problem is more related to a core issue between your state consistency and the mui lib and less about final form.
The fix would be to make sure the field has an entry in initialValues set to a string. You could optionally put in guards to check for undefined and use '' instead when that's the case.
Take note that '' is actually still falsey so !input.value would evaluate to true when its empty string still. Not sure you care, but something to keep in mind.

Textfield position shifts up after making a selection MUI?

I have this Autocomplete component, it renders a TextField with options to select from , problem is once I make a selection, it losses margin top, my bet is on the text, since there is no text there is no margin! I partially solved it by add in a empty space, which proves my point but still what is the right way to solve this, any idea how to stop this from happening and yet be able to pass an empty text without losing the margin.
The reason it doesn't work is because of the label, you set the label to an empty string whenever there is a selected value:
label={selected.item === "" ? "test" : ""}
Which affects the following rule:
label + .MuiInput-formControl {
margin-top: 16px;
}
If the label is empty, it will be removed from the DOM, so margin-top doesn't get applied anymore. A simple workaround is simply set the label to a non-empty string even if you don't want to display it:
label={selected.item === "" ? "test" : " "}
You can use placeholder props to the TextField component inside Autocomplete rather than conditional label props.
And then if you want to add margin, you can add some styling to the Autocomplete component like style={{marginTop:20}} or using MUI makeStyles API.
https://v4.mui.com/styles/api/#makestyles-styles-options-hook
...
<Autocomplete
id="combo-box-demo"
key={bool}
options={arr}
getOptionLabel={(option) => option}
onChange={handleChange}
style={{marginTop: 20}} // or using makeStyles
renderInput={(params) => (
<TextField
{...params}
placeholder="test" //label="test"
/>
)}
/>
...

React tabs - switching destroys component, need to maintain the same component

I am trying to make a multi-tabbed SPA with React and Material-UI. I use code from this demo as an example: https://codesandbox.io/s/qlq1j47l2w
It appears that if I follow the aforementioned example, I end up returning new instance of the component with tab contents each time I navigate between the tabs:
<Tabs
value={value}
onChange={this.handleChange}
indicatorColor="primary"
textColor="primary"
scrollable
scrollButtons="auto"
>
<Tab label="Tab 1" />
<Tab label="Tab 2" />
</Tabs>
</AppBar>
{value === 0 && <Tab1Contents/>}
{value === 1 && <Tab2Contents/>}
As Tab1Contents is a form, I would like its internal state to be retained, instead of loading a new instance of the component, which the code above appears to do.
What is the best way to get React to use only one instance of the component and 'memorise field values'?
EDIT
I have added Redux to the example, but the store corresponding to the form within the Tab is destroyed the moment I switch away. Is there any other way to implement tabs in React that would hide the tab contents, instead of destroying them and re-creating them from scratch each time I navigate away?
The solution to my problem was quite simple! If you don't want to destroy the component (remove it from DOM), you can simply hide it!
Instead of:
{value === 0 && <Tab1Contents/>}
Use:
<div style={{ display: value === 0? 'block': 'none'}}>
<Tab1Contents/>
</div>
It was already mentioned that you have to prevent your component from being removed from the DOM. The easiest solution with MUI5 TabPanel is in fact to just replace
{value === index && children}
with
{children}
that means your Tabpanel would look like that:
import * as React from "react";
const TabPanel= ({ children, value, index, ...other }) => {
return (
<div
role="tabpanel"
hidden={value !== index}
id={`tabpanel-${index}`}
aria-labelledby={`tab-${index}`}
{...other}
>
{children}
{/* {value === index && children} // This was removed */}
</div>
);
};
export default TabPanel;
No additional logic necessary as the hidden prop already takes care of the visibility aspect. This way your components should maintain their state!
You would need to persist the state between the tab changes. I prototyped a form using React forms documentation for Tab1Container and as you play around with it, the value will disappear
https://codesandbox.io/s/material-demo-ojwhr
What you ideally need to use something like Redux, which will use a store to keep the information even between the state changes like Tab clicks.
Hope this helps!

Material-UI V0.20.2 and React 16.8.3 Autocomplete, enter event does not get triggered to confirm selection

I have the following component:
<AutoComplete
fullWidth
errorText={dirty ? error : null}
onNewRequest={value => onChange(countryCodesByName[value])}
onUpdateInput={value => onChange(countryCodesByName.hasOwnProperty(value) ? countryCodesByName[value] : value)}
searchText={countries[value] || value}
{...inputProps}
{...custom}
menuStyle={{ overflowY: 'auto' }}
/>
My problem is, when searching for a country and I press enter to confirm the selection in the drop-down, the enter event is not triggered. The up and down arrows are working to navigate the list of countries but enter not.
With react 15 it worked because I had react-tap-event-plugin, but is deprecated:
React 16.4 removes a lot of internals (#121) this plugin depends on and will break the plugin.
Since the problem it solves has been fixed in most browsers by now you should migrate away from this plugin.
Update the version of you material ui on version: v3.9.2
https://material-ui.com/versions/

antd Select turn off autocomplete

I am trying to turn off autocomplete on an ant design Select but nothing I try works. This is obviously problematic as chromes (only one tested at this point) autocomplete completely covers the Select Options (Image below).
I am using antd in React and have tried the following on both the Select tag and Form tag:
autoComplete="off"
autoComplete="nope"
autoComplete="business-state"
and none of them seem to turn it off.
Here is a link to a code sandbox with almost the same code but the problem is it does not reproduce the same issue. For some reason the exact same code on sandbox doesn't show the autocomplete. I added an email input to show to test if autocomplete works on that and sure enough it does. I am at a loss for why this is working differently in sandbox than from my server.
https://codesandbox.io/s/wovnzpl9xw
Here is what it looks like when a user is typing to search the Options
Here is what it should look like (it only looks like this on focus but as soon as a user types chrome shows autocomplete)
Figured out a solution to my question, thanks to #trkaplan for pointing me in the right direction. Not the most elegant solution I am guessing so if anyone else has a better way to implement this I am all ears. I essentially had to create an onFocus method for the Select that grabs all of the elements with class ant-select-search__field loops through them and utilize setAttribute to change the autocomplete attribute to an arbitrary value in order to disable it.
onFocus={() => {
if (!this.state.autocompleteDisabled) {
let i;
const el = document.getElementsByClassName(
"ant-select-search__field"
);
for (i = 0; i < el.length; i++) {
el[i].setAttribute(
"autocomplete",
"registration-select"
);
}
this.setState({ autocompleteDisabled: true });
}
}}
I have applied to add the autocomplete="none" into the Select and it is looking fine.
<Select
showSearch
optionFilterProp="children"
onChange={onChange}
onFocus={onFocus}
onBlur={onBlur}
onSearch={onSearchZipCode}
filterOption={(input, option) => option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
placeholder="Choose a City"
autoComplete="none"
// disabled={true}
>
{cities.map((city) => (
<Select.Option key={city.id} value={city.id}>
{city.name}
</Select.Option>
))}
</Select>;
You could also use AutoComplete instead of Select, because with this it's possible to change the underlying Input
<AutoComplete ... >
<Input autoComplete='off' id='myId' />
</AutoComplete>
AutoComplete is "new" and intended to be used if you are fetching data for the input instead of the data being static, but essentially its just a copy of Select and could be used the same way.
Another thing that worked very well for me was the following:
<FormItem>
<Select
autoComplete="new-state"
{...otherProps}
/>
</FormItem>
The autoComplete prop currently isn't documented properly and they don't even cater for TS users properly.
But this worked, if you've got that new word in the autoComplete prop, Chrome (and hopefully other browsers) will ignore that field.
For TypeScript users that have justifiably strict type rules:
<Select
// eslint-disable-next-line #typescript-eslint/ban-ts-comment
// #ts-ignore
autoComplete="new-state"
{...otherProps}
/>
You have to set filterOption to false
See this code sandbox
The only "clean" way I found to have no autocomplete when the label of the input is supposed to be aucompleted according to chrome (see here: Which Attributes Does Chrome Autofill Expect?) is to modify the labels by inserting invisible character ‍ in the label
<Form.Item label={<>Cou‍ntry</>}>
for example will be interpreted as
<Form.Item label={<>Cou‍ntry</>}>
But chrome does not recognize it as a Country input field
In my project I had dynamic inputs so the solution was (using _.times):
<Form.Item key={id} label={_.times(name.length, num => <>{tag.name.charAt(num)}‍</>)}>
Hacky but future proof (I hope)
Going to the FormItem component you can set the name props to "stateEnum" and then your browser won't just assume the field name from the label.
It works nicely for me on Google Chrome.
Example:
<FormItem label="State" name="stateEnum">
...
</FormItem>

Resources