How to display a span based on a invalid input SASS - reactjs

im using SASS and i would like to display an error span when the input is invalid, can this be done using just SASS?
this is the input div:
const FormInput = (props) => {
const [focused, setFocused] = useState(false);
const { label, errorMessage, onChange, id, ...inputProps } = props;
const handleFocus = (e) => {
setFocused(true);
};
return (
<div className="formInput">
<span>{errorMessage}</span>
<input
{...inputProps}
onChange={onChange}
onBlur={handleFocus}
onFocus={() =>
inputProps.name === "confirmPassword" && setFocused(true)
}
focused={focused.toString()}
className="Input"
/>
</div>
);
};
and this is the SASS file i can change the border color of the input field it self but idk how to change the display of the span from none to block when the input is invalid
.formInput {
display: flex;
flex-direction: column;
width: 90%;
height: max-content;
span {
color: red;
font-size: 13px;
display: none;
padding: 0.5rem 0;
}
.Input {
border: none;
outline: none;
background-color: var(--inputColor);
border-radius: 8px;
padding: 20px;
&:invalid[focused="true"] {
border: 1px solid red;
}
}
}
the inputs are validated using html pattern with regex and required attribute
like this:
const inputs = [
{
id: 1,
name: "email",
type: "email",
placeholder: "Email",
errorMessage: "It should be a valid email address!",
label: "Email",
required: true,
},
{
id: 2,
name: "password",
type: "password",
placeholder: "Password",
errorMessage:
"Password should be 8-20 characters and include at least 1 letter, 1 number and 1 special character!",
label: "Password",
pattern: `/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[0-9a-zA-Z]{8,}$/`,
required: true,
},
]

I found a way to do this, first you have to make sure that the span is placed after the input like this
<div className="formInput">
<input
{...inputProps}
onChange={onChange}
onBlur={handleFocus}
onFocus={() =>
inputProps.name === "confirmPassword" && setFocused(true)
}
focused={focused.toString()}
className="Input"
/>
<span>{errorMessage}</span>
</div>
and then add this to the SCSS file:
.Input {
border: none;
outline: none;
background-color: var(--inputColor);
border-radius: 8px;
padding: 20px;
&:invalid[focused="true"] {
border: 1px solid red;
}
&:invalid[focused="true"] {
display: block;
**~ {
span {
display: block;
}**
}
}
}

Yeah, you can do it with pseudo class like the following example, you need to put the span tag just next to the input tag, here I'm using a text input element but you can change it as you require:
HTML
<input name="question1" type="text" value="prefixed value" required> <span></span>
CSS
input[type="text"]:invalid + span:after {
content: '✖';
}
input[type="text"]:valid + span:after {
content: '✓';
}
Here the content will change if the person leave this input field empty.

Related

how to apply a multiconditional to style a react component?

