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:
Mike Jolley 2019-07-29 13:00:26 +01:00 committed by GitHub
parent 20f90ab1cb
commit 5789a74409
13 changed files with 1289 additions and 333 deletions

View File

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

View File

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

View File

@ -2,7 +2,7 @@
margin-left: 20px;
}
.wc-block-product-categories {
svg {
.components-placeholder__label svg {
margin-right: 1ch;
fill: currentColor;
}

View File

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

View File

@ -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;
}

View File

@ -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 || [] ) );
} );

View File

@ -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;
}
}

View File

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

View File

@ -1,3 +0,0 @@
module.exports = {
presets: [ '@wordpress/babel-preset-default' ],
};

File diff suppressed because it is too large Load Diff

View File

@ -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",

View File

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

View File

@ -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',
],
},
},
},
],
},