├── .eslintcache ├── .gitignore ├── README.md ├── img └── thumb.png ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.js ├── componentes └── Input.js ├── elementos └── Formularios.js ├── estilos.css └── index.js /.eslintcache: -------------------------------------------------------------------------------- 1 | [{"D:\\xampp\\htdocs\\tutoriales\\react\\validacion-formulario\\src\\index.js":"1","D:\\xampp\\htdocs\\tutoriales\\react\\validacion-formulario\\src\\App.js":"2","D:\\xampp\\htdocs\\tutoriales\\react\\validacion-formulario\\src\\elementos\\Formularios.js":"3","D:\\xampp\\htdocs\\tutoriales\\react\\validacion-formulario\\src\\componentes\\Input.js":"4"},{"size":220,"mtime":1611429076875,"results":"5","hashOfConfig":"6"},{"size":4833,"mtime":1611452043009,"results":"7","hashOfConfig":"6"},{"size":2949,"mtime":1611517670476,"results":"8","hashOfConfig":"6"},{"size":1293,"mtime":1611451147204,"results":"9","hashOfConfig":"6"},{"filePath":"10","messages":"11","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"12"},"1st86mo",{"filePath":"13","messages":"14","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"12"},{"filePath":"15","messages":"16","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"17","messages":"18","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"12"},"D:\\xampp\\htdocs\\tutoriales\\react\\validacion-formulario\\src\\index.js",[],["19","20"],"D:\\xampp\\htdocs\\tutoriales\\react\\validacion-formulario\\src\\App.js",[],"D:\\xampp\\htdocs\\tutoriales\\react\\validacion-formulario\\src\\elementos\\Formularios.js",[],"D:\\xampp\\htdocs\\tutoriales\\react\\validacion-formulario\\src\\componentes\\Input.js",[],{"ruleId":"21","replacedBy":"22"},{"ruleId":"23","replacedBy":"24"},"no-native-reassign",["25"],"no-negated-in-lhs",["26"],"no-global-assign","no-unsafe-negation"] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Guía Completa para Principiantes de Validación de Formularios en React 2 | ### [Tutorial: https://youtu.be/tli5n_NqQW8 ](https://youtu.be/tli5n_NqQW8 ) 3 | 4 | ![Guía Completa para Principiantes de Validación de Formularios en React](https://raw.githubusercontent.com/falconmasters/validacion-formularios-react/master/img/thumb.png) 5 | 6 | Por: [FalconMasters](http://www.falconmasters.com) -------------------------------------------------------------------------------- /img/thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/falconmasters/validacion-formularios-react/5d357b70bea37ab1c9b9801ea4c1b9192126ff94/img/thumb.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "validacion-formulario", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@fortawesome/fontawesome-svg-core": "^1.2.34", 7 | "@fortawesome/free-solid-svg-icons": "^5.15.2", 8 | "@fortawesome/react-fontawesome": "^0.1.14", 9 | "@testing-library/jest-dom": "^5.11.9", 10 | "@testing-library/react": "^11.2.3", 11 | "@testing-library/user-event": "^12.6.2", 12 | "react": "^17.0.1", 13 | "react-dom": "^17.0.1", 14 | "react-scripts": "4.0.1", 15 | "styled-components": "^5.2.1", 16 | "web-vitals": "^0.2.4" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject" 23 | }, 24 | "eslintConfig": { 25 | "extends": [ 26 | "react-app", 27 | "react-app/jest" 28 | ] 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/falconmasters/validacion-formularios-react/5d357b70bea37ab1c9b9801ea4c1b9192126ff94/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/falconmasters/validacion-formularios-react/5d357b70bea37ab1c9b9801ea4c1b9192126ff94/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/falconmasters/validacion-formularios-react/5d357b70bea37ab1c9b9801ea4c1b9192126ff94/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import {Formulario, Label, ContenedorTerminos, ContenedorBotonCentrado, Boton, MensajeExito, MensajeError} from './elementos/Formularios'; 3 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 4 | import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; 5 | import Input from './componentes/Input'; 6 | 7 | const App = () => { 8 | const [usuario, cambiarUsuario] = useState({campo: '', valido: null}); 9 | const [nombre, cambiarNombre] = useState({campo: '', valido: null}); 10 | const [password, cambiarPassword] = useState({campo: '', valido: null}); 11 | const [password2, cambiarPassword2] = useState({campo: '', valido: null}); 12 | const [correo, cambiarCorreo] = useState({campo: '', valido: null}); 13 | const [telefono, cambiarTelefono] = useState({campo: '', valido: null}); 14 | const [terminos, cambiarTerminos] = useState(false); 15 | const [formularioValido, cambiarFormularioValido] = useState(null); 16 | 17 | const expresiones = { 18 | usuario: /^[a-zA-Z0-9_-]{4,16}$/, // Letras, numeros, guion y guion_bajo 19 | nombre: /^[a-zA-ZÀ-ÿ\s]{1,40}$/, // Letras y espacios, pueden llevar acentos. 20 | password: /^.{4,12}$/, // 4 a 12 digitos. 21 | correo: /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/, 22 | telefono: /^\d{7,14}$/ // 7 a 14 numeros. 23 | } 24 | 25 | const validarPassword2 = () => { 26 | if(password.campo.length > 0){ 27 | if(password.campo !== password2.campo){ 28 | cambiarPassword2((prevState) => { 29 | return {...prevState, valido: 'false'} 30 | }); 31 | } else { 32 | cambiarPassword2((prevState) => { 33 | return {...prevState, valido: 'true'} 34 | }); 35 | } 36 | } 37 | } 38 | 39 | const onChangeTerminos = (e) => { 40 | cambiarTerminos(e.target.checked); 41 | } 42 | 43 | const onSubmit = (e) => { 44 | e.preventDefault(); 45 | 46 | if( 47 | usuario.valido === 'true' && 48 | nombre.valido === 'true' && 49 | password.valido === 'true' && 50 | password2.valido === 'true' && 51 | correo.valido === 'true' && 52 | telefono.valido === 'true' && 53 | terminos 54 | ){ 55 | cambiarFormularioValido(true); 56 | cambiarUsuario({campo: '', valido: ''}); 57 | cambiarNombre({campo: '', valido: null}); 58 | cambiarPassword({campo: '', valido: null}); 59 | cambiarPassword2({campo: '', valido: 'null'}); 60 | cambiarCorreo({campo: '', valido: null}); 61 | cambiarTelefono({campo: '', valido: null}); 62 | 63 | // ... 64 | } else { 65 | cambiarFormularioValido(false); 66 | } 67 | } 68 | 69 | return ( 70 |
71 | 72 | 82 | 92 | 101 | 110 | 120 | 130 | 131 | 132 | 133 | 134 | 144 | 145 | {formularioValido === false && 146 |

147 | 148 | Error: Por favor rellena el formulario correctamente. 149 |

150 |
} 151 | 152 | Enviar 153 | {formularioValido === true && Formulario enviado exitosamente!} 154 | 155 |
156 |
157 | ); 158 | } 159 | 160 | export default App; -------------------------------------------------------------------------------- /src/componentes/Input.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Input, Label, GrupoInput, LeyendaError, IconoValidacion} from './../elementos/Formularios'; 3 | import { faCheckCircle, faTimesCircle } from '@fortawesome/free-solid-svg-icons'; 4 | 5 | const ComponenteInput = ({estado, cambiarEstado, tipo, label, placeholder, name, leyendaError, expresionRegular, funcion}) => { 6 | const onChange = (e) => { 7 | cambiarEstado({...estado, campo: e.target.value}); 8 | } 9 | 10 | const validacion = () => { 11 | if(expresionRegular){ 12 | if(expresionRegular.test(estado.campo)){ 13 | cambiarEstado({...estado, valido: 'true'}); 14 | } else { 15 | cambiarEstado({...estado, valido: 'false'}); 16 | } 17 | } 18 | 19 | if(funcion){ 20 | funcion(); 21 | } 22 | } 23 | 24 | return ( 25 |
26 | 27 | 28 | 38 | 42 | 43 | {leyendaError} 44 |
45 | ); 46 | } 47 | 48 | export default ComponenteInput; -------------------------------------------------------------------------------- /src/elementos/Formularios.js: -------------------------------------------------------------------------------- 1 | import styled, {css} from 'styled-components'; 2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 3 | 4 | const colores = { 5 | borde: "#0075FF", 6 | error: "#bb2929", 7 | exito: "#1ed12d" 8 | } 9 | 10 | const Formulario = styled.form` 11 | display: grid; 12 | grid-template-columns: 1fr 1fr; 13 | gap: 20px; 14 | 15 | @media (max-width: 800px){ 16 | grid-template-columns: 1fr; 17 | } 18 | `; 19 | 20 | const Label = styled.label` 21 | display: block; 22 | font-weight: 700; 23 | padding: 10px; 24 | min-height: 40px; 25 | cursor: pointer; 26 | 27 | ${props => props.valido === 'false' && css` 28 | color: ${colores.error}; 29 | `} 30 | `; 31 | 32 | const GrupoInput = styled.div` 33 | position: relative; 34 | z-index: 90; 35 | `; 36 | 37 | const Input = styled.input` 38 | width: 100%; 39 | background: #fff; 40 | border-radius: 3px; 41 | height: 45px; 42 | line-height: 45px; 43 | padding: 0 40px 0 10px; 44 | transition: .3s ease all; 45 | border: 3px solid transparent; 46 | 47 | &:focus { 48 | border: 3px solid ${colores.borde}; 49 | outline: none; 50 | box-shadow: 3px 0px 30px rgba(163,163,163, 0.4); 51 | } 52 | 53 | ${props => props.valido === 'true' && css` 54 | border: 3px solid transparent; 55 | `} 56 | 57 | ${props => props.valido === 'false' && css` 58 | border: 3px solid ${colores.error} !important; 59 | `} 60 | `; 61 | 62 | const LeyendaError = styled.p` 63 | font-size: 12px; 64 | margin-bottom: 0; 65 | color: ${colores.error}; 66 | display: none; 67 | 68 | ${props => props.valido === 'true' && css` 69 | display: none; 70 | `} 71 | 72 | ${props => props.valido === 'false' && css` 73 | display: block; 74 | `} 75 | `; 76 | 77 | const IconoValidacion = styled(FontAwesomeIcon)` 78 | position: absolute; 79 | right: 10px; 80 | bottom: 14px; 81 | z-index: 100; 82 | font-size: 16px; 83 | opacity: 0; 84 | 85 | ${props => props.valido === 'false' && css` 86 | opacity: 1; 87 | color: ${colores.error}; 88 | `} 89 | 90 | ${props => props.valido === 'true' && css` 91 | opacity: 1; 92 | color: ${colores.exito}; 93 | `} 94 | `; 95 | 96 | const ContenedorTerminos = styled.div` 97 | grid-column: span 2; 98 | 99 | input { 100 | margin-right: 10px; 101 | } 102 | 103 | @media (max-width: 800px){ 104 | grid-column: span 1; 105 | } 106 | `; 107 | 108 | const ContenedorBotonCentrado = styled.div` 109 | display: flex; 110 | flex-direction: column; 111 | align-items: center; 112 | grid-column: span 2; 113 | 114 | @media (max-width: 800px){ 115 | grid-column: span 1; 116 | } 117 | `; 118 | 119 | const Boton = styled.button` 120 | height: 45px; 121 | line-height: 45px; 122 | width: 30%; 123 | background: #000; 124 | color: #fff; 125 | font-weight: bold; 126 | border: none; 127 | border-radius: 3px; 128 | cursor: pointer; 129 | transition: .1s ease all; 130 | 131 | &:hover { 132 | box-shadow: 3px 0px 30px rgba(163,163,163, 1); 133 | } 134 | `; 135 | 136 | const MensajeExito = styled.p` 137 | font-size: 14px; 138 | color: ${colores.exito}; 139 | `; 140 | 141 | const MensajeError = styled.div` 142 | height: 45px; 143 | line-height: 45px; 144 | background: #F66060; 145 | padding: 0px 15px; 146 | border-radius: 3px; 147 | grid-column: span 2; 148 | p { 149 | margin: 0; 150 | } 151 | b { 152 | margin-left: 10px; 153 | } 154 | `; 155 | 156 | export { 157 | Formulario, 158 | Label, 159 | GrupoInput, 160 | Input, 161 | LeyendaError, 162 | IconoValidacion, 163 | ContenedorTerminos, 164 | ContenedorBotonCentrado, 165 | Boton, 166 | MensajeExito, 167 | MensajeError 168 | }; -------------------------------------------------------------------------------- /src/estilos.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap'); 2 | 3 | * { 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | background: #e5e5e5; 9 | font-family: 'Roboto', sans-serif; 10 | } 11 | 12 | main { 13 | max-width: 800px; 14 | width: 90%; 15 | margin: auto; 16 | padding: 40px; 17 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './estilos.css'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ); --------------------------------------------------------------------------------