Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Reduce number of dependencies for the product categories list block #771

Merged
merged 14 commits into from
Jul 29, 2019
Merged
68 changes: 20 additions & 48 deletions assets/js/blocks/product-categories/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,13 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Component, createRef, Fragment } from 'react';
import classnames from 'classnames';
import { Component, createRef, Fragment } from '@wordpress/element';
import { IconButton, Placeholder } from '@wordpress/components';
import { repeat } from 'lodash';
import PropTypes from 'prop-types';
import { withInstanceId } from '@wordpress/compose';

/**
* Internal dependencies
*/
import { buildTermsTree } from './hierarchy';
import { IconFolder } from '../../components/icons';

function getCategories( { hasEmpty, isHierarchical } ) {
const categories = wc_product_block_data.productCategories.filter(
( cat ) => hasEmpty || !! cat.count
);
return isHierarchical ?
buildTermsTree( categories ) :
categories;
}
import uniqueID from '../../utils/uniqueid';

/**
* Component displaying the categories as dropdown or list.
Expand All @@ -36,6 +22,10 @@ class ProductCategoriesBlock extends Component {
this.renderOptions = this.renderOptions.bind( this );
}

componentWillMount() {
this.id = uniqueID( '', 'wc-block-product-categories' );
mikejolley marked this conversation as resolved.
Show resolved Hide resolved
}

onNavigate() {
const { isPreview = false } = this.props;
const url = this.select.current.value;
Expand Down Expand Up @@ -76,17 +66,16 @@ class ProductCategoriesBlock extends Component {
const count = hasCount ? `(${ cat.count })` : null;
return [
<option key={ cat.term_id } value={ cat.link }>
{ repeat( '–', depth ) } { cat.name } { count }
{ '–'.repeat( depth ) } { cat.name } { count }
</option>,
!! cat.children && !! cat.children.length && this.renderOptions( cat.children, depth + 1 ),
];
} );
}

render() {
const { attributes, instanceId } = this.props;
const { attributes, categories } = this.props;
const { className, isDropdown } = attributes;
const categories = getCategories( attributes );
const classes = classnames(
'wc-block-product-categories',
className,
Expand All @@ -96,11 +85,11 @@ class ProductCategoriesBlock extends Component {
}
);

const selectId = `prod-categories-${ instanceId }`;
const selectId = `prod-categories-${ this.id }`;

return (
<Fragment>
{ categories.length > 0 ? (
{ categories.length > 0 && (
<div className={ classes }>
{ isDropdown ? (
<Fragment>
Expand All @@ -115,43 +104,26 @@ class ProductCategoriesBlock extends Component {
{ this.renderOptions( categories ) }
</select>
</div>
<IconButton
<button
type="button"
className="components-button components-icon-button"
mikejolley marked this conversation as resolved.
Show resolved Hide resolved
aria-label={ __( 'Go to category', 'woo-gutenberg-products-block' ) }
icon="arrow-right-alt2"
label={ __( 'Go to category', 'woo-gutenberg-products-block' ) }
onClick={ this.onNavigate }
/>
>
<svg aria-hidden="true" role="img" focusable="false" className="dashicon dashicons-arrow-right-alt2" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
<path d="M6 15l5-5-5-5 1-2 7 7-7 7z"></path>
</svg>
</button>
</Fragment>
) : (
this.renderList( categories )
) }
</div>
) : (
<Placeholder
className="wc-block-product-categories"
icon={ <IconFolder /> }
label={ __( 'Product Categories List', 'woo-gutenberg-products-block' ) }
>
{ __( "This block shows product categories for your store. In order to preview this you'll first need to create a product and assign it to a category.", 'woo-gutenberg-products-block' ) }
</Placeholder>
) }
</Fragment>
);
}
}

ProductCategoriesBlock.propTypes = {
/**
* The attributes for this block.
*/
attributes: PropTypes.object.isRequired,
/**
* A unique ID for identifying the label for the select dropdown.
*/
instanceId: PropTypes.number,
/**
* Whether this is the block preview or frontend display.
*/
isPreview: PropTypes.bool,
};

export default withInstanceId( ProductCategoriesBlock );
export default ProductCategoriesBlock;
17 changes: 15 additions & 2 deletions assets/js/blocks/product-categories/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@
import { __ } from '@wordpress/i18n';
import { Fragment } from '@wordpress/element';
import { InspectorControls } from '@wordpress/editor';
import { PanelBody, ToggleControl } from '@wordpress/components';
import { PanelBody, ToggleControl, Placeholder } from '@wordpress/components';

/**
* Internal dependencies
*/
import './editor.scss';
import Block from './block.js';
import ToggleButtonControl from '../../components/toggle-button-control';
import getCategories from './get-categories';
import { IconFolder } from '../../components/icons';

export default function( { attributes, setAttributes } ) {
const { hasCount, hasEmpty, isDropdown, isHierarchical } = attributes;
const categories = getCategories( attributes );

return (
<Fragment>
Expand Down Expand Up @@ -69,7 +72,17 @@ export default function( { attributes, setAttributes } ) {
/>
</PanelBody>
</InspectorControls>
<Block attributes={ attributes } isPreview />
{ categories.length > 0 ? (
<Block attributes={ attributes } categories={ categories } isPreview />
) : (
<Placeholder
className="wc-block-product-categories"
icon={ <IconFolder /> }
label={ __( 'Product Categories List', 'woo-gutenberg-products-block' ) }
>
{ __( "This block shows product categories for your store. In order to preview this you'll first need to create a product and assign it to a category.", 'woo-gutenberg-products-block' ) }
</Placeholder>
) }
</Fragment>
);
}
2 changes: 1 addition & 1 deletion assets/js/blocks/product-categories/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
margin-left: 20px;
}
.wc-block-product-categories {
svg {
.components-placeholder__label svg {
margin-right: 1ch;
fill: currentColor;
}
Expand Down
10 changes: 6 additions & 4 deletions assets/js/blocks/product-categories/frontend.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
/**
* External dependencies
*/
import { forEach } from 'lodash';
import { render } from '@wordpress/element';
import { render } from 'react-dom';

/**
* Internal dependencies
*/
import Block from './block.js';
import getCategories from './get-categories';

const containers = document.querySelectorAll(
'.wp-block-woocommerce-product-categories'
);

if ( containers.length ) {
forEach( containers, ( el ) => {
containers.forEach( function( el ) {
mikejolley marked this conversation as resolved.
Show resolved Hide resolved
const data = JSON.parse( JSON.stringify( el.dataset ) );
const attributes = {
hasCount: data.hasCount === 'true',
hasEmpty: data.hasEmpty === 'true',
isDropdown: data.isDropdown === 'true',
isHierarchical: data.isHierarchical === 'true',
};
const categories = getCategories( attributes );

el.classList.remove( 'is-loading' );

render( <Block attributes={ attributes } />, el );
render( <Block attributes={ attributes } categories={ categories } />, el );
} );
}
16 changes: 16 additions & 0 deletions assets/js/blocks/product-categories/get-categories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Internal dependencies
*/
import { buildTermsTree } from './hierarchy';

/**
* Returns categories in tree form.
*/
export default function( { hasEmpty, isHierarchical } ) {
const categories = wc_product_block_data.productCategories.filter(
( cat ) => hasEmpty || !! cat.count
);
return isHierarchical ?
buildTermsTree( categories ) :
categories;
}
12 changes: 4 additions & 8 deletions assets/js/blocks/product-categories/hierarchy.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
/**
* External dependencies
*/
import { forEach, groupBy } from 'lodash';

/**
* Returns terms in a tree form.
*
Expand All @@ -11,7 +6,8 @@ import { forEach, groupBy } from 'lodash';
* @return {Array} Array of terms in tree format.
*/
export function buildTermsTree( list = [] ) {
const termsByParent = groupBy( list, 'parent' );
// Use reduce instead of lodash groupBy.
const termsByParent = list.reduce( ( r, v, i, a, k = v.parent ) => ( ( r[ k ] || ( r[ k ] = [] ) ).push( v ), r ), {} );

const fillWithChildren = ( terms ) => {
return terms.map( ( term ) => {
Expand All @@ -27,8 +23,8 @@ export function buildTermsTree( list = [] ) {
const tree = fillWithChildren( termsByParent[ '0' ] || [] );
delete termsByParent[ '0' ];

// anything left in termsByParent has no visible parent
forEach( termsByParent, ( terms ) => {
// anything left in termsByParent has no visible parent. Native JS instead of lodash forEach.
mikejolley marked this conversation as resolved.
Show resolved Hide resolved
Object.keys( termsByParent ).forEach( function( terms ) {
tree.push( ...fillWithChildren( terms || [] ) );
} );

Expand Down
17 changes: 17 additions & 0 deletions assets/js/utils/uniqueid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const ids = [];

/**
* Returns a unique ID.
*
* This is an alternative for withInstanceId from @wordpress/compose to avoid using that dependency on the frontend.
*
* @param {string} prefix Prefix for the ID. Should be the component name.
* @param {string} group ID group.
*/
export default function( prefix = 'id', group = '' ) {
if ( ! ids[ group ] ) {
ids[ group ] = 0;
}
ids[ group ]++;
return `${ prefix }${ ids[ group ] }`;
}
3 changes: 0 additions & 3 deletions babel.config.js

This file was deleted.

Loading