Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: automatically forward block props to children of BlocksRenderer #805

Merged
merged 59 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
f869143
feat: app router poc
nicholasio May 24, 2024
5df7f07
chore: adjust exports conditions
nicholasio May 27, 2024
e7dc174
wip: wip
nicholasio Jun 6, 2024
461a648
fix: merge conflicts
nicholasio Jun 17, 2024
fb075b2
feat: initial pass at fetch functions
nicholasio Jun 20, 2024
41f73e3
feat: more progress on data fetching on app router
nicholasio Jun 22, 2024
e4055dd
feat: fix tests and code sharing
nicholasio Jun 23, 2024
fb0e26b
feat: exploring multi site support in app router
nicholasio Jun 23, 2024
3b73131
feat(data-fetching): wrapping up first pass at data-fetching
nicholasio Jun 24, 2024
26c35a7
fix: removing hooks package
nicholasio Jun 24, 2024
0a0284b
chore: update workspaces config
nicholasio Jun 24, 2024
ce3ec6d
fix: build error
nicholasio Jun 24, 2024
8f078f6
feat(data-fetching): fetchAppSettings & queryAppSettings
nicholasio Jun 24, 2024
5a183aa
fix: build issue
nicholasio Jun 25, 2024
e5f741e
feat(data-fetching): queryAuthorArchive
nicholasio Jun 25, 2024
6f68dd8
feat: tag page
nicholasio Jun 25, 2024
21990bb
feat(data-fetching): querySearch and queryTerms
nicholasio Jun 25, 2024
30f94c7
feat(data-fetching): queryPostOrPosts
nicholasio Jun 25, 2024
156072f
fix: build error with pages router project
nicholasio Jun 25, 2024
fe647b5
fix: another build issue
nicholasio Jun 25, 2024
c3cf144
feat(data-fetching): issue a not-found if no redirect is found
nicholasio Jun 25, 2024
d96e10c
test: add more data-fetching tests
nicholasio Jun 25, 2024
3006585
test: more tests
nicholasio Jun 26, 2024
46971cb
test: add more tests
nicholasio Jun 28, 2024
dad963d
chore: changeset
nicholasio Jun 28, 2024
80c3576
chore: fix lock file
nicholasio Jun 28, 2024
a7eebf7
fix: re-export other server-compatible components
nicholasio Jun 28, 2024
96a5c2f
fix: remove unnecessary cache headers
nicholasio Jun 28, 2024
cd76604
feat: first pass at previewRouteHandler
nicholasio Jul 1, 2024
8b2858b
feat: preview fixes
nicholasio Jul 1, 2024
d7cc96f
feat: add PreviewIndicator component
nicholasio Jul 3, 2024
c945166
test: adding test for queryPost preview logic
nicholasio Jul 3, 2024
b01a4ba
chore: update re-exports
nicholasio Jul 3, 2024
631dbdc
fix: config loading for route handlers
nicholasio Jul 3, 2024
5a787aa
docs: update jsdocs
nicholasio Jul 3, 2024
2a17cf1
feat: update single route to render post content
nicholasio Jul 4, 2024
98bae20
chore: changeset
nicholasio Jul 4, 2024
08ae034
feat: revalidateRouteHandler
nicholasio Jul 9, 2024
db7486e
refactor: small refactor
nicholasio Jul 9, 2024
ad6beaf
test: first pass at setting a source url and mocking requests based o…
nicholasio Jul 9, 2024
a7dea3a
test: updating tests of next package
nicholasio Jul 9, 2024
e38627b
test: fix tests
nicholasio Jul 9, 2024
bd70af9
test: fixing more tests
nicholasio Jul 9, 2024
2dc5a06
test: fix last test
nicholasio Jul 9, 2024
f86a8cb
fix: can't use force-static with draft mode
nicholasio Jul 9, 2024
a91a47d
test: add tests for previewRouteHandler
nicholasio Jul 9, 2024
b585500
test: previewRouteHandler tests
nicholasio Jul 11, 2024
68db922
test: tests for revalidateRouteHandler
nicholasio Jul 11, 2024
0e6e5e3
feat: automatically forward block props to children of BlocksRenderer
nicholasio Jul 12, 2024
1c19843
test: add test for
nicholasio Jul 12, 2024
81abdf3
test: fix snapshots
nicholasio Jul 12, 2024
7af7b2d
Merge branch 'develop' into feature/block-renderer-app-router
nicholasio Jul 13, 2024
6d2dc90
feat: querying menu and block settings
nicholasio Jul 13, 2024
86b5875
chore: simplyfing code
nicholasio Jul 13, 2024
99a3b1d
fix: queryAppSettings
nicholasio Jul 15, 2024
22bf68d
fix: test and acccept empty query
nicholasio Jul 15, 2024
c81658a
fix: menu component
nicholasio Jul 15, 2024
4adc11b
chore: changeset
nicholasio Jul 15, 2024
0753094
feat: add theme settings provider
nicholasio Jul 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/tall-moles-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@headstartwp/core": minor
"@headstartwp/next": minor
---

