Change Gutenberg category checkboxes to radios

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 );

Leave a Comment