Skip to content

Instantly share code, notes, and snippets.

@5ally

5ally/README.md Secret

Created August 31, 2021 18:29
Show Gist options
  • Save 5ally/edaa7a3d788bc73efe1d8196c714327a to your computer and use it in GitHub Desktop.
Save 5ally/edaa7a3d788bc73efe1d8196c714327a to your computer and use it in GitHub Desktop.
get-coached/sandc block - see https://wordpress.stackexchange.com/a/393967/137402 for details

What I did/changed

  1. I added width: 300 to MY_TEMPLATE in index.js
  2. I removed both posts and options from the block type's attributes list
  3. I used getEntityRecords() and getEntityRecord() in place of wp-api
  4. I changed reps and sets to "actual" number, i.e. type is number and source is the block comment delimiter tag. (but source should not be set in the attribute object)
  5. So because they're now actual numbers, I used Number() (to cast the input to a number) when saving the attribute to ensure it's saved properly.
  6. I used the optional chaining operator, e.g. selectedPost?.featured_media
  7. There are other changes, but just see the lines with //<number> like //1.

PS: I didn't reindent the code, but I did remove unwanted whitespaces and lines.

/**
* Retrieves the translation of text.
*
* @see https://developer.wordpress.org/block-editor/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/packages/packages-block-editor/#useBlockProps
*/
import {
InnerBlocks,
useBlockProps,
InspectorControls,
} from '@wordpress/block-editor';
/**
* Other things needed
*
*/
import { SelectControl, TextControl } from '@wordpress/components';
import { Component } from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
/**
* 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.scss';
function mySelectPosts({clientId, attributes, setAttributes }) {
// Used to reset inner blocks if we've changed the selected exercise
const { replaceInnerBlocks } = useDispatch("core/block-editor");
const { inner_blocks } = useSelect(select => ({
inner_blocks: select("core/block-editor").getBlocks(clientId)
}));
/**
* Called when the inspector controls select box is changed
* Saves the value of the selected post to the selectedPost attribute
* and resets the innerblock
**/
const onSelectPost = ( post ) => {
// reset the inner
let inner_blocks_new = [];
replaceInnerBlocks(clientId, inner_blocks_new, false);
setAttributes( {
selectedPost: parseInt(post),
} );
};
const posts = useSelect( 'core' ).getEntityRecords( 'postType', 'exercise', { //1
// *no _embed here
// This should always be set to avoid duplicates in the post selection dropdown.
// But of course, you can change the value, e.g. to 20 or, the max value - 100.
per_page: 10,
} );
const selectedPost = useSelect( select => { //2
const { getEntityRecord } = select( 'core' );
const post_id = attributes.selectedPost;
// Fetch the post from the REST API, if we have a valid post ID.
return post_id && getEntityRecord( 'postType', 'exercise', post_id, {
// Request featured media along with the standard post data.
_embed: 'wp:featuredmedia',
// *no per_page here
} );
// This useSelect callback has one dependency - the selected post ID.
}, [ attributes.selectedPost ] );
/* Set of functions for saving out attributes
* reps, sets, notes
*/
const onChangeReps = ( reps ) => {
setAttributes( {
reps: Number( reps ), //3
} );
}
const onChangeSets = ( sets ) => {
setAttributes( {
sets: Number( sets ), //4
} );
}
const onChangeNotes = ( notes ) => {
setAttributes( {
notes: notes,
} );
}
// will contain the text output for the edit.
let output = "";
const blockProps = useBlockProps( {
className: "sandcexercise",
} );
// we've selected a post, so grab the bits from that post to put into the html
if (selectedPost) { //5
// grab the things we are putting in the innerblock from the post
let mediaID = selectedPost?.featured_media || 0; //6
let mediaURL = selectedPost?._embedded?.['wp:featuredmedia'][0]?.source_url || ''; //7
let exerciseLink = selectedPost.link;
// remove any html content from the excerpt as it explodes the inner block :/
let strippedContent = selectedPost.excerpt.rendered.replace(/(<([^>]+)>)/gi, "");
// create a linked heading
let heading = '<a href="' + exerciseLink + '">' + selectedPost.title.rendered + '</a>';
// build the innerblocks template
const MY_TEMPLATE = [
[ 'core/heading', { content: heading } ],
[ 'core/image', { id: mediaID, url: mediaURL, href: exerciseLink, align: "right", sizeSlug: "medium", caption: "Click for instructions", width: 300 } ], //*
[ 'core/paragraph', { content: strippedContent } ]
];
output = <InnerBlocks
template={ MY_TEMPLATE }
templateLock=""
/>
} else {
output = <p>Select an exercise from the right</p>
}
//8 I changed the SelectControl's options - a "loading" message is shown if "posts"
// is yet filled.
// return the edit html.
return [
<div { ...blockProps } key="sandcexercise">
<InspectorControls>
<div id="sandcexercise-controls">
<SelectControl
onChange={ onSelectPost }
value={ attributes.selectedPost }
label={ __( 'Select a Post' ) }
options={posts ? [
...[ { value: 0, label: __( 'Select an exercise' ) } ],
...posts.map( post => ( {
value: post.id,
label: post.title.rendered,
})),
] : [
{ value: 0, label: __( 'Loading the posts list..' ) },
]}
/>
</div>
</InspectorControls>
{output}
<TextControl
{ ...blockProps }
label="Reps:"
type="number"
className="thereps"
onChange={ onChangeReps }
value={ attributes.reps }
/>
<TextControl
{ ...blockProps }
label="Sets:"
type="number"
className="thesets"
onChange={ onChangeSets }
value={ attributes.sets }
/>
<TextControl
{ ...blockProps }
label="Notes:"
type="text"
className="thenotes"
onChange={ onChangeNotes }
value={ attributes.notes }
/>
</div>
]
}
export default mySelectPosts;
/**
* 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';
import {
InnerBlocks,
useBlockProps,
} from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n'; // Import __() from wp.i18n
/**
* 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.scss';
/**
* Internal dependencies
*/
import Edit from './edit';
/**
* Every block starts by registering a new block type definition.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
*/
registerBlockType('get-coached/sandc', {
title: 'Foo Block', //1
apiVersion: 2,
attributes: {
/*2 don't add this as an attribute - posts: {
type: 'array',
default: [],
},*/
/*3 unused attribute - post: {
type: 'object',
},*/
selectedPost: {
type: 'number',
default: 0
},
/*4 don't add this as an attribute - options: {
type: 'array',
default: [ { value: 0, label: __( 'Select a Post' ) } ],
},*/
reps: {
type: 'number',
//5 source: 'number', //* should've been source: 'text'
//6 selector: '.thereps',
default: 15,
},
sets: {
type: 'number', //7 it's "type" and not "types"
//8 source: 'number', //* should've been source: 'text'
//9 selector: '.thesets',
default: 3,
},
notes: {
type: 'string', //10 was - types: 'text',
source: 'text',
selector: '.thenotes',
default: '',
},
},
edit: Edit,
save: function( props ) {
const blockProps = useBlockProps.save();
const { attributes } = props;
return (
<div { ...blockProps } key="sandcexercise">
<InnerBlocks.Content />
<div className="repsrow"><div className="repslabel">Reps:</div><div className="thereps">{ attributes.reps }</div></div>
<div className="setsrow"><div className="setslabel">Sets:</div><div className="thesets">{ attributes.sets }</div></div>
<div className="notesrow"><div className="noteslabel">Notes:</div><div className="thenotes">{ attributes.notes }</div></div>
</div>
);
},
});
@tomjn
Copy link

tomjn commented Sep 9, 2021

Am I right in thinking that if I want the author of the plan to be able to tweak the heading that's been outputting, that will need to come put as a heading block, and therefore innerblocks is the right way to do it? Or is this a fundamental misunderstanding I have?

I think it's a miscommunicated requirement, though I imagine that's what the notes section of the block was for? A squat is still a squat regardless of the person. You coulld allow them to add paragraphs underneath etc. Though if your excerpt contains HTML tags then I don't see how that means it can't be turned into a paragraph tag, paragraphs have HTML tags too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment