diff --git a/CHANGELOG.md b/CHANGELOG.md
index bde7a748..d340d692 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,8 @@
All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Each changelog entry gets prefixed with the category of the item (Added, Changed, Depreciated, Removed, Fixed, Security).
## [2024.04]
+- Removed: `example` custom block in favor of custom block generation through `npm run create-block`.
+- Added: Custom block external template (+ documentation) that allows us to quickly create blocks through the command line using `npm run create-block`. [[MOOSE-77]](https://moderntribe.atlassian.net/browse/MOOSE-77)
- Changed: Remove Gravity Forms as a composer dependency and the respective mtribe.site composer utility. Gravity Forms should be added directly to a project repo when required.
- Chore: Composer updates including plugins: advanced-custom-fields-pro:6.2.9, duracelltomi-google-tag-manager:1.20.2, limit-login-attempts-reloaded:2.26.8, safe-svg:2.2.4, seo-by-rank-math:1.0.216, user-switching:1.7.3
- Chore: Update NPM packages, including swapping browser-sync-webpack-plugin to browser-sync-v3-webpack-plugin for correct version support.
diff --git a/dev/templates/block/edit.js.mustache b/dev/templates/block/edit.js.mustache
new file mode 100644
index 00000000..215316c3
--- /dev/null
+++ b/dev/templates/block/edit.js.mustache
@@ -0,0 +1,47 @@
+import { __ } from '@wordpress/i18n';
+import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
+import { PanelBody, TextControl } from '@wordpress/components';
+{{#isDynamicVariant}}
+import ServerSideRender from '@wordpress/server-side-render';
+{{/isDynamicVariant}}
+
+import './editor.pcss';
+
+export default function Edit( { attributes, setAttributes, isSelected } ) {
+ const blockProps = useBlockProps();
+
+ const { exampleTextControl } = attributes;
+
+ return (
+
+ {{#isDynamicVariant}}
+
+ {{/isDynamicVariant}}
+ {{#isStaticVariant}}
+
{ __( '{{title}} – hello from the editor!', '{{namespace}}' ) }
+ { exampleTextControl ?
{ exampleTextControl }
: '' }
+ {{/isStaticVariant}}
+ { isSelected && (
+
+
+
+ setAttributes( { exampleTextControl: value } )
+ }
+ />
+
+
+ ) }
+
+ );
+}
diff --git a/dev/templates/block/editor.pcss.mustache b/dev/templates/block/editor.pcss.mustache
new file mode 100644
index 00000000..f99842a2
--- /dev/null
+++ b/dev/templates/block/editor.pcss.mustache
@@ -0,0 +1,9 @@
+/* -------------------------------------------------------------------------
+ *
+ * Block - {{title}} - Styles - Editor Only
+ *
+ * ------------------------------------------------------------------------- */
+
+{{#isDynamicVariant}}section{{/isDynamicVariant}}.wp-block-{{namespace}}-{{slug}} {
+ border: 1px dotted #f00;
+}
diff --git a/dev/templates/block/index.js.mustache b/dev/templates/block/index.js.mustache
new file mode 100644
index 00000000..510d5051
--- /dev/null
+++ b/dev/templates/block/index.js.mustache
@@ -0,0 +1,23 @@
+import { registerBlockType } from '@wordpress/blocks';
+
+import './style.pcss';
+
+import Edit from './edit';
+{{#isStaticVariant}}
+import save from './save';
+{{/isStaticVariant}}
+import metadata from './block.json';
+
+registerBlockType( metadata.name, {
+ /**
+ * @see ./edit.js
+ */
+ edit: Edit,
+ {{#isStaticVariant}}
+
+ /**
+ * @see ./save.js
+ */
+ save,
+ {{/isStaticVariant}}
+} );
diff --git a/dev/templates/block/render.php.mustache b/dev/templates/block/render.php.mustache
new file mode 100644
index 00000000..096255e5
--- /dev/null
+++ b/dev/templates/block/render.php.mustache
@@ -0,0 +1,12 @@
+{{#isDynamicVariant}}
+
+
+{{/isDynamicVariant}}
diff --git a/dev/templates/block/save.js.mustache b/dev/templates/block/save.js.mustache
new file mode 100644
index 00000000..987fbfcf
--- /dev/null
+++ b/dev/templates/block/save.js.mustache
@@ -0,0 +1,16 @@
+{{#isStaticVariant}}
+import { useBlockProps } from '@wordpress/block-editor';
+
+export default function save( { attributes } ) {
+ const blockProps = useBlockProps.save();
+
+ const { exampleTextControl } = attributes;
+
+ return (
+
+ { '{{title}} – hello from the saved content!' }
+ { exampleTextControl ? { exampleTextControl }
: '' }
+
+ );
+}
+{{/isStaticVariant}}
diff --git a/dev/templates/block/style.pcss.mustache b/dev/templates/block/style.pcss.mustache
new file mode 100644
index 00000000..cf124647
--- /dev/null
+++ b/dev/templates/block/style.pcss.mustache
@@ -0,0 +1,11 @@
+/* -------------------------------------------------------------------------
+ *
+ * Block - {{title}} - Styles - FE / Editor
+ *
+ * ------------------------------------------------------------------------- */
+
+.wp-block-{{namespace}}-{{slug}} {
+ background-color: #21759b;
+ color: #fff;
+ padding: 2px;
+}
diff --git a/dev/templates/block/view.js.mustache b/dev/templates/block/view.js.mustache
new file mode 100644
index 00000000..b9fc4922
--- /dev/null
+++ b/dev/templates/block/view.js.mustache
@@ -0,0 +1,25 @@
+/**
+ * Use this file for JavaScript code that you want to run in the front-end
+ * on posts/pages that contain this block.
+ *
+ * When this file is defined as the value of the `viewScript` property
+ * in `block.json` it will be enqueued on the front end of the site.
+ *
+ * Example:
+ *
+ * ```js
+ * {
+ * "viewScript": "file:./view.js"
+ * }
+ * ```
+ *
+ * If you're not making any changes to this file because your block doesn't need any
+ * JavaScript running in the front-end, then you should delete this file and remove
+ * the `viewScript` property from `block.json`.
+ *
+ * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/#view-script
+ */
+
+/* eslint-disable no-console */
+console.log( 'Hello World! (from {{namespace}}-{{slug}} block)' );
+/* eslint-enable no-console */
diff --git a/dev/templates/index.js b/dev/templates/index.js
new file mode 100644
index 00000000..c154ea67
--- /dev/null
+++ b/dev/templates/index.js
@@ -0,0 +1,30 @@
+const { join } = require( 'path' );
+
+module.exports = {
+ blockTemplatesPath: join( __dirname, 'block' ),
+ defaultValues: {
+ attributes: {
+ exampleTextControl: {
+ type: 'string',
+ default: '',
+ },
+ },
+ dashicon: 'block-default',
+ supports: {
+ html: false,
+ align: [ 'wide', 'grid', 'full' ],
+ spacing: {
+ margin: true,
+ padding: true,
+ },
+ },
+ textdomain: 'tribe',
+ viewScript: 'file:./view.js',
+ },
+ variants: {
+ static: {},
+ dynamic: {
+ render: 'file:./render.php',
+ },
+ },
+};
diff --git a/dev/templates/package.json b/dev/templates/package.json
new file mode 100644
index 00000000..1c7422ed
--- /dev/null
+++ b/dev/templates/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "tribe-block-templates",
+ "version": "1.0.0",
+ "description": "Custom block templates for the WP create-block script to use",
+ "author": "Modern Tribe ",
+ "license": "GPL-2.0-or-later",
+ "keywords": [],
+ "homepage": "https://github.com/moderntribe/moose#readme",
+ "repository": "https://github.com/moderntribe/moose",
+ "bugs": {
+ "email": "admin@tri.be"
+ },
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ }
+}
diff --git a/docs/block-templates.md b/docs/block-templates.md
new file mode 100644
index 00000000..124a1733
--- /dev/null
+++ b/docs/block-templates.md
@@ -0,0 +1,25 @@
+# Block Templates
+
+Last Updated: 04/23/24
+
+Currently we support creating static and dynamic custom blocks. Generated custom blocks ultimately live in the core theme under `./blocks/tribe`.
+
+Please see the WordPress documentation on [external block templates here](https://developer.wordpress.org/news/2024/04/16/creating-an-external-project-template-for-create-block/).
+
+## Usage
+
+In your command line tool, you can run `npm run create-block`. This will trigger the create block tool. Once within this tool, follow the prompts to complete creating your block
+
+- Steps to create a custom block: http://p.tri.be/i/Iaz9Ym
+ 1. Choose the template variant (static or dynamic)
+ 2. Enter the slug for the block
+ 3. Enter the name for the block
+ 4. Enter a short description of the block
+ 5. Pick a [dashicon](https://developer.wordpress.org/resource/dashicons/) to assign to the block
+ 6. Choose a category to assign to your block
+- Created block markup (static variant): http://p.tri.be/i/0HRhUd
+
+## Gotchas
+
+- Because the `create-block` script inherits the code that creates the "plugin" version of a block, it also inherits the code where the `textdomain` property in the `block.json` file is automatically set to the defined block slug. This only applies to the `block.json` file, as we use the `namespace` in the template files to get around this. Please remember to update the `textdomain` property in the `block.json` file once your block gets generated.
+- Once the block is created, don't forget to add the block slug to the block definers `TYPES` array in the core plugin under `./src/Blocks/Blocks_Definer.php`.
diff --git a/package.json b/package.json
index 92a9647e..7579c394 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,8 @@
"config": {
"coreThemeDir": "./wp-content/themes/core",
"corePluginDir": "./wp-content/plugins/core",
- "coreThemeBlocksDir": "./wp-content/themes/core/blocks"
+ "coreThemeBlocksDir": "./wp-content/themes/core/blocks",
+ "coreBlockTemplatesDir": "../../../../../dev/templates"
},
"devDependencies": {
"@csstools/postcss-global-data": "^2.1.0",
@@ -55,7 +56,7 @@
"lint:configs": "wp-scripts lint-js \"./*.js\"",
"lint:configs:fix": "wp-scripts lint-js \"./*.js\" --fix",
"lint:pkg-json": "wp-scripts lint-pkg-json",
- "create-block": "cd \"$npm_package_config_coreThemeBlocksDir/tribe\" && npx @wordpress/create-block --no-plugin --namespace tribe",
+ "create-block": "cd \"$npm_package_config_coreThemeBlocksDir/tribe\" && npx @wordpress/create-block --no-plugin --namespace tribe --template $npm_package_config_coreBlockTemplatesDir",
"packages-update": "wp-scripts packages-update",
"check-engines": "wp-scripts check-engines",
"check-licenses": "wp-scripts check-licenses",
diff --git a/wp-content/themes/core/blocks/tribe/example/block.json b/wp-content/themes/core/blocks/tribe/example/block.json
deleted file mode 100644
index 06c8e0ec..00000000
--- a/wp-content/themes/core/blocks/tribe/example/block.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "$schema": "https://schemas.wp.org/trunk/block.json",
- "apiVersion": 3,
- "name": "tribe/example",
- "version": "0.1.0",
- "title": "Example Block",
- "category": "tribe-custom",
- "icon": "smiley",
- "description": "Example block scaffolded with Create Block tool.",
- "supports": {
- "html": false
- },
- "textdomain": "example",
- "editorScript": "file:./index.js",
- "editorStyle": "file:./index.css",
- "style": "file:./style-index.css"
-}
diff --git a/wp-content/themes/core/blocks/tribe/example/edit.js b/wp-content/themes/core/blocks/tribe/example/edit.js
deleted file mode 100644
index 0bac2118..00000000
--- a/wp-content/themes/core/blocks/tribe/example/edit.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * Retrieves the translation of text.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
- */
-import { __ } from '@wordpress/i18n';
-
-/**
- * React hook that is used to mark the block wrapper element.
- * It provides all the necessary props like the class name.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
- */
-import { useBlockProps } from '@wordpress/block-editor';
-
-/**
- * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
- * Those files can contain any CSS code that gets applied to the editor.
- *
- * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
- */
-import './editor.pcss';
-
-/**
- * The edit function describes the structure of your block in the context of the
- * editor. This represents what the editor will render when the block is used.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
- *
- * @return {Element} Element to render.
- */
-export default function Edit() {
- return (
-
- { __( 'Example Block – hello from the editor!', 'example' ) }
-
- );
-}
diff --git a/wp-content/themes/core/blocks/tribe/example/editor.pcss b/wp-content/themes/core/blocks/tribe/example/editor.pcss
deleted file mode 100644
index 19319320..00000000
--- a/wp-content/themes/core/blocks/tribe/example/editor.pcss
+++ /dev/null
@@ -1,9 +0,0 @@
-/**
- * The following styles get applied inside the editor only.
- *
- * Replace them with your own styles or remove the file completely.
- */
-
-.wp-block-tribe-example {
- outline: 1px dotted #f00;
-}
diff --git a/wp-content/themes/core/blocks/tribe/example/index.js b/wp-content/themes/core/blocks/tribe/example/index.js
deleted file mode 100644
index 693a3739..00000000
--- a/wp-content/themes/core/blocks/tribe/example/index.js
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Registers a new block provided a unique name and an object defining its behavior.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
- */
-import { registerBlockType } from '@wordpress/blocks';
-
-/**
- * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
- * All files containing `style` keyword are bundled together. The code used
- * gets applied both to the front of your site and to the editor.
- *
- * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
- */
-import './style.pcss';
-
-/**
- * Internal dependencies
- */
-import Edit from './edit';
-import save from './save';
-import metadata from './block.json';
-
-/**
- * Every block starts by registering a new block type definition.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
- */
-registerBlockType( metadata.name, {
- /**
- * @see ./edit.js
- */
- edit: Edit,
-
- /**
- * @see ./save.js
- */
- save,
-} );
diff --git a/wp-content/themes/core/blocks/tribe/example/save.js b/wp-content/themes/core/blocks/tribe/example/save.js
deleted file mode 100644
index 5f9e1b93..00000000
--- a/wp-content/themes/core/blocks/tribe/example/save.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * React hook that is used to mark the block wrapper element.
- * It provides all the necessary props like the class name.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
- */
-import { useBlockProps } from '@wordpress/block-editor';
-
-/**
- * The save function defines the way in which the different attributes should
- * be combined into the final markup, which is then serialized by the block
- * editor into `post_content`.
- *
- * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#save
- *
- * @return {Element} Element to render.
- */
-export default function save() {
- return (
-
- { 'Example Block – hello from the saved content!' }
-
- );
-}
diff --git a/wp-content/themes/core/blocks/tribe/example/style.pcss b/wp-content/themes/core/blocks/tribe/example/style.pcss
deleted file mode 100644
index f4ce12e4..00000000
--- a/wp-content/themes/core/blocks/tribe/example/style.pcss
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
- * The following styles get applied both on the front of your site
- * and in the editor.
- *
- * Replace them with your own styles or remove the file completely.
- */
-
-.wp-block-tribe-example {
- background-color: #21759b;
- color: #fff;
- padding: 2px;
-}