Skip to content

Instantly share code, notes, and snippets.

@richaber
Created November 9, 2022 16:30
Show Gist options
  • Save richaber/481e1b463c1bac7e4efed1b12686200b to your computer and use it in GitHub Desktop.
Save richaber/481e1b463c1bac7e4efed1b12686200b to your computer and use it in GitHub Desktop.
components/SelectOptions.js
/**
* SelectOptions is a child Component
* for use with WordPress' SelectControl Component
* to easily implement Optgroups.
* See the {@link Option} and {@link Optgroup} typedefs for the expected data shape(s).
*
* SelectControl doesn't appear to natively support an optgroup data shape
* (as of @wordpress/components v 22.0.0 anyway),
* and the SelectControl documentation
* only provides a lone optgroup example using hardcoded markup.
*
* @link https://developer.wordpress.org/block-editor/reference-guides/components/select-control/
*
* @example <caption>SelectControl without SelectOptions example</caption>
* <SelectControl
* label={ __( 'Select an item:' ) }
* value={ item } // e.g: value = 'a'
* onChange={ ( selection ) => { setItem( selection ) } } >
* <optgroup label="America">
* <option value="America/Chicago">Chicago</option>
* <option value="America/Chihuahua">Chihuahua</option>
* <option value="America/Costa_Rica">Costa Rica</option>
* </optgroup>
* </SelectControl>
*
* @example <caption>SelectControl with SelectOptions example (where optGroups is
* a variable defined elsewhere in the shape of {@link Optgroup})</caption>
* <SelectControl
* label={ __( 'Select an item:' ) }
* value={ item } // e.g: value = 'a'
* onChange={ ( selection ) => { setItem( selection ) } } >
* <SelectOptions options={optGroups} />
* </SelectControl>
*/
import {Component} from '@wordpress/element';
/**
* The Option element data object.
*
* @typedef {Object} Option
* @property {string} Option.label - The Option label (Also used as inner text).
* @property {string} Option.value - The Option value.
* @property {bool} [Option.disabled] - If this Boolean attribute is set, this option is not selectable.
*
* @example <caption>Option example shape</caption>
* {
* "value": "America/Chicago",
* "label": "Chicago",
* "disabled": false
* }
*/
/**
* The Option Group element data object.
*
* @typedef {Object} Optgroup
* @property {string} Optgroup.label - The Optgroup label.
* @property {Option[]} Optgroup.options - An array of Option elements.
* @property {bool} [Optgroup.disabled] - If this Boolean attribute is set, none of the items in this option group are selectable.
*
* @example <caption>Optgroup example shape</caption>
* {
* "label": "America",
* "disabled": false
* "options": [
* {
* "value": "America/Chicago",
* "label": "Chicago",
* "disabled": false
* }
* ],
* }
*/
/**
* SelectOptions Component.
*/
export default class SelectOptions extends Component {
constructor(props) {
super(props);
this.state = {
hasError: false
};
}
static getDerivedStateFromError() {
return {
hasError: true
};
}
componentDidCatch(error) {
const {
name,
onError
} = this.props;
if (onError) {
onError(name, error);
}
}
/**
* Return an option tag element.
*
* @param {Option} option - An {@link Option} object.
*
* @returns {JSX.Element}
*/
getOptionTag = function (option) {
return (
<option value={option.value}
key={option.value}
disabled={option.disabled}>
{option.label}
</option>
);
};
/**
* Get the option tags.
*
* @param {Option[]|Option} options - An array of, or a single {@link Option} object.
*
* @returns {JSX.Element[]|JSX.Element} - Returns an array of, or a single, {@link JSX.Element}
*/
getOptionTags = function (options) {
if (
!options.isArray && options.hasOwnProperty('label')
&& options.hasOwnProperty('value')
) {
// Single Option object.
return this.getOptionTag(options);
}
return options.map((option) => {
// Array of Option objects.
return this.getOptionTag(option);
});
};
/**
* Get the Option and Optgroup tags.
*
* @param {(Optgroup|Option)[]} data Array of {@link Option}/{@link Optgroup} objects.
* @returns {JSX.Element[]}
*/
getOptions = function (data) {
var tags = [];
for (let i = 0; i < data.length; i++) {
if (
data[i].hasOwnProperty('label')
&& data[i].hasOwnProperty('options')
) {
/**
* If it has a label and options, it is an {@link Optgroup}.
*/
tags.push(this.getOptgroupTag(data[i]));
} else if (
data[i].hasOwnProperty('label')
&& data[i].hasOwnProperty('value')
) {
/**
* If it has a label and value, it is an {@link Option}.
*/
tags.push(this.getOptionTag(data[i]));
}
}
return tags;
};
/**
* Return an optgroup tag element, with it's child option tag elements.
*
* @param {Optgroup} optgroup - An Optgroup object.
*
* @returns {JSX.Element}
*/
getOptgroupTag = function (optgroup) {
var children = this.getOptionTags(optgroup.options);
return (
<optgroup key={optgroup.label}
label={optgroup.label}
disabled={optgroup.disabled}>
{children}
</optgroup>
);
};
render() {
if (!this.state.hasError) {
return this.getOptions(this.props.options);
}
return null;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment