How to check checkbox when testing if it has defaultChecked? - javascript

Here is a demo of what I'm trying to achieve: link
The thing is, I see that I can change the value and make the checkbox checked if there is no defaultChecked property, but it's legacy code and I can't take it out easily, I need that property there.
I need: e.currentTarget.checked to come true in the testing environment, it comes false. IT does come true when I remove defaultChecked false, but I need it there.
I use react-testing-library with jest.
And code from demo here:
export class OptionCheckbox extends React.Component {
changeHandler = e => {
console.log("onchange triggered", e.currentTarget.checked);
};
render() {
return (
<div>
<input
onChange={this.changeHandler}
defaultChecked={false}
id="test"
name="test"
type="checkbox"
/>
<label htmlFor="test">
<span />
Test
</label>
</div>
);
}
}
// TEST
test("Example test of change event", () => {
const component = renderIntoDocument(<OptionCheckbox />);
component.getByLabelText("Test").setAttribute("checked", "");
fireEvent.change(component.getByLabelText("Test"));
component.unmount();
});

The solution is to access property directly, rather than through setAttribute method.This will do what I wanted and get me e.currentTarget.checked === true in the onchange handler.
UPDATE: This was for the old version of the library, around 3.x.y. In the new version I don't do what described below, and just use fireEvent.click;
/* SOLUTION */
component.getByLabelText("Test").checked = true;
fireEvent.change(component.getByLabelText("Test"));
/* SOLUTION */

Related

I want the input data to go into the title and the textarea data to go into the content

import React from 'react';
export default class CreateNote extend React.component {
constructor(props) {
super(props);
this.state = {note:{title:" ",content:" "} };
console.log(this.state);
}
const inputEvent = (event) => {
const value = event.target.value;
const name = event.target.name;
this.setState({
note:{title: ,content: }
})
}
render(){
return(
<input
type="text"
placeholder="Title"
name="title"
id=""
value={note.title}
onChange={inputEvent}
/>
<textarea
name="contant"
id=""
value={note.contant}
cols=""
rows=""
placeholder="Write a Notes"
onChange={inputEvent}
onClick={expanded}>
</textarea>
)
}
When I write text in the input field and textarea then do not go data to note state. I want the input data to go into the title and the textarea data to go into the content.
Now I will write what on setState? I want to see result in the console.
Tip: Your code is riddled with errors and typos (e.g., CreateNote extend React.component instead of CreateNote extends React.Component , writing const for a function inside a Class Component, contant instead of content). For better chances of getting a help, kindly post the working code that you have, so that duplicating the issue becomes easier for the people looking to help you.
Now on to the solution. If you are a beginner, the best way to get what you want is to make separate functions - one that is triggered when the text inside the input is changed, and the other which is triggered when the text inside textarea is changed.
Note that you have kept title and content inside a state object note. This means in order to change title or content, you have to update the entire note object. Be careful if you update just one key of note without persisting the other one (e.g., if you update title only and want to leave content unaffected), you should use the spread operator ... which helps to clone the object's values, so then after that you can update the value of the key you want.
titleChange = (e) => {
this.setState({
note: {
...this.state.note,
title: e.target.value
}
});
};
contentChange = (e) => {
this.setState({
note: {
...this.state.note,
content: e.target.value
}
});
};
Update, a cleaner approach: We can also use the "name" attribute of the input and textarea to our benefit and combine the two functions into one (I've named it inputChange )
inputChange = (e) => {
this.setState({
note: {
...this.state.note,
[e.target.name]: e.target.value
}
});
};
You can find a working CodeSandBox here. Note that using spread operator will not be necessary if you move out the required fields title and content out from notes and make them state variables directly. Check out the AppWithoutNote.js file to see how it can be implemented.

Disable scrolling on `<input type=number>` in React

This question was asked before but provided solution is just for jQuery, as I'm facing same problem in ReactJs.
Is it possible to disable the scroll wheel changing the number in an input number field? I removed spinner arrows using CSS but mouse wheel still working and messing the functionality.
I want input type number because it gives numeric keyboard on mobile/touch devices.
Simplest answer:
<input type="number" onWheel={(e) => e.target.blur()} />
e is short for event.
This also works:
<input type="number" onWheel={() => document.activeElement.blur()} />
Either of these can be used either in a functional or in a class component.
You can blur the field on onWheel handler. Something like this
<input type='number' onWheel={ event => event.currentTarget.blur() } />
I had the same problem, except I was working on desktop version only.
To blur the focus on the input as suggested in other answers works to stop the scrolling. But it wasn't what I wanted. I still want the user to be able to change the input with the keyboard.
So to disable scrolling on <input type=number> in React I added an onFocus property as follows:
<input
//...some input properties...//
type="number"
onFocus={(e) => e.target.addEventListener("wheel", function (e) { e.preventDefault() }, { passive: false })}
/>
It worked fine for me. I hope it helps others.
I solved this using a functional component that wraps the input element and adds an event listener for "wheel" and prevents default behavior. I find this preferable to using blur() which may have undesirable UX.
// Simply a wrapper for <input type="number"/> that disables scrolling to increment
import { useEffect, useRef } from "react";
export const NumberInput = (props) => {
const quantityInputRef = useRef(null);
useEffect(() => {
const ignoreScroll = (e) => {
e.preventDefault();
};
quantityInputRef.current && quantityInputRef.current.addEventListener("wheel", ignoreScroll);
}, [quantityInputRef]);
return <input ref={quantityInputRef} type="number" {...props} />;
};
In production, I actually use this component to disable scroll-incrementing for the <TextField> component from material-ui instead of <input>. I've used the native input in my example because that's what the question was asking about.
I don't think this is the best solution if you only want a numeric keyboard since there is a property that actually let you set whatever keyboard type you want, e.g. inputMode="numeric"
Changing global events is not good practice and neither is blurring out of the field.
In react version you should use ref. Take a look at the example below :
import React, { Component, createRef } from "react";
class MyInput extends Component {
constructor(props) {
super(props);
this.inputRef = createRef();
}
onWheel = () => {
this.inputRef.current.blur();
};
render() {
return (
<div>
My input number :
<input type="number" ref={this.inputRef} onWheel={this.onWheel} />
</div>
);
}
}
export default MyInput;
codesandbox here
this can also be achieved using css.
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button
{
-webkit-appearance: none;
margin: 0;
}

In Reactjs how can I do custom message for checkbox. I see there are lot of answers for this issue which works in javascript not in reactjs

I am still getting select checkbox but I need to show "agree terms" (I do see lot of examples similar to this in javascript but those are not working for me)
please someone help me with this.
Thanks
Update:
I got the output here is the link which I referred: https://www.the-art-of-web.com/html/html5-checkbox-required/#example1
One way to go is to create a stateful component that keeps track of just one piece of state: whether it is checked or not. You can then derive your message from the checked state inside of your render method. For instance, you can create a helper method that returns your custom validation message when not checked and returns an empty string when checked. See code snippet.
const setCustomValidity = isChecked => {
if (isChecked) {
return '';
} else {
return 'You must agree to the terms';
}
}
class Checkbox extends React.Component {
constructor(props) {
super(props);
this.state = {
checked: false,
}
this.handleChange = this.handleChange.bind(this);
}
handleChange() {
this.setState({
checked: !this.state.checked,
})
}
render() {
const message = setCustomValidity(this.state.checked);
return (
<div>
<p className="customMessage">{message}</p>
<input
type="checkbox"
className="checkBox"
name="checkbox"
onChange={this.handleChange}
required
/>
</div>
);
}
}
ReactDOM.render(<Checkbox />, document.querySelector("#app"));
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
You should post your solution so others can learn from it.
Here is a solution:
<input
id="checkers"
value={this.state.checkered}
onInvalid={(e) => {
e.target.setCustomValidity('Please agree to the terms and conditions!');
}}
onChange={(e) => {
**e.target.setCustomValidity(" ");
setTimeout((e) => {
document.getElementById('checkers').setCustomValidity("");
}, 200)**
this.setState({
checkered: !this.state.checkered
});
}}
type="checkbox"
required
>
</input>
Btw, for this particular case I do not believe you need the code in bold. Though for those who end up having this problem in the future... there is a funny glitch sometimes with the setCustomValidity function wherein even after the validation message fires, the message will keep firing on onInput or onChange of the input field. In my case, e.target.setCustomValidity("") did not actually work (still not sure why), though had to pass a blank space into the a setCustomValidity function to make it work. With this, I then had to reset the setCustomValidity on a time out. Hacky though works like a charm now. If you want to pass in different messages depending on the error, just look at the e.target.validity to see what errors are firing and then return a new message with the setCustomValidity.

Dispatching and listeing for events in different components - reactjs

I am trying to create a custom event in one component and add an event listener in another component. The component that is listening for the event contains a function that I want to execute on the event. Below are what I have in the two components, I just feel like I'm going about this in the wrong way...
Component #1
toggleWidget() {
const event = new CustomEvent('sliderClicked', {
bubbles: true,
});
const sliderToggle = document.getElementById('input');
sliderToggle.dispatchEvent(event);
this.setState({
checked: !this.state.checked,
});
}
/* and then in my render... */
render() {
const displaySlider = this.state.isSliderDisplayed ? (
<div className="slider-container" >
<label className="switch" htmlFor="input">
<input type="checkbox" checked={this.state.checked} onChange={this.toggleWidget} id="input" />
<span className="slider round" />
</label>
<p className="batch-slider-title"> Batch Widget </p>
</div>) : null;`
Component Two
window.addEventListener('sliderClicked', this.refreshLayout);`
Any ideas as to what I may be doing wrong?
Basically it should work, but in react - if you rendered an element in a component you can use the ref to access it:
<input
type="checkbox"
checked={this.state.checked}
onChange={this.toggleWidget}
id="input"
ref={(c) => this.input = c}
/>
And your toggleWidget function should be something like this:
toggleWidget() {
...
this.input.dispatchEvent(event);
...
}
In React it's pretty common to pass down callbacks from parent to child.
const Child = ({handleClick}) => (
<div onClick={ handleClick } >Click me!</div>
);
const Parent = () => {
const childClickHandler = event => {
// do stuff
alert('My child is calling?');
}
return (
<Child handleClick={ childClickHandler }/>
);
};
Maybe that could work for you? You can try the code here. (JSFiddle)
Refs are generally considered something to avoid in React as they couple components together. see the documentation here:
https://facebook.github.io/react/docs/refs-and-the-dom.html
Your first inclination may be to use refs to "make things happen" in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy. Often, it becomes clear that the proper place to "own" that state is at a higher level in the hierarchy. See the Lifting State Up guide for examples of this.
Try using a global state container like redux and when you "toggleWidget" in one component, set a property in your redux store. Listen to that property by setting it as a prop in your second component(the one that you want to respond to a change/toggle). On change of that property your component will have the "componentWillReceiveProps" lifecycle method called and you can then have your "responding" component take whatever action you like.

React - change input defaultValue by passing props

Consider this example:
var Field = React.createClass({
render: function () {
// never renders new value...
return (
<div>
<input type="text" defaultValue={this.props.value || ''} />
</div>
);
}
});
var App = React.createClass({
getInitialState: function () {
return {value: 'Hello!'};
},
changeTo: function (str) {
this.setState({value: str});
},
render: function () {
return (
<div>
<Field value={this.state.value} />
<button onClick={this.changeTo.bind(null, 'Whyyyy?')}>Change to "Whyyyy?"</button>
<button onClick={this.changeTo.bind(null, void 0)}>Change to undefined</button>
</div>
);
}
});
React.render(
<App />,
document.getElementById('app')
);
I want to pass value into defaultValue as prop of dumb input component. However it never re-renders it.
As a previous answer mentioned, defaultValue only gets set on initial load for a form. After that, it won't get "naturally" updated because the intent was only to set an initial default value.
You can get around this if you need to by passing a key to the wrapper component, like on your Field or App component, though in more practical circumstances, it would probably be a form component. A good key would be a unique value for the resource being passed to the form - like the id stored in the database, for example.
In your simplified case, you could do this in your Field render:
<div key={this.props.value}>
<input type="text" defaultValue={this.props.value || ''} />
</div>
In a more complex form case, something like this might get what you want if for example, your onSubmit action submitted to an API but stayed on the same page:
const Form = ({item, onSubmit}) => {
return (
<form onSubmit={onSubmit} key={item.id}>
<label>
First Name
<input type="text" name="firstName" defaultValue={item.firstName} />
</label>
<label>
Last Name
<input type="text" name="lastName" defaultValue={item.lastName} />
</label>
<button>Submit!</button>
</form>
)
}
Form.defaultProps = {
item: {}
}
Form.propTypes = {
item: PropTypes.object,
onSubmit: PropTypes.func.isRequired
}
When using uncontrolled form inputs, we generally don't care about the values until after they are submitted, so that's why it's more ideal to only force a re-render when you really want to update the defaultValues (after submit, not on every change of the individual input).
If you're also editing the same form and fear the API response could come back with different values, you could provide a combined key of something like id plus timestamp.
defaultValue only works for the initial load. After that, it won't get updated. You need to maintain the state for you Field component:
var Field = React.createClass({
//transfer props to state on load
getInitialState: function () {
return {value: this.props.value};
},
//if the parent component updates the prop, force re-render
componentWillReceiveProps: function(nextProps) {
this.setState({value: nextProps.value});
},
//re-render when input changes
_handleChange: function (e){
this.setState({value: e.target.value});
},
render: function () {
// render based on state
return (
<div>
<input type="text" onChange={this._handleChange}
value={this.state.value || ''} />
</div>
);
}
});
I'm fairly certain this has to do with Controlled vs. Uncontrolled inputs.
If I understand correctly, since your <input> is Uncontrolled (doesn't define a value attribute), then the value will always resolve to the value that it is initialized with. In this case Hello!.
In order to overcome this issue, you can add a value attribute and set it during the onChange:
var Field = React.createClass({
render: function () {
// never renders new value...
return (
<div>
<input type="text" defaultValue={this.props.default || ''} value={this.props.value} />
</div>
);
}
});
Here is a plunker showing the change.
You can make the input conditionally and then every time you want to force an update of the defaultValue you just need to unmount the input and then immediately render it again.
The issue is here:
onClick={this.changeTo.bind(null, 'Whyyyy?')}
I'm curious why you bind to null.
You want to bind to 'this', so that changeTo will setState in THIS object.
Try this
<button onClick={this.changeTo.bind(this, 'Whyyyy?')}>Change to "Whyyyy?"</button>
<button onClick={this.changeTo.bind(this, void 0)}>Change to undefined</button>
In Javascript, when a function is called, its called in the scope where it was called from, not where it was written (I know, seems counter intuitive). To ensure it is called in the context you write it, you need to '.bind(this)'.
To learn more about binding and function scope, there are lots of online tutes, (some much better than others) - you might like this one: http://ryanmorr.com/understanding-scope-and-context-in-javascript/
I also recommend using the React Dev tools if you are using firefox or chrome, this way you would have been able to see that state.message was not changing:
https://facebook.github.io/react/blog/2015/09/02/new-react-developer-tools.html
Use conditional rendering, then the component will load correct initial value. Something like in this module:
class MenuHeaderInput extends React.Component{
constructor(props){
super(props);
this.handleBlur = this.handleBlur.bind (this);
}
handleBlur (e) {
this.props.menuHeaderUpdate(e.target.value);
}
render(){
if (this.props.menuHeader) {
return (
<div className="w3-row w3-margin" onClick = {() => this.props.handleTitleClick (10)}>
<div className="w3-third" ><pre></pre></div>
<input
className = {"w3-third w3-input w3-jumbo " + EDIT_COLOR}
type = "text"
defaultValue = {this.props.menuHeader}
onBlur = {this.handleBlur}
/>
<div className="w3-third" ><pre></pre></div>
</div>
)
}
else {
return null;
}
}
}
Related to Sia's excellent answer above: https://stackoverflow.com/a/41962233/4142459.
For my case I had a few ways in which a form could be updated:
users could input values into form fields
An API request allowed users to restore from previous versions
Users could navigate to a filled out form (using queryParams of the URL)
clearing the form fields.
Etc more ways of allowing all the fields or just a single change to happen from user action or websockets.
I found that the easiest way to make sure the state of the form is reflected in its inputs is indeed:
To provide a manually-controlled key prop on the top level of the form or parent element to the form (as long as it is above the inputs in the DOM tree.
When users are typing a key update does not need to happen.
I made the key be a simple formHistoricalVersion and as certain updates external to a user typing/selecting/etc interacting with the form field's values happened I incremented the formHistoricalVersion.
This made sure that the state of the form whether by user action or by API request was in-sync--I had complete control over it.
Other solutions I tried:
While making the API request make the whole form disappear (when loading change to a loading spinner instead of the form). Disadvantage to performance and for clearForm it was a bit crazy to do, but possible with setImmediate to convert the form to a loading spinner when they first clear it, then setting isLoading back to false in the setImmediate.
Adding a key on each input: this worked amazingly, but it had a weird blip whenever users would type so I had to get rid of it.
Putting a static key for the form (field.id) (as suggested by above answer) didn't cover all the use cases I had.
In conclusion, it worked pretty easily to set the key of the form with react/redux, I just would add the equivalent of:
return {
...state,
formFieldState: payload.formFields,
historicalFormVersion: state.historicalFormVersion + 1
}
This was necessary because I was using some 3rd party libraries and my own Numeric Input that took in value as a prop but used value as a defaultValue:
const NumberDisplay: FunctionComponent = ({ value, setValue }) => (
<input
defaultValue={convertToSpecialNumberDisplay(value)}
onBlur={(e) => convertToSpecialNumberDisplay(e.target.value)}
onFocus={(e) => convertToNumberFromDisplay(e.target.value)}
onChange={(e) => setValue(e.target.value)}
/>
)
Approximate Redux of overall Form:
const FullForm: FunctionComponent = () => {
const dispatch = useDispatch();
const formState = useState((state) => state.formState);
const formHistoricalVersion = useState((state) => state.formHistoricalVersion);
return (
<form key={formHistoricalVersion}>
{renderFormFields(formState, dispatch)}
</form>
)
}
I also face this problem, what I did was to manually update the input value when the props has change. Add this to your Field react class:
componentWillReceiveProps(nextProps){
if(nextProps.value != this.props.value) {
document.getElementById(<element_id>).value = nextProps.value
}
}
You just need to add an id attribute to your element so that it can be located.

Categories

Resources