Skip to content

Commit

Permalink
chore: formik, yup and reusable forms
Browse files Browse the repository at this point in the history
  • Loading branch information
EricRibia committed Aug 13, 2024
1 parent 60fb444 commit 8ed1e92
Show file tree
Hide file tree
Showing 20 changed files with 1,340 additions and 266 deletions.
16 changes: 4 additions & 12 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import Screen from "./components/Screen";
import { useState } from "react";
import AppTextInput from "./components/AppTextInput";
import AppPicker from "./components/AppPicker";
import LoginScreen from "./Screens/LoginScreen";
import ListingsScreen from "./Screens/ListingsScreen";
import ListingEditScreen from "./Screens/ListingEditScreen";

const categories = [
{
Expand All @@ -22,18 +25,7 @@ const categories = [
export default function App() {
const [isNew, setIsNew] = useState(false);
const [category, setCategory] = useState("Furniture");
return (
<Screen>
<AppPicker
selectedItem={category}
onSelectItem={(item) => setCategory(item)}
icon="apps"
placeholder="Category"
items={categories}
/>
<AppTextInput icon="email" placeholder="Category" />
</Screen>
);
return <ListingEditScreen />;
}

const styles = StyleSheet.create({});
81 changes: 81 additions & 0 deletions Screens/ListingEditScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { View, StyleSheet, Image } from "react-native";
import * as Yup from "yup";
import Screen from "../components/Screen";
import {
AppForm,
AppFormField,
AppSubmitButton,
AppFormPicker,
} from "../components/forms";

const validationSchema = Yup.object().shape({
title: Yup.string().required().min(3).label("Title"),
price: Yup.string().required().min(1).max(10000).label("Price"),
description: Yup.string().required().min(3).label("Description"),
category: Yup.string().required().min(4).label("Category"),
// category: Yup.object().required().nullable().label("Category"),
});

const formFields = {
title: "",
price: "",
description: "",
category: "",
};
const categories = [
{
label: "Furniture",
value: 1,
},
{
label: "Clothing",
value: 2,
},
{
label: "Cameras",
value: 3,
},
];
export default function () {
return (
<Screen styles={styles.container}>
<AppForm
initialValues={formFields}
onSubmit={(values) => console.log(values)}
validationSchema={validationSchema}
>
<AppFormField
autoCorrect={false}
maxLength={255}
placeholder="Title"
name="title"
/>
<AppFormField
keyboardTyoe="numeric"
maxLength={8}
placeholder="0.00"
name="price"
/>
<AppFormPicker
items={categories}
name="category"
placeholder="Category"
/>
<AppFormField
multiline
maxLength={255}
placeholder="Description"
numberOfLines={3}
name="description"
/>
<AppSubmitButton label="Post" />
</AppForm>
</Screen>
);
}

const styles = StyleSheet.create({
container: {
padding: 10,
},
});
55 changes: 55 additions & 0 deletions Screens/LoginScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { StyleSheet, Image } from "react-native";
import * as Yup from "yup";
//in app imports
import Screen from "../components/Screen";
import { AppForm, AppFormField, AppSubmitButton } from "../components/forms";

const validationSchema = Yup.object().shape({
email: Yup.string().required().email().label("Email"),
password: Yup.string().required().min(4).label("Password"),
});
export default function () {
return (
<Screen styles={styles.container}>
<Image style={styles.logo} source={require("../assets/benz-logo.png")} />
<AppForm
initialValues={{ email: "", password: "" }}
onSubmit={(values) => console.log(values)}
validationSchema={validationSchema}
>
<AppFormField
icon="email"
autoCapitalize="none"
autoCorrect={false}
keyboardType="email-address"
textContentType="emailAddress"
placeholder="Email"
name="email"
/>
<AppFormField
icon="lock"
autoCapitalize="none"
autoCorrect={false}
secureTextEntry={true}
textContentType="password"
name="password"
placeholder="Password"
/>
<AppSubmitButton label="Login" />
</AppForm>
</Screen>
);
}

const styles = StyleSheet.create({
container: {
padding: 10,
},
logo: {
width: 100,
height: 100,
alignSelf: "center",
marginTop: 60,
marginBottom: 30,
},
});
Binary file added assets/benz-logo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/benz-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions components/AppButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
StyleSheet,
Text,
TouchableOpacity,
View,
ViewStyle,
} from "react-native";
import React from "react";
import defaultStyles from "../default-styles";

interface Props {
label: string;
styles?: ViewStyle;
onPress: () => void;
}

const AppButton: React.FC<Props> = ({ styles: propStyles, onPress, label }) => {
return (
<TouchableOpacity onPress={onPress}>
<View style={[styles.btnStyles, propStyles]}>
<Text style={styles.textStyles}>{label}</Text>
</View>
</TouchableOpacity>
);
};

const styles = StyleSheet.create({
btnStyles: {
width: "100%",
borderRadius: 1000,
padding: 15,
justifyContent: "center",
alignItems: "center",
backgroundColor: defaultStyles.colors.secondary,
marginVertical: 10,
},
textStyles: {
color: "#fff",
textTransform: "uppercase",
fontWeight: "500",
fontSize: 18,
},
});

export default AppButton;
17 changes: 14 additions & 3 deletions components/AppPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import React, { useState } from "react";
import defaultStyles from "./../default-styles";
import Screen from "./Screen";
import PickerItem from "./PickerItem";
import AppText from "./AppText";

export interface AppTextProps {
icon: string;
Expand Down Expand Up @@ -43,9 +44,13 @@ const AppPicker: React.FC<AppTextProps> = ({
style={styles.icon}
/>
)}
<Text style={[styles.text, defaultStyles.textInput]}>
{selectedItem ? selectedItem : placeholder}
</Text>
{selectedItem ? (
<AppText style={[styles.text, defaultStyles.textInput]}>
{selectedItem}
</AppText>
) : (
<AppText style={styles.placeholder}>{placeholder}</AppText>
)}
<MaterialCommunityIcons
name="chevron-down"
size={20}
Expand Down Expand Up @@ -87,10 +92,16 @@ const styles = StyleSheet.create({
},
text: {
flex: 1,
fontSize: 18,
},
icon: {
marginRight: 10,
},
placeholder: {
color: defaultStyles.colors.medium,
fontSize: 18,
flex: 1,
},
});

export default AppPicker;
2 changes: 1 addition & 1 deletion components/AppText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from "react";
//in app imports

interface Props {
children: React.ReactNode;
children: React.ReactNode | string | any;
style?: React.ComponentProps<typeof Text>["style"];
}
const AppText: React.FC<Props> = ({ children, style }) => {
Expand Down
6 changes: 5 additions & 1 deletion components/AppTextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ const AppTextInput: React.FC<AppTextProps> = ({ icon, ...otherProps }) => {
color={defaultStyles.colors.medium}
style={styles.icon}
/>
<TextInput style={defaultStyles.textInput} {...otherProps} />
<TextInput
placeholderTextColor={defaultStyles.colors.medium}
style={defaultStyles.textInput}
{...otherProps}
/>
</View>
);
};
Expand Down
9 changes: 7 additions & 2 deletions components/Screen.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import { SafeAreaView, StyleSheet } from "react-native";
import { SafeAreaView, StyleSheet, ViewStyle } from "react-native";
import Constants from "expo-constants";
import React from "react";

interface ScreenProps {
backgroundColor?: string;
styles?: ViewStyle;
children: React.ReactNode;
}

const Screen: React.FC<ScreenProps> = (props) => {
return (
<SafeAreaView
style={[styles.screen, { backgroundColor: props.backgroundColor }]}
style={[
styles.screen,
props.styles,
{ backgroundColor: props.backgroundColor },
]}
>
{props.children}
</SafeAreaView>
Expand Down
27 changes: 27 additions & 0 deletions components/forms/AppForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from "react";
import { Formik } from "formik";

interface Props {
initialValues: any;
onSubmit: (values) => void;
validationSchema: any;
}

const AppForm: React.FC<Props> = ({
initialValues,
onSubmit,
validationSchema,
children,
}) => {
return (
<Formik
initialValues={initialValues}
onSubmit={onSubmit}
validationSchema={validationSchema}
>
{() => <>{children}</>}
</Formik>
);
};

export default AppForm;
34 changes: 34 additions & 0 deletions components/forms/AppFormField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { StyleSheet } from "react-native";
import React from "react";
//in app imports
import AppTextInput from "../AppTextInput";
import { useFormikContext } from "formik";
import ErrorMessage from "./ErrorMessage";

interface Props {
name: string;
}

const AppFormField: React.FC<Props> = ({ name, ...otherProps }) => {
const { setFieldTouched, handleChange, errors, touched } = useFormikContext();
return (
<>
<AppTextInput
// icon="lock"
// autoCapitalize="none"
// autoCorrect={false}
// secureTextEntry={true}
// textContentType="password"
onBlur={() => setFieldTouched(name)}
onChangeText={handleChange(name)}
// placeholder="Password"
{...otherProps}
/>
<ErrorMessage visible={touched[name]} error={errors[name]} />
</>
);
};

const styles = StyleSheet.create({});

export default AppFormField;
30 changes: 30 additions & 0 deletions components/forms/AppFormPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { StyleSheet } from "react-native";
import React from "react";
import { useFormikContext } from "formik";
import AppPicker from "../AppPicker";
import ErrorMessage from "./ErrorMessage";

interface Props {
name: string;
items: { label: string; value: any }[];
}

const AppFormPicker: React.FC<Props> = ({ items, name, placeholder }) => {
const { errors, values, setFieldValue, touched } = useFormikContext();
return (
<>
<AppPicker
icon="apps"
placeholder={placeholder}
items={items}
onSelectItem={(item) => setFieldValue(name, item)}
selectedItem={values[name] as string}
/>
<ErrorMessage visible={touched[name]} error={errors[name]} />
</>
);
};

const styles = StyleSheet.create({});

export default AppFormPicker;
Loading

0 comments on commit 8ed1e92

Please sign in to comment.