Fakultas Ilmu Komputer UI

Commit 80fb39ac authored by Wulan Mantiri's avatar Wulan Mantiri
Browse files

Implement step-by step form

parent 43018dfb
......@@ -4,6 +4,7 @@ import { createStackNavigator } from '@react-navigation/stack';
import { ThemeProvider } from 'react-native-elements';
import Toast from 'react-native-toast-message';
import { DietelaCoverLoader } from 'components/core';
import ContextProvider, { UserContext } from 'provider';
import { theme } from 'styles/theme';
......@@ -14,13 +15,18 @@ import { getNavigation } from './schema';
const Stack = createStackNavigator();
const NavigationStack: FC = () => {
const { isAuthenticated, isUnpaidClient } = useContext(UserContext);
const { isAuthenticated, isUnpaidClient, isFirstLoading } = useContext(
UserContext,
);
const { initialRoute, navigation } = getNavigation(
isAuthenticated,
isUnpaidClient,
);
if (isFirstLoading) {
return <DietelaCoverLoader />;
}
return (
<NavigationContainer>
<Stack.Navigator
......
import React from 'react';
import { fireEvent, render } from '@testing-library/react-native';
import StepByStepForm from '.';
const mockedNavigate = jest.fn();
jest.mock('@react-navigation/native', () => {
return {
useNavigation: () => ({
navigate: mockedNavigate,
}),
};
});
describe('StepByStepForm component', () => {
const pages = ['hai', 'hello', 'hei'];
const props = {
pages: pages.map((name) => ({
name,
route: name,
})),
title: 'title',
currentPage: 0,
finishRedirectRoute: 'finishroute',
};
it('only has "Lanjut" button when form is never filled', () => {
const { getByText, queryByText } = render(<StepByStepForm {...props} />);
const nextButton = getByText(/Lanjut/i);
expect(nextButton).toBeTruthy();
expect(queryByText(/Ubah/i)).toBeFalsy();
});
it('has "Ubah" button when form has been filled', () => {
const { queryByText } = render(
<StepByStepForm {...props} currentPage={1} />,
);
expect(queryByText(/Ubah/i)).toBeTruthy();
});
it('redirects to designated route when button "Ubah" or "Lanjut" is pressed', () => {
const { getByText } = render(<StepByStepForm {...props} currentPage={1} />);
const nextButton = getByText(/Lanjut/i);
expect(nextButton).toBeTruthy();
fireEvent.press(nextButton);
expect(mockedNavigate).toHaveBeenCalled();
});
it('redirects to finish route when button "Selesai" is pressed', () => {
const { getByText } = render(
<StepByStepForm {...props} currentPage={pages.length} />,
);
const finishButton = getByText(/Selesai/i);
expect(finishButton).toBeTruthy();
fireEvent.press(finishButton);
expect(mockedNavigate).toHaveBeenCalled();
});
});
import React, { FC } from 'react';
import { useNavigation } from '@react-navigation/native';
import { View } from 'react-native';
import { Button, Text } from 'react-native-elements';
import { styles } from './styles';
import { Props } from './types';
const StepByStepForm: FC<Props> = ({
title,
pages,
currentPage,
defaultValues,
finishRedirectRoute,
}) => {
const navigation = useNavigation();
const getButton = (i: number, route: string) => {
let buttonLabel;
if (i < currentPage) {
buttonLabel = 'Ubah';
} else if (i === currentPage) {
buttonLabel = 'Lanjut';
}
return buttonLabel ? (
<Button
title={buttonLabel}
onPress={() => navigation.navigate(route, defaultValues)}
buttonStyle={styles.button}
titleStyle={styles.buttonTitle}
/>
) : null;
};
return (
<View style={styles.container}>
<View style={styles.titleContainer}>
<Text style={styles.title}>{title}</Text>
</View>
<View style={styles.pageContainer}>
{pages.map((page, i) => (
<View style={styles.page} key={i}>
<Text
style={[
styles.number,
currentPage < i ? styles.notfilled : styles.filled,
]}>
{i + 1}
</Text>
<Text
style={[
styles.md,
currentPage < i ? styles.notfilled : styles.filled,
]}>
{page.name}
</Text>
<View style={styles.buttonContainer}>
{getButton(i, page.route)}
</View>
</View>
))}
</View>
<View style={styles.titleContainer}>
<Button
title="Selesai"
onPress={() => navigation.navigate(finishRedirectRoute)}
buttonStyle={styles.finishButton}
titleStyle={styles.buttonTitle}
disabled={currentPage !== pages.length}
/>
</View>
</View>
);
};
export default StepByStepForm;
import { StyleSheet } from 'react-native';
import { colors, typography } from 'styles';
export const styles = StyleSheet.create({
container: {
flex: 1,
paddingHorizontal: 20,
},
titleContainer: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flex: 1,
},
title: {
...typography.headingLarge,
textAlign: 'center',
},
pageContainer: {
flex: 3,
},
notfilled: {
color: colors.formLabel,
},
filled: {
color: colors.textBlack,
...typography.headingMedium,
fontSize: 18,
},
page: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
padding: 14,
borderBottomColor: colors.border,
borderBottomWidth: 1,
},
number: {
...typography.bodyLarge,
flex: 0.1,
},
buttonContainer: {
paddingLeft: 10,
flex: 0.27,
},
md: {
...typography.bodyLarge,
flex: 0.63,
},
button: {
padding: 10,
backgroundColor: colors.primaryYellow,
},
finishButton: {
borderRadius: 20,
width: 150,
backgroundColor: colors.secondaryVariant,
},
buttonTitle: {
color: colors.textBlack,
},
});
export interface Props {
title: string;
pages: {
name: string;
route: string;
}[];
currentPage: number;
defaultValues?: any;
finishRedirectRoute: string;
}
export { default as MultipleCheckbox } from './MultipleCheckbox';
export { default as MultipleChoice } from './MultipleChoice';
export { default as RadioButton, RadioButtonGroup } from './RadioButton';
export { default as StepByStepForm } from './StepByStepForm';
export { default as TextField } from './TextField';
......@@ -11,6 +11,7 @@ import {
ProgramDetail,
NutritionistDetail,
PaymentResult,
ExtendedQuestionnaire,
} from 'scenes';
import { FC } from 'react';
......@@ -86,6 +87,11 @@ export const privateNavigation: NavRoute[] = [
component: ComingSoonPage,
header: 'Profile',
},
{
name: ROUTES.extendedQuestionnaire,
component: ExtendedQuestionnaire,
header: 'Diet Questionnaire',
},
];
export const testNavigation: NavRoute[] = [
......
......@@ -4,6 +4,10 @@ export const comingSoon = '*';
const questionnaire = 'questionnaire';
export const allAccessQuestionnaire = `${questionnaire}/all-access`;
export const dietelaQuizResult = `${questionnaire}/dietela-quiz-result`;
export const consentForm = `${questionnaire}/consent`;
export const extendedQuestionnaire = `${questionnaire}/extended`;
export const extendedQuestionnaireById = (id: number) =>
`${extendedQuestionnaire}/${id}`;
export const checkout = 'checkout';
export const choosePlan = `${checkout}/choose-plan`;
......
......@@ -5,14 +5,6 @@ import * as ROUTES from 'constants/routes';
import InitialPage from '.';
describe('InitialPage', () => {
test('shows dietela cover loader when is loading', async () => {
const { queryByTestId } = render(<InitialPage />, ROUTES.initial, {
userContext: { isFirstLoading: true },
});
expect(queryByTestId('background')).toBeFalsy();
});
test('has background image', () => {
const { queryByTestId } = render(<InitialPage />, ROUTES.initial);
......
import React, { FC, useContext } from 'react';
import React, { FC } from 'react';
import { useNavigation } from '@react-navigation/native';
import { View, Text, ImageBackground, Image } from 'react-native';
import { BigButton, Link, DietelaCoverLoader } from 'components/core';
import { BigButton, Link } from 'components/core';
import { banner_girl_eating, logo_white_small } from 'assets/images';
import * as ROUTES from 'constants/routes';
import { UserContext } from 'provider';
import { layoutStyles, typographyStyles } from 'styles';
import { styles } from './styles';
const InitialPage: FC = () => {
const navigation = useNavigation();
const { isFirstLoading } = useContext(UserContext);
if (isFirstLoading) {
return <DietelaCoverLoader />;
}
return (
<ImageBackground
source={banner_girl_eating}
......
......@@ -6,6 +6,7 @@ export { default as ComingSoonPage } from './common/ComingSoonPage';
export { default as AllAccessQuestionnaire } from './questionnaire/AllAccessQuestionnaire';
export { default as DietelaQuizResult } from './questionnaire/DietelaQuizResult';
export { default as ExtendedQuestionnaire } from './questionnaire/ExtendedQuestionnaire';
export { default as Checkout } from './cart/Checkout';
export { default as ChoosePlan } from './cart/ChoosePlan';
......
import React from 'react';
import { render } from 'utils/testing';
import * as ROUTES from 'constants/routes';
import ExtendedQuestionnaire from '.';
describe('ExtendedQuestionnaire', () => {
it('renders correctly', () => {
render(<ExtendedQuestionnaire />, ROUTES.extendedQuestionnaire);
});
});
import React, { FC, useState } from 'react';
import * as ROUTES from 'constants/routes';
import { StepByStepForm } from 'components/form';
import { pages } from './schema';
const ExtendedQuestionnaire: FC = () => {
const [currentPage] = useState(1);
return (
<StepByStepForm
currentPage={currentPage}
title={'Lengkapi Profil Anda'}
pages={pages.map((name, i) => ({
name,
route: ROUTES.extendedQuestionnaireById(i),
}))}
finishRedirectRoute={ROUTES.profile}
/>
);
};
export default ExtendedQuestionnaire;
export const pages: string[] = [
'Identitas Diri',
'Pola Makan',
'Konsumsi Makanan Seharian',
'Gaya Hidup dan Kebiasaan Diet',
'Kondisi Pribadi',
];
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment