Skip to content

Commit

Permalink
Feature/dynamic pages (#8)
Browse files Browse the repository at this point in the history
* Add get Pages query

* Add landing page

* Resolve fetched content

* Render Dynamic Page

* Setup Components Mapping
  • Loading branch information
usulpro authored Dec 27, 2023
1 parent 242f5c1 commit 4026fac
Show file tree
Hide file tree
Showing 20 changed files with 996 additions and 100 deletions.
3 changes: 3 additions & 0 deletions apps/web/components/ProductSlider/ProductSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { SfScrollable } from '@storefront-ui/react';
import { ProductCard } from '~/components';
import type { ProductSliderProps } from '~/components';
import { useProducts } from '~/hooks';
import withShopify from '~/sdk/shopify/withShopify';

export function ProductSlider({ className, collection, ...attributes }: ProductSliderProps) {
const { products } = useProducts(collection);
Expand Down Expand Up @@ -36,3 +37,5 @@ export function ProductSlider({ className, collection, ...attributes }: ProductS
</SfScrollable>
);
}

export const ProductSliderBlock = withShopify({ wrapperFn: (v) => v, isDebug: true })(ProductSlider);
61 changes: 11 additions & 50 deletions apps/web/components/RenderContent/RenderContent.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,16 @@
import { Fragment } from 'react';
import { Page, Hero, Display, Heading, CategoryCard, ProductSlider } from '~/components';
import type { RenderContentProps } from '~/components';
import { BlockComponent } from '~/hooks';
import { getBlockComponent } from '../blocksConfig';
import { UnknownBlock } from './UnknownBlock';

export function RenderContent({ content, ...attributes }: RenderContentProps): JSX.Element {
type RenderContentProps = {
contentBlock: BlockComponent;
};

export function RenderContent({ contentBlock, ...attributes }: RenderContentProps) {
const BlockComponent = getBlockComponent(contentBlock) || UnknownBlock;
return (
<div {...attributes}>
{content.map(({ fields }, index) => (
<Fragment key={`${fields.component}-${index}`}>
{(() => {
switch (fields.component) {
case 'Hero': {
return (
<Hero
image={fields.image}
subtitle={fields.subtitle}
title={fields.title}
description={fields.description}
primaryButtonLink={fields.primaryButtonLink}
primaryButtonText={fields.primaryButtonText}
secondaryButtonLink={fields.secondaryButtonLink}
secondaryButtonText={fields.secondaryButtonText}
/>
);
}
case 'Heading': {
return <Heading title={fields.title} tag={fields.tag} className={fields.className} />;
}
case 'Card': {
return <CategoryCard items={fields.items} />;
}
case 'Display': {
return <Display items={fields.items} />;
}
case 'ProductSlider': {
return (
<ProductSlider
collection="hidden-homepage"
className="max-w-screen-3xl mx-auto px-4 md:px-10 mb-20"
/>
);
}
case 'Page': {
return <Page />;
}
default: {
return <p>component {fields.component} is not registered</p>;
}
}
})()}
</Fragment>
))}
<div>
<BlockComponent {...contentBlock.fields} contentBlock={contentBlock} {...attributes} />
</div>
);
}
17 changes: 17 additions & 0 deletions apps/web/components/RenderContent/UnknownBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { BlockComponent } from '~/hooks';

type UnknownBlockProps = {
contentBlock: BlockComponent;
};

export const UnknownBlock = ({ contentBlock }: UnknownBlockProps) => {
return (
<div>
<h3>Unknown Content Block</h3>
<p>Type: {contentBlock.contentType}</p>
<p>ID: {contentBlock.id}</p>
{/* <pre>{JSON.stringify(contentBlock.fields, null, 2)}</pre> */}
<hr />
</div>
);
};
22 changes: 19 additions & 3 deletions apps/web/components/RenderContent/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
import type { ContentDynamicPage } from '~/hooks';
/* eslint-disable no-use-before-define */
type Component = {
__typename: string;
id: string;
type: string;
fields: ContentField[];
};

type ContentField = {
type: string;
key: string;
value: string;
reference?: Component | null;
};

export type RenderContentProps = {
content: ContentDynamicPage['content'];
type ContentBlock = {
__typename: string;
id: string;
type: string;
fields: ContentField[];
};
21 changes: 21 additions & 0 deletions apps/web/components/RichText/RichText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { BlockComponent } from '~/hooks';
import withShopify from '~/sdk/shopify/withShopify';

type Props = {
contentBlock: BlockComponent;
};

export const RichText = ({ contentBlock }: Props) => {
return (
<div>
<h3>Rich Text Block</h3>
<p>Type: {contentBlock.contentType}</p>
<p>ID: {contentBlock.id}</p>
{/* <pre>{JSON.stringify(contentBlock.fields, null, 2)}</pre> */}
<hr />
</div>
);
};

export const RichTextBlock = withShopify({ wrapperFn: (v) => v, isDebug: true })(RichText);
1 change: 1 addition & 0 deletions apps/web/components/RichText/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './RichText';
21 changes: 21 additions & 0 deletions apps/web/components/blocksConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { DisplayBlock, CategoryCardBlock, HeroBlock, ProductSliderBlock } from '~/components';
import { BlockComponent } from '~/hooks';
import { RichTextBlock } from './RichText';

const contentMap = {
// display: () => DisplayBlock,
collection_card: () => CategoryCardBlock,
// hero: () => HeroBlock,
// richtext_block: () => RichTextBlock,
// product_slider: () => ProductSliderBlock,
};

type ContentMapKey = keyof typeof contentMap;

export const getBlockComponent = (contentBlock: BlockComponent) => {
const getComponent = contentMap[contentBlock.contentType as ContentMapKey];
if (!getComponent) {
return;
}
return getComponent();
};
18 changes: 18 additions & 0 deletions apps/web/components/ui/CategoryCard/CategoryCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Image from 'next/image';
import Link from 'next/link';
import type { CategoryCardProps } from '~/components';
import withShopify, { ShopifyBlock } from '~/sdk/shopify/withShopify';

export function CategoryCard({ items, ...attributes }: CategoryCardProps) {
return (
Expand Down Expand Up @@ -30,3 +31,20 @@ export function CategoryCard({ items, ...attributes }: CategoryCardProps) {
</div>
);
}

// SpecificFieldsType would be your custom type for the fields in BlockComponent
type SpecificFieldsType = {
// ... define the fields structure here
};

const wrapperFn = (contentBlock: ShopifyBlock<SpecificFieldsType>): CategoryCardProps => {
// Convert Shopify content to the format required by CategoryCard
// ...

return {
items: [{ id: 123 }],
// ... props for CategoryCard
};
};

export const CategoryCardBlock = withShopify<SpecificFieldsType>({ wrapperFn, isDebug: true })(CategoryCard);
3 changes: 3 additions & 0 deletions apps/web/components/ui/Display/Display.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Link from 'next/link';
import { SfButton } from '@storefront-ui/react';
import classNames from 'classnames';
import type { DisplayProps } from '~/components';
import withShopify from '~/sdk/shopify/withShopify';

export function Display({ items, ...attributes }: DisplayProps) {
return (
Expand Down Expand Up @@ -45,3 +46,5 @@ export function Display({ items, ...attributes }: DisplayProps) {
</div>
);
}

export const DisplayBlock = withShopify({ wrapperFn: (v) => v, isDebug: true })(Display);
3 changes: 3 additions & 0 deletions apps/web/components/ui/Hero/Hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Image from 'next/image';
import Link from 'next/link';
import { SfButton } from '@storefront-ui/react';
import type { HeroProps } from '~/components';
import withShopify from '~/sdk/shopify/withShopify';

export function Hero({
image,
Expand Down Expand Up @@ -48,3 +49,5 @@ export function Hero({
</div>
);
}

export const HeroBlock = withShopify({ wrapperFn: (v) => v, isDebug: true })(Hero);
77 changes: 77 additions & 0 deletions apps/web/hooks/useContent/getPageQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
export const getPageQuery = `#graphql
query GetPage($slug: String!) {
page(handle: $slug) {
id
title
handle
seo {
title
description
}
content: metafield(namespace: "custom", key: "content") {
id
key
namespace
value
references(first: 50) {
edges {
node {
__typename
... on Metaobject {
id
type
fields {
type
key
value
reference {
__typename
... on Collection {
id
title
slug: handle
}
... on Product {
id
title
slug: handle
}
... on Metaobject {
id
handle
type
fields {
key
type
value
reference {
__typename
... on Page {
id
title
slug: handle
}
... on MediaImage {
id
alt
image {
url
altText
width
height
id
}
}
}
}
}
}
}
}
}
}
}
}
}
}
`;
Loading

0 comments on commit 4026fac

Please sign in to comment.