Skip to content

Instantly share code, notes, and snippets.

@thatdevgirl
Created November 29, 2018 15:29
Show Gist options
  • Save thatdevgirl/e20f72d5b985c6eab441bdf491b3edf0 to your computer and use it in GitHub Desktop.
Save thatdevgirl/e20f72d5b985c6eab441bdf491b3edf0 to your computer and use it in GitHub Desktop.
Simple Autocomplete component for WP Gutenberg
/**
* A very simple autocomplete component
*
* This is to replace the OOTB Gutenberg Autocomplete component because it is
* currently broken as of v4.5.1.
*
* See Github issue: https://github.com/WordPress/gutenberg/issues/10542
*
* Note: The options array should be an array of objects containing labels and values; i.e.:
* [
* { value: 'first', label: 'First' },
* { value: 'second', label: 'Second' }
* ]
*/
// Load external dependency.
import { isEmpty } from 'lodash';
function MyAutocomplete( {
label,
id,
value,
onChange,
options = [],
} ) {
// Construct a unique ID for this block.
const blockId = `my-autocomplete-${ id }`;
// Function to handle the onChange event.
const onChangeValue = ( event ) => {
onChange( event.target.value );
};
// Return the block, but only if options were passed in.
return ! isEmpty( options ) && (
<div>
{ /* Label for the block. */ }
<label for={ blockId }>
{ label }
</label>
{ /* Input field. */ }
<input
list={ blockId }
value={ value }
onChange={ onChangeValue }
/>
{ /* List of all of the autocomplete options. */ }
<datalist id={ blockId }>
{ options.map( ( option, index ) =>
<option value={ option.value } label={ option.label } />
) }
</datalist>
</div>
);
};
export default MyAutocomplete;
@mohsinrafiq
Copy link

mohsinrafiq commented Aug 13, 2019

Hi @simonhammes,

Yes I did, Please find the PHP code below.

/**
 * Prefix_Core_Mag_Stories class
 * 
 * This is used to define Mag Stories.
 * 
 * @link        https://abc.com
 * @since       1.0.0
 * @package     Prefix_Core
 * @subpackage  Prefix_Core/includes/blocks
 * @author      ABC <[email protected]>
 */

class Prefix_Core_Mag_Stories {
    /**
     * Initialize the class and set its properties.
     *
     * @since   1.0.0
     */
    public function __construct() {
		
        // Register Block - Mag Stories.
		add_action( 'init', array ( $this, 'prefix_block_mag_stories' ) );
    }
	
	public function prefix_block_mag_stories() {
		
		// Skip block registration if Gutenberg is not enabled/merged.
		if ( !function_exists('register_block_type') ) {
			return;
		}
		
		$dir = dirname(__FILE__);
		$index_js = 'mag-stories.js';
		
		wp_register_script(
			'prefix-mag-stories-js',
			plugins_url( $index_js, __FILE__ ),
			array(
				'wp-blocks',
				'wp-element',
				'wp-i18n'
			),
			filemtime( "$dir/$index_js" ),
			true
		);
		
		// Collect Post Tags Data
		$tags[] = array(
			'label' => esc_html__( 'All Tags', 'prefix-core' ),
			'value' => '',
		);
		
		$jumbo_stories_tags = get_tags();
		foreach ( $jumbo_stories_tags as $jumbo_stories_tag ) {
			$tags[] = array(
				'label' => $jumbo_stories_tag->name,
				'value' => $jumbo_stories_tag->term_id,
			);
		}
		
		
		wp_localize_script(
			'prefix-mag-stories-js',
			'mag_stories',
			array(
				'tags' => $tags,
			)
		);
		
		register_block_type( 'mydomain-blocks/mag-stories', array(
			'editor_script' => 'prefix-mag-stories-js',
			'render_callback' => [ $this, 'prefix_block_mag_stories_handler' ],
			'attributes' => apply_filters(
				'jumbo_stories_attributes', [
					'className' => array( 'type' => 'string', 'default' => null ),
					'tag' => array( 'type' => 'string', 'default' => '' )
				]
			)
		));
	}
	
	/**
	 * Handler for Mag Stories block
	 * 
	 * @access public
     * @param array $atts - [$tag] attributes.
	 *
	 * @return string
	 */
	public function prefix_block_mag_stories_handler( $atts = [] ) {
		
		// normalize attribute keys, lowercase.
        $atts = array_change_key_case( (array)$atts, CASE_LOWER );
		
		return $this->prefix_mag_stories( $atts );
	}
	
	/**
	 * Output the Mag Stories 1
	 *
	 *
	 * @return string
	 */
	public function prefix_mag_stories( $atts ) {
		
		$args = array(
			'post_type' => 'post',
			'posts_per_page' => 6
		);
		
		if ( ! empty( $atts['tag'] ) ) {
			$args['tag__in'] = $atts['tag'];
		}
		
		$mag_story_posts = new WP_Query( $args );
		
		// Start output.
		ob_start();
		
		if ( $mag_story_posts ->have_posts() ) :
                       return $mag_story_posts;
		endif;
		
		$html = ob_get_clean();
			
		// Return output.
        return $html;
	}
}
new Prefix_Core_Mag_Stories();

@thatdevgirl
Copy link
Author

Hi @mohsinrafiq -

The example I gave is in ES6. For ES5, you actually need to use the node require() function; import is an ES6 feature.

var MyAutocomplete = require( './MyAutocomplete' );

@techjewel
Copy link

How to create an autocomplete for all the text blocks. Say, I want to add emoji picker for text components.

@thatdevgirl
Copy link
Author

@techjewel The AutoComplete is a component that you can use more than once, just like any other component. So, expanding on my previous comment at https://gist.github.com/thatdevgirl/e20f72d5b985c6eab441bdf491b3edf0#gistcomment-2982019, you can do something like this:

import MyAutocomplete from 'simple-autocomplete.js'; // adjust if you saved this file somewhere else.

( function() {
  const emojis = [ 
    { 'value': 1, 'label': 'First option' }
    { 'value': 2, 'label': 'Second option' }
  ];

  registerBlockType( 'my/custom_block', {
    title: 'My custom block',
    // Other block registration code goes here.

    edit: ( props ) {
      const { isSelected } = props;
      const { attribute1, attribute2 } = props.attributes;

      return (
        { isSelected && (
          <InspectorControls>
            <PanelBody title='Things'>
            
              <MyAutocomplete
                label='Attribute 1'
                value={ attribute1 }
                onChange={ onChangeAttribute1 }
                options={ emojis }
              />

             <MyAutocomplete
                label='Attribute 2'
                value={ attribute2 }
                onChange={ onChangeAttribute2 }
                options={ emojis }
              />

            </PanelBody>
          </InspectorControls>
        ) }
      );
    }
  }
} )();

@wlcdesigns
Copy link

This helped a lot. Thanks!

@mipon
Copy link

mipon commented Jan 16, 2022

I get this error in the browser console every time an item is selected.

 Uncaught TypeError: Cannot read properties of undefined (reading 'toLowerCase')
at HTMLDocument.eval (eval at <anonymous> (eval at ExRmtSvrCd (eval at <anonymous> (wit.js:206))), <anonymous>:1:29484)

By the way, is it possible to search posts by typing a partial post title?

@thatdevgirl
Copy link
Author

@mipon I just tried and I am not seeing that error. (Testing on Chrome latest.) I am also curious about the error talking about toLowerCase (which is not used by this component). I wonder if it is conflicting with something else in your code. Would you be comfortable sharing the code that is using the autocomplete component?

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