Reduce number of dependencies for the product categories list block (https://github.com/woocommerce/woocommerce-blocks/pull/771)
* unqiueID helper to replace compose * move get categories function to own file * fix svg styling * remove lodash dependency * Refactor block/edit to use less dependencies * Babel config to skip wp.element * update comments * Update assets/js/blocks/product-categories/frontend.js ie11 compatible for each on nodelist Co-Authored-By: Albert Juhé Lluveras <contact@albertjuhe.com> * move id generation to constructor * simplify webpack config * Remove components CSS dependency * use HOC for component ID * Correct case on ComponentId
This commit is contained in:
parent
20f90ab1cb
commit
5789a74409
|
@ -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 withComponentId from '../../utils/with-component-id';
|
||||
|
||||
/**
|
||||
* Component displaying the categories as dropdown or list.
|
||||
|
@ -76,7 +62,7 @@ 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 ),
|
||||
];
|
||||
|
@ -84,9 +70,8 @@ class ProductCategoriesBlock extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { attributes, instanceId } = this.props;
|
||||
const { attributes, categories, ComponentId } = this.props;
|
||||
const { className, isDropdown } = attributes;
|
||||
const categories = getCategories( attributes );
|
||||
const classes = classnames(
|
||||
'wc-block-product-categories',
|
||||
className,
|
||||
|
@ -96,11 +81,11 @@ class ProductCategoriesBlock extends Component {
|
|||
}
|
||||
);
|
||||
|
||||
const selectId = `prod-categories-${ instanceId }`;
|
||||
const selectId = `prod-categories-${ ComponentId }`;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{ categories.length > 0 ? (
|
||||
{ categories.length > 0 && (
|
||||
<div className={ classes }>
|
||||
{ isDropdown ? (
|
||||
<Fragment>
|
||||
|
@ -115,43 +100,26 @@ class ProductCategoriesBlock extends Component {
|
|||
{ this.renderOptions( categories ) }
|
||||
</select>
|
||||
</div>
|
||||
<IconButton
|
||||
<button
|
||||
type="button"
|
||||
className="wc-block-product-categories__button"
|
||||
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 withComponentId( ProductCategoriesBlock );
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
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
|
||||
|
@ -12,9 +12,12 @@ import { PanelBody, ToggleControl } from '@wordpress/components';
|
|||
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>
|
||||
|
@ -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,7 +2,7 @@
|
|||
margin-left: 20px;
|
||||
}
|
||||
.wc-block-product-categories {
|
||||
svg {
|
||||
.components-placeholder__label svg {
|
||||
margin-right: 1ch;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
/**
|
||||
* 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 ) => {
|
||||
Array.prototype.forEach.call( containers, ( el ) => {
|
||||
const data = JSON.parse( JSON.stringify( el.dataset ) );
|
||||
const attributes = {
|
||||
hasCount: data.hasCount === 'true',
|
||||
|
@ -22,8 +22,10 @@ if ( containers.length ) {
|
|||
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 );
|
||||
} );
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -1,8 +1,3 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { forEach, groupBy } from 'lodash';
|
||||
|
||||
/**
|
||||
* Returns terms in a tree form.
|
||||
*
|
||||
|
@ -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' );
|
||||
// Group terms by the parent ID.
|
||||
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 ) => {
|
||||
|
@ -27,8 +23,7 @@ export function buildTermsTree( list = [] ) {
|
|||
const tree = fillWithChildren( termsByParent[ '0' ] || [] );
|
||||
delete termsByParent[ '0' ];
|
||||
|
||||
// anything left in termsByParent has no visible parent
|
||||
forEach( termsByParent, ( terms ) => {
|
||||
Object.keys( termsByParent ).forEach( function( terms ) {
|
||||
tree.push( ...fillWithChildren( terms || [] ) );
|
||||
} );
|
||||
|
||||
|
|
|
@ -18,3 +18,57 @@
|
|||
background: currentColor;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.wc-block-product-categories__button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
margin: 0;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
background: none;
|
||||
padding: 8px;
|
||||
color: #555d66;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
svg {
|
||||
fill: currentColor;
|
||||
outline: none;
|
||||
}
|
||||
.screen-reader-text {
|
||||
height: auto;
|
||||
}
|
||||
&:active {
|
||||
color: currentColor;
|
||||
}
|
||||
&:disabled,
|
||||
&[aria-disabled="true"] {
|
||||
cursor: default;
|
||||
opacity: 0.3;
|
||||
}
|
||||
&:focus:enabled {
|
||||
background-color: #fff;
|
||||
color: #191e23;
|
||||
box-shadow: inset 0 0 0 1px #6c7781, inset 0 0 0 2px #fff;
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
&:not(:disabled):not([aria-disabled="true"]):hover {
|
||||
background-color: #fff;
|
||||
color: #191e23;
|
||||
box-shadow: inset 0 0 0 1px #e2e4e7, inset 0 0 0 2px #fff, 0 1px 1px rgba(25, 30, 35, 0.2);
|
||||
}
|
||||
&:not(:disabled):not([aria-disabled="true"]):active {
|
||||
outline: none;
|
||||
background-color: #fff;
|
||||
color: #191e23;
|
||||
box-shadow: inset 0 0 0 1px #ccd0d4, inset 0 0 0 2px #fff;
|
||||
}
|
||||
&[aria-disabled="true"]:focus,
|
||||
&:disabled:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import { Component } from 'react';
|
||||
|
||||
const ids = [];
|
||||
|
||||
/**
|
||||
* HOC that gives a component a unique ID.
|
||||
*
|
||||
* This is an alternative for withInstanceId from @wordpress/compose to avoid using that dependency on the frontend.
|
||||
*/
|
||||
const withComponentId = ( OriginalComponent ) => {
|
||||
return class WrappedComponent extends Component {
|
||||
generateUniqueID() {
|
||||
const group = WrappedComponent.name;
|
||||
|
||||
if ( ! ids[ group ] ) {
|
||||
ids[ group ] = 0;
|
||||
}
|
||||
|
||||
ids[ group ]++;
|
||||
|
||||
return ids[ group ];
|
||||
}
|
||||
|
||||
render() {
|
||||
const ComponentId = this.generateUniqueID();
|
||||
|
||||
return <OriginalComponent
|
||||
{ ...this.props }
|
||||
ComponentId={ ComponentId }
|
||||
/>;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default withComponentId;
|
|
@ -1,3 +0,0 @@
|
|||
module.exports = {
|
||||
presets: [ '@wordpress/babel-preset-default' ],
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -36,6 +36,12 @@
|
|||
"devDependencies": {
|
||||
"@babel/core": "7.5.5",
|
||||
"@wordpress/babel-preset-default": "4.3.0",
|
||||
"@wordpress/browserslist-config": "^2.1.4",
|
||||
"babel-plugin-transform-async-generator-functions": "^6.24.1",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.23.0",
|
||||
"babel-plugin-transform-react-jsx": "^6.24.1",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"@wordpress/blocks": "6.4.0",
|
||||
"@wordpress/components": "8.0.0",
|
||||
"@wordpress/date": "3.3.0",
|
||||
|
|
|
@ -30,7 +30,7 @@ class Assets {
|
|||
*/
|
||||
public static function register_assets() {
|
||||
self::register_style( 'wc-block-editor', plugins_url( 'build/editor.css', __DIR__ ), array( 'wp-edit-blocks' ) );
|
||||
self::register_style( 'wc-block-style', plugins_url( 'build/style.css', __DIR__ ), array( 'wp-components' ) );
|
||||
self::register_style( 'wc-block-style', plugins_url( 'build/style.css', __DIR__ ), array() );
|
||||
|
||||
// Shared libraries and components across all blocks.
|
||||
self::register_script( 'wc-blocks', plugins_url( 'build/blocks.js', __DIR__ ), array(), false );
|
||||
|
|
|
@ -101,7 +101,12 @@ const GutenbergBlocksConfig = {
|
|||
{
|
||||
test: /\.jsx?$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader?cacheDirectory',
|
||||
options: {
|
||||
presets: [ '@wordpress/default' ],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.s[c|a]ss$/,
|
||||
|
@ -152,7 +157,18 @@ const BlocksFrontendConfig = {
|
|||
{
|
||||
test: /\.jsx?$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader?cacheDirectory',
|
||||
options: {
|
||||
...require( '@wordpress/babel-preset-default' ),
|
||||
plugins: [
|
||||
'@babel/plugin-transform-react-jsx',
|
||||
'@babel/plugin-transform-runtime',
|
||||
'transform-object-rest-spread',
|
||||
'transform-async-generator-functions',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue