I have a simple snapshot test with RTL and Jest, any other prop I use beside type and placeholder I don't see it in the resulting snapshot. This is my input component: (I'm using styled component)
const TextField = ({ className, placeholder, onChange, type }) => {
return (
<Styled>
<Styled.input
type={type}
placeholder={placeholder}
className={className}
onChange={onChange}
/>
</Styled>
);
};
This is my test:
it('shall render correctly', () => {
const { asFragment } = render(
<ThemeProvider theme={theme}>
<TextField placeholder="Ramdom text" type="search" onChange={jest.fn()} />
</ThemeProvider>,
);
expect(asFragment()).toMatchSnapshot();
});
The resulting snapshot:
exports[`TextField component shall render correctly 1`] = `
<DocumentFragment>
.c0 {
position: relative;
}
.c1 {
width: 100%;
outline: none;
border: solid 0px;
border-radius: 10em;
background: #f4f5f8;
padding: 8px 8px 8px NaNpx;
font: 400 1.6rem/normal 'Open Sans','Helvetica','Arial',sans-serif;
-webkit-letter-spacing: normal;
-moz-letter-spacing: normal;
-ms-letter-spacing: normal;
letter-spacing: normal;
text-transform: none;
}
<label
class="c0"
>
<input
class="c1"
placeholder="Ramdom text"
type="search"
/>
</label>
</DocumentFragment>
`;
As you can see there's no onChange prop in the input props.
onChange in your JSX is event handler provided by React and it's not shown in the actual DOM, which RTL renders.
Related
I wanted to know whether my onAdd function in <NewItemButton> will get the latest value of text state.
import { useState } from "react";
import {
NewItemFormContainer,
NewItemInput,
NewItemButton
} from "./styles";
type NewItemFormProps = {
onAdd(text: string): void
}
const NewItemForm = (props: NewItemFormProps) => {
const [text, setText] = useState("");
return (
<NewItemFormContainer>
<NewItemInput
value={text}
onChange={(e) => setText(e.target.value)}
/>
<NewItemButton onClick={() => props.onAdd(text)}>
Create
</NewItemButton>
</NewItemFormContainer>
);
}
export default NewItemForm;
If it does not get the latest value, what other ways can you suggest me? One that comes to my mind to use Refs (forwardRef) and send it directly to the html input element and then call props.onAdd with the current value. But the thing is that I am using styled-components and my NewItemInput looks like this
export const NewItemInput = styled.input`
border-radius: 3px;
border: none;
box-shadow: #091e4240 0px 1px 0px 0px;
margin-bottom: 0.5rem;
padding: 0.5rem 1rem;
width: 100%;
background: #484747;
color: #f1f1f1;
`
So how will do that?
I'm trying to pass some props when I use my custom component with Styled-component.
This is the container I want to focus on, and I want to change the flex-direction via props:
const InputInnerContainer = styled.View`
width: 100%;
height: 42px;
padding-horizontal: 5px;
flex-direction: ${props => props.right ? "row-reverse": "row"};
align-items: center;
justify-content: space-between;
border-radius: 4px;
border-width: 1px;
background-color: ${inputBackgroundColor};
border: 2px solid ${inputBorderColor};
`;
This is my custom component "Input":
const Input = ({ onChangeText, icon, value, label,iconPosition}) => {
return (
<InputContainer>
{label && <LabelText>{label}</LabelText>}
<InputInnerContainer {...iconPosition}>
<View>{icon && <LabelText>{icon}</LabelText>}</View>
<InputField onChangeText={onChangeText} value={value} />
</InputInnerContainer>
</InputContainer>
);
};
And this is where I call the custom component:
<Input
label="Password"
onChangeText={(text) => onChangeText(text)}
value={value}
icon="HIDE"
iconPosition="right"
/>
The props I want to pass is "iconPosition". But I'm not sure how to pass it into the Styled-component. I'm still relatively new to Styled-component, so any ideas are welcome.
Try without destructuring it. Not this way:
<InputInnerContainer {...iconPosition}/>
But this way:
<InputInnerContainer iconPosition={iconPosition}/>
You also need to update your styled component:
const InputInnerContainer = styled.View`
...
flex-direction: ${props => props.iconPosition === "right" ? "row-reverse" : "row"}
...
`
I tried to override the style provided by material ui using css
this is the js file
import TextField from '#material-ui/core/TextField';
import classes from './InputFields.module.css';
export const InputFields = (props) => {
return (
<div className={classes.fields}>
<TextField
id={props.id}
className={classes.field}
label={props.label}
variant="outlined"
type={props.type}
onChange={props.onChange}
value={props.value}
error={props.error}
required
/>
</div>
)
}
export default InputFields
and this is the css file
.fields {
margin: 1rem;
}
.field .MuiInputBase-input{
height: 3rem;
border: 5px solid green;
border-radius: 3px;
font-size: large;
}
any help will be appreciated
Add important key word to a css
.fields {
margin: 1rem !imporant;
}
Add important key word to a css where you wnat to override it will override the predefined css
I don't recommend you to use !important, at least it be necessary:
You need to apply some changes in your css definition to define a selector with a hight level of priority (https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity). In this case you can use input type selector, it has more priority than the class selector:
.fields {
margin: 1rem;
}
.field input { //--> add input selector
height: 3rem;
border: 5px solid green;
border-radius: 3px;
font-size: large;
}
See: https://codesandbox.io/s/material-demo-forked-ko3kl
You can create a custom TextField component with customized input props.
const useStyles = makeStyles(() =>
createStyles({
root: {
height: "3rem",
border: "5px solid green",
borderRadius: 3,
fontSize: "large"
}
}),
);
function MyTextField(props: TextFieldProps) {
const classes = useStyles();
return (
<TextField
variant="outlined"
InputProps={{ classes } as Partial<OutlinedInputProps>}
{...props}
/>
);
}
This is based on 'RedditTextField' example on materialui's customized input documentation here.
I have created a multi-step form to be able to create a smooth and easy onboarding.
I am not able to properly display the button and the input/label.
I am looking to get the label and input align left and the button previous and next displayed on the same line but one of the left and one on the right. Also my I reach the latest form, the 'next' button is no more displayed and I show a submit.
The code works, it's just the display arrangement which not good.
This how it looks:
and I am more looking for something like this:
Only the back and Next are not properly dispayed on this image, it should be closer to the input.
Otherwise, it's exactly what I am looking
Label then input (always below the label), and then the buttons below the input and label.
Here is the code:
MasterForm:
import React from 'react';
import ClassCreationFormStep1 from './ClassCreationFormStep1'
import ClassCreationFormStep2 from './ClassCreationFormStep2'
import ClassCreationFormStep3 from './ClassCreationFormStep3'
import ClassCreationFormStep4 from './ClassCreationFormStep4'
import ClassCreationFormStep5 from './ClassCreationFormStep5'
import ClassCreationFormStep6 from './ClassCreationFormStep6'
import ClassCreationFormStep7 from './ClassCreationFormStep7'
import ClassCreationFormStep8 from './ClassCreationFormStep8'
import ClassCreationFormStep9 from './ClassCreationFormStep9'
import ClassCreationFormStep10 from './ClassCreationFormStep10'
import ClassCreationFormStep11 from './ClassCreationFormStep11'
import ClassCreationFormStep12 from './ClassCreationFormStep12'
import ClassCreationFormStep13 from './ClassCreationFormStep13'
import './CreateClassOnBoardingForm.css';
class CreateClassOnBoardingForm extends React.Component {
constructor(props) {
super(props)
// Set the initial input values
this.state = {
currentStep: 1, // Default is Step 1
classTeacherName: '',
classProfilePic: '',
classEmail: '',
className: '',
classAttendeesWillLearn: '',
classMaxClass: '',
classWhatToBring: '',
classWillBe: '',
classLocation: '',
classCost: '',
typeOfClass: '',
classExtra: '',
classPics: '',
}
// Bind the submission to handleChange()
this.handleChange = this.handleChange.bind(this)
this._next = this._next.bind(this)
this._prev = this._prev.bind(this)
}
_next() {
let currentStep = this.state.currentStep
// If the current step is 1 or 2, then add one on "next" button click
currentStep = currentStep >= 12? 13: currentStep + 1
this.setState({
currentStep: currentStep
})
}
_prev() {
let currentStep = this.state.currentStep
// If the current step is 2 or 3, then subtract one on "previous" button click
currentStep = currentStep <= 1? 1: currentStep - 1
this.setState({
currentStep: currentStep
})
}
// Use the submitted data to set the state
handleChange(event) {
const {name, value} = event.target
this.setState({
[name]: value
})
}
// Trigger an alert on form submission
handleSubmit = (event) => {
event.preventDefault()
const { classTeacherName, classProfilePic, classEmail,
className, classAttendeesWillLearn,classMaxClass, classWhatToBring,
classWillBe, classLocation, classCost, typeOfClass, classExtra, classPics } = this.state
alert(`Your registration detail: \n
classTeacherName: ${classTeacherName} \n
classProfilePic: ${classProfilePic} \n
classEmail: ${classEmail} \n
className: ${className} \n
classAttendeesWillLearn: ${classAttendeesWillLearn} \n
classMaxClass: ${classMaxClass} \n
classWhatToBring: ${classWhatToBring} \n
classWillBe: ${classWillBe} \n
classLocation: ${classLocation} \n
classCost: ${classCost} \n
typeOfClass: ${typeOfClass} \n
classExtra: ${classExtra} \n
classPics: ${classPics} \n
`)
window.open("/successfull", "_self") //to open new page
}
get previousButton(){
let currentStep = this.state.currentStep;
// If the current step is not 1, then render the "previous" button
if(currentStep !==1){
return (
<button
className="blue-button"
type="button" onClick={this._prev}>
Previous
</button>
)
}
// ...else return nothing
return null;
}
get nextButton(){
let currentStep = this.state.currentStep;
if(currentStep <13){
return (
<button
className="blue-button"
type="button" onClick={this._next}>
Next
</button>
)
}
// ...else render nothing
return null;
}
render() {
return (
<React.Fragment>
<p>Step {this.state.currentStep} </p>
<form onSubmit={this.handleSubmit}>
<ClassCreationFormStep1
currentStep={this.state.currentStep}
handleChange={this.handleChange}
classTeacherName={this.state.classTeacherName}
/>
<ClassCreationFormStep2
currentStep={this.state.currentStep}
handleChange={this.handleChange}
classProfilePic={this.state.classProfilePic}
/>
....
<ClassCreationFormStep13
currentStep={this.state.currentStep}
handleChange={this.handleChange}
classPics={this.state.classPics}
/>
{this.previousButton}
{this.nextButton}
</form>
</React.Fragment>
)
}
}
export default CreateClassOnBoardingForm;
The Css below is used on the master and child
.blue-button {
border-radius: 21px;
background-color: #14cff0;
border-color: #14cff0;
font-family: Source Sans Pro;
font-size: 13px;
font-weight: bold;
text-align: center;
color: #ffffffff;
box-shadow: 0px 8px 18px 0 rgba(0,0,0,0.14);
padding-top: 5px;
padding-bottom: 7px;
padding-left: 20px;
padding-right: 20px;
}
.label-txt {
font-family: Source Sans Pro;
font-size: 30px;
font-weight: bold;
font-stretch: normal;
font-style: normal;
line-height: 0.77;
letter-spacing: -0.6px;
text-align: left;
color: #333333;
}
.form-control-village {
font-family: Source Sans Pro;
font-size: 16px;
line-height: 1.6;
text-align: left;
color: #616161;
padding: 0px 0px 0px 0px;
margin: 0px 0px 0px 0px;
background-color: #ffffff;
border-bottom: 3px solid #ff7255;
border-top: 0px none;
border-left: 0px none;
border-right: 0px none;
}
and here is the child form:
1st one:
import React from 'react';
import TextContents from '../../assets/translations/TextContents'
import './CreateClassOnBoardingForm.css';
class ClassCreationFormStep1 extends React.Component {
render() {
if (this.props.currentStep !== 1) { // Prop: The current step
return null
}
return(
<div className="form-group">
<label className="label-txt" htmlFor="classTeacherName">{TextContents.FormClassTeacherName}</label>
<input
className="form-control-village"
id="classTeacherName"
name="classTeacherName"
type="text"
placeholder=""
value={this.props.classTeacherName} // Prop: The email input data
onChange={this.props.handleChange} // Prop: Puts data into state
/>
</div>
)
}
}
export default ClassCreationFormStep1
second one:
import React from 'react';
import TextContents from '../../assets/translations/TextContents'
import './CreateClassOnBoardingForm.css';
class ClassCreationFormStep2 extends React.Component {
render() {
if (this.props.currentStep !== 2) { // Prop: The current step
return null
}
return(
<div className="form-group">
<label className="label-txt" htmlFor="classProfilePic">{TextContents.FormClassProfilePic}</label>
<input
className="form-control-village"
id="classProfilePic"
name="classProfilePic"
type="file"
value={this.props.classProfilePic} // Prop: The email input data
onChange={this.props.handleChange} // Prop: Puts data into state
/>
</div>
)
}
}
export default ClassCreationFormStep2
and the latest one, when submit shows up
import React from 'react';
import TextContents from '../../assets/translations/TextContents'
import './CreateClassOnBoardingForm.css';
class ClassCreationFormStep13 extends React.Component {
render() {
if (this.props.currentStep !== 13) { // Prop: The current step
return null
}
return(
<React.Fragment>
<div className="form-group">
<label className="label-txt" htmlFor="classPics">{TextContents.FormClassPics}</label>
<input
className="form-control-village"
id="classPics"
name="classPics"
type="file"
multiple
value={this.props.classPics} // Prop: The email input data
onChange={this.props.handleChange} // Prop: Puts data into state
/>
</div>
<button
className="blue-button"
type="submit">
{TextContents.SubmitBtn}
</button>
</React.Fragment>
)
}
}
export default ClassCreationFormStep13
Any idea how to make it nice like the latest image I have posted
=====
I am looking to have something like this:
if you want them positioned to the bottom left and right you need to set a height and position: relative
<div className="container>
<button className="backButton>Back</button>
<button className="nextButton>Next</button>
</div>
.container {
height: 200px; // example
width: 200px;
position: relative;
}
.backButton {
position: absolute;
bottom: 0;
left: 0;
}
.nextButton {
position: absolute;
bottom: 0;
right: 0;
}
I have a component that is already built out like so:
const Banner = ({ image, heading, text }) => (
<Container>
<Background src={image}>
<BannerContent>
<h1>{heading}</h1>
<h2>{text}</h2>
</BannerContent>
</Background>
</Container>
);
const BannerContent = styled.div`
h1 {
font-size: 24px;
}
h2 {
font-size: 16px;
}
`;
and I'm trying to override the styles of the h1 and h2 and add new styles like so in another component:
const PageBanner = styled(Banner)`
h1 {
font-size: 20px;
width: ...
}
h2 {
font-size: 13px;
width: ...
}
`;
However, none of that is happening. I'm assuming it's because it's nested in there? Am I able to override the styles? Or should I just build a similar component to it?
If you are styling one of your own custom components, you must make sure you use the className prop that styled components gives to the component.
const Banner = ({ image, heading, text, className }) => (
<Container className={className}>
<Background src={image}>
<BannerContent>
<h1>{heading}</h1>
<h2>{text}</h2>
</BannerContent>
</Background>
</Container>
);
const PageBanner = styled(Banner)`
h1 {
font-size: 20px;
width: ...
}
h2 {
font-size: 13px;
width: ...
}
`;