Skip to content

Commit

Permalink
Contentful dependencies, components
Browse files Browse the repository at this point in the history
  • Loading branch information
CNanninga committed Nov 20, 2023
1 parent 8ea49ed commit 4d10b11
Show file tree
Hide file tree
Showing 9 changed files with 289 additions and 0 deletions.
25 changes: 25 additions & 0 deletions apps/core/components/cms/Banner.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import RichText from './RichText';

export default function Banner({ className, block }) {
const { heading, imagePosition, backgroundColor, style, content, image } = block;

const bgClass = style == 'light' ? `bg-${backgroundColor}-200` : `bg-${backgroundColor}-800`;
const textColorClass = style == 'light' ? 'text-slate-800' : 'text-slate-200';

const bannerImg = (
<div>
<img src={`${image?.url}?w=750`} alt={image?.description} />
</div>
);

return (
<div className={`${className} rounded-2xl p-12 md:grid md:grid-cols-2 md:gap-8 ${bgClass}`}>
{imagePosition === 'left' && bannerImg}
<div className={`${textColorClass}`}>
<h3 className="mb-8 text-3xl font-bold">{heading}</h3>
<RichText className={`text-xl ${textColorClass}`} content={content} />
</div>
{imagePosition === 'right' && bannerImg}
</div>
);
}
11 changes: 11 additions & 0 deletions apps/core/components/cms/CmsContent.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import ContentBlock from 'components/cms/ContentBlock';

export default function CmsContent({ blocks, className = '' }) {
return (
<div className={`${className} grid grid-cols-6 gap-6`}>
{blocks.map((block) => (
<ContentBlock key={block.sys.id} block={block} />
))}
</div>
);
}
30 changes: 30 additions & 0 deletions apps/core/components/cms/ContentBlock.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Banner from 'components/cms/Banner';
import ImageBlock from 'components/cms/ImageBlock';
import RichText from 'components/cms/RichText';
import SimpleText from 'components/cms/SimpleText';

const BLOCK_TYPE_BANNER = 'BlockBanner';
const BLOCK_TYPE_RICHTEXT = 'BlockRichText';
const BLOCK_TYPE_SIMPLETEXT = 'BlockSimpleText';
const BLOCK_TYPE_IMG = 'BlockImage';

export default function ContentBlock({ block }) {
switch (block['__typename']) {
case BLOCK_TYPE_BANNER:
return <Banner className="col-span-6 mx-auto my-6 max-w-screen-lg" block={block} />;

case BLOCK_TYPE_RICHTEXT:
return (
<RichText className="col-span-6 mx-auto my-6 max-w-screen-md" content={block.content} />
);

case BLOCK_TYPE_SIMPLETEXT:
return <SimpleText className="my-6 max-w-screen-md" block={block} />;

case BLOCK_TYPE_IMG:
return <ImageBlock className="my-6" block={block} />;

default:
return null;
}
}
33 changes: 33 additions & 0 deletions apps/core/components/cms/ImageBlock.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export default function ImageBlock({ className, block }) {
let sizeClass, imgSize;
switch (block.size) {
case 'full':
sizeClass = 'col-span-6';
imgSize = 1000;
break;

case 'two-thirds':
sizeClass = 'col-span-4';
imgSize = 700;
break;

case 'half':
sizeClass = 'col-span-3';
imgSize = 500;
break;

case 'third':
sizeClass = 'col-span-2';
imgSize = 400;
break;

default:
sizeClass = null;
}

return (
<div className={`${className} ${sizeClass} mx-auto block`}>
<img src={`${block.image?.url}?w=${imgSize}`} alt={block.image?.description} />
</div>
);
}
26 changes: 26 additions & 0 deletions apps/core/components/cms/RichText.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
import { BLOCKS } from '@contentful/rich-text-types';

function RichTextAsset({ id, content }) {
const assetLinks = content?.links?.assets?.block ?? [];

const asset = assetLinks.find((asset) => asset.sys.id === id);

return asset?.url ? (
<img className="mx-auto block" src={asset.url} alt={asset.description} />
) : null;
}

export default function RichText({ content, className }) {
return (
<div className={`${className} prose dark:prose-invert`}>
{documentToReactComponents(content.json, {
renderNode: {
[BLOCKS.EMBEDDED_ASSET]: (node) => (
<RichTextAsset id={node.data.target.sys.id} content={content} />
)
}
})}
</div>
);
}
27 changes: 27 additions & 0 deletions apps/core/components/cms/SimpleText.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import RichText from './RichText';

export default function SimpleText({ className, block }) {
let sizeClass;
switch (block.size) {
case 'full':
sizeClass = 'col-span-6';
break;

case 'two-thirds':
sizeClass = 'col-span-4';
break;

case 'half':
sizeClass = 'col-span-3';
break;

case 'third':
sizeClass = 'col-span-2';
break;

default:
sizeClass = null;
}

return <RichText content={block.content} className={`${className} ${sizeClass}`} />;
}
112 changes: 112 additions & 0 deletions apps/core/lib/contentful/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
const QUERY_CONTENT = `
query ContentCollection(
$type: String
$slug: String
) {
categoryContentCollection(
where: {slug: $slug, type: $type},
limit: 1
) {
items {
contentCollection(limit: 10) {
items {
__typename
... on BlockBanner {
sys {
id
}
heading
imagePosition
backgroundColor
style
content {
json
}
image {
title
description
url
}
}
... on BlockRichText {
sys {
id
}
content {
json
links {
assets {
block {
title
description
url
sys {
id
}
}
}
}
}
}
... on BlockSimpleText {
sys {
id
}
size
content {
json
}
}
... on BlockImage {
sys {
id
}
size
image {
title
description
url
}
}
}
}
}
}
}
`;

const extractCategoryContent = (responseData) => {
return responseData?.data?.categoryContentCollection?.items?.[0]?.contentCollection?.items;
};

async function fetchGraphQL(query, variables, cache = 'force-cache') {
const fetchOpts = {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.CONTENTFUL_ACCESS_TOKEN}`
},
body: JSON.stringify({
...(query && { query }),
...(variables && { variables })
})
};
if (cache !== 'no-store') {
fetchOpts.next = {
revalidate: parseInt(process.env.FETCH_REVALIDATE_TIME)
};
} else {
fetchOpts.cache = cache;
}

return fetch(
`https://graphql.contentful.com/content/v1/spaces/${process.env.CONTENTFUL_SPACE_ID}`,
fetchOpts
).then((response) => response.json());
}

export async function getContentBlocks(type, slug) {
const blocks = await fetchGraphQL(QUERY_CONTENT, { type, slug });
return extractCategoryContent(blocks);
}
2 changes: 2 additions & 0 deletions apps/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"dependencies": {
"@bigcommerce/catalyst-client": "workspace:^",
"@bigcommerce/reactant": "workspace:^",
"@contentful/rich-text-react-renderer": "^15.19.0",
"@contentful/rich-text-types": "^16.3.0",
"@icons-pack/react-simple-icons": "^9.1.0",
"@vercel/analytics": "^1.1.1",
"clsx": "^2.0.0",
Expand Down
23 changes: 23 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4d10b11

Please sign in to comment.