Skip to content

Commit

Permalink
Feature/query loop block (#810)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholasio authored Jul 17, 2024
1 parent d696703 commit ca94535
Show file tree
Hide file tree
Showing 16 changed files with 144 additions and 18 deletions.
6 changes: 6 additions & 0 deletions .changeset/two-weeks-applaud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@headstartwp/core": minor
"@headstartwp/next": minor
---

Introduce `blockContext` prop to BlocksRenderer and `handleError` in query functions
20 changes: 19 additions & 1 deletion packages/core/src/react/components/BaseBlocksRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ const { default: parse, domToReact } = HtmlReactParser;
/**
* The interface any children of {@link BlocksRenderer} must implement.
*/
export interface BlockProps<BlockAttributes extends IDataWPBlock = IDataWPBlock> {
export interface BlockProps<
BlockAttributes extends IDataWPBlock = IDataWPBlock,
Context extends Record<string, unknown> = Record<string, unknown>,
> {
/**
* A test function receives a domNode and returns a boolean value indicating
* whether that domNode should be replaced with the React component
Expand Down Expand Up @@ -64,6 +67,11 @@ export interface BlockProps<BlockAttributes extends IDataWPBlock = IDataWPBlock>
* The style tag of the domNode as an object.
*/
style?: Record<string, string>;

/**
* An optional context that is passed to all children components
*/
blockContext?: Context;
}

/**
Expand Down Expand Up @@ -121,6 +129,11 @@ export interface BlockRendererProps {
* Whether to forward the block attributes to the children components.
*/
forwardBlockAttributes?: boolean;

/**
* An optional context that is passed to all children components
*/
blockContext?: Record<string, unknown>;
}

interface BaseBlockRendererProps extends BlockRendererProps {
Expand Down Expand Up @@ -153,6 +166,7 @@ export function BaseBlocksRenderer({
children,
settings,
forwardBlockAttributes = false,
blockContext,
}: BaseBlockRendererProps) {
const blocks: ReactNode[] = React.Children.toArray(children);

Expand Down Expand Up @@ -216,6 +230,10 @@ export function BaseBlocksRenderer({
blockProps.block = { attributes, name, className };
}

if (typeof blockContext !== 'undefined') {
blockProps.blockContext = { ...blockContext };
}

component = React.createElement(
block.type,
blockProps,
Expand Down
25 changes: 25 additions & 0 deletions packages/core/src/react/components/__tests__/BlocksRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -239,4 +239,29 @@ describe('BlocksRenderer', () => {

expect(container).toMatchSnapshot();
});

it('forward context to the component', () => {
const DivToP = ({
block,
blockContext,
}: BlockProps<{ blockAttribute: string }, { contextProp: string }>) => {
return (
<p className={block?.className}>
{JSON.stringify(block)} - {JSON.stringify(blockContext)}
</p>
);
};

const { container } = render(
<BlocksRenderer
html={`<div class="my-class" data-wp-block-name='10up/custom-block' data-wp-block='${JSON.stringify({ blockAttribute: 'this is a block attribute' })}'></div>`}
forwardBlockAttributes
blockContext={{ contextProp: 'this is a context prop' }}
>
<DivToP test={(node) => isBlockByName(node, '10up/custom-block')} />
</BlocksRenderer>,
);

expect(container).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ exports[`BlocksRenderer forward blockProps to the component 1`] = `
</div>
`;

exports[`BlocksRenderer forward context to the component 1`] = `
<div>
<p
class="my-class"
>
{"attributes":{"blockAttribute":"this is a block attribute"},"name":"10up/custom-block","className":"my-class"}
-
{"contextProp":"this is a context prop"}
</p>
</div>
`;

exports[`BlocksRenderer works correctly with chinese content 1`] = `
<div>
<h3
Expand Down
3 changes: 2 additions & 1 deletion packages/next/src/rsc/data/queries/prepareQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function prepareQuery<P>(
query: NextQueryProps<P>,
_config: HeadlessConfig | undefined = undefined,
) {
const { routeParams, ...rest } = query;
const { routeParams, handleError = true, ...rest } = query;

const path = routeParams?.path ?? '';
const siteConfig = routeParams?.site
Expand Down Expand Up @@ -45,5 +45,6 @@ export function prepareQuery<P>(
options,
path: pathname,
config: config ?? getHeadstartWPConfig(),
handleError,
};
}
4 changes: 2 additions & 2 deletions packages/next/src/rsc/data/queries/queryAppSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ export async function queryAppSettings<
T extends AppEntity = AppEntity,
P extends EndpointParams = EndpointParams,
>(q: AppQueryProps<P> & NextQueryProps<P> = {}, _config: HeadlessConfig | undefined = undefined) {
const { config, ...query } = prepareQuery<P>(q, _config);
const { config, handleError, ...query } = prepareQuery<P>(q, _config);

try {
const result = await fetchAppSettings<T, P>(query, config);

return result;
} catch (error) {
if (error instanceof Error) {
if (error instanceof Error && handleError) {
await handleFetchError(error, config, query.path);
}
throw error;
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/rsc/data/queries/queryAuthorArchive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ export async function queryAuthorArchive<
T extends PostEntity = PostEntity,
P extends PostsArchiveParams = PostsArchiveParams,
>(q: NextQueryProps<P> = {}, _config: HeadlessConfig | undefined = undefined) {
const { config, ...query } = prepareQuery<P>(q, _config);
const { config, handleError, ...query } = prepareQuery<P>(q, _config);

try {
const result = await fetchAuthorArchive<T, P>(query, config);

return result;
} catch (error) {
if (error instanceof Error) {
if (error instanceof Error && handleError) {
await handleFetchError(error, config, query.path);
}
throw error;
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/rsc/data/queries/queryPost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export async function queryPost<
T extends PostEntity = PostEntity,
P extends PostParams = PostParams,
>(q: NextQueryProps<P> = {}, _config: HeadlessConfig | undefined = undefined) {
const { config, ...query } = prepareQuery<P>(q, _config);
const { config, handleError, ...query } = prepareQuery<P>(q, _config);

try {
const { isEnabled } = draftMode();
Expand Down Expand Up @@ -42,7 +42,7 @@ export async function queryPost<

return result;
} catch (error) {
if (error instanceof Error) {
if (error instanceof Error && handleError) {
await handleFetchError(error, config, query.path);
}
throw error;
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/rsc/data/queries/queryPostOrPosts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ export async function queryPostOrPosts<
T extends PostEntity = PostEntity,
P extends PostOrPostsParams = PostOrPostsParams,
>(q: NextQueryProps<P> = {}, _config: HeadlessConfig | undefined = undefined) {
const { config, ...query } = prepareQuery<P>(q, _config);
const { config, handleError, ...query } = prepareQuery<P>(q, _config);

try {
const result = await fetchPostOrPosts<T, P>(query, config);

return result;
} catch (error) {
if (error instanceof Error) {
if (error instanceof Error && handleError) {
await handleFetchError(error, config, query.path);
}
throw error;
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/rsc/data/queries/queryPosts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ export async function queryPosts<
T extends PostEntity = PostEntity,
P extends PostsArchiveParams = PostsArchiveParams,
>(q: NextQueryProps<P> = {}, _config: HeadlessConfig | undefined = undefined) {
const { config, ...query } = prepareQuery<P>(q, _config);
const { config, handleError, ...query } = prepareQuery<P>(q, _config);

try {
const result = await fetchPosts<T, P>(query, config);

return result;
} catch (error) {
if (error instanceof Error) {
if (error instanceof Error && handleError) {
await handleFetchError(error, config, query.path);
}
throw error;
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/rsc/data/queries/querySearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ export async function querySearch<
T extends PostSearchEntity | TermSearchEntity = PostSearchEntity | TermSearchEntity,
P extends SearchParams = SearchParams,
>(q: NextQueryProps<P> = {}, _config: HeadlessConfig | undefined = undefined) {
const { config, ...query } = prepareQuery<P>(q, _config);
const { config, handleError, ...query } = prepareQuery<P>(q, _config);

try {
const result = await fetchSearch<T, P>(query, config);

return result;
} catch (error) {
if (error instanceof Error) {
if (error instanceof Error && handleError) {
await handleFetchError(error, config, query.path);
}
throw error;
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/rsc/data/queries/queryTerms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ export async function queryTerms<
T extends TermEntity = TermEntity,
P extends TaxonomyArchiveParams = TaxonomyArchiveParams,
>(q: NextQueryProps<P> = {}, _config: HeadlessConfig | undefined = undefined) {
const { config, ...query } = prepareQuery<P>(q, _config);
const { config, handleError, ...query } = prepareQuery<P>(q, _config);

try {
const result = await fetchTerms<T, P>(query, config);

return result;
} catch (error) {
if (error instanceof Error) {
if (error instanceof Error && handleError) {
await handleFetchError(error, config, query.path);
}
throw error;
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/rsc/data/queries/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export type NextQueryProps<P> = {
site?: string;
[k: string]: unknown;
};
handleError?: boolean;
} & Omit<QueryProps<P>, 'path'>;
5 changes: 3 additions & 2 deletions projects/wp-nextjs-app/src/app/(single)/[...path]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BlocksRenderer, HtmlDecoder } from '@headstartwp/core/react';
import { HtmlDecoder } from '@headstartwp/core/react';
import { HeadstartWPRoute, queryPost } from '@headstartwp/next/app';
import Blocks from '../../../components/Blocks';

const Single = async ({ params }: HeadstartWPRoute) => {
const { data } = await queryPost({
Expand All @@ -20,7 +21,7 @@ const Single = async ({ params }: HeadstartWPRoute) => {
<HtmlDecoder html={data.post.title.rendered ?? ''} />
</h1>

<BlocksRenderer html={data.post.content.rendered ?? ''} />
<Blocks html={data.post.content.rendered ?? ''} />
</article>
);
};
Expand Down
18 changes: 18 additions & 0 deletions projects/wp-nextjs-app/src/components/Blocks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { BlocksRenderer } from '@headstartwp/core/react';
import React from 'react';
import { isBlockByName } from '@headstartwp/core';
import { PostList } from './Blocks/PostList';

type BlockProps = {
html: string;
};

const Blocks: React.FC<BlockProps> = ({ html }) => {
return (
<BlocksRenderer forwardBlockAttributes html={html}>
<PostList test={(node) => isBlockByName(node, 'core/query')} />
</BlocksRenderer>
);
};

export default Blocks;
44 changes: 44 additions & 0 deletions projects/wp-nextjs-app/src/components/Blocks/PostList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { BlockProps } from '@headstartwp/core/react';
import { queryPosts } from '@headstartwp/next/app';

interface PostListProps
extends BlockProps<{ queryId: number; query: { perPage: number; postType: string } }> {}

export const PostList: React.FC<PostListProps> = async ({ block }) => {
if (!block) {
return null;
}

const { query } = block.attributes;

try {
const {
data: { posts },
} = await queryPosts({
// todo: map the rest of the query object
params: { per_page: query.perPage, postType: query.postType },
// setting handle error to false will disable automatic handling of errors
// i.e you have to handle the error yourself
handleError: false,
});

return (
<>
<h2>Post List</h2>
<pre>
<code>{JSON.stringify({ query }, null, 2)}</code>
</pre>

<ul>
{posts.map((post) => (
<li key={post.id}>
#{post.id} -{post.title.rendered}
</li>
))}
</ul>
</>
);
} catch (e) {
return 'no posts found';
}
};

0 comments on commit ca94535

Please sign in to comment.