Svelte Jest test case making document.getElementId as null - javascript

I am trying to write the unit test case for my svelte component using jest-transform-svelte library. Following is the component:
XYZ.svelte
<script>
function submit() {
var name = document.getElementById("name").value; // Here document.getElementById("name") is coming as null when run from unit test case.
dispatch("abc", {
text: name
});
}
</script>
<input id="name" value="" type="text" label="Name" />
<button label="Submit" variant="primary" on:click={submit} />
XYZ.test.js
import XYZ from "XYZ.svelte";
describe("XYZ Component", () => {
it('dispatches abc event on submit', (next) => {
const target = document.createElement('div');
const xyz = new XYZ({ target: target });
setTimeout(() => {
const button = target.querySelector("button");
button.click();
xyz.$on('abc', event => {
next();
});
}, 10);
});
});
When button.click on the test case happens, the call is triggering the submit function in XYZ.svelte but,
document.getElementById("name")
is coming as null because of which getting null pointer exception error.
Any idea what I am doing wrong?

In Svelte you can eschew document.getElementById by binding the element's reference to a variable. Try changing your component as follows:
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
let name = null;
function submit () {
dispatch("abc", {
text: name
});
}
</script>
<input bind:value={name} type="text" label="Name" />
<button label="Submit" variant="primary" on:click={submit} />

Related

Svelte / SvelteKit 'before:event'?

I have a custom Form.svelte component, which has its own submit handler, however I would like to expose this somewhat, to allow me to have a specific function run before the submit function is called.
A simplified version:
Form.svelte
<script>
const handleSubmit = async () => {
// Do things
}
</script>
<form on:submit={handleSubmit} class="new-form">
<slot />
</form>
customers/new/+page.svelte
<script lang="ts">
type Customer = { postcode: string, shipping_postcode: string };
let duplicateAddress = false;
const customer = { postcode: "", shipping_postcode: "" };
const beforeSubmit = () => {
if (duplicateAddress) customer.shipping_postcode = customer.postcode;
if (!customer.postcode) {
// Throw an error, for example.
}
}
</script>
<Form before:submit={beforeSubmit} data={customer}>
<input type="text" bind:value={customer.postcode} placeholder="Postcode" />
<input type="checkbox" bind:checked={duplicateAddress} />
</Form>
Is this possible and, if so, how do I implement this?
You could create an event dispatcher and emit your own event called e.g. beforeSubmit and give it a handler with on:beforeSubmit where you use the component:
<!-- Form.svelte -->
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
const handleSubmit = async () => {
dispatch('beforeSubmit');
// Do things
}
</script>
<form on:submit={handleSubmit} class="new-form">
<slot />
</form>
<!-- customers/new/+page.svelte -->
<script lang="ts">
// ...
const beforeSubmit = () => {
// ...
}
</script>
<Form on:beforeSubmit={beforeSubmit} data={customer}>
<input type="text" bind:value={customer.postcode} placeholder="Postcode" />
<input type="checkbox" bind:checked={duplicateAddress} />
</Form>
Tholle's answer shows how to do it correctly (and recommended) according to svelte way. But because of I was late and I have already started writing an answer, I will offer an alternative method as an option:
it would be possible to pass the beforeSubmit function as an argument to the Form component. This would allow in such a way as shown below, to make the execution of handleSubmit dependent on the result of the beforeSubmit execution
<script lang="ts">
import Form from './Form.svelte';
let duplicateAddress = false;
const customer = { postcode: "", shipping_postcode: "" };
const beforeSubmit = (e) => {
if (duplicateAddress) customer.shipping_postcode = customer.postcode;
e.preventDefault()
if (!customer.postcode) {
// Throw an error, for example.
alert('cancel')
return false;
}
return true;
}
</script>
<Form data={customer} beforeSubmit={beforeSubmit}>
<input type="text" bind:value={customer.postcode} placeholder="Postcode" />
<input type="checkbox" bind:checked={duplicateAddress} />
</Form>
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
export let beforeSubmit = null;
const handleSubmit = async (e) => {
alert('submit')
}
</script>
<form on:submit={beforeSubmit ? e => beforeSubmit(e) && handleSubmit(e) : handleSubmit} class="new-form">
<slot />
</form>

TypeError: props.set is not a function

