Archived course lesson
Add RecipeListItem component
Objective: We will create the recipe card that we will use on the recipe list type of pages.
Now that we have the <RecipeList> working, it is time to create the <RecipeListItem> component. First, let’s add two packages that we will need. The first is graphcms-image and this will handle asynchronously loading the images from GraphCMS and displaying them in a progressive manner. That means we will first see a blurry image, which then will progressively get filled in as the rest of the image gets downloaded. Next we will install the react-ellipsis-text component which will allow us to ensure that if a title or description is particuarly long that we can cut it off with a certain point and add an ellipsis so that it won’t wreck the flow of our cards.
npm install --save react-ellipsis-text graphcms-image
Now let’s create our component
import { Recipe } from '../generated/apollo-components';
import { Col } from 'antd';
import styled from 'styled-components';
import EllipsisText from 'react-ellipsis-text';
import GraphImage from 'graphcms-image';
import Link from 'next/link';
const StyledRecipe = styled(Col)`
${({ theme }) => `
padding: 0px ${theme['padding-small']};
.card {
cursor: pointer;
margin-bottom: ${theme['margin-small']};
height: 340px;
border-radius: 8px;
box-shadow: 0 0 16px ${theme['border-color']};
border: ${theme['border-width']} solid ${theme['border-color']};
}
.graphcms-image-outer-wrapper{
border: 0px;
.graphcms-image-wrapper {
border: 0px;
position: relative;
float: left;
width: 100%;
height: 200px;
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: cover;
img {
text-align: center;
border-radius: 6px 6px 0px 6px;
}
}
}
p {
padding: 0px ${theme['padding-small']};
}
h3 {
padding: 0px ${theme['padding-small']};
line-height: 1.5em;
}
`}
`;
export const RecipeListItem = ({
recipe,
parentRoute,
}: {
recipe: Recipe;
parentRoute: string;
}) => {
const { title, description, image, id } = recipe;
return (
<StyledRecipe
xs={{ span: 24 }}
sm={{ span: 12 }}
lg={{ span: 8 }}
xl={{ span: 6 }}
>
<div className="card">
<Link href={`/${parentRoute}/${id}`}>
<div>{image ? <GraphImage image={image} /> : null}</div>
</Link>
<h3>{title}</h3>
<p>
<EllipsisText text={description} length={110} />
</p>
</div>
</StyledRecipe>
);
};
Now, lets update our <RecipeList> component to add our fancy new list item component to it. The final file will look like the following:
import { QueryHookOptions, useQuery } from '@apollo/react-hooks';
import { recipesGraphQL } from '../graphql/queries/recipes';
import { userLikesGraphQL } from '../graphql/queries/userLikes';
import * as _ from 'lodash';
import { Row } from 'antd';
import { Recipe } from '../generated/apollo-components';
import { Loading } from './notify/Loading';
import { Warning } from './notify/Warning';
import { Error } from './notify/Error';
import { RecipeListItem } from './RecipeListItem';
export enum queryEnum {
userLikes = 'userLikes',
recipes = 'recipes',
}
type RecipesListProps = {
options?: QueryHookOptions;
parentRoute: string;
queryType: queryEnum;
};
export const RecipesList = ({
options,
parentRoute,
queryType,
}: RecipesListProps) => {
const query =
queryType === queryEnum.recipes ? recipesGraphQL : userLikesGraphQL;
const { loading, data, error } = useQuery(query, options);
const parentArray = _.get(data, queryType);
const recipesList = _.map(parentArray, (value) =>
_.get(value, 'recipe', value),
);
if (loading) return <Loading />;
if (error) return <Error errorText={`${error}`} />;
if (recipesList.length === 0)
return (
<Warning
warningHeader="No Recipes"
warningText="No Recipes are present. Why not add one?"
/>
);
return (
<Row>
{recipesList.map((recipe: Recipe) => (
<RecipeListItem
recipe={recipe}
parentRoute={parentRoute}
key={`${recipe.id}-${queryType}`}
/>
))}
</Row>
);
};