Add `forwardBlockProps` to BlocksRenderer which automatically forwards block props to children components
6,306 changes: 2,189 additions & 4,117 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/core/jest.setup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'whatwg-fetch';
import 'isomorphic-fetch';
import { TextDecoder, TextEncoder } from 'util';
import { server } from './test/server';

Expand Down
6 changes: 3 additions & 3 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"msw": "^0.35.0",
"ts-jest": "^29.0.3",
"typescript": "^5.4.5",
"whatwg-fetch": "^3.6.2",
"isomorphic-fetch": "^3.0.0",
"tsc-esm-fix": "^2.20.27",
"@types/react": "^18",
"@types/react-dom": "^18"
Expand All @@ -80,8 +80,8 @@
"html-react-parser": "^3.0.4",
"path-to-regexp": "^6.2.0",
"react-inspector": "^6.0.1",
"swr": "^2.1.5",
"xss": "^1.0.14",
"swr": "^2.2.5",
"xss": "^1.0.15",
"deepmerge": "^4.3.1"
},
"peerDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/data/api/__tests__/fetch-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { apiGet } from '../fetch-utils';

describe('apiGet', () => {
it('makes a fetch call', async () => {
const result = await apiGet('/test-endpoint');
const result = await apiGet('https://js1.10up.com/test-endpoint');

expect(result.json.ok).toBeTruthy();
});
Expand Down
93 changes: 79 additions & 14 deletions packages/core/src/data/fetchFn/fetchAppSettings.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,93 @@
import { AppSettingsStrategy, EndpointParams, executeFetchStrategy } from '../strategies';
import { HeadlessConfig } from '../../types';
import { getHeadstartWPConfig, getWPUrl } from '../../utils';
import { AppEntity } from '../types';
import { getHeadstartWPConfig, getObjectProperty, getWPUrl } from '../../utils';
import { AppEntity, MenuItemEntity } from '../types';
import { QueryProps } from './types';

/**
*
* @param query
* @param _config
* @returns
*/
export type AppQueryProps<P> = QueryProps<P> & {
menu?: string;
blockSetting?: {
blockName?: string;
setting: string;
};
};

export type AppQueryResult<T> = {
data: T;
menu?: MenuItemEntity[];
blockSettingValue?: unknown;
};

export function flatToHierarchical(flat: MenuItemEntity[]): MenuItemEntity[] {
const roots: MenuItemEntity[] = [];

const all: Record<number, MenuItemEntity> = {};
flat.forEach((item, index) => {
all[item.ID] = { ...item, children: [], order: index };
});

Object.keys(all).forEach((key) => {
const id = Number(key);
const item = all[id];
const parentId = Number(item.menu_item_parent);

if (parentId === 0) {
roots.push(item);
} else if (item.menu_item_parent in all) {
const p = all[item.menu_item_parent];
if (!('children' in p)) {
p.children = [];
}
p.children.push(item);
}
});

roots.sort((a, b) => a.order - b.order);
roots.forEach((root) => {
root?.children?.sort((a, b) => a.order - b.order);
});

return roots;
}

export async function fetchAppSettings<
T extends AppEntity = AppEntity,
P extends EndpointParams = EndpointParams,
>(query: Omit<QueryProps<P>, 'path'>, _config: HeadlessConfig | undefined = undefined) {
const { params = {}, options } = query;
>(
query: Omit<AppQueryProps<P>, 'path'> = {},
_config: HeadlessConfig | undefined = undefined,
): Promise<AppQueryResult<T>> {
const { params = {}, options, menu, blockSetting } = query;

const config = _config ?? getHeadstartWPConfig();

const {
data: { result },
} = await executeFetchStrategy<T, P>(fetchAppSettings.fetcher<T, P>(), config, params, options);
const { data } = await executeFetchStrategy<T, P>(
fetchAppSettings.fetcher<T, P>(),
config,
params,
options,
);

const result: AppQueryResult<T> = { data: data.result };

if (menu && data.result.menus[menu]) {
result.menu = flatToHierarchical(data.result.menus[menu]);
}

if (blockSetting && data['theme.json']) {
const blockSettingValue = blockSetting?.blockName
? getObjectProperty(
data.result['theme.json'],
`blocks.${blockSetting?.blockName}.${blockSetting.setting}`,
)
: getObjectProperty(result.data['theme.json'], blockSetting.setting);

if (blockSettingValue) {
result.blockSettingValue = blockSettingValue;
}
}

return { data: result };
return result;
}

fetchAppSettings.fetcher = <
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/data/fetchFn/fetchAuthorArchive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { getWPUrl } from '../../utils';
export async function fetchAuthorArchive<
T extends PostEntity = PostEntity,
P extends PostsArchiveParams = PostsArchiveParams,
>(query: QueryProps<P>, _config: HeadlessConfig | undefined = undefined) {
>(query: QueryProps<P> = {}, _config: HeadlessConfig | undefined = undefined) {
return fetchPosts<T, P>(query, _config, fetchAuthorArchive.fetcher<T, P>());
}

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/data/fetchFn/fetchPost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { QueryProps } from './types';
export async function fetchPost<
T extends PostEntity = PostEntity,
P extends PostParams = PostParams,
>(query: QueryProps<P>, _config: HeadlessConfig | undefined = undefined) {
>(query: QueryProps<P> = {}, _config: HeadlessConfig | undefined = undefined) {
const { params = {}, options, path = '' } = query;

const config = _config ?? getHeadstartWPConfig();
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/data/fetchFn/fetchPostOrPosts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export async function fetchPostOrPosts<
T extends PostEntity = PostEntity,
P extends PostOrPostsParams = PostOrPostsParams,
>(
query: QueryProps<P>,
query: QueryProps<P> = {},
_config: HeadlessConfig | undefined = undefined,
): Promise<FetchPostsOrPostsReturnType<T>> {
const { params = {}, options, path = '' } = query;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/data/fetchFn/fetchPosts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export async function fetchPosts<
T extends PostEntity = PostEntity,
P extends PostsArchiveParams = PostsArchiveParams,
>(
query: QueryProps<P>,
query: QueryProps<P> = {},
_config: HeadlessConfig | undefined = undefined,
fetcher: PostsArchiveFetchStrategy<T, P> | undefined = undefined,
) {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/data/fetchFn/fetchSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { getPostAuthor, getPostTerms } from '../utils';
export async function fetchSearch<
T extends PostSearchEntity | TermSearchEntity = PostSearchEntity | TermSearchEntity,
P extends SearchParams = SearchParams,
>(query: QueryProps<P>, _config: HeadlessConfig | undefined = undefined) {
>(query: QueryProps<P> = {}, _config: HeadlessConfig | undefined = undefined) {
const { params = {}, options, path = '' } = query;

const config = _config ?? getHeadstartWPConfig();
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/data/fetchFn/fetchTerms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { QueryProps } from './types';
export async function fetchTerms<
T extends TermEntity = TermEntity,
P extends TaxonomyArchiveParams = TaxonomyArchiveParams,
>(query: QueryProps<P>, _config: HeadlessConfig | undefined = undefined) {
>(query: QueryProps<P> = {}, _config: HeadlessConfig | undefined = undefined) {
const { params = {}, options, path = '' } = query;

const config = _config ?? getHeadstartWPConfig();
Expand Down
92 changes: 92 additions & 0 deletions packages/core/src/dom/parseBlockAttributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Element } from 'html-react-parser';
import { FrameworkError, warn } from '../utils';

export interface IDataWPBlock {
[key: string]: unknown;
}

const BLOCK_MISSING = '_HEADLESS_/_MISSING__BLOCK_';

export const DEFAULT_BLOCK_ELEMENT = new Element('div', {
'data-wp-block': JSON.stringify({}),
'data-wp-block-name': BLOCK_MISSING,
});

/**
* Parses Json without throwing errors
*
* @param json Serialized JSON
* @returns JSON object
*/
function safeParsing(json): Record<string, any> {
let parsed = {};

try {
parsed = JSON.parse(json);
} catch (e) {
// do nothing
}

return parsed;
}

/**
* Represents a parsed block, i.e a block parsed from `data-wp-block` and `data-wp-block-name` attributes
*/
export type ParsedBlock<T extends IDataWPBlock = IDataWPBlock> = {
/**
* The Block attributes
*/
attributes: T;

/**
* The Block name
*/
name: string;

/**
* The Block class name
*/
className: string;
};

/**
* Returns the block name and attributes
*
* @param node DomNode
*
* @returns
*/
export function parseBlockAttributes(node?: Element): ParsedBlock {
if (typeof node === 'undefined') {
throw new FrameworkError('You are calling `parseBlockAttributes` on a undefined node');
}

if (
typeof node.attribs['data-wp-block-name'] === 'undefined' &&
typeof node.attribs['data-wp-block'] === 'undefined'
) {
warn(
'[parseBlockAttributes] You are using the `parseBlockAttributes` hook in a node that is not a block.',
);
}

const blockName = node.attribs['data-wp-block-name'] || '';

if (blockName === BLOCK_MISSING) {
if (typeof node === 'undefined') {
throw new FrameworkError('You are calling `parseBlockAttributes` on a undefined node');
}
}

const attrs: IDataWPBlock = node.attribs['data-wp-block']
? safeParsing(node.attribs['data-wp-block'])
: {};

if (attrs.style) {
attrs.styleConfig = attrs.style;
delete attrs.style;
}

return { attributes: attrs, name: blockName, className: node.attribs.class };
}
7 changes: 5 additions & 2 deletions packages/core/src/react/blocks/AudioBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
'use client';

import type { Element, Text } from 'html-react-parser';
import { isBlock } from '../../dom';
import { IBlock } from '../components';
import { defaultElement, useBlock, useBlockAttributes } from './hooks';
import { useBlock, useBlockAttributes } from './hooks';
import { IBlockAttributes } from './types';
import { DEFAULT_BLOCK_ELEMENT } from '../../dom/parseBlockAttributes';

/**
* The interface for components rendered by {@link AudioBlock}
Expand Down Expand Up @@ -56,7 +59,7 @@ export interface IAudioBlock extends IBlock<AudioBlockProps> {}
*
*/
export function AudioBlock({
domNode: node = defaultElement,
domNode: node = DEFAULT_BLOCK_ELEMENT,
children,
component: Component,
style,
Expand Down
7 changes: 5 additions & 2 deletions packages/core/src/react/blocks/ButtonBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
'use client';

import { Element, Text } from 'html-react-parser';
import { isBlock } from '../../dom';
import { IBlock } from '../components';
import { IBlockAttributes } from './types';

import { defaultElement, useBlock } from './hooks';
import { useBlock } from './hooks';
import { useBlockAttributes } from './hooks/useBlockAttributes';
import { DEFAULT_BLOCK_ELEMENT } from '../../dom/parseBlockAttributes';

/**
* The interface for components rendered by {@link ButtonBlock}
Expand Down Expand Up @@ -60,7 +63,7 @@ export interface IButtonBlock extends IBlock<ButtonBlockProps> {}
*
*/
export function ButtonBlock({
domNode: node = defaultElement,
domNode: node = DEFAULT_BLOCK_ELEMENT,
children,
component: Component,
style,
Expand Down
7 changes: 5 additions & 2 deletions packages/core/src/react/blocks/ButtonsBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
'use client';

import { isBlock } from '../../dom';
import { DEFAULT_BLOCK_ELEMENT } from '../../dom/parseBlockAttributes';
import { IBlock } from '../components';
import { defaultElement, useBlock, useBlockAttributes } from './hooks';
import { useBlock, useBlockAttributes } from './hooks';
import { IBlockAttributes } from './types';

export interface ButtonsBlockProps extends IBlockAttributes {}
Expand All @@ -26,7 +29,7 @@ export interface IButtonsBlock extends IBlock<ButtonsBlockProps> {}
* @param props Component properties
*/
export function ButtonsBlock({
domNode: node = defaultElement,
domNode: node = DEFAULT_BLOCK_ELEMENT,
children,
component: Component,
style,
Expand Down
7 changes: 5 additions & 2 deletions packages/core/src/react/blocks/CodeBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
'use client';

import { isBlock } from '../../dom';
import { DEFAULT_BLOCK_ELEMENT } from '../../dom/parseBlockAttributes';
import { IBlock } from '../components';
import { defaultElement, useBlock } from './hooks';
import { useBlock } from './hooks';
import { useBlockAttributes } from './hooks/useBlockAttributes';
import { IBlockAttributes } from './types';

Expand Down Expand Up @@ -30,7 +33,7 @@ export interface ICodeBlock extends IBlock<CodeBlockProps> {}
* @param props Component properties
*/
export function CodeBlock({
domNode: node = defaultElement,
domNode: node = DEFAULT_BLOCK_ELEMENT,
children,
component: Component,
style,
Expand Down
Loading
Loading