I'm extremely new to ReactJS and have been struggling quite a bit. Right now, I'm trying to create a setting in an admin page where the admin can enter text and update a section on the web app's home page. I created const [aboutBox, setAboutBox] = useState("") in Home.js and passed it as props to another file. However, every time I try to type anything in the text box on the admin page, it throws the error "TypeError: props.setAboutBox is not a function". I have used the same format to create other functions and those have worked, so I'm not exactly sure what's going wrong. Any help would be greatly appreciated!! Thank you guys so much!
const HomeAboutForm = (props) => {
const handleInputChange = event => {
console.log('handle input change')
const box = event.target;
props.setAboutBox({ ...props.aboutBox,box})
}
return (
<form>
<TextField
onSubmit = { event => {
event.preventDefault();
if (!props.aboutBox) return
const box = event.target;
props.setAboutBox({ ...props.aboutBox,box})
console.log(props.aboutBox)
}}
placeholder="Enter new text for About Section here"
multiline
value={props.aboutBox}
variant="outlined"
rows={4}
style={{ margin: "10px", width: '600px' }}
rowsMax={6}
required
input type="text" name="name" value={props.aboutBox} onChange={handleInputChange}
>
<input type="text" name="name" value={props.aboutBox} onChange={handleInputChange} />
</TextField>
<p></p>
<input type="submit" value="Submit" />
</form>
);
};
EDIT: The parent component to HomeAboutForm also has its own parent component, but the code calling HomeAboutForm is
<HomeAboutForm
aboutBox = {props.aboutBox}
setAboutBox = {props.setAboutBox}/>
There is however, a function called getChoiceView that is called like {getChoiceView(pageNum,props)} and then from there, the function has the code
const getChoiceView = (pageNum, props) => {
switch (pageNum) {
case 0:
return <AddPhoto />;
case 1:
return <HomeUpdateAboutView
aboutBox = {props.aboutBox}
setAboutBox = {props.setAboutBox}/>;
case 2:
return <HomeUpdateStylistForm />;
default:
return "Unknown pageView";
}
};
Could how I'm calling getChoiceView be the issue?

How to change a class component (input, conditional) to functional component with useState - hook?

I'd like to change a working class component into a functional component. It's basically an input field that should:
1) Dynamically display/Mirror whatever you enter (see the h1 below the form)
2) Show "no data provided" if nothing entered
The error message says "Failed to compile ... unexpected use of "event""
import React, { useState } from "react"
function Exercise2() {
const [input, inputChange] = useState({firstName: ""})
const handleChange = () => {
inputChange({[event.target.name]: event.target.value});
}
let display
if (useState.firstName != "") {
display = useState.firstName
} else {
display = "no data provided!"
}
return (
<div>
<form>
<input
type="text"
name="firstName"
placeholder="Enter your data here"
value = "input.firstName"
onChange = "handleChange"
/>
</form>
<h1>{display}</h1>
</div>
)
}
export default Exercise2
Can someone point out what I am missing (without altering the code structure too much since I want to stick to my beginners logic). Thx
PS: this was my class component which worked perfectly and that I am trying to translate
class Exercise1 extends React.Component {
constructor() {
super()
this.state = {
firstName:""
}
this.handleChange = this.handleChange.bind(this)
}
handleChange (event) {
this.setState({
[event.target.name]: event.target.value
})
}
render() {
let display
if(this.state.firstName != "") {
display=this.state.firstName
} else {
display="no data provided!"
}
return (
<div>
<form>Input:
<input
type="text"
name="firstName"
placeholder = "Enter your data here!"
value={this.state.firstName}
onChange={this.handleChange}
/>
</form>
<h1>{display}</h1>
</div>
)
}
}
So, the key things are:
1) Not placing the variable/method names in curly braces
value={input.firstName}
onChange={handleChange}
2) Not including an event arg in your handleChange method:
const handleChange = (event) => {
Here I've corrected those problems and also changed how the display is being rendered to make it more "React-like".
const { useState } = React;
function Exercise2() {
// Here you're setting state and assigning an object to it with
// once property: `firstName`
const [ input, inputChange ] = useState({ firstName: '' });
const handleChange = (event) => {
// The click event has been passed in.
// `target` is the element that's been clicked. We're just grabbing the name and
// value from it. We're assigning the name `firstName` as a dynamic property key name
// (we wrap it in square braces), and assign the value to it. We do that because we want to
// update the `firstName` property in the state object.
// Because state is an object (and we might eventually have other properties in there
// that we want to keep when we update this value) we return an object containing all
// the properties of input, and the new dynamic property
inputChange({ ...input, [event.target.name]: event.target.value });
}
// `input` is an object with a firstName property.
// We can destructure the `firstName` property from the state to make
// it easier to work with
const { firstName } = input;
console.log(input)
let display;
if (firstName.length) {
display = firstName;
} else {
display = "no data provided!";
}
return (
<div>
<form>
<input
type="text"
name="firstName"
placeholder="Enter your data here"
value={firstName}
onChange={handleChange}
/>
</form>
<h1>{display}</h1>
</div>
);
}
ReactDOM.render(<Exercise2 />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"/>
import React, { useState } from "react"
function Exercise2() {
const [input, inputChange] = useState({firstName: ""})
const handleChange = (event) => {
inputChange({[event.target.name]: event.target.value});
}
let display
if (input.firstName !== "") {
display = input.firstName
} else {
display = "no data provided!"
}
return (
<div>
<form>
<input
type="text"
name="firstName"
placeholder="Enter your data here"
value = {display}
onChange = {handleChange}
/>
</form>
<h1>{display}</h1>
</div>
)
}
export default Exercise2
You're syntax seems to be wrong, you should try something like
import React, { useState } from "react"
function Exercise2() {
const [formData, changeFormData] = useState({firstName: ""});
const handleChange = (event) => {
changeFormData({[event.target.name]: event.target.value});
}
return (
<div>
<form>
<input
type="text"
name="firstName"
placeholder="Enter your data here"
value={formData.firstName}
onChange={handleChange}
/>
</form>
<h1>{formData.firstName !== "" ? formData.firstName : 'no data provided!'}</h1>
//the above line is a ternary operator, basically it reads as if( input.firstName !== "") return input.firstName : else return 'no data provided!'
</div>
)
}
export default Exercise2

Clear an input field with Reactjs?

I am using a variable below.
var newInput = {
title: this.inputTitle.value,
entry: this.inputEntry.value
};
This is used by my input fields.
<input type="text" id="inputname" className="form-control" ref={el => this.inputTitle = el} />
<textarea id="inputage" ref={el => this.inputEntry = el} className="form-control" />
<button className="btn btn-info" onClick={this.sendthru}>Add</button>
Once I activate {this.sendthru} I want to clear my input fields. However, I am uncertain how to do so.
Also, as shown in this example, it was pointed out to me that I should use the ref property for input values. What I am unclear of is what exactly does it mean to have {el => this.inputEntry = el}. What is the significance of el in this situation?
Let me assume that you have done the 'this' binding of 'sendThru' function.
The below functions clears the input fields when the method is triggered.
sendThru() {
this.inputTitle.value = "";
this.inputEntry.value = "";
}
Refs can be written as inline function expression:
ref={el => this.inputTitle = el}
where el refers to the component.
When refs are written like above, React sees a different function object each time so on every update, ref will be called with null immediately before it's called with the component instance.
Read more about it here.
Declare value attribute for input tag (i.e value= {this.state.name}) and if you want to clear this input value you have to use this.setState({name : ''})
PFB working code for your reference :
<script type="text/babel">
var StateComponent = React.createClass({
resetName : function(event){
this.setState({
name : ''
});
},
render : function(){
return (
<div>
<input type="text" value= {this.state.name}/>
<button onClick={this.resetName}>Reset</button>
</div>
)
}
});
ReactDOM.render(<StateComponent/>, document.getElementById('app'));
</script>
I'm not really sure of the syntax {el => this.inputEntry = el}, but when clearing an input field you assign a ref like you mentioned.
<input type="text" ref="someName" />
Then in the onClick function after you've finished using the input value, just use...
this.refs.someName.value = '';
Edit
Actually the {el => this.inputEntry = el} is the same as this I believe. Maybe someone can correct me. The value for el must be getting passed in from somewhere, to act as the reference.
function (el) {
this.inputEntry = el;
}
I have a similar solution to #Satheesh using React hooks:
State initialization:
const [enteredText, setEnteredText] = useState('');
Input tag:
<input type="text" value={enteredText} (event handler, classNames, etc.) />
Inside the event handler function, after updating the object with data from input form, call:
setEnteredText('');
Note: This is described as 'two-way binding'
You can use input type="reset"
<form action="/action_page.php">
text: <input type="text" name="email" /><br />
<input type="reset" defaultValue="Reset" />
</form>
Now you can use the useRef hook to get some magic if you do not want to use the useState hook:
function MyComponent() {
const inputRef = useRef(null);
const onButtonClick = () => {
// #ts-ignore (us this comment if typescript raises an error)
inputRef.current.value = "";
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={onButtonClick}>Clear input</button>
</>
);
}
As I mentioned, if you are using useState that is the best way. I wanted to show you also this special approach.
Also after React v 16.8+ you have an ability to use hooks
import React, {useState} from 'react';
const ControlledInputs = () => {
const [firstName, setFirstName] = useState(false);
const handleSubmit = (e) => {
e.preventDefault();
if (firstName) {
console.log('firstName :>> ', firstName);
}
};
return (
<>
<form onSubmit={handleSubmit}>
<label htmlFor="firstName">Name: </label>
<input
type="text"
id="firstName"
name="firstName"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
/>
<button type="submit">add person</button>
</form>
</>
);
};
You can use useState:
import React, { useState } from 'react';
const [inputTitle, setInputTitle] = useState('');
then add value to your input component:
render() {
<input type="text" onChange={(e) => setInputTitle(e.target.value)}
value={inputTitle} />
<button onClick={handleSubmit} type="submit">Submit</button>
}
On your submit handler function:
setInputTitle('');
document.querySelector('input').defaultValue = '';
On the event of onClick
this.state={
title:''
}
sendthru=()=>{
document.getElementByid('inputname').value = '';
this.setState({
title:''
})
}
<input type="text" id="inputname" className="form-control" ref={el => this.inputTitle = el} />
<button className="btn btn-info" onClick={this.sendthru}>Add</button>
I used the defaultValue property, useRef, and onClick to achieve this.
let ref = useRef()
and then inside the return:
<input type="text" defaultValue="bacon" ref={ref} onClick={() => ref.current.value = ""} />
also if you want to use onChange for the input it wouldn't require any more configuration and you can just use it. If you want to have a dynamic defaultValue then you absolutely can, with useState.
A simple way to reset the input in React is by implementing the onBlur inside the input.
onBlur={cleanSearch}
ej:
const [search, setSearch] = useState('')
const handleSearch = ({target}) =>{
setSearch(target.value)
}
const cleanSearch = () =>setSearch('')
<input
placeholder="Search…"
inputProps={{ 'aria-label': 'search' }}
value={search}
onChange={handleSearch}
onBlur={cleanSearch}
/>
The way I cleared my form input values was to add an id to my form tag.
Then when I handleSubmit I call this.clearForm()
In the clearForm function I then use document.getElementById("myForm").reset();
import React, {Component } from 'react';
import './App.css';
import Button from './components/Button';
import Input from './components/Input';
class App extends Component {
state = {
item: "",
list: []
}
componentDidMount() {
this.clearForm();
}
handleFormSubmit = event => {
this.clearForm()
event.preventDefault()
const item = this.state.item
this.setState ({
list: [...this.state.list, item],
})
}
handleInputChange = event => {
this.setState ({
item: event.target.value
})
}
clearForm = () => {
document.getElementById("myForm").reset();
this.setState({
item: ""
})
}
render() {
return (
<form id="myForm">
<Input
name="textinfo"
onChange={this.handleInputChange}
value={this.state.item}
/>
<Button
onClick={this.handleFormSubmit}
> </Button>
</form>
);
}
}
export default App;

How to trigger INPUT FILE event REACTJS by another DOM

I have a INPUT BUTTON and INPUT FILE, I want to click the BUTTON and it will trigger the INPUT FILE event in REACT JS.
React.createElement('input',{type:'file', name:'myfile'})
then the button
React.createElement('a',{onClick: this.doClick},'Select File')
So how to define and trigger the INPUT FILE click event when we click the A HREF?
Your help is appreciate.
:-)
Update: Sep 18, 2021
Note: On NextJS, I was facing onChange event is not trigged from input file element. For that, we can use onInputCapture or onChangeCapture. For more detailed information, Stackoverflow - onChange event is not firing
Basic example on onChangeCapture as per our requirement. Requires React ^16.8,
const Dummy = () => {
const inputFileRef = React.useRef();
const onFileChangeCapture = ( e: React.ChangeEvent<HTMLInputElement> ) {
/*Selected files data can be collected here.*/
console.log(e.target.files);
};
const onBtnClick = () => {
/*Collecting node-element and performing click*/
inputFileRef.current.click();
};
return (
<form>
<input
type="file"
ref={inputFileRef}
onChangeCapture={onFileChangeCapture}
/>
<button onClick={onBtnClick}>Select file</button>
</form>
);
};
Using useRef Hook in functional components. Requires React ^16.8,
const Dummy = () => {
const inputFileRef = useRef( null );
const onFilechange = ( e ) => {
/*Selected files data can be collected here.*/
console.log( e.target.files );
}
const onBtnClick = () => {
/*Collecting node-element and performing click*/
inputFileRef.current.click();
}
return (
<form className="some-container">
<input
type="file"
ref={inputFileRef}
onChange={onFileChange}
/>
<button onClick={onBtnClick}>Select file</button>
</form>
)
}
Class Implementation with React.createRef() and handling click with node element.
class Dummy extends React.Component {
constructor( props ) {
super( props );
this.inputFileRef = React.createRef();
this.onFileChange = this.handleFileChange.bind( this );
this.onBtnClick = this.handleBtnClick.bind( this );
}
handleFileChange( e ) {
/*Selected files data can be collected here.*/
console.log( e.target.files );
}
handleBtnClick() {
/*Collecting node-element and performing click*/
this.inputFileRef.current.click();
}
render() {
return (
<form className="some-container">
<input
type="file"
ref={this.inputFileRef}
onChange={this.onFileChange}
/>
<button onClick={this.onBtnClick}>Select file</button>
</form>
)
}
}
You don't need jQuery for this. You don't even need an event handler. HTML has a specific element for this, called label.
First, make sure your input element has an id attribute:
React.createElement('input',{type:'file', name:'myfile', id:'myfile'})
Then, instead of:
React.createElement('a',{onClick: this.doClick},'Select File')
Try:
React.createElement('label',{htmlFor: 'myfile'},'Select File')
(Instead of adding htmlFor and id attributes, another solution is to make the input element a child of the label.)
Now clicking the label should trigger the same behaviour as clicking the input itself.
You could trigger the input type file with ref, f.e:
on your class component:
<input
ref={fileInput => this.fileInput = fileInput}
type="file"
/>
<button onClick={this.triggerInputFile}> Select File </button>
and make a function on that class component too:
triggerInputFile = () => this.fileInput.click()
Using Hooks with useref:
import React, {useRef} from 'react';
const FancyInput = () => {
const fileInput = useRef(null)
const handleClick = () => {
fileInput.current.click()
}
const handleFileChange = event => {
console.log("Make something")
}
return(
<div className="patientactions-container">
<input
type="file"
onChange={(e) => handleFileChange(e)}
ref={fileInput}
/>
<div onClick={() => handleClick()}></div>
</div>
)
}
export default FancyInput;
Building on the answer from #YÒGÎ , here is an implementation using TypeScript:
class Dummy extends React.Component {
fileInputRef: React.RefObject<HTMLInputElement> = React.createRef();
forwardClickToInputElement = () => {
this.fileInputRef.current!.click();
};
handleUploadDemand = (ie: ChangeEvent<HTMLInputElement>) => {
const fileList: FileList = ie.target.files;
// do something with the FileList, for example:
const fileReader = new FileReader();
fileReader.onload = () => {
const str = String(fileReader.result);
try {
const parsedContent = YOUR_OWN_PARSING(str);
} catch (error) {
// YOUR OWN ERROR HANDLING
}
};
fileReader.readAsBinaryString(fileList[0])
}
render() {
return (
<div className="some-container">
<button onClick={this.forwardClickToInputElement}>Select File</button>
<input ref={this.fileInputRef} type="file" onChange={this.handleSelectFile} hidden={true}/>
</div>
)
}
}
References:
Solution for how to use refs in React with Typescript https://stackoverflow.com/a/50505931/2848676
Use ! operator for ref type narrowing https://medium.com/#martin_hotell/react-refs-with-typescript-a32d56c4d315
const CustomInput = () => {
const handleClick = () => {
document.getElementById("file_upload").click();
};
const handleFileChange = (event) => {
console.log("Make something");
};
return (
<div className="patientactions-container">
<input type="file" id="file_upload" onChange={(e) => handleFileChange(e)} />
<div onClick={() => handleClick()}></div>
</div>
);
};
export default CustomInput;
EDIT: This is a question I answered a long time ago not knowing very much react at this time. The fun thing is that it has been considered valid ^^.
So for anyone reading this answer; this answer is wrong and is a very good example of something you shouldn't do in react.
Please find below a nice anti-pattern, again, don't do it.
=================================================
You can achieve this using jQuery:
this.doClick: function() {
$('input[type=file]').trigger('click');
}
React does not provide specific functions to trigger events, you can use jQuery or simply native Javascript: see Creating and triggering events on MDN

Categories