Restrict input to positive numbers in react-select's Creatable - reactjs

I am trying to use react-select's Creatable for an input field of type number. Code below.
import React from 'react';
import Tooltip from '#atlaskit/tooltip';
import Creatable from "react-select/creatable";
import { components } from 'react-select';
const Input = props => {
if (props.isHidden) {
return <components.Input {...props} />;
}
return (
<div>
<Tooltip content={'Custom Input'}>
<components.Input {...props} type="number"/>
</Tooltip>
</div>
);
};
const handleInputChange = (value,action) => {
console.log(value);
console.log(action);
}
export default () => (
<Creatable
closeMenuOnSelect={false}
components={{ Input }}
backspaceRemovesValue
isClearable
onInputChange={handleInputChange}
/>
);
When I type a - or e the onInputChange is not trigerred and the value of the Creatable is not being set. Also if I try to remove the symbols using a backspace it does not work either. Any idea on how to restrict the input to positive numbers only?
Here is a codesandbox example to see what is happening.

The react-select library provides us with a onKeyDown prop that will pass on the event to the callback function.So, all I had to do was use this prop and then prevent default if the user enters a character that I am not expecting them to enter.
Codesandbox example

Related

Add custom data- attribute to Option component react-select

I am creating a test for my dropdown searchable component using react-select library.
I am not able to add data-testid attribute to the Option component when customizing that component as it is defined in react-select documentation.
The data-testid attribute does not show in the DOM for Option element.
Option component
import Select, { components } from 'react-select';
const CustomOption = (props: OptionProps<SearchDropdownOption, false>) => (
<components.Option {...props} data-testid="test-id" />
);
For example i had a success with the Input component for search version of the dropdown and data-testid attribute shows in the DOM:
Input component
import Select, { components } from 'react-select';
const CustomInput = (props: InputProps<SearchDropdownOption, false>) => (
<components.Input {...props} data-testid="test-id" />
);
And than using it in Select component:
<Select<SearchDropdownOption, false>
components={{
Input: CustomInput,
Option: CustomOption,
}}
isSearchable={isSearchable}
/>
It is impossible to add custom attribute data-testid directly to the Option component as i did for Input component. I need to extend this component with an HTML span element, or any other, and add this attribute to that element directly:
const CustomOption = (props: OptionProps<SearchDropdownOption, false>) => {
return (
<components.Option {...props}>
<span data-testid="test-id" key={props.innerProps.key}>
{props.data.label}
</span>
</components.Option>
);
};
NOTE
This key prop is important as it gives the regular React uniqueness to the element and value for that can be used from the react-select's innerProps property:
key={props.innerProps.key}
const OptionWrapper = (customProps: IOptionProps) =>
(nativeProps: OptionProps) => (
<components.Option
{...nativeProps}
innerProps={{
...nativeProps.innerProps,
...customProps
}}
/>
);
Option = OptionWrapper({ 'data-testid':"test-id" })
Try this

React- how to enable a button when TextField has value

So I'm trying to enable a button as soon as I enter a value in the TextField. The only way it is enabled is if i click outside the TextField after I put a value inside the Textfield. I know i'm missing something small but I still haven't found the correct way. I would appreciate your advice .
The code for the TextField and LoadingButton
the Window
I have included only the relevant part.
import { useState } from "react";
export default function Home() {
const [name, setName] = useState("");
const hanldeUserInput = (e) => {
setName(e.target.value);
};
return (
<Dialog>
<TextField onChange={hanldeUserInput} value={name} />
<LoadingButton disabled={name === ""}>Save</LoadingButton>
</Dialog>
);
}
You could keep track of the current value inside your textfield and store it inside the state.
const [txtFieldValue, setTxtFieldValue] = useState<string>('')
and write a function which is triggered on the change event in your textfield:
function handleTxtFieldChange(event) {
if (event.target.id === 'myTextFieldId') {
setTxtFieldValue(event.target.value)
}
}
And then you can adjust your textfield:
<TextField
id={'myTextFieldId'}
// ... all your other stuff
onChange={(event) => {
handleTxtFieldChange(event)
}}
/>
and you can then use the set txtFieldValue to render the button like:
{txtFieldValue != '' ? <Button /> : <></>}
or if you just want to disable it you can just use the
txtFieldValue != ''
as a boolean.

How can we set default value for React RRule Generator in react-admin?

I am using react-admin and react-rrule-generator (https://github.com/Fafruch/react-rrule-generator). Create / Adding records is working fine while using rrule widget. But whenever I try to edit a record, the widget should have its values automatically filled based on the record's values. But the value is always the default one provided by the widget itself. Here is my code:
main_file.jsx
export const JobCreate = (props) => {
return (
<Create {...props}>
<SimpleForm>
<CustomRRuleInput name="recurrency" label="Recurrency" />
</SimpleForm>
</Create>
)
}
recurrency_field.jsx
export const CustomRRuleInput = (props) => {
const {
input: { onChange },
meta: { touched, error },
} = useInput(props)
return (
<Labeled label={props.label}>
<RRuleGenerator
onChange={onChange}
name={props.name}
/>
</Labeled>
)
}
If I add value={props.record.recurrency} in RRuleGenerator component, I can't change values because I kind of fixed / hardcoded its value which is constant even if I try to change them. If this widget had a prop called defaultValue then it would have worked out!
How can I achieve this?
If you check closely the documentation's Inputs/Writing your own input part you will notice that custom input compoenents using either useField or useInput hooks still receive the source prop which is passed inside the input as part of the hook parameters.
Try this:
Inside main_file.jsx
<CustomRRuleInput source="recurrency" label="Recurrency" />
Inside recurrency_field.jsx
const {
input: { name, onChange },
meta: { touched, error },
} = useInput(props)
return (
<Labeled label={props.label}>
<RRuleGenerator
onChange={onChange}
name={name}
/>
</Labeled>
)
Never mind I did it! I can use this for creation as well as updating records. I also used rrule library for converting rrule to human readable text which gets displayed in TextInput field just below RRule widget. The text dynamically changes when you change data in RRule widget.
recurrency_field.jsx
import RRuleGenerator from "react-rrule-generator"
import React, { useState } from "react"
import { useInput, Labeled, TextInput } from "react-admin"
import { rrulestr } from "rrule"
export const CustomRRuleInput = (props) => {
const record = props.record
const {
input: { onChange },
} = useInput(props)
const [state, setState] = useState(record[props.name])
return (
<>
<Labeled label={props.label}>
<RRuleGenerator
onChange={(val) => {
setState(val)
onChange(val)
}}
value={state}
name={props.name}
/>
</Labeled>
<TextInput
fullWidth
disabled
label={"RRule Text"}
value={state ? rrulestr(state).toText() : ""}
/>
</>
)
}
main_file.jsx
<CustomRRuleInput name="recurrency" label="Recurrency(r rule)" />

Custom react hook to set input form not working

I've tried creating a custom hook, following the examples an article about handling forms in react using hooks. I should also add I'm very new to react.
This is the content of hooks/useInput.js:
import { useState } from "react";
function useInput(initialValue) {
const [value, setValue] = useState(initialValue);
function handleChange(event) {
console.log(event.target.name);
console.log(event.target.value);
setValue(event.target.value);
}
return [value, handleChange];
}
export default useInput;
In a component I have the following:
import useInput from "hooks/useInput";
export default function Address(props) {
const [fullName, setFullName] = useInput("");
return (
<FormControl fullWidth>
<CustomInput
required
labelText="Full name"
id="fullName"
formControlProps={{
fullWidth: true
}}
value={fullName}
onChange={setFullName}
/>
</FormControl>
);
}
When I try to input some text (or even if I try to change the default state), nothing happens.
Any breakpoint that I set in useInput.js are not hit and no logging appears in the console.
Where am I going wrong?
If you want to see your current input value with a button click you can see like this. I didn't even change your userInput.js file. I can see the updated value by doing this and console as well.
export default function Address(props) {
const [fullName, setFullName] = useInput("");
return (
<>
<input
placeholder="Name"
value={fullName}
onChange={setFullName}
/>
<button onClick={() => {
console.log(fullName);
}}>Submit</button>
</>
);
}
Since I don't know about your CustomInput, I have written this with default input and button. Please check your CustomInput correctly. Because default input is working.

Range bar in redux form

i have a range bar which i create from react-slider. I want to use it in a redux-form. My problem is that the values are not returning. To be more specific when i sumbit my form the other fields return values while this range bar returns undefined. How do i have to use the Range in a redux-form?? My code is
<FormGroup className="col-sm-3 ">
<Label for="value-slider">VALUE</Label>{' '}
<Field
id="value-slider"
name="value-slider"
component={Range}
/>
</FormGroup>
You cannot pass any random component to component prop. It must implement interface of redux-form (see usage), what Range does not. In short, passed element must at least trigger event with input.onChange and display value from input.value prop:
const MyRange = props => (
<Range value={props.input.value} onChange={props.input.onChange} />
)
And then use it:
<Field
id="value-slider"
name="value-slider"
component={MyRange}
/>
I've run into this issue just now, thanks to maddox2 for the answer which set me along the right lines, however, as one user pointed out in the comments above, the solution above will give TypeError: value.map is not a function.
To fix this, you need to explicitly set the type of the field in initialValues, when you use reduxForm()to create the form. Example code is below.
Note how additional props used by Range are also passed to ReduxRange in the props prop, and that these are then passed down to the Range in the wrapping component.
import React from 'react';
import { Field, reduxForm } from 'redux-form'
import Range from 'rc-slider/lib/Range';
import 'rc-slider/assets/index.css';
// initial wrapper - note alterations
const ReduxRange = props => {
const { input: { value, onChange } } = props
return (
<Range
value={props.input.value}
onChange={props.input.onChange}
{...props}
/>
)
}
// the form
export const SomeReduxForm = props => {
const { handleSubmit } = props;
// these props will get passed to the Range
const rangeProps = {
defaultValue:[2020, 2040],
marks:{
2020: '2020',
2030: '2030',
2040: '2040',
2050: '2050',
2060: '2060',
2070: '2070',
2080: '2080',
},
pushable:true,
allowCross:false,
min:2018,
max:2080,
step:1,
}
return (
<section className="box box-default">
<form>
<div className="box-header">Year Range</div>
<div className="box-body">
<Field
name="yearRange"
component={ReduxRange}
props={rangeProps}
/>
</div>
</form>
</section>
)
}
export default reduxForm({
form: 'someReduxForm',
initialValues: {
yearRange: [2020, 2040]
}
})(SomeReduxForm)

Resources