Block Making
concepts for making blocks in this project
Last updated
Was this helpful?
concepts for making blocks in this project
Last updated
Was this helpful?
This is the file structure from create-react-block. You can copy an existing block for convenience.
import { registerBlockType } from "@wordpress/blocks";
import { __ } from "@wordpress/i18n";
import "./style.scss";
import edit from "./edit";
import save from "./save";
import { PREFIX } from "../../utils/config";
registerBlockType("gravity-platform-core/promotions", {
title: __("Promotions", PREFIX),
category: "gravity-platform",
attributes: {
formId: {
type: "number",
default: 1,
},
selectedPromotionIds: {
type: "array",
default: [],
},
},
edit,
save,
});
Any block attributes that you rely on to hydrate your components should be registered in index.tsx with their type and ideally a default value.
function gravity_platform_core_blocks_init()
{
register_block_type(__DIR__ . '/src/blocks/showroom');
register_block_type(__DIR__ . '/src/blocks/promotions');
register_post_type( 'promotions',
array(
'labels' => array(
'name' => __( 'Promotions' ),
'singular_name' => __( 'Promotion' )
),
'public' => true,
'has_archive' => true,
'rewrite' => array('slug' => 'promotions'),
'show_in_rest' => true,
'show_in_graphql' => true,
'hierarchical' => true,
'graphql_single_name' => 'promotion',
'graphql_plural_name' => 'promotions',
)
);
}
add_action('init', 'gravity_platform_core_blocks_init');
You can use the GraphQl IDE in the Wordpress Backend to build your query.
import { gql } from "@apollo/client";
/**
* GraphQL countries query.
*/
const GET_PROMOTIONS = gql`query GET_PROMOTIONS{
promotions {
nodes {
title
slug
fields {
description
disclaimer
endDate
subtitle
summary
image {
altText
sourceUrl
}
}
}
}
}`;
export default GET_PROMOTIONS;
/**
* Importing React is required for use in Gutenberg since WP Scripts doesn't compile components
* as UMD modules (React is injected as a UMD module automatically in Next.js environments without an import)
*/
import React from "react";
import client from "../../../lib/wp/connector";
import GET_PROMOTIONS_QUERY from "../../../lib/wp/queries/get-promotions";
import Link from "next/link";
type Props = {
promotions: any[];
};
export default function PromotionsBlock({ promotions }: Props) {
return (
<section className="text-gray-700 ">
<div className="container items-center px-5 py-8 mx-auto lg:px-24">
<div className="flex flex-wrap mb-12 text-left">
{
promotions.map( promotion => (
<div className="w-full p-6 mx-auto lg:w-1/3">
<div className="shadow-xl rounded-xl bg-gray-50">
<img className="object-cover object-center w-full lg:h-48 md:h-36 rounded-t-xl" src={'http://' + promotion.fields.image.sourceUrl.substring(8)} alt="blog" />
<div className="p-4 lg:p-8 bg-gray-50">
<h1 className="mx-auto mb-4 text-2xl font-semibold leading-none tracking-tighter text-black lg:text-3xl title-font">{ promotion.title }</h1>
<h2 className="mb-8 text-xs font-semibold tracking-widest text-black uppercase title-font" dangerouslySetInnerHTML={{__html: promotion.fields.subtitle}}></h2>
</div>
<div className="px-6 py-4 bg-gray-100 rounded-b-xl">
<Link href={'promotions/' + promotion.slug}>
<a className="px-4 py-1 mr-1 text-base text-gray-500 transition duration-500 ease-in-out transform rounded-md focus:shadow-outline focus:outline-none focus:ring-2 ring-offset-current ring-offset-2 hover:text-black ">
Details »
</a>
</Link>
<a href="#" className="inline-flex items-center mt-auto font-semibold text-blue-600 lg:mb-0 hover:text-black " title="Read Full Article"> </a>
</div>
</div>
</div>
)
)
}
</div>
</div>
</section>
);
}
const getPropsData = async () => {
const { data } = await client.query({
query: GET_PROMOTIONS_QUERY
});
return {
props: {
promotions: data?.promotions?.nodes ?? []
}
};
};
/**
* Called from WP's block preview screen.
* Separate from getServerSideProps because private/session based data might need to be mocked.
* @returns
*/
export const getPreviewProps = async () => {
return await getPropsData();
};
/**
* Called by the page the block exists on within it's getServerSideProps method.
* The result is passed to the component props.
* @returns
*/
export const getServerSideProps = async () => {
return await getPropsData();
};
When creating & registering your block the naming convention is to kabab-case rather than TitleCase or camelCase.
import { GetServerSideProps } from "next";
import { ReactElement } from "react";
import Layout from "../../components/Layout";
import client from "../../lib/wp/connector";
import { PROMOTION_BY_SLUG_QUERY } from "../../lib/wp/queries/promotion-by-slug";
import { getSiteByDomain } from "../../lib/sites";
type Props = {
promotion: any;
};
export default function PromotionDetail({ promotion }: Props): ReactElement {
return (
<Layout title={promotion.title}>
<section className="text-gray-600 body-font">
<div className="container mx-auto flex px-5 py-24 md:flex-row flex-col items-center">
<div className="lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center">
<h1 className="title-font sm:text-4xl text-3xl mb-4 font-medium text-gray-900">{promotion.fields.subtitle}</h1>
<p className="mb-8 leading-relaxed" dangerouslySetInnerHTML={{__html: promotion.fields.summary}}></p>
<p className="text-sm mt-2 text-gray-500 mb-8 w-full" dangerouslySetInnerHTML={{__html: promotion.fields.description}}></p>
</div>
<div className="lg:max-w-lg lg:w-full md:w-1/2 w-5/6">
<img className="object-cover object-center w-full lg:h-48 md:h-36 rounded-t-xl" src={'http://' + promotion.fields.image.sourceUrl.substring(8)} alt={promotion.title} />
</div>
</div>
</section>
</Layout>
);
}
export const getServerSideProps: GetServerSideProps = async ({
req,
res,
resolvedUrl
}) => {
res.setHeader(
"Cache-Control",
"public, s-maxage=1, stale-while-revalidate=86399" // 24 hours
);
// allow self-signed certs when developing locally
if (process.env.NODE_ENV === "development") {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
}
const site = getSiteByDomain(req.headers.host);
if (!site) {
return {
notFound: true
};
}
const uri = resolvedUrl;
const { data } = await client.query({
query: PROMOTION_BY_SLUG_QUERY,
variables: { uri }
});
return {
props: {
promotion: data?.promotion
}
};
};
import { gql } from "@apollo/client";
export const PROMOTION_BY_SLUG_QUERY = gql `
query Promotion($uri: ID!) {
promotion(id: $uri, idType: URI) {
id
slug
title
fields {
description
disclaimer
endDate
subtitle
summary
image {
altText
sourceUrl
}
}
}
}
`