Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
18b3359
created a custom blocks doc page in MDX with images
Fran-A-Dev Nov 14, 2024
38433f1
Rename and revise override blocks docs page
kellenmace Nov 22, 2024
bd51744
Merge branch 'toolkit' into docs-mdx-addon-customblocks
Fran-A-Dev Dec 17, 2024
8234912
added edits to override blocks then merged up to date toolkit branch …
Fran-A-Dev Dec 17, 2024
4395d4a
Merge branch 'toolkit' into docs-mdx-addon-customblocks
Fran-A-Dev Dec 18, 2024
fc0ac85
deleted rendering blocks with template hierarchy and just created one…
Fran-A-Dev Dec 18, 2024
f66aeff
added fs names and code block highlights.
Fran-A-Dev Dec 19, 2024
eea1b10
Merge branch 'toolkit' into docs-mdx-addon-customblocks
moonmeister Dec 20, 2024
3b623a7
docs: add link to new doc
moonmeister Dec 20, 2024
587e6e9
ittle removal and alt text
moonmeister Dec 20, 2024
034eb98
Merge branch 'toolkit' into docs-mdx-addon-customblocks
moonmeister Dec 30, 2024
18ab4c4
fix #221
moonmeister Jan 22, 2025
0585397
misc cleanup
moonmeister Jan 22, 2025
5ebea7f
revert template blocks post
moonmeister Jan 30, 2025
1b77530
restored rendering blocks reg
moonmeister Jan 30, 2025
440efec
reset nav
moonmeister Jan 30, 2025
4508460
Merge branch 'toolkit' into docs-mdx-addon-customblocks
moonmeister Jan 30, 2025
9064212
Merge branch 'toolkit' into docs-mdx-addon-customblocks
moonmeister Jan 30, 2025
45e1c01
Merge branch 'toolkit' into docs-mdx-addon-customblocks
moonmeister Jan 30, 2025
fa9e502
rewrite: update the intro
moonmeister Jan 31, 2025
0491de5
fix: correct url
moonmeister Jan 31, 2025
e0bcd95
feat: add info block directing to custom blocks
moonmeister Jan 31, 2025
21a8cc5
Merge branch 'toolkit' into docs/custom-blocks
moonmeister Feb 3, 2025
3e6d366
Merge branch 'toolkit' into docs/custom-blocks
moonmeister Feb 6, 2025
3754a6f
docs: nav
moonmeister Feb 10, 2025
9bb873a
docs: restructuring
moonmeister Feb 10, 2025
61b3ee7
cleanup
moonmeister Feb 10, 2025
147b945
blocks redirect
moonmeister Feb 10, 2025
66d84d8
chore: rework url in prep for refocuss of doc
moonmeister Feb 12, 2025
62fd107
fix: make default tab size 2 in code blocks
moonmeister Feb 12, 2025
693cced
fix: rework article to deal with a variety of usecases
moonmeister Feb 12, 2025
d31666a
fix: reqork component
moonmeister Feb 13, 2025
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
10 changes: 10 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ const nextConfig = {
},
redirects() {
return [
{
source: "/guide/how-to-create-a-block-from-a-third-party-plugin",
destination: "/docs/how-to/custom-blocks/",
permanent: true,
},
{
source: "/guide/how-to-create-a-block-from-the-wordpress-blocks-list",
destination: "/docs/how-to/custom-blocks/",
permanent: true,
},
{
source: "/guide/how-to-migrate-from-legacy-faust",
destination: "/docs/how-to/migrate-from-legacy-faust/",
Expand Down
40 changes: 40 additions & 0 deletions src/pages/docs/explanation/blocks-faq/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
export const metadata = {
title: "Blocks FAQ",
description: "Frequently asked questions about Blocks.",
};

## _"What if the block is missing some attributes?"_

If the block does not have any attributes or has only a few of them declared in the `block.json` for that block (you can view the `block.json` file for the block at https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/), you can still try to extend the block API by declaring additional attributes for that block.

Follow the [filters reference guide](reference/plugin-filters) to create a block that uses the `additional_block_attributes` property. The attributes will then be available to query from that block.

```php
class CoreCode extends WPGraphQL\ContentBlocks\Blocks\Block
{
protected ?array $additional_block_attributes = array(
// Write your attributes here
);
}
```

> [!NOTE]
> If you include those extensions in a custom plugin, your Headless Website application is dependent on the inclusion of this plugin. You need to make sure you bundle them together; otherwise, the queries you perform in the application will fail.

## _"Can I style the block differently?"_"

Yes, you can style the block in many ways, choosing to ignore some of the attributes altogether. You can also use an external React Library to style the block, such as Material UI or ChakraUI.

Bear in mind that this will almost always result in a degraded user editing experience, as the styles in the WordPress block editor view won't match the styles of the rendered headless page.

## _"What if the block contains custom JavaScript assets?"_

Some Core blocks include JavaScript assets that are injected into the WordPress page so they can run in the front view. Many [dynamic blocks](https://developer.wordpress.org/block-editor/how-to-guides/block-tutorial/creating-dynamic-blocks/) use this functionality to include user interactivity. Since React is not bundled as a dependency in the browser, the client-side code that WordPress ships to the frontend in traditional WordPress sites typically consists of plain JavaScript or jQuery.

To review and handle bundled JavaScript assets, consider these options:

- **Include them in your code**: This is not recommended, as React does not play well with plain JavaScript and jQuery, which may lead to compatibility issues.
- **Rewrite them as React components**: You can attempt to rewrite the code in React. If the bundled code can be understood and rewritten with low effort, then this could be a viable approach.
- **Use an equivalent React Component from a library**: A simpler alternative is to find a compatible React package and use it instead of replicating the block's interactivity. This can often free the developer from implementing the functionality from scratch.

Inevitably, this is a common challenge when using Blocks in a Headless Website setup, so it's up to you to weigh the pros and cons of each approach.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
320 changes: 320 additions & 0 deletions src/pages/docs/how-to/custom-blocks/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
export const metadata = {
title: "Customize Blocks Rendering",
description:
"Learn to add custom implementations of WordPress Blocks in your Faust.js project.",
};

Whether you want to customize the look and feel of a [core WordPress block](https://wordpress.org/documentation/article/wordpress-block-editor/), implement a [custom block](https://developer.wordpress.org/block-editor/getting-started/tutorial/), or add a block from a third-party plugin, Faust.js makes it easy to render WP blocks to your liking. This guide will show you how to render a block in your Faust.js project.

## 0. Prerequisites

Ensure that you have completed the steps in our [Rendering blocks](/docs/how-to/rendering-blocks/) guide and have WordPress blocks rendering in your app successfully before proceeding with the steps below.

## 1. Choose a block to override

For this guide, we are going to override the core WordPress Code Block that renders a formatted block of code..

![WordPress Code block in the Block editor](./images/core-code-block.png)

## 2. Review block features and settings

Try using the block yourself in the WordPress block editor and get familiar with the features it has. Review the settings it has in the Settings Panel and change a few of them to see what they do.

## 3. Inspect the block data via the GraphiQL IDE

In the WordPress admin sidebar, go to `GraphQL` > `GraphiQL IDE` to open the GraphiQL IDE. If you're not familiar, this is a tool we can use for composing and testing GraphQL queries. This can help you understand what data your frontend app will receive when it executes a particular query.

Paste the following query into the GraphiQL IDE and hit the `▶️` button to execute the query. Replace `/posts/testing` with the path to the blog post that contains the block you want to override.

```graphql
{
post(id: "/posts/testing", idType: URI) {
editorBlocks {
renderedHtml
... on CoreCode {
attributes {
borderColor
backgroundColor
content
style
textColor
fontSize
}
}
}
}
}
```

> [!IMPORTANT] Block Type
> Each block is given a unique GraphQL type. In this case, the Core Code block is a type of `CoreCode`. You can find the block type by looking at the `__typename` field. If you need help figuring this out, the following query will list all the blocks on a post with their types:
>
> ```graphql
> {
> post(id: "/posts/testing", idType: URI) {
> editorBlocks {
> __typename
> type
> name
> }
> }
> }
> ```
>
> The `__typename` or `type` fields will show you the block type for each block on a post. You may find it useful to know this is generated from the `$block_type` argument in the block's [`registerBlockType`](https://developer.wordpress.org/reference/functions/register_block_type/) function; shown here in the `name` field.

The GraphiQL IDE will then display the data that was returned in the response to that query. It should look something like the example response below.

```json
{
"data": {
"post": {
"editorBlocks": [
{
"renderedHtml": "\n<pre class=\"wp-block-code has-border-color has-tertiary-background-color has-text-color has-background has-small-font-size\" style=\"border-color:#333333;border-width:2px;color:#333333\"><code>// Computes the nth Fibonacci number\nfunction fib(n) {\n var a = 0, b = 1, c;\n if (n &lt; 3) {\n if (n &lt; 0) return fib(-n);\n if (n === 0) return 0;\n return 1;\n }\n while (--n)\n c = a + b, a = b, b = c;\n return c;\n}</code></pre>\n",
"attributes": {
"borderColor": null,
"backgroundColor": "tertiary",
"content": "// Computes the nth Fibonacci number\nfunction fib(n) {\n var a = 0, b = 1, c;\n if (n &lt; 3) {\n if (n &lt; 0) return fib(-n);\n if (n === 0) return 0;\n return 1;\n }\n while (--n)\n c = a + b, a = b, b = c;\n return c;\n}",
"style": "{\"color\":{\"text\":\"#333333\"},\"border\":{\"color\":\"#333333\",\"width\":\"2px\"}}",
"textColor": null,
"fontSize": "small"
}
}
]
}
}
}
```

You can try modifying the query and running it again to see how the response changes. To learn about the fields that are available for that block, click the `Docs` link in GraphiQL, search for `CoreCode`, and click on `CoreCodeAttributes`. You will see the following:

![Core code block attributes](./images/core-code-block-ide-attributes.png)

> [!INFO]- Styles
> Notice that with some of the attributes like `backgroundColor`, the editor used the [`theme.json`](https://developer.wordpress.org/themes/global-settings-and-styles/introduction-to-theme-json/) palette slug name instead of the actual value, i.e., `tertiary`. It also stored the custom hardcoded styles in the `style` fields as a JSON string.

## 4. Define the block in your frontend app

### A. Create a new block component

Create a new `CoreCode.js` file inside of your `wp-blocks` folder that contains the following code.

```js title="wp-blocks/CoreCode.js"
export default function CoreCode(props) {
console.log(props.attributes);
return <div>CoreCode</div>;
}

CoreCode.displayName = "CoreCode";
```

For now, we're just rendering a placeholder `div` and logging the data to the console. This will be replaced with actual block code in subsequent steps.

> [!INFO]- Context
> The `displayName` property is defined here so that when we run the `editorBlocks` query in subsequent steps, the `__typename` field in the query will match this string. This is required so that the `WordPressBlocksViewer` component can resolve and render the block properly.

### B. Register the new block component

Open the `index.js` file inside the `wp-blocks` folder and add your new block like this:

```js title="wp-blocks/index.js"
import { CoreBlocks } from "@faustwp/blocks";
import CoreCode from "./CoreCode";

export default {
...CoreBlocks,
CoreCode: CoreCode, // [++code]
};
```

> [!NOTE]- Context
> This file will be used to import all of the blocks you're overriding and make them available as the default export, which will be passed into `WordPressBlocksProvider` in a subsequent step.
>
> We also define the name of the block as `CoreCode` (the property name to the left of the ":" character). This key must match the `__typename` value that the blocks has in the GraphQL schema.

### C. Create the block GraphQL fragment

Create a fragment that describes the request block fields and attributes. First, `import { gql } from @apollo/client`. Then add the `CoreCode.fragments` code block, as shown below. Include all relevant fields for your implementation.

```js {1, 08-24} title="wp-blocks/CoreCode.js "
import { gql } from "@apollo/client";

export default function CoreCode({ attributes }) {
console.log(attributes);
return <div>CoreCode</div>;
}

CoreCode.fragments = {
key: `CoreCodeBlockFragment`,
entry: gql`
fragment CoreCodeBlockFragment on CoreCode {
attributes {
borderColor
backgroundColor
content
style
textColor
fontSize
fontFamily
cssClassName
}
}
`,
};
```

> [!INFO]- Context
> Attaching the fragment as a property of the function component (`CoreCode.fragments = ...`) is a convention that Faust.js uses to [colocate fragments](https://www.apollographql.com/docs/react/data/fragments#colocating-fragments) with the components that use them.

### D. Include the block fragment in your page template queries

Now, you can include the block fragment inside of the GraphQL query for your posts and pages.

The example below shows a `/wp-templates/single.js` template.

```js {2, 9, 18} title="wp-templates/single.js"
import { gql } from "@apollo/client";
import blocks from "../wp-blocks";

export default function Page(props) {
// ...
}

Page.query = gql`
${blocks.CoreCode.fragments.entry}
query GetPost {
post(id: $databaseId, idType: DATABASE_ID, asPreview: $asPreview) {
editorBlocks {
name
__typename
renderedHtml
id: clientId
parentClientId
...${blocks.CoreCode.fragments.key}
}
}
}
`;
```

- `import blocks from "../wp-blocks";{:js}` imports the blocks we're overriding.
- `${blocks.CoreCode.fragments.entry}{:js}` interpolates the `CoreCode` GraphQL fragment into the page query.
- `...${blocks.CoreCode.fragments.key}{:js}` tells GraphQL to insert the `CoreCode` GraphQL fragment inside of the `editorBlocks { ... }{:gql}` block.

With this code in place, when the GraphQL query for the single page template is executed, the response will now include data for the `CoreCode` block. You can follow this pattern in your other page templates to query for block data within them, as well.

### E. Verify the block data in the frontend app

If you navigate to the page that contains this block, you should be able to inspect the properties in the console and see your block attributes that were logged by `console.log(props.attributes)` in `CoreCode.js`.

![Browser Console showing logged object](./images/block-console.png)

## 5. Implement the block

You now have the block data available in your frontend app and are ready to implement the block's structure, styling, and functionality.

> [!IMPORTANT] Options
> You've done the hard work of getting the data into your app and integrating with the `@faustwp/blocks` package. How you implement this block is completely up to you. You can use any React component library, CSS-in-JS solution, or custom styles you like.
>
> Keep in mind, If your implementation deviates from the WordPress implementation, your content editors might find this confusing. For this guide, we'll show you a basic implementation of the `CoreCode` block.

Replace the `CoreCode` component with the following code. This code will render the block in the frontend app with a similar structure to the WordPress block editor.

```js title="wp-blocks/CoreCode.js"
import { gql } from "@apollo/client";

export default function CoreCode({ attributes }) {
return (
<pre className={attributes?.cssClassName}>
<code>{`${attributes?.content}`}</code>
</pre>
);
}

CoreCode.fragments = {
key: `CoreCodeBlockFragment`,
entry: gql`
fragment CoreCodeBlockFragment on CoreCode {
attributes {
content
cssClassName
}
}
`,
};

CoreCode.displayName = "CoreCode";
```

## 6. Implement the block styles

Let's add some code highlighting to this component to really make it pop.

> [!IMPORTANT] Options
> We're going to leave the specific styling up you. You can choose to statically style in here or you could leverage the other available `attributes` to style the block dynamically from the WordPress block editor. Faust does [support styling](/docs/reference/get-styles/) blocks using your WordPress Block theme styles.

### A. Install the `highlight.js` package by running the following command:

```bash
npm add highlight.js
```

### B. Import the `highlight.js` package in the `_app.js` file:

```js title="_app.js"
// THIS IS VERY IMPORTANT.
// In Next.js, put this in your _app.js file
import "highlight.js/styles/github.css";
```

### C. Update the Component

Update `CoreCode.js` with the code bellow to add code highlighting:

```js title="wp-blocks/CoreCode.js"
import { gql } from "@apollo/client";
import { useEffect, useRef } from "react";
import hljs from "highlight.js";

export default function CoreCode({ attributes }) {
// Create a ref for the <code> element
const codeRef = useRef(null);

useEffect(() => {
// Once the component has rendered, apply highlight.js to the code element
if (codeRef.current) {
hljs.highlightElement(codeRef.current);
}
}, [attributes?.content]);

return (
<pre className={attributes?.cssClassName}>
<code
ref={codeRef}
dangerouslySetInnerHTML={{ __html: attributes?.content }}
/>
</pre>
);
}

CoreCode.fragments = {
key: `CoreCodeBlockFragment`,
entry: gql`
fragment CoreCodeBlockFragment on CoreCode {
attributes {
content
cssClassName
}
}
`,
};

CoreCode.displayName = "CoreCode";
```

## 7. Wrapping up...

Navigate to a page that contains the `CoreCode` block and you should see the code block with syntax highlighting applied.

![](./images/core-code-block-decoupled-preview.png)
Loading