I'm beginner to Unit test Cases. I want to write a test case for multiple inputs and radio buttons onchange method. I've gone through the link https://www.pluralsight.com/guides/how-to-test-react-components-in-typescript but multiple onChange methods have been written for separate inputs.
Here is my code:
<TextField id="outlined-basic" label="first Name" variant="outlined" name="fName" onChange={(e) => this.updateData("firstName", e.target.value)} />
<TextField id="filled-basic" variant="filled" name="lName" label="last Name" onChange={(e) => this.updateData("lastName", e.target.value)} />
<RadioGroup row aria-labelledby="demo-row-radio-buttons-group-label" name="row-radio-buttons-group">
<FormControlLabel value="female" control={<Radio />} label="Female" name="genderFemale" onChange={(e) => this.updateData("gender", e.target.value)} />
<FormControlLabel value="male" control={<Radio />} label="Male" name="genderMale" onChange={(e) => this.updateData("gender", e.target.value)} />
<FormControlLabel value="other" control={<Radio />} label="Other" name="Other" onChange={(e) => this.updateData("gender", e.target.value)} />
</RadioGroup>
onChange method:
updateData = (fieldKey: string, value: any) => {
setFormData({
...formData,
[fieldKey]: value,
})
}
Can anyone help me on this to write onChange method test case passing 2 parameters (key, value)?
It would be more helpful. Thanks in advance.
I am using material ui autocomolete. I want to pass name, value and onchange on autocomplete, the same way we do for TextField. How to do achieve that. ?? My code is not working.
<Autocomplete
id="combo-box-demo"
fullWidth
options={props.propsmaster}
getOptionLabel={(option) => option.abc || option.xyz}
size="small"
renderInput={(params) =>
<TextField
{...params}
label={propsMain.abc}
variant="outlined"
name="name"
onChange={(e) => exhandleChange(e)}
value={values.name}
/>
}
/>
<Autocomplete
id="combo-box-demo"
fullWidth
options={props.propsmaster}
getOptionLabel={(option) => option.abc || option.xyz}
size="small"
name="name"
onChange={(e) => exhandleChange(e)}
value={values.name}
renderInput={(params) =>
<TextField
{...params}
label={propsMain.abc}
variant="outlined"
/>
}
/>
I am using this form with material-ui components. Instead of writing the inline style that I am currently using for width, I wanted to opt for css-in-js. I have used styled-components previously but I don't think there's a form element with that.
The only example I came across was one where they had used built-in styled component labels. Since I have implemented validation on these material ui text fields as well so I don't want to change the structure. What would be the suitable way to put the style in css-in-js. I would prefer if there's a solution with styled-components.
return (
<div className="main-content">
<form
style={{ width: '100%' }}
onSubmit={e => {
e.preventDefault();
submitForm(email);
}}>
<div>
<TextField
variant="outlined"
margin="normal"
id="email"
name="email"
helperText={touched.email ? errors.email : ''}
error={touched.email && Boolean(errors.email)}
label="Email"
value={email}
onChange={change.bind(null, 'email')}
/>
<CustomButton
disabled={!isValid || !email}
text={'Remove User'}
/>
</div>
</form>
</div>
);
just make the styled form:
import styled from 'styled-components';
const Form = styled.form`
width: 100%;
`;
and use it:
return (
<div className="main-content">
<Form
onSubmit={e => {
e.preventDefault();
submitForm(email);
}}>
<div>
<TextField
variant="outlined"
margin="normal"
id="email"
name="email"
helperText={touched.email ? errors.email : ''}
error={touched.email && Boolean(errors.email)}
label="Email"
value={email}
onChange={change.bind(null, 'email')}
/>
<CustomButton
disabled={!isValid || !email}
text={'Remove User'}
/>
</div>
</Form>
</div>
);
I am taking an input from the user of the card number and wants that the length entered by user must not be less than and more than 12. Here is the declaration of my textfield.
<TextField
id="SigninTextfield"
label="Aadhaar number"
id="Aadhar"
lineDirection="center"
required={true}
type="number"
maxLength={12}
style={styles.rootstyle}
erorText="Please enter only 12 digits number"
/>
Now I am not understanding whether to use javascript or any event handler for restricting the length.
You can set the maxLength property for limiting the text in text box.
Instead of onChange method you can pass maxLength to the inputProps (lowercase i, not InputProps) props of material-ui TextField.
<TextField
required
id="required"
label="Required"
defaultValue="Hello World"
inputProps={{ maxLength: 12 }}
/>
Basically we can edit all input element's native attrs via inputProps object.
Link to TextField Api
I found another solution here.
<TextField
required
id="required"
label="Required"
defaultValue="Hello World"
onInput = {(e) =>{
e.target.value = Math.max(0, parseInt(e.target.value) ).toString().slice(0,12)
}}/>
<TextField
autoFocus={true}
name="name"
onChange={handleChange}
placeholder="placeholder"
id="filled-basic"
variant="filled"
size="small"
fullWidth
inputProps={{
maxLength: 25,
}}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
/>
<TextField
id="username"
name="username"
label={translate('username')}
onChange={handleChange}
onBlur={handleBlur}
value={values.username}
error={Boolean(errors.username) && touched.username}
helperText={(errors.username && touched.username && translate(errors.username))}
required
inputProps={{maxLength :20}}
/>
Is it worth noting that Material-ui <TextField /> component doesn not have a maxlength property. However, the original html <input> does. So you don't really need to create any extra function to get this to work. Just use the base <input> attributes using inputProps.
The actual answer is this:
inputProps={
{maxLength: 22}
}
// Result => <input maxlength="yourvalue" />
What this does is it sets the maxlength attribute of the underlying <input> resulting in: <input maxlength="yourvalue" />. Another important thing to note here is that you use inputProps and not InputProps. The one you're targeting is the small letter inputProps.
I discovered something weird on the behavior between TextField and Input from Material UI
They are very similar to each other, the problem I see is the following:
On the TextField component, if you use InputProps with capital "I", the Adorments are shown, but in the other hand if you use it as lowercase "inputProps", the maxLength Html attribute is TAKEN as valid, but not the adorments
I ended up using INPUT instead of a TextField so you can use adorments in
<TextField
variant="outlined"
required
fullWidth
error={errors.email}
id="email"
label={t("signup-page.label-email")}
name="email"
onChange={handleChange}
inputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton aria-label="toggle password visibility">
<EmailIcon />
</IconButton>
</InputAdornment>
),
maxLength: 120,
}}
/>
in the above code the adorment is ignored, but maxLength taken used as "inputProps" camel case
The below code is a working example, as you might see, I embraced it as in the old style within a Form Control, the input label and the input "outlinedInput"
<FormControl variant="outlined" fullWidth>
<InputLabel htmlFor="firstName">Password</InputLabel>
<OutlinedInput
value={values.firstName}
autoComplete="outlined"
name="firstName"
variant="outlined"
required
fullWidth
error={errors.firstName}
id="firstName"
label={t("signup-page.label-firstname")}
onChange={handleChange}
autoFocus
inputProps={{ maxLength: 32 }}
/>
</FormControl>
Solution. My final recommendation, use an OutlinedInput instead of a TextField, so you can put in a separated way Adorments and also maxLength
Below a working example with FormControl OutlinedInput, inputProps - maxLength and an end Adorment Icon
<FormControl variant="outlined" fullWidth>
<InputLabel htmlFor="password">Password</InputLabel>
<OutlinedInput
value={values.passwordConfirm}
variant="outlined"
required
fullWidth
error={errors.passwordConfirm}
name="passwordConfirm"
label={t("signup-page.label-password-confirm")}
type={values.showPasswordConfirm ? "text" : "password"}
id="password-confirm"
onChange={handleChange}
inputProps= {{maxLength:32}}
endAdornment= {
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword("passwordConfirm")}
onMouseDown={handleMouseDownPassword}
>
{values.showPasswordConfirm ? (
<Visibility />
) : (
<VisibilityOff />
)}
</IconButton>
</InputAdornment>
}
/>
{errors.passwordConfirm && (
<p className="error"> {errors.passwordConfirm} </p>
)}
</FormControl>
If you're using MUI 5, version 5.0.6, due to a support for legacy, you will have to do something like this,
<TextField
id="standard-textarea"
label="A label"
placeholder="Some text here"
multiline
variant="standard"
defaultValue={"Hello"}
inputProps={{
maxLength: 255,
}}
InputProps={{
disableUnderline: true,
}}
/>
The TextField supports both inputProps and InputProps, but some properties don't work vice versa.
maxLength does not work as expected in InputProps, and a property like disableUnderline for the MUI 5 does not work as expected in inputProps.
Be careful with a possible typo with the i.
See the API for more information, https://mui.com/api/text-field/.
The accepted answer won't work in Firefox for large numbers (greater than 16 digits). Numbers just behaves in a weird way.
For a 22 length field we ended up using this:
<TextField
required
validateOnBlur
field="cbu"
label="22 dígitos del CBU"
validate={validate.required}
type="text"
inputProps={
{maxLength: 22}
}
onInput={(e) => { e.target.value = e.target.value.replace(/[^0-9]/g, '') }}
/>
Basically, native maxLength constraint for text fields, plus a conversion from string to numbers "on the fly".
Improvement
Also you may prefer to make this reusable and more semantic.
# constraints.js
const onlyNumbers = (e) => { e.target.value = e.target.value.replace(/[^0-9]/g, '') };
# form.js
<TextField
field="cbu"
label="22 dígitos del CBU"
inputProps={
{maxLength: 22}
}
onInput={(e) => onlyNumbers(e) }
The material-design <TextField /> component haven't any length property.
You can create yours in the onChange() method.
updateTextField(event,value){
if(value.length <= 12){
//Update your state
}
else{
//Value length is biggest than 12
}
}
<TextField
id="SigninTextfield"
label="Aadhaar number"
id="Aadhar"
lineDirection="center"
required={true}
type="number"
onChange={(e,v) => this.updateTextField(e,v)}
style={styles.rootstyle}
erorText="Please enter only 12 digits number"
/>
In your change handler, just write.
if (event.target.value.length !== maxLength ) return false;
I had a similar issue, but with TextareaAutosize. Unfortunately,
inputProps={{ maxLength: 12 }}
doesn't work with TextareaAutosize.
The below workaround works for TextareaAutosize and for both text and numbers.
Inspired by the accepted answer to this question - https://stackoverflow.com/a/49130234/5236534
onInput = {(e) =>{
e.target.value = (e.target.value).toString().slice(0,10)
import * as React from 'react';
import TextareaAutosize from '#mui/material/TextareaAutosize';
export default function MinHeightTextarea() {
return (
<TextareaAutosize
aria-label="minimum height"
minRows={3}
placeholder="Minimum 3 rows"
style={{ width: 200 }}
onInput = {(e) =>{
e.target.value = (e.target.value).toString().slice(0,10)
}}
/>
);
}
Link to demo: Limiting character length in MUI TextareaAutosize
<TextField
id="SigninTextfield"
label="Aadhaar number"
id="Aadhar"
lineDirection="center"
required={true}
type="number"
inputProps={{
maxLength: 20,
}}
style={styles.rootstyle}
erorText="Please enter only 12 digits number"
/>
For people who still are looking for a solution for the number field, this solution worked fine for me.
onInput={(e: any) => {
e.target.value = Math.max(0, parseInt(e.target.value))
.toString()
.slice(0, 2);
}}
Make sure to use any.
I am trying to populate a form with initial data however I am unsure of the syntax for this functionality and how it's suppose to be applied. initially the form I am using is a component that I also use to create a client. I am trying to reuse this form as the form for editing a client. It has two functions.
As per suggestions in Redux I have a this as a component and I also have a container.
Now, using the redux tool in Chrome I can look at the state and its clear the action has added an "editClient" entry with data so I do have the data. Its called "editClient" in the state.
My problem is that I do not know how to use this to set these state values as initial values. I have looked carefully at the docs but I am confused as to the way it should be stuctured.
Here is my client form in its entirety:
import React, { PropTypes } from 'react'
import { Field, reduxForm, FormSection } from 'redux-form'
import { connect } from 'react-redux'
import { Col, Row } from 'react-bootstrap'
import { Button, Glyphicon, Panel } from 'react-bootstrap'
import Moment from 'moment'
import Address from '../../address/addressContainer'
import FormField from '../../formComponents/formField'
import CheckboxField from '../../formComponents/checkboxField'
import TextField from '../../formComponents/textField'
import StaticText from '../../formComponents/staticText'
import TextAreaField from '../../formComponents/textAreaField'
import DateField from '../../formComponents/datefield'
import reducer from '../edit/reducer'
export const CLIENT_FORM_NAME = 'Client'
const required = value => (value ? undefined : 'Required')
const maxLength = max => value =>
value && value.length > max ? `Must be ${max} characters or less` : undefined
const number = value =>
value && isNaN(Number(value)) ? 'Must be a number' : undefined
const minValue = min => value =>
value && value < min ? `Must be at least ${min}` : undefined
const email = value =>
value && !/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)
? 'Invalid email address'
: undefined
const tooOld = value =>
value && value > 65 ? 'You might be too old for this' : undefined
const aol = value =>
value && /.+#aol\.com/.test(value)
? 'Really? You still use AOL for your email?'
: undefined
const normalizeMobilePhone = value => {
if (!value) {
return value
}
const onlyNums = value.replace(/[^\d]/g, '')
if (onlyNums.length <= 4) {
return onlyNums
}
if (onlyNums.length <= 8) {
return `${onlyNums.slice(0, 4)} ${onlyNums.slice(4)}`
}
return `${onlyNums.slice(0, 4)} ${onlyNums.slice(4, 7)} ${onlyNums.slice(7, 10)}`
}
export const Client = (props) => {
const {
handleSubmit,
companyValue,
isWarrantyCompanyValue,
isEditMode } = props
const { reset } = props
return (
<Row>
<Col md={12}>
<h2><Glyphicon glyph="edit" /> {isEditMode ? 'Edit' : 'New'} Client</h2>
<hr />
<form onSubmit={handleSubmit} className="form-horizontal">
{isEditMode && (
<Panel header={<h3>Client - Basic Details</h3>}>
<Row>
<Field component={StaticText}
name="clientNo"
id="clientNo"
label="Client No."
fieldCols={4}
labelCols={4}
controlCols={8}
/>
<Field component={StaticText}
name="dateCreated"
id="dateCreated"
label="Date Created."
fieldCols={4}
labelCols={4}
controlCols={8}
/>
<Field component={StaticText}
name="userName"
id="userName"
label="Created By."
fieldCols={4}
labelCols={4}
controlCols={8}
/>
</Row>
<Row>
<Field
component={props => {
return (
<StaticText {...props}>
<p
className="form-control-static"
>
<Glyphicon glyph={props.input.value ? 'ok' : 'remove'} />
{' '}{props.input.value ? 'Has jobs attached' : 'No jobs attached'}
</p>
</StaticText>
)
}}
name="activity"
id="activity"
label="Activity"
fieldCols={4}
labelCols={4}
controlCols={8}
/>
<Field component={CheckboxField}
name="active"
id="active"
label="De-Activate"
checkboxLabel="De activate this client"
fieldCols={4}
labelCols={4}
controlCols={8}
/>
</Row>
</Panel>
)}
<Panel header={<h3>Client - CompanyDetails</h3>}>
<Row>
<Field component={CheckboxField}
id="company"
name="company"
label="Company?"
checkboxLabel="Client represents a company"
fieldCols={6}
labelCols={3}
controlCols={9}
/>
</Row>
{companyValue && (
<div>
<Row>
<Field component={TextField}
name="companyName"
id="companyName"
type="text"
label="Company Name"
placeholder="Enter company name..."
fieldCols={6}
labelCols={3}
controlCols={9}
/>
<Field component={TextField}
name="abn"
id="abn"
type="text"
label="ABN."
fieldCols={6}
labelCols={3}
controlCols={5}
/>
</Row>
<Row>
<Field component={CheckboxField}
id="isWarrantyCompany"
name="isWarrantyCompany"
label="Warranty Company?"
checkboxLabel="Client represents a warranty company"
fieldCols={6}
labelCols={3}
controlCols={9}
/>
{isWarrantyCompanyValue && (
<Field component={CheckboxField}
id="requiresPartsPayment"
name="requiresPartsPayment"
label="Requires Parts Payment?"
checkboxLabel="We pay for parts"
fieldCols={6}
labelCols={3}
controlCols={9}
/>
)}
</Row>
<Row>
<Field component={TextField}
name="companyEmail"
id="companyEmail"
type="email"
label="Spare Parts Email."
placeholder="Enter spare parts email..."
fieldCols={6}
labelCols={3}
controlCols={9}
/>
</Row>
</div>
)}
</Panel>
<Panel header={<h3>Client - {companyValue ? 'Company Contact' : 'Personal'} Details</h3>}>
<Row>
<Field component={TextField}
name="clientFirstName"
id="clientFirstName"
type="text"
label="First Name."
placeholder="Enter first name..."
fieldCols={6}
labelCols={3}
controlCols={9}
validate={[required]}
/>
<Field component={TextField}
name="clientLastName"
id="clientLastName"
type="text"
label="Last Name."
placeholder="Enter last name..."
fieldCols={6}
labelCols={3}
controlCols={9}
/>
</Row>
<Row>
<Field component={TextField}
name="mobilePhone"
id="mobilePhone"
type="text"
label="Mobile No."
placeholder="Enter mobile No..."
fieldCols={6}
labelCols={3}
controlCols={5}
normalize={normalizeMobilePhone}
/>
<Field component={TextField}
name="phone"
id="phone"
type="text"
label="Phone No."
placeholder="Enter phone No..."
fieldCols={6}
labelCols={3}
controlCols={5}
/>
</Row>
<Row>
<Field component={TextField}
name="email"
id="email"
type="email"
label="Email."
placeholder="Enter email address..."
fieldCols={6}
labelCols={3}
controlCols={9}
/>
</Row>
</Panel>
<FormSection name="Address">
<Address />
</FormSection>
<Panel header={<h3>Notes</h3>}>
<Row>
<Field component={TextAreaField}
id="notes"
name="notes"
label="Notes."
placeholder="Enter notes here..."
fieldCols={12}
labelCols={1}
controlCols={11}
/>
</Row>
</Panel>
<Panel header={<h3>Client - Bank Details</h3>}>
<Row>
<Field component={TextField}
name="bankName"
id="bankName"
type="text"
label="Bank Name."
placeholder="Enter bank name..."
fieldCols={4}
labelCols={4}
controlCols={8}
/>
<Field component={TextField}
name="bsb"
id="bsb"
type="text"
label="BSB No."
placeholder="Enter BSB No..."
fieldCols={4}
labelCols={4}
controlCols={8}
/>
<Field component={TextField}
name="account"
id="account"
type="text"
label="Account No."
placeholder="Enter Account No..."
fieldCols={4}
labelCols={4}
controlCols={8}
/>
</Row>
</Panel>
<div className="panel-body">
<Row>
<Col xs={4}>
<Row>
<Col xs={8} xsOffset={4}>
<Button bsStyle="primary" type="submit" bsSize="small">
<Glyphicon glyph="ok" /> Submit
</Button>
{' '}
<Button type="reset" bsSize="small" onClick={reset}>
<Glyphicon glyph="ban-circle" /> Clear
</Button>
</Col>
</Row>
</Col>
</Row>
</div>
</form>
</Col>
</Row >
)
}
let ClientForm = reduxForm({
form: CLIENT_FORM_NAME,
})(Client)
ClientForm = connect(
state => ({
initialValues: state.editClient // pull initial values from client reducer
}),
{ reducer } // bind client loading action creator
)(Client)
export default ClientForm
I have added the following at the bottom as per the redux form example:
ClientForm = connect(
state => ({
initialValues: state.editClient // pull initial values from client reducer
}),
{ reducer } // bind client loading action creator
)(Client)
...wnd when I save it I get the following error.
Exception: Call to Node module failed with error: Error: Field must be inside a component decorated with reduxForm()
I believe I have not understood how to set the initial values.
Where have I gone wrong and what I need to do to make this take the state values and load them?
The parameter for the function that connect returns should be ClientForm instead of Client. Try this:
ClientForm = connect(
state => ({
initialValues: state.editClient // pull initial values from client reducer
}),
{ reducer } // bind client loading action creator
)(ClientForm)