Shortcode to Gutenberg-block: additional text on front-end and conditional display

Although a little different than I had initially anticipated, I have found/created a solution to my two problems.

Conditionals

I have opted to move non-required input to the sidebar, using the InspectorControls-element. Reason for this is that I realised that it would be impossible to hide a field (in the editor-part of the screen) that was left blank (required to show an accurate front-end representation), while still being able to edit that field.

For example, this is the definition of a textfield in the sidebar.

el(TextControl, {
    type: 'text',
    value: attr.location,
    label: __('Location', 'peerlings-blocks'),
    onChange: function (newLocation) {
        props.setAttributes({location: newLocation})
    }
}),

This is (in the edit– and save-parts) the code used for displaying it, if the field has been used

attr.location && el('span', {className: 'location'}, attr.location),

The reason why I originally didn’t want to move these details to the sidebar was that I thought they then wouldn’t show in the editor-preview, but using the code above, that was possible to achieve. Problem solved.

Separators and wrappers

This took some more fiddling to get to work, for which I had a few revelations:

  • The selector (defined as part of any attribute) looks at the HTML extracted from post_content to find the value of a particular attribute. If this selection finds other HTML than expected, the block validation fails.
  • The edit-part is used to display the block-‘preview’ in the WordPress Dashboard. Also, it reads the HTML from post_content, when a block is edited again. On the other hand, the save-part transforms the attribute values to a HTML that is sent to (and saved in) post_content. Essentially, save encodes the block to HTML, edit decodes the HTML to attributes (found in the selectors discussed previously). This explains why minor differences between the two (in the parts found by the selectors) immediately invalidate the block.

Given the above, I placed the separators and wrappers outside of the elements that are selected to retrieve the attributes. That resulted in some extra wrapper elements, but does the trick. To illustrate, the shortcode had the following HTML output:

<span class="cv-item-details">08/2013 - current | 6 hours per week</span>

Now, that line looks like this:

<span class="resume-item-details">
    <span class="duration">
        08/2013 – current
    </span> 
    | 
    <span class="hours">
        <span class="data">
            6
        </span>
     hours per week
    </span>
</span>

The problems I had originally envisioned (separators in between block-level input fields) were essentially solved by moving a number of input fields to the sidebar (as outlined above). Separators between input fields that do remain are only included in the save-part, and that works as intended – as shown in the code below.

edit-part:

el('span', {className: 'resume-item-title'},
    el(RichText, {
        key: 'editable',
        tagName: 'strong',
        className: 'name',
        placeholder: __('Name', 'peerlings-blocks'),
        keepPlaceholderOnFocus: true,
        value: attr.name,
        onChange: function (newName) {
            props.setAttributes({name: newName})
        }
    }),
    el(RichText, {
        key: 'editable',
        className: 'organisation',
        placeholder: __('Organisation', 'peerlings-blocks'),
        keepPlaceholderOnFocus: true,
        value: attr.organisation,
        onChange: function (newOrganisation) {
            props.setAttributes({organisation: newOrganisation.replace('<br>', '')})
        }
    })
),

save-part (note the comma between the two RichText-parts, conditional on attr.organisation not being empty):

el('span',
    {className: 'resume-item-title'},
    el(RichText.Content, {
        tagName: 'strong',
        className: 'name',
        value: attr.name
    }),
    attr.organisation && ', ',
    el(RichText.Content, {
        tagName: 'span',
        className: 'organisation',
        value: attr.organisation
    })
),

You can find the full JS-file of the block (at least, the current version) here.

Leave a Comment