GatsbyWPGutenberg / gatsby-wordpress-gutenberg

Enhance your gatsby project with gutenberg

Home Page:https://gatsbywpgutenberg.netlify.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Epic

pristas-peter opened this issue · comments

Intro

This epic is to track development a framework for Gatsby's plugins/themes which will represent a mini framework for working with Gutenberg inside Gatsby and vice versa in WordPress.

Philosophy

The suite should work with official Gatsby's WordPress plugin (currently with gatsby-source-wordpress version 3 with upcoming major rewrite of version 4 in mind).

Goals

The major goal of this mini framework is to be able to map WordPress blocks to Gatsby's react components and also be able to render Gatsby's component output as block content inside Gutenberg admin (this should help to be able to work seamlessly with WordPress's plugins like Yoast and so). You can think of this as framework to completely replace WordPress theming in php with Gatsby.

Another feature to consider is to be able to register block with its attributes inside Gatsby and it will automatically import itself inside WordPress with opinionated look and feel (considering carbon blocks as an underlying library).

Framework

gatsby-plugin-wordpress-gutenberg

??? or maybe gatsby-source-wordpress-gutenberg or gatsby-transformer-gutenberg

This is the backbone of the framework which takes Gatsby's WordPress post node representation and acts on post's html content change. This will transform post's html content into structured content of Gatsby's nodes which will represent Gutenberg blocks. The GraphQL type representation should be similar to types of wp-graphql-gutenberg plugin (which i'm author of), but this suite won't use this plugin internally and rather do a rewrite in JS using puppeteer which will open WordPress Gutenberg admin in headless browser and do the transformation itself. This should fix the scaling problem of wp-graphql-gutenberg, where you have to save the post before being able to query it in wp-graphql.

gatsby-theme-wordpress-gutenberg

This plugin should do the mapping of Gatsby's post block node to react component file and then be able to generate <Blocks> component which will render all blocks/components for the current post.

My current implementation works using gatsby-source-filesystem to track directory with react component files which have defined GraphQL fragments on Gutenberg block type. Upon scanning the source file you can then know the mapping of block->component_file (you know which component has a fragment on which block type). This way you only import blocks/components which are on that post so everything is still optimized.

Then the plugin takes the the post content blocks and generates the block component file which represents all blocks of the given post (recursively supporting inner blocks, so the blocks's innerBlocks map to components children). A small example below.

This:

<!-- wp:heading {"level":3} -->
<h3>Welcome to WordPress.</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing! </p>
<!-- /wp:paragraph -->

Is converted to this:

/* eslint-disable */
/* Warning: this file is autogenerated, any changes would be lost */

import React from 'react';
import { graphql } from 'gatsby';
import CoreHeading from '/Users/peter/Developer/pjur/src/gatsby-theme-wp-graphql-gutenberg/components/core/heading.tsx';
import UnknownBlock from '/Users/peter/Developer/pjur/src/gatsby-theme-wp-graphql-gutenberg/components/unknown-block.tsx';

const Blocks = ({blocks}) => {
  return (
    <>
      {blocks.map((block, i) => {
        if (!block) {
          return null;
        }

        const children = block.innerBlocks ? <Blocks blocks={block.innerBlocks} /> : null;

        if (block.__typename === 'WP_CoreHeadingBlock') {
          return <CoreHeading {...block} children={children} key={i} />;
        }

        return <UnknownBlock {...block} children={children} key={i} />;
      })}
    </>
  );
};

Note the special UnknownBlock component which will represent block which is not yet implemented in Gatsby and will display raw html through dangerouslySetInnerHtml. We can maybe also take this so far, that we query also for block's css styles from WordPress and also import and display those.

And this is an example of CoreHeading:

import * as React from 'react';
import { graphql } from 'gatsby';
import { CoreHeadingFragment } from 'graphql/types';
import Align from 'components/align';
import { Typography } from '@rmwc/typography';
import { Grid, GridCell } from '@rmwc/grid';

require('@material/layout-grid/dist/mdc.layout-grid.css');

export const query = graphql`
  fragment CoreHeading on WP_CoreHeadingBlock {
    attributes {
      className
      content
      level
    }
  }
`;

export interface Props extends CoreHeadingFragment {}

export default React.memo<Props>(props => {
  const { attributes } = props;

  if (!attributes) {
    return null;
  }

  return (
    <Align>
      <Grid>
        <GridCell span={12}>
          <Typography use={(attributes.className as any) || `headline${attributes.level}`} tag={`h${attributes.level}`}>
            <span dangerouslySetInnerHTML={{ __html: attributes.content }}></span>
          </Typography>
        </GridCell>
      </Grid>
    </Align>
  );
});

Since this is a Gatsby theme, we can implement a set of components for core blocks which can be shadowed further on in userland.

And the last thing that the theme could do is to generate component file with the whole post's Gatsby's page query and create Gatsby's page automatically (with support to turn off this feature).

export const pageQuery = graphql`
  fragment GutenbergBlocks847 on WP_Block {
    __typename
    ...AcfPjurProduct
    ...AcfPjurFooter
  }
  query GetGutenbergBlocks847 {
    wp {
      node(id: "cHJvZHVjdDo4NDc=") {
        ...on WP_VariableProduct {
          blocks { ...GutenbergBlocks847 }
        }
      }
    }
  }`;

export default ({data}) => <>
<SomeOtherComponentWhichYouCanShadowToRenderSeoOrOtherThings/>
<Blocks blocks={data.wp.node.blocks} />
</>

Note that these examples currently work with queryingwp-graphql-gutenberg.

Live Previews

Since Gutenberg is sort of redux/react application, we can insert our own root reducer to the store and generate events upon every change. Then our Gatsby page can listen to the event through BroadcastChannel api and act upon this. My current implementation of this rewrites Gatsby's queries to Apollo queries and then queries Gatsby's develop server with new block content. TODO: Think of an implementation which could work without Gatsby's develop server as mediator.

Registering blocks

TODO...
Brief overview in Goals section
My initial though is to scan source files with babel for our opinionated api method and then generate the php's block code accordingly and transfer it to our WordPress plugin dir.

Rendering components in WordPress admin

TODO...
Brief overview in Goals section

My initial though is to create a "hidden" page which would take block name and its attributes in a query param and then render the component accordingly.

Useful links

gatsbyjs/gatsby#19292
gatsbyjs/gatsby#19092
https://github.com/htmlburger/carbon-fields
https://github.com/pristas-peter/wp-graphql-gutenberg