Skip to content

Commit 2495840

Browse files
committed
ref:#187 15 adding context form validation
1 parent 722a324 commit 2495840

14 files changed

Lines changed: 157 additions & 227 deletions

hooks/15_Context/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,11 @@
3939
"webpack-dev-server": "^3.11.0"
4040
},
4141
"dependencies": {
42+
"@lemoncode/fonk": "^1.3.0",
43+
"@lemoncode/fonk-formik": "^4.0.1",
4244
"@material-ui/core": "^4.11.0",
4345
"@material-ui/icons": "^4.9.1",
44-
"lc-form-validation": "^2.0.0",
46+
"formik": "^2.1.5",
4547
"react": "^16.13.1",
4648
"react-dom": "^16.13.1",
4749
"react-router-dom": "^5.2.0"

hooks/15_Context/src/api/login.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
import {LoginEntity} from '../model/login';
1+
import { LoginEntity } from "../model/login";
22

33
// Just a fake loginAPI
4-
export const isValidLogin = (loginInfo : LoginEntity) : boolean =>
5-
(loginInfo.login === 'admin' && loginInfo.password === 'test');
4+
export const isValidLogin = (loginInfo: LoginEntity): Promise<boolean> =>
5+
new Promise((resolve) => {
6+
setTimeout(() => {
7+
// mock call
8+
resolve(loginInfo.login === "admin" && loginInfo.password === "test");
9+
}, 500);
10+
});

hooks/15_Context/src/app.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from "react";
22
import { HashRouter, Switch, Route } from "react-router-dom";
3-
import { LoginPage } from "./pages/loginPage";
3+
import { LoginContainer } from "./pages/login.container";
44
import { PageB } from "./pages/pageB";
55
import { SessionProvider } from "./common";
66

@@ -10,7 +10,7 @@ export const App = () => {
1010
<SessionProvider>
1111
<HashRouter>
1212
<Switch>
13-
<Route exact={true} path="/" component={LoginPage} />
13+
<Route exact={true} path="/" component={LoginContainer} />
1414
<Route path="/pageB" component={PageB} />
1515
</Switch>
1616
</HashRouter>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export * from "./notification";
2-
export * from "./textFieldForm";
2+
export * from "./textField.component";
33
export * from "./sessionContext";

hooks/15_Context/src/common/notification.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ export const NotificationComponent: React.FC<Props> = (props) => {
2626
return (
2727
<Snackbar
2828
anchorOrigin={{
29-
vertical: "bottom",
30-
horizontal: "left",
29+
vertical: "top",
30+
horizontal: "right",
3131
}}
3232
open={show}
3333
autoHideDuration={3000}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import * as React from "react";
2+
import { useField } from "formik";
3+
import MuiTextField, { TextFieldProps } from "@material-ui/core/TextField";
4+
5+
export const TextFieldComponent: React.FC<TextFieldProps> = (props) => {
6+
const [field, meta] = useField(props.name);
7+
const textFieldProps = Boolean(field) ? field : props;
8+
const hasError = Boolean(meta && meta.touched && meta.error);
9+
10+
return (
11+
<>
12+
<MuiTextField
13+
{...props}
14+
name={textFieldProps.name}
15+
onChange={textFieldProps.onChange}
16+
onBlur={textFieldProps.onBlur}
17+
value={textFieldProps.value}
18+
error={hasError}
19+
helperText={hasError ? meta.error : ""}
20+
fullWidth={true}
21+
margin="normal"
22+
/>
23+
</>
24+
);
25+
};

hooks/15_Context/src/common/textFieldForm.tsx

Lines changed: 0 additions & 41 deletions
This file was deleted.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import * as React from "react";
2+
import { LoginEntity, createEmptyLogin } from "../model/login";
3+
import { TextFieldComponent } from "../common";
4+
import { Form } from "formik";
5+
import createStyles from "@material-ui/styles/createStyles";
6+
import makeStyles from "@material-ui/styles/makeStyles";
7+
import Button from "@material-ui/core/Button";
8+
import Card from "@material-ui/core/Card";
9+
import CardHeader from "@material-ui/core/CardHeader";
10+
import CardContent from "@material-ui/core/CardContent";
11+
import { loginFormValidation } from "./login.validation";
12+
import { Formik } from "formik";
13+
14+
interface PropsForm {
15+
onLogin: (login: LoginEntity) => void;
16+
}
17+
18+
// https://material-ui.com/styles/api/#makestyles-styles-options-hook
19+
const useFormStyles = makeStyles((theme) =>
20+
createStyles({
21+
formContainer: {
22+
display: "flex",
23+
flexDirection: "column",
24+
justifyContent: "center",
25+
},
26+
card: {
27+
maxWidth: 400,
28+
margin: "0 auto",
29+
},
30+
})
31+
);
32+
33+
export const LoginComponent: React.FC<PropsForm> = (props) => {
34+
const classes = useFormStyles();
35+
const { onLogin } = props;
36+
37+
return (
38+
<Card className={classes.card}>
39+
<CardHeader title="Login" />
40+
<CardContent>
41+
<Formik
42+
onSubmit={onLogin}
43+
initialValues={createEmptyLogin()}
44+
validate={loginFormValidation.validateForm}
45+
>
46+
{() => (
47+
<Form>
48+
<div className={classes.formContainer}>
49+
<TextFieldComponent label="Name" name="login" />
50+
<TextFieldComponent
51+
label="Password"
52+
type="password"
53+
name="password"
54+
/>
55+
<Button type="submit" variant="contained" color="primary">
56+
Login
57+
</Button>
58+
</div>
59+
</Form>
60+
)}
61+
</Formik>
62+
</CardContent>
63+
</Card>
64+
);
65+
};
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import * as React from "react";
2+
import { useHistory } from "react-router-dom";
3+
import { LoginEntity } from "../model/login";
4+
import { isValidLogin } from "../api/login";
5+
import { LoginComponent } from "./login.component";
6+
import { NotificationComponent, SessionContext } from "../common";
7+
import { light } from "@material-ui/core/styles/createPalette";
8+
9+
interface Props {}
10+
11+
export const LoginContainer: React.FC<Props> = (props) => {
12+
const loginContext = React.useContext(SessionContext);
13+
const history = useHistory();
14+
const [isShowAlert, setShowAlert] = React.useState(false);
15+
16+
const loginSucceeded = (isValid: boolean, login: LoginEntity) => {
17+
if (isValid) {
18+
history.push("/pageB");
19+
loginContext.updateLogin(login.login);
20+
} else {
21+
setShowAlert(true);
22+
}
23+
};
24+
25+
const handleLogin = (login: LoginEntity) => {
26+
isValidLogin(login).then((isValid) => loginSucceeded(isValid, login));
27+
};
28+
29+
return (
30+
<>
31+
<LoginComponent onLogin={handleLogin} />
32+
<NotificationComponent
33+
show={isShowAlert}
34+
message="Invalid Form"
35+
onClose={() => setShowAlert(false)}
36+
/>
37+
</>
38+
);
39+
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { ValidationSchema, Validators } from "@lemoncode/fonk";
2+
import { createFormikValidation } from "@lemoncode/fonk-formik";
3+
4+
const validationSchema: ValidationSchema = {
5+
field: {
6+
login: [Validators.required],
7+
password: [Validators.required],
8+
},
9+
};
10+
11+
export const loginFormValidation = createFormikValidation(validationSchema);

0 commit comments

Comments
 (0)