Skip to content

Commit

Permalink
filter menu by diet, make radio buttons extensible
Browse files Browse the repository at this point in the history
  • Loading branch information
Remy committed May 13, 2022
1 parent 93b3b74 commit f7c861e
Show file tree
Hide file tree
Showing 21 changed files with 15,872 additions and 7,059 deletions.
21 changes: 21 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const nextJest = require('next/jest');

const createJestConfig = nextJest({
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
dir: './',
});

// Add any custom config to be passed to Jest
const customJestConfig = {
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
moduleNameMapper: {
// Handle module aliases (this will be automatically configured for you soon)
'^@/components/(.*)$': '<rootDir>/components/$1',

'^@/pages/(.*)$': '<rootDir>/pages/$1',
},
testEnvironment: 'jest-environment-jsdom',
};

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig);
6 changes: 6 additions & 0 deletions jest.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Optional: configure or set up a testing framework before each test.
// If you delete this file, remove `setupFilesAfterEnv` from `jest.config.js`

// Used for __tests__/testing-library.js
// Learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';
22,438 changes: 15,579 additions & 6,859 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"serve": "node server.cjs",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"test": "tsc && jest"
},
"dependencies": {
"@emotion/react": "^11.7.1",
Expand All @@ -34,7 +35,10 @@
"devDependencies": {
"@next/eslint-plugin-next": "^12.0.7",
"@octokit/types": "^6.34.0",
"@types/jest": "^27.0.3",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.2.0",
"@testing-library/user-event": "^14.2.0",
"@types/jest": "^27.5.1",
"@types/node": "^16.11.11",
"@types/react": "^18.0.8",
"@typescript-eslint/eslint-plugin": "^5.8.1",
Expand All @@ -43,6 +47,8 @@
"eslint-config-next": "12.0.4",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-react": "^7.28.0",
"jest": "^28.1.0",
"jest-environment-jsdom": "^28.1.0",
"typescript": "latest"
}
}
14 changes: 7 additions & 7 deletions src/components/FormFields/DietPreferenceField.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type React from 'react';
import { Box, MenuItem, TextField } from '@mui/material';
import { DietType, IFieldProps } from '../Interfaces';
import { IFieldProps } from '../Interfaces';

export const DietPreferenceField = ({ onChange, errors, register, defaultValue = DietType.Unknown }: IFieldProps) =>
export const DietPreferenceField = ({ onChange, errors, register, defaultValue }: IFieldProps) =>
{
return(
<Box sx={{marginBottom: '2rem'}}>
Expand All @@ -11,15 +11,15 @@ export const DietPreferenceField = ({ onChange, errors, register, defaultValue =
sx={{ width: '100%' }}
label="Diet"
defaultValue={defaultValue}
inputProps={register('diet', { required: 'Please enter a dietary preference' })}
inputProps={register('diet', { required: 'If you&apos;re eating, please enter a dietary preference' })}
onChange={onChange}
error={!!errors?.diet}
helperText={errors?.diet?.message}
>
<MenuItem value={DietType.Meat}>No restrictions</MenuItem>
<MenuItem value={DietType.Vegetarian}>Vegetarian</MenuItem>
<MenuItem value={DietType.Vegan}>Vegan</MenuItem>
<MenuItem value={DietType.NoFood}>No food for me</MenuItem>
<MenuItem value={'Meat'}>No restrictions</MenuItem>
<MenuItem value={'Vegetarian'}>Vegetarian</MenuItem>
<MenuItem value={'Vegan'}>Vegan</MenuItem>
<MenuItem value={'NoFood'}>No food for me</MenuItem>
</TextField>
</Box>);
};
6 changes: 2 additions & 4 deletions src/components/FormFields/FormHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DietType, GuestDocument, Menu } from './../Interfaces';
import { GuestDocument, Menu } from './../Interfaces';
import { RsvpData } from '../Interfaces';

const initialMenu: Menu = {
Expand All @@ -13,8 +13,6 @@ const initialMenu: Menu = {
foodOption9: false,
foodOption10: false,
foodOption11: false,
foodOption12: false,
foodOption13: false,
euroStarter: '',
euroMain: '',
euroDessert: ''
Expand All @@ -29,7 +27,7 @@ const initialGuest: GuestDocument = {
hasPlusOne: false,
menu: initialMenu,
cuisine: 'euro',
diet: DietType.Meat
diet: 'Meat'
};

export const initialState: RsvpData = {
Expand Down
33 changes: 15 additions & 18 deletions src/components/Interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import { ChangeEvent } from 'react';
import { Control, FieldErrors, UseFormRegister } from 'react-hook-form';

export const DietType = {
Unknown: '',
Meat: 'anything goes',
Vegetarian: 'veggie',
Vegan: 'vegan',
NoFood: 'no food'
} as const;
type DietType = typeof DietType[keyof typeof DietType];
export type DietType = 'Meat' | 'Vegetarian' | 'Vegan' | 'NoFood';

// Full mongodb document
export type GuestDocument = {
Expand Down Expand Up @@ -54,8 +47,6 @@ export type Menu = {
foodOption9: boolean,
foodOption10: boolean,
foodOption11: boolean,
foodOption12: boolean,
foodOption13: boolean,
euroStarter: number | string,
euroMain: number | string,
euroDessert: number | string,
Expand All @@ -64,19 +55,25 @@ export type Menu = {
export type MenuType = 'foodOption1' | 'foodOption2' | 'foodOption3' |
'foodOption4' | 'foodOption5' | 'foodOption6' | 'foodOption7' |
'foodOption8' | 'foodOption9' | 'foodOption10' | 'foodOption11'|
'foodOption11'| 'foodOption12'| 'foodOption13' | 'euroStarter' |
'euroMain' | 'euroDessert';
'euroStarter' | 'euroMain' | 'euroDessert';
export interface RHFormControlProps {
control: Control<GuestDocument, object>;
}
export type CuisineType = 'euro' | 'afro';
export interface ICuisineOptionProps extends RHFormControlProps{
defaultValues?: GuestDocument;
export type CuisineType = 'euro' | 'afro' | 'neither';
export interface ICuisineOptionProps extends IMenuDefaultProps{
cuisineType: CuisineType;
}
export interface IMenuOptionProps extends RHFormControlProps{
defaultValues?: MenuType;
export interface IMenuTypeOptionProps extends RHFormControlProps{
defaultValues?: MenuType | undefined;
diet: DietType;
}

export interface IMenuDefaultProps extends RHFormControlProps{
defaultValues?: Menu;
defaultValues?: GuestDocument;
cuisineType: CuisineType;
diet: DietType;
}
export interface IMenuOptionProps extends RHFormControlProps{
defaultValues?: Menu | undefined;
diet: DietType;
}
63 changes: 9 additions & 54 deletions src/components/MenuForm.tsx
Original file line number Diff line number Diff line change
@@ -1,67 +1,22 @@
import React, { useState } from 'react';
import { Box, Typography, FormControl, RadioGroup, Radio, FormControlLabel } from '@mui/material';
import { Controller, useForm } from 'react-hook-form';
import { Box, Typography } from '@mui/material';
import { useForm } from 'react-hook-form';
import { AfroMenuOptions } from './MenuOptions/AfroMenuOptions';
import { DessertOptions } from './MenuOptions/DessertOptions';
import { EuroMenuOptions } from './MenuOptions/EuroMenuOptions';
import { CuisineType, ICuisineOptionProps } from './Interfaces';
import { CuisineType, ICuisineOptionProps, IMenuDefaultProps } from './Interfaces';
import { CuisineTypeOptions } from './MenuOptions/CuisineTypeOptions';

export default function MenuForm({ control, defaultValues }: ICuisineOptionProps) {
export default function MenuForm({ control, cuisineType, diet, defaultValues }: IMenuDefaultProps) {
const [isEuropean, setIsEuropean] = useState(defaultValues?.cuisine === 'euro' ?? true);

const { reset } = useForm();

// const resetFoodOptions = (firstOptionNumber: number, lastOptionNumber: number, defaultValue: false | string) => {
// const resetObject: any = {};

// for (let i = firstOptionNumber; i <= lastOptionNumber; i++) {
// resetObject[`menu.foodOption${i}`] = defaultValue;
// }
// return resetObject;
// };
// const handleEuroCuisine = () => {
// // 0 - 11 is euro
// setIsAfrican(false);
// setIsEuropean(true);
// reset(resetFoodOptions(0, 11, false));
// };
// const handleAfroCuisine = () => {
// setIsEuropean(false);
// setIsAfrican(true);
// reset(resetFoodOptions(12, 27, ''));
// };

const handleCuisineChange = (cuisine: CuisineType) =>
setIsEuropean(cuisine === 'euro');

return (
<Box sx={{marginTop: '2rem', marginBottom: '2rem'}}>
<Typography variant="h3" sx={{ fontSize: '2.5rem'}}>What cuisine type would you like?</Typography>
<FormControl>
<Controller
control={control}
defaultValue={defaultValues?.cuisine ?? 'euro'}
name="cuisine"
render={({field}) =>
<RadioGroup {...field}>
<FormControlLabel
value={'euro'}
control={<Radio />}
label="European"
onClick={() => handleCuisineChange('euro')}
/>
<FormControlLabel
value={'afro'}
control={<Radio />}
label="West African 🇳🇬"
onClick={() => handleCuisineChange('afro')}
/>
</RadioGroup>}
/>
</FormControl>
{ !isEuropean && <AfroMenuOptions control={control} defaultValues={defaultValues?.menu}/>}
{ isEuropean && <EuroMenuOptions control={control} defaultValues={defaultValues?.menu}/>}
<DessertOptions control={control} defaultValues={defaultValues?.menu} />
<Box sx={{my: '2rem'}}>
{ cuisineType === 'afro' && <AfroMenuOptions control={control} diet={diet} defaultValues={defaultValues?.menu}/>}
{ cuisineType === 'euro' && <EuroMenuOptions control={control} diet={diet} defaultValues={defaultValues?.menu}/>}
<DessertOptions control={control} defaultValues={defaultValues?.menu} diet={diet} />
</Box>
);
}
Expand Down
6 changes: 3 additions & 3 deletions src/components/MenuOptions/AfroMenuOptions.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type React from 'react';
import { IMenuDefaultProps } from '../Interfaces';
import { IMenuOptionProps } from '../Interfaces';
import CheckboxList from '../TextList';
import { afroMenuItems } from './MenuHelpers';

export const AfroMenuOptions = ({control, defaultValues}: IMenuDefaultProps) =>
<CheckboxList listItems={afroMenuItems} control={control} defaultValues={defaultValues} />;
export const AfroMenuOptions = ({control, diet, defaultValues}: IMenuOptionProps) =>
<CheckboxList listItems={afroMenuItems} control={control} defaultValues={defaultValues} diet={diet} />;
37 changes: 37 additions & 0 deletions src/components/MenuOptions/CuisineTypeOptions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type React from 'react';
import { FormControl, FormControlLabel, RadioGroup, Radio, Box, Typography } from '@mui/material';
import { Control, Controller } from 'react-hook-form';
import { CuisineType, GuestDocument } from '../Interfaces';

interface ICuisineTypeOptions {
defaultValues: CuisineType | undefined;
control: Control<GuestDocument, object>;
// handleCuisineChange: (cuisine: CuisineType) => void;
}

export const CuisineTypeOptions = ({defaultValues = 'euro', control /*, handleCuisineChange*/}: ICuisineTypeOptions) =>
<Box>
<Typography variant="h3" sx={{ fontSize: '2.5rem'}}>What cuisine type would you like?</Typography>
<FormControl>
<Controller
control={control}
defaultValue={defaultValues}
name="cuisine"
render={({field}) =>
<RadioGroup {...field}>
<FormControlLabel
value={'euro'}
control={<Radio />}
label="European"
// onClick={() => handleCuisineChange('euro')}
/>
<FormControlLabel
value={'afro'}
control={<Radio />}
label="West African 🇳🇬"
// onClick={() => handleCuisineChange('afro')}
/>
</RadioGroup>}
/>
</FormControl>
</Box>;
10 changes: 5 additions & 5 deletions src/components/MenuOptions/DessertOptions.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import type React from 'react';
import { FormControlLabel, Radio, RadioGroup, Typography } from '@mui/material';
import { Controller } from 'react-hook-form';
import { IMenuDefaultProps } from '../Interfaces';
import { dessertItems } from './MenuHelpers';
import { IMenuOptionProps } from '../Interfaces';
import { dessertItems, shouldShowBasedOnDietChoice } from './MenuHelpers';

export const DessertOptions = ({control, defaultValues }: IMenuDefaultProps) =>
export const DessertOptions = ({control, defaultValues, diet }: IMenuOptionProps) =>
<>
<Typography variant="h4" sx={{ my: '1rem', fontSize: '2rem'}}>Dessert</Typography>
<Controller
name='menu.euroDessert'
control={control}
defaultValue={defaultValues?.euroDessert ?? ''}
defaultValue={defaultValues?.euroDessert}
render={({field}) =>
<RadioGroup {...field}>
{dessertItems.map(x => <FormControlLabel key={x.key} value={x.key} control={<Radio />} label={x.primary} />)}
{dessertItems.map(x => shouldShowBasedOnDietChoice(diet, x.diet) && <FormControlLabel key={x.key} value={x.key} control={<Radio />} label={x.primary} />)}
</ RadioGroup>}
/>
</>;
52 changes: 25 additions & 27 deletions src/components/MenuOptions/EuroMenuOptions.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,31 @@
import type React from 'react';
import { FormControl, FormControlLabel, Radio, RadioGroup, Typography } from '@mui/material';
import { Controller } from 'react-hook-form';
import type { IMenuDefaultProps } from '../Interfaces';
import { euroMainItems, euroStarterItems } from './MenuHelpers';
import type { IMenuOptionProps } from '../Interfaces';
import { euroMainItems, euroStarterItems, shouldShowBasedOnDietChoice } from './MenuHelpers';
import { useMemo } from 'react';

export const EuroMenuOptions = ({ control, defaultValues }: IMenuDefaultProps) =>
export const EuroMenuOptions = ({ control, defaultValues, diet }: IMenuOptionProps) =>
{
return (<FormControl>
<Typography variant="h4" sx={{ my: '1rem', fontSize: '2rem'}}>Starters</Typography>
<Controller
name='menu.euroStarter'
control={control}
defaultValue={defaultValues?.euroStarter ?? ''}
render={({field}) =>
<RadioGroup {...field} value={field.value || undefined}>
{euroStarterItems.map(x => <FormControlLabel key={x.key} value={x.key} control={<Radio />} label={x.primary} />)}
</RadioGroup>
}
/>
<Typography variant="h4" sx={{ my: '1rem', fontSize: '2rem'}}>Mains - all are served with Seasonal Vegetables and Potatoes</Typography>
<Controller
name='menu.euroMain'
control={control}
defaultValue={defaultValues?.euroMain}
render={({field}) =>
<RadioGroup {...field} value={field.value || undefined}>
{euroMainItems.map(x => <FormControlLabel key={x.key} value={x.key} control={<Radio />} label={x.primary} />)}
</RadioGroup>
}
/>
</FormControl>);
return (
<FormControl>
<Typography variant="h4" sx={{ my: '1rem', fontSize: '2rem'}}>Starters</Typography>
<Controller
name='menu.euroStarter'
control={control}
defaultValue={defaultValues?.euroStarter}
render={({field}) =><RadioGroup {...field} value={field.value}>
{euroStarterItems.map(x => shouldShowBasedOnDietChoice(diet, x.diet) && <FormControlLabel key={x.key} value={x.key} control={<Radio />} label={x.primary} />)}
</RadioGroup>}
/>
<Typography variant="h4" sx={{ my: '1rem', fontSize: '2rem'}}>Mains - all are served with Seasonal Vegetables and Potatoes</Typography>
<Controller
name='menu.euroMain'
control={control}
defaultValue={defaultValues?.euroMain}
render={({field}) => <RadioGroup {...field} value={field.value}>
{euroMainItems.map(x => shouldShowBasedOnDietChoice(diet, x.diet) && <FormControlLabel key={x.key} value={x.key} control={<Radio />} label={x.primary} />)}
</RadioGroup>}
/>
</FormControl>);
};
Loading

1 comment on commit f7c861e

@vercel
Copy link

@vercel vercel bot commented on f7c861e May 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nupito – ./

nupito-git-main-remy90.vercel.app
nupito-remy90.vercel.app
shaunandcharlotte.co.uk

Please sign in to comment.