3

Any ideal way how to change Gutenberg's categories checkboxes to radio inputs? I have tried using this plugin: Categories Metabox Enhanced.

But it doesn't work fully well with Gutenberg, meaning my conditions to show some field when a term is checked doesn't work. Also this plugin adds an extra meta box without replacing Gutenberg's default categories box.

enter image description here

1

Fortunately there is a hook that we can use to customize what component is used to render the taxonomy panels called editor.PostTaxonomyType.

Gutenberg renders taxonomy panels with a component called PostTaxonomies, which really just checks whether the taxonomy is heirarchical or not, and passes the props along to either the HierarchicalTermSelector or FlatTermSelector components accordingly. Normally, these two components don't appear to be exposed in the Gutenberg API, except for within the editor.PostTaxonomyType hook, which passes the relevant component as the 1st argument.

From there, all we have to do is extend the component, override the renderTerms method to change the input type from checkbox to radio, and override the onChange method to only return one selected term.

Unfortunately, extending the class within the hook seemed to cause a noticable performance hit, but storing the extended class in the window seemed to mitigate that.

PostTaxonomies

HierarchicalTermSelector

/**
 * External dependencies
 */
import { unescape as unescapeString } from 'lodash';

function customizeTaxonomySelector( OriginalComponent ) {
    return function( props ) {
        if ( props.slug === 'my_taxonomy') {
            if ( ! window.HierarchicalTermRadioSelector ) {
                window.HierarchicalTermRadioSelector = class HierarchicalTermRadioSelector extends OriginalComponent {
                    // Return only the selected term ID
                    onChange( event ) {
                        const { onUpdateTerms, taxonomy } = this.props;
                        const termId = parseInt( event.target.value, 10 );
                        onUpdateTerms( [ termId ], taxonomy.rest_base );
                    }

                    // Copied from HierarchicalTermSelector, changed input type to radio
                    renderTerms( renderedTerms ) {
                        const { terms = [] } = this.props;
                        return renderedTerms.map( ( term ) => {
                            const id = `editor-post-taxonomies-hierarchical-term-${ term.id }`;
                            return (
                                <div key={ term.id } className="editor-post-taxonomies__hierarchical-terms-choice">
                                    <input
                                        id={ id }
                                        className="editor-post-taxonomies__hierarchical-terms-input"
                                        type="radio"
                                        checked={ terms.indexOf( term.id ) !== -1 }
                                        value={ term.id }
                                        onChange={ this.onChange }
                                    />
                                    <label htmlFor={ id }>{ unescapeString( term.name ) }</label>
                                    { !! term.children.length && <div className="editor-post-taxonomies__hierarchical-terms-subchoices">{ this.renderTerms( term.children ) }</div> }
                                </div>
                            );
                        } );
                    }
                };
            }
            return <window.HierarchicalTermRadioSelector { ...props } />;
        }
        return <OriginalComponent { ...props } />;
    };
}
wp.hooks.addFilter( 'editor.PostTaxonomyType', 'my-custom-plugin', customizeTaxonomySelector );
  • Thanks for the thoughtful and complete working solution to this problem, i have implemented this approach to replace custom taxonomy checkboxes with a selectControl component with little effort. – luckyape Oct 7 at 19:47
0

Yes, Categories Metabox Enhanced quite cleverly adds its own custom meta boxes displaying the options of categories and custom taxonomies the way you configured it in the plugin's settings (as checkbox, radios or select).

You then additionally just need to ensure to hide the default meta boxes manually by clicking the three small dots at the top of the post edit screen to "Adjust display" and uncheck "Categories" and your custom taxonomies.

Above UI setting probably can be overridden by every other user. To programmatically remove the default category meta box from Gutenberg do the following. Source: [GitHub] Custom Taxonomy: Show in REST, Hide default Gutenberg Panel (comment).

/**
 * Disable display of Gutenberg Post Setting UI for a specific
 * taxonomy. While this isn't the official API for this need,
 * it works for now because only Gutenberg is dependent on the
 * REST API response.
 */
add_filter('rest_prepare_taxonomy', function ($response, $taxonomy) {
  if ('category' === $taxonomy->name) {
    $response->data['visibility']['show_ui'] = FALSE;
  }
  return $response;
}, 10, 2);
0

Just in case someone needs the select box version, you can try the following, A slight modification to @Paul_Walton answer:

const {
  SelectControl,
} = wp.components;

function customizeTaxonomySelector(OriginalComponent) {
  return function(props) {
    if (props.slug === 'my_taxonomy') {
      if (!window.HierarchicalTermRadioSelector) {
        window.HierarchicalTermRadioSelector = class HierarchicalTermRadioSelector extends OriginalComponent {
          // Return only the selected term ID
          onChange(val) {
            console.info(event);
            const {
              onUpdateTerms,
              taxonomy
            } = this.props;
            const termId = parseInt(val, 10);
            onUpdateTerms([termId], taxonomy.rest_base);
          }
          // Copied from HierarchicalTermSelector, changed input type to radio
          renderTerms(renderedTerms) {
            const {
              terms = []
            } = this.props;
            return ( < SelectControl label = {
                __('Select some users:')
              }
              value = {
                terms[0]
              } // e.g: value = [ 'a', 'c' ]
              onChange = {
                this.onChange
              }
              options = {
                renderedTerms.map((term) => {
                  return {
                    value: term.id,
                    label: term.name
                  };
                })
              }
              />)
            }
          };
        }
        return <window.HierarchicalTermRadioSelector { ...props
        }
        />;
      }
      return <OriginalComponent { ...props
      }
      />;
    };
  }
  wp.hooks.addFilter('editor.PostTaxonomyType', 'my-plugin', customizeTaxonomySelector);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.