I have a form where the borders of the inputs have 2 colors
-Grey: when the component is loaded
-blue: when the input is not empty
and I would like to apply a third color when the user presses the register button and the inputs are not empty that changes the border color to red. but I do not know how
Page
const hanleClear= () => {
setCompania(0);
setDepartamento(0);
setUnidad(0);
setLocalidad(0);
setActivoTipo(0);
setActivoTipoCatego(0);
setMarca("");
setColor("");
setModelo("");
setComponente("");
setSerial("");
setObservacion("");
};
const hanleSerial= (e) => {
setSerial(e.target.value.toUpperCase());
};
const handleSumit = function (e) {
e.preventDefault();
if (serial === 0) {
// Apply color red.
}
let form = {
idinformacion: compania,
iddepartamento: departamento,
idunidad: unidad,
};
let Data = JSON.stringify(form );
ServiceSerial.Create(Data);
};
<InputGroup
input_label={"Serial"}
input_type={"text"}
input_value={serial}
input_placeholder={"Serial"}
state_name={serial}
set_state_name={setSerial}
on_change={hanleSerial}
/>
<button onClick={hanleSumit}>Register</button>
<br />
<button onClick={hanleClear}>Limpiar</button>
<br />
Componenet
import {
Input,
Label,
InputGroupContainer,
WrapperInput,
} from "../components/FormStyled";
const InputGroup = function ({
input_label,
input_type,
input_value,
input_name,
input_placeholder,
on_change,
on_key_down,
}) {
return (
<InputGroupContainer>
<Label>{input_label}</Label>
<WrapperInput>
<Input
type={input_type}
placeholder={input_placeholder}
value={input_value}
name={input_name}
onChange={on_change}
onKeyDown={on_key_down}
className={!input_value ? "" : "Activated"}
/>
</WrapperInput>
{/* <span>{errorMessage}</span> */}
</InputGroupContainer>
);
};
export default InputGroup;
css
// LABEL
export const Label = styled.label`
font-size: 16px;
cursor: pointer;
letter-spacing: 0.8px;
color: black;
margin-top: 15px;
margin-bottom: 5px;
font-family: "Inter";
font-weight: 900;
`;
// INPUT
export const Input = styled.input`
font-size: 16px;
font-weight: normal;
font-family: "Inter";
letter-spacing: 0.3px;
width: 100%;
height: 40px;
line-height: 40px;
padding: 0px 5px 0px 10px;
border: 2px solid #ccc;
border-radius: 0px;
transition: 0.3s ease all;
border-radius: 3px;
color: black;
&:focus {
outline: none;
border: 2px solid #00aea9;
}
&.Activated {
border: 2px solid #00aea9;
}
`;
// Input Group
export const InputGroupContainer = styled.div`
margin: 15px 0px;
position: relative;
width: 100%;
`;
I can give you a general solution. If you get it, you can adapt it to your specific code.
I use a state called inputState to denote if the input is empty/filled/filled&pressed.
Then your component just composes style based on that state whenever rendering.
function ComponentABC() {
const [inputState, setInputState] = useState(1);
let inputStyle = {borderCorlor: 'grey'};
if(inputState == 1) { /* empty */
inputStyle = {borderCorlor: 'grey'};
}
else if(inputState == 2) { /* filled */
inputStyle = {borderCorlor: 'blue'};
}
else if(inputState == 3) { /* filled & pressed */
inputStyle = {borderCorlor: 'red'};
}
// when pressed button, change color
function handleRegister(e) {
if(inputValue) {
setInputState(3);
}
}
function handleInputChange(e) {
// when input is filled, change color
if(inputValue) {
setInputState(2);
}
// when input is empty, change color
else if(!inputValue) {
setInputState(1);
}
}
return (
<div>
<input style={inputStyle} onChange={handleInputChange}/>
<button onClick={handleRegister}>register</button>
</div>
);
}

Adding New Input Field using button in ReactJs

I am working on a project so I am trying to add a new input field after clicking the add(+) button in the form using react-Hooks. It would be very helpful if someone can figure this out so I can learn a new thing. Thankyou in advance.[React events handling link is added][1]
Here is the code of the form and css file
import Navbar from "./Navbar/Navbar";
import "./UserDetails.css";
function UserDetails() {
const [Info, setInfo] = useState({
username: "",
email: "",
bio: "",
positions: "",
});
const { username, email, bio, positions } = Info;
const onChange = (e) => setInfo({ ...Info, [e.target.name]: e.target.value });
const onSubmit = (e) => {
e.preventDefault();
console.log(Info);
};
return (
<div>
<Navbar />
<div className="full">
<h1 className="top">Edit Profile</h1>
<img
className="pic"
src="https://www.pngitem.com/pimgs/m/146-1468479_my-profile-icon-blank-profile-picture-circle-hd.png"
alt=""
></img>
<div className="inputs">
<form className="frms" onSubmit={(e) => onSubmit(e)}>
<input
className="insides"
type="text"
placeholder="Username"
name="username"
value={username}
onChange={(e) => onChange(e)}
></input>
<input
className="insides"
type="email"
placeholder="email"
name="email"
value={email}
onChange={(e) => onChange(e)}
></input>
<textarea
placeholder="Add Bio here..."
name="bio"
value={bio}
onChange={(e) => onChange(e)}
></textarea>
</form>
</div>
<div className="addbtn">
<input
className="insidesp"
type="text"
placeholder="positions held..."
name="positions"
value={positions}
onChange={(e) => onChange(e)}
></input>
<button>+</button>
<br></br>
<button className="savebtn">Save Changes</button>
</div>
</div>
</div>
);
}
export default UserDetails;
.full {
text-align: center;
border: 2px solid white;
margin: 0 auto;
padding-top: 1%;
width: 50%;
padding-bottom: 1%;
margin-top: 2%;
border-radius: 30px;
box-shadow: 5px 6px 5px 6px rgb(158, 170, 238);
}
.top {
color: rgb(88 99 161);
}
.pic {
height: 40%;
width: 30%;
}
.inputs input {
display: block;
width: 70%;
margin: 0 auto;
margin-top: 2%;
border: 2px solid rgb(88 99 161);
border-radius: 7px;
}
.inputs textarea {
display: block;
width: 70%;
margin: 0 auto;
margin-top: 2%;
border: 2px solid rgb(88 99 161);
border-radius: 7px;
}
.pic {
margin: 2%;
}
.savebtn {
margin-top: 2%;
background-color: rgb(88 99 161);
border: 2px solid rgb(158, 170, 238);
border-radius: 6px;
}
.addbtn {
display: inline;
}
.addbtn input {
margin-right: 1%;
width: 65%;
margin-top: 2%;
border: 2px solid rgb(88 99 161);
border-radius: 7px;
}
.addbtn button {
background-color: rgb(88 99 161);
border-radius: 6px;
border: 2px solid rgb(158, 170, 238);
}```
[1]: https://reactjs.org/docs/handling-events.html
Here is a great article that explains solution for your question step by step:
https://www.cluemediator.com/add-or-remove-input-fields-dynamically-with-reactjs

Input checkbox didn't work what I expected

I tried to make an input checkbox when I click the input checkbox, it should be displayed a check image like this.
However, it didn't show the checkbox and I am not sure how to check that the input box was checked or not. Could you help me with what part do I missed and where is something wrong?
I really appreciate your help!
This is CSS inputl and label part
.colors {
display: flex;
flex-direction: column;
span {
margin-bottom: 20px;
}
.colorLists {
margin-left: 10px;
display: flex;
flex-wrap: wrap;
.colorLayout {
display: flex;
flex-direction: column;
position: relative;
width: 33%;
height: 80px;
flex-shrink: 0;
align-items: center;
justify-content: center;
.checkboxLabel {
background-color: beige;
border: 1px solid #ccc;
border-radius: 50%;
cursor: pointer;
height: 28px;
left: 0;
position: absolute;
top: 40;
width: 28px;
&:after {
border: 2px solid #fff;
border-top: none;
border-right: none;
content: '';
height: 6px;
left: 7px;
opacity: 0;
position: absolute;
top: 8px;
transform: rotate(-45deg);
width: 12px;
// opacity: 0.2;
}
}
input[type='checkbox'] {
visibility: hidden;
}
input[type='checkbox']:checked {
& + label {
background-color: beige;
border-color: beige;
&:after {
opacity: 1;
}
}
}
.productColor {
margin-top: 70px;
font-size: 13px;
margin-right: 21px;
}
}
}
}
.sizes {
.HorizontalLine {
margin-top: 25px;
}
.span {
}
.sizeLists {
margin-top: 20px;
margin-bottom: 20px;
button {
margin: 5px;
width: 44px;
height: 32px;
background-color: white;
border: 1px solid silver;
border-radius: 15%;
}
}
}
This is js part
<div className="colors">
<span>색상</span>
<ul className="colorLists">
{COLOR_LISTS.map((color, idx) => {
return (
<li className="colorLayout" key={idx}>
<input type="checkbox" />
<label
className="checkboxLabel"
for="checkbox"
style={{ backgroundColor: color.colorProps }}
/>
<span className="productColor">{color.color_name}</span>
</li>
);
})}
</ul>
</div>
In react you have to set the htmlFor property for the label instead of for.
The value should be the same as the id from the input.
Then you can add a value property for the input which is used for adding/removing the item in the list of selected items.
For this purpose a handleChange function can be defined.
const [selectedItems, setSelectedItems] = useState([]);
function handleChange(e) {
let newSelected = [];
if (selectedItems.includes(e.target.value)) {
newSelected = selectedItems.filter((item) => item !== e.target.value);
} else {
newSelected = [...selectedItems, e.target.value];
}
setSelectedItems(newSelected);
}
return (
<div className="colors">
<span>색상</span>
<ul className="colorLists">
{COLOR_LISTS.map((color, idx) => {
return (
<li className="colorLayout" key={idx}>
<input
onChange={handleChange}
type="checkbox"
id={idx}
value={color.color_name}
checked={selectedItems.includes(color.color_name)}
/>
<label
className="checkboxLabel"
htmlFor={idx}
style={{ backgroundColor: color.colorProps }}
/>
<span className="productColor">{color.color_name}</span>
</li>
);
})}
</ul>
</div>
);
EDIT: Since you are using a class component it can be rewrittenlike this:
export default class CheckboxListComponent extends Component {
constructor(props) {
super(props);
this.state = { selectedItems: [] };
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
let newSelected = [];
if (this.state.selectedItems.includes(e.target.value)) {
newSelected = this.state.selectedItems.filter(
(item) => item !== e.target.value
);
} else {
newSelected = [...this.state.selectedItems, e.target.value];
}
this.setState({ selectedItems: newSelected });
}
render() {
return (
<div className="colors">
<span>색상</span>
<ul className="colorLists">
{COLOR_LISTS.map((color, idx) => {
return (
<li className="colorLayout" key={idx}>
<input
onChange={this.handleChange}
type="checkbox"
id={idx}
value={color.color_name}
checked={this.state.selectedItems.includes(color.color_name)}
/>
<label
className="checkboxLabel"
htmlFor={idx}
style={{ backgroundColor: color.colorProps }}
/>
<span className="productColor">{color.color_name}</span>
</li>
);
})}
</ul>
</div>
);
}
}
You must tell react that your input is checked so that your CSS will apply it. selected ids must be kept in a place for future existence check. in the following code, I named this array selecetedIdx.
You also need to add idx on selection(via onChange event handler) or wrap them all in form and add them via extra dom attribute.
class Main extends Component {
// initialize selectedIdx with [] in your state (esp constructor)
// e.g. this.state = {/* rest of state, */ selectedIdx: []}
render() {
return (
{COLOR_LISTS.map((color, idx) => {
return (
// ...
<input
type="checkbox"
checked={selectedIdx.includes(idx)}
onChange={() => this.setState(state => ({
selectedIdx: [...state.selectedIdx, idx]
}))}
/>
// ...
)}
)
}
Your checkbox element needs name and value properties and would normally be a child of the <form> element.

React input checkbox checked

I have use React-redux and styled components for my app. I store my initial state theme as a string which is light and dark. then I connect my styled components intial light theme and dark theme in my root app. My dark mood works fine when i used select options but when i used input checkbox it does not work. I never used input checkbox, after reading couple example I used checked and put my initial theme(which is coming from my redux store), then in my handleChange I did, if the event target has dark then dispatch the dark theme. But nothing happening in that handle change. don't know what i am doing wrong.
Here is my toggle component
import React, { useState } from 'react';
import styled from 'styled-components';
import { useDispatch, useSelector } from 'react-redux';
import { appSettings } from '../../state/appSettings';
import { TRootState } from '../../state/index';
export default function Toggle({ }: IProp) {
const dispatch = useDispatch();
const { "appSettings": appSettingState } = useSelector((state: TRootState) => state);
const { theme } = appSettingState || {};
console.log(theme); // inital state which is "light".
return (
<>
{/* This input checkbox does not work */}
<CheckBoxWrapper>
<CheckBox
onChange={(e) => { // This function does not work
e.target.value === `dark` ?
dispatch(appSettings?.actions?.enableDarkTheme()) :
dispatch(appSettings?.actions?.enableLightTheme());
}}
id="toggleSwitch"
type="checkbox"
Checked={theme === `light`}
/>
<CheckBoxLabel htmlFor="toggleSwitch" />
</CheckBoxWrapper>
<br></br>
{/* THIS SELECT OPTIONS WORK FINE. AND I CAN GET DARK AND LIGHT THEME */}
<h2>Theme</h2>
<select
name="theme"
id="theme-select"
value={theme}
onChange={(e) => {
if (e.target.value === `dark`) {
dispatch(appSettings?.actions?.enableDarkTheme());
} else {
dispatch(appSettings?.actions?.enableLightTheme());
}
}}
>
<option value="dark">Dark</option>
<option value="light">Light</option>
</select>
</>
);
}
// This toogle input styled
const CheckBoxWrapper = styled.div`
position: fixed;
top:10px;
right:10px;
`;
const CheckBoxLabel = styled.label`
position: absolute;
top: 0;
left: 0;
width: 42px;
height: 26px;
border-radius: 15px;
background: #bebebe;
cursor: pointer;
&::after {
content: "";
display: block;
border-radius: 50%;
width: 18px;
height: 18px;
margin: 3px;
background: #ffffff;
box-shadow: 1px 3px 3px 1px rgba(0, 0, 0, 0.2);
transition: 0.2s;
}
`;
const CheckBox = styled.input`
opacity: 0;
z-index: 1;
border-radius: 15px;
width: 42px;
height: 26px;
&:checked + ${CheckBoxLabel} {
background: #4fbe79;
&::after {
content: "";
display: block;
border-radius: 50%;
width: 18px;
height: 18px;
margin-left: 21px;
transition: 0.2s;
}
}
`;
Check the change event's checked property. e.target.checked
<CheckBox
onChange={(e: any) => {
e.target.checked
? dispatch(appSettings?.actions?.enableDarkTheme())
: dispatch(appSettings?.actions?.enableLightTheme());
}}
id="toggleSwitch"
type="checkbox"
Checked={theme === `light`}
/>

Ref issue using react-hook-form

I'm trying to create a form validation with react-hook-form in my current project. I've already tried different approaches but always I got errors because of the ref attribute. If I change the <FormField> to input, it starts to work.
Any idea how to solve this?
Contact
import React from 'react';
import { useForm } from "react-hook-form";
import FormField from '../../components/FormField';
import Button from '../../components/Button';
const Contact = () => {
const { handleSubmit, register, errors } = useForm();
const onSubmit = values => console.log(values);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<FormField
name="email"
onChange={() => { console.log("changed!") }}
ref={register({
required: "Required",
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: "invalid email address"
}
})}
/>
<p style={{ color: "red" }}>
{errors.email && errors.email.message}
</p>
<Button>Submit</Button>
</form>
);
};
export default Contact;
FormField
import React from "react";
import PropTypes from "prop-types";
import styled, { css } from "styled-components";
const FormFieldWrapper = styled.div`
position: relative;
textarea {
min-height: 150px;
}
input[type="color"] {
padding-left: 67px;
}
`;
const Label = styled.label``;
Label.Text = styled.span`
color: #e5e5e5;
height: 57px;
position: absolute;
top: 0;
left: 16px;
display: flex;
align-items: center;
transform-origin: 0% 0%;
font-size: 18px;
font-style: normal;
font-weight: 300;
transition: 0.1s ease-in-out;
`;
const Input = styled.input`
background: #53585d;
color: #f5f5f5;
display: block;
width: 100%;
height: 57px;
font-size: 18px;
outline: 0;
border: 0;
border-top: 4px solid transparent;
border-bottom: 4px solid #53585d;
padding: 16px 16px;
margin-bottom: 45px;
resize: none;
border-radius: 4px;
transition: border-color 0.3s;
&:focus {
border-bottom-color: var(--primary);
}
&:focus:not([type="color"]) + ${Label.Text} {
transform: scale(0.6) translateY(-10px);
}
${({ value }) => {
const hasValue = value.length > 0;
return (
hasValue &&
css`
&:not([type="color"]) + ${Label.Text} {
transform: scale(0.6) translateY(-10px);
}
`
);
}}
`;
function FormField({ label, type, name, value, onChange, ref }) {
const isTypeTextArea = type === "textarea";
const tag = isTypeTextArea ? "textarea" : "input";
return (
<FormFieldWrapper>
<Label>
<Input
as={tag}
type={type}
value={value}
name={name}
onChange={onChange}
ref={ref}
/>
<Label.Text>{label}:</Label.Text>
</Label>
</FormFieldWrapper>
);
}
FormField.defaultProps = {
type: "text",
value: "",
};
FormField.propTypes = {
label: PropTypes.string,
name: PropTypes.string.isRequired,
type: PropTypes.string,
value: PropTypes.string,
onChange: PropTypes.func,
ref: PropTypes.func
};
export default FormField;
Errors:
Referring to the docs the register should be used as below so we won't get refs issues and also the register will change the value inside the input so we don't need to pass a value prop :
Contact :
import React from "react";
import { useForm } from "react-hook-form";
import FormField from "../../components/FormField";
import Button from "../../components/Button";
const Contact = () => {
const { handleSubmit, register, errors } = useForm();
const onSubmit = (values) => console.log("values", values);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<FormField
name="email"
onChange={() => {
console.log("changed!");
}}
register={register}
/>
<p style={{ color: "red" }}>{errors.email && errors.email.message}</p>
<Button>Submit</Button>
</form>
);
};
export default Contact;
FormField :
import React from "react";
import PropTypes from "prop-types";
import styled, { css } from "styled-components";
const FormFieldWrapper = styled.div`
position: relative;
textarea {
min-height: 150px;
}
input[type="color"] {
padding-left: 67px;
}
`;
const Label = styled.label``;
Label.Text = styled.span`
color: #e5e5e5;
height: 57px;
position: absolute;
top: 0;
left: 16px;
display: flex;
align-items: center;
transform-origin: 0% 0%;
font-size: 18px;
font-style: normal;
font-weight: 300;
transition: 0.1s ease-in-out;
`;
const Input = styled.input`
background: #53585d;
color: #f5f5f5;
display: block;
width: 100%;
height: 57px;
font-size: 18px;
outline: 0;
border: 0;
border-top: 4px solid transparent;
border-bottom: 4px solid #53585d;
padding: 16px 16px;
margin-bottom: 45px;
resize: none;
border-radius: 4px;
transition: border-color 0.3s;
&:focus {
border-bottom-color: var(--primary);
}
&:focus:not([type="color"]) + ${Label.Text} {
transform: scale(0.6) translateY(-10px);
}
${({ value = {} }) => { // here you should find an other approch because there is no value props
const hasValue = value.length > 0;
return (
hasValue &&
css`
&:not([type="color"]) + ${Label.Text} {
transform: scale(0.6) translateY(-10px);
}
`
);
}}
`;
const FormField = ({ label, type, name, onChange, register }) => {
const isTypeTextArea = type === "textarea";
const tag = isTypeTextArea ? "textarea" : "input";
return (
<FormFieldWrapper>
<Label>
<Input
as={tag}
type={type}
// value={value} it's not a controlled input! so the register'ill provide the value
name={name}
onChange={onChange}
ref={register({
required: "Required",
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: "invalid email address",
},
})}
/>
<Label.Text>{label}:</Label.Text>
</Label>
</FormFieldWrapper>
);
};
FormField.defaultProps = {
type: "text",
value: "",
};
FormField.propTypes = {
label: PropTypes.string,
name: PropTypes.string.isRequired,
type: PropTypes.string,
value: PropTypes.string,
onChange: PropTypes.func,
ref: PropTypes.func,
};
export default FormField;

Resources