Start using `block.json` and convert to TS the `Product by Category` block (https://github.com/woocommerce/woocommerce-blocks/pull/6680)
* Start using `block.json` and convert to TS the `Product by Category` block * Address feedback to remove some TS errors * Remove unnecessary prop
This commit is contained in:
parent
a4077d865c
commit
ad4fb01228
|
@ -1,327 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { BlockControls, InspectorControls } from '@wordpress/block-editor';
|
||||
import ServerSideRender from '@wordpress/server-side-render';
|
||||
import {
|
||||
Button,
|
||||
Disabled,
|
||||
PanelBody,
|
||||
Placeholder,
|
||||
ToolbarGroup,
|
||||
withSpokenMessages,
|
||||
} from '@wordpress/components';
|
||||
import { Component } from '@wordpress/element';
|
||||
import PropTypes from 'prop-types';
|
||||
import GridContentControl from '@woocommerce/editor-components/grid-content-control';
|
||||
import GridLayoutControl from '@woocommerce/editor-components/grid-layout-control';
|
||||
import ProductCategoryControl from '@woocommerce/editor-components/product-category-control';
|
||||
import ProductOrderbyControl from '@woocommerce/editor-components/product-orderby-control';
|
||||
import ProductStockControl from '@woocommerce/editor-components/product-stock-control';
|
||||
import { gridBlockPreview } from '@woocommerce/resource-previews';
|
||||
import { Icon, file } from '@wordpress/icons';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
|
||||
const EmptyPlaceholder = () => (
|
||||
<Placeholder
|
||||
icon={ <Icon icon={ file } /> }
|
||||
label={ __( 'Products by Category', 'woo-gutenberg-products-block' ) }
|
||||
className="wc-block-products-grid wc-block-products-category"
|
||||
>
|
||||
{ __(
|
||||
'No products were found that matched your selection.',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</Placeholder>
|
||||
);
|
||||
|
||||
/**
|
||||
* Component to handle edit mode of "Products by Category".
|
||||
*/
|
||||
class ProductByCategoryBlock extends Component {
|
||||
static propTypes = {
|
||||
/**
|
||||
* The attributes for this block
|
||||
*/
|
||||
attributes: PropTypes.object.isRequired,
|
||||
/**
|
||||
* The register block name.
|
||||
*/
|
||||
name: PropTypes.string.isRequired,
|
||||
/**
|
||||
* A callback to update attributes
|
||||
*/
|
||||
setAttributes: PropTypes.func.isRequired,
|
||||
|
||||
// from withSpokenMessages
|
||||
debouncedSpeak: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
changedAttributes: {},
|
||||
isEditing: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const { attributes } = this.props;
|
||||
|
||||
if ( ! attributes.categories.length ) {
|
||||
// We've removed all selected categories, or no categories have been selected yet.
|
||||
this.setState( { isEditing: true } );
|
||||
}
|
||||
}
|
||||
|
||||
startEditing = () => {
|
||||
this.setState( {
|
||||
isEditing: true,
|
||||
changedAttributes: {},
|
||||
} );
|
||||
};
|
||||
|
||||
stopEditing = () => {
|
||||
this.setState( {
|
||||
isEditing: false,
|
||||
changedAttributes: {},
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* Set changed attributes to state.
|
||||
*
|
||||
* @param {Object} attributes List of attributes to set.
|
||||
*/
|
||||
setChangedAttributes = ( attributes ) => {
|
||||
this.setState( ( prevState ) => {
|
||||
return {
|
||||
changedAttributes: {
|
||||
...prevState.changedAttributes,
|
||||
...attributes,
|
||||
},
|
||||
};
|
||||
} );
|
||||
};
|
||||
|
||||
save = () => {
|
||||
const { changedAttributes } = this.state;
|
||||
const { setAttributes } = this.props;
|
||||
|
||||
setAttributes( changedAttributes );
|
||||
this.stopEditing();
|
||||
};
|
||||
|
||||
getInspectorControls() {
|
||||
const { attributes, setAttributes } = this.props;
|
||||
const { isEditing } = this.state;
|
||||
const {
|
||||
columns,
|
||||
catOperator,
|
||||
contentVisibility,
|
||||
orderby,
|
||||
rows,
|
||||
alignButtons,
|
||||
stockStatus,
|
||||
} = attributes;
|
||||
|
||||
return (
|
||||
<InspectorControls key="inspector">
|
||||
<PanelBody
|
||||
title={ __(
|
||||
'Product Category',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
initialOpen={
|
||||
! attributes.categories.length && ! isEditing
|
||||
}
|
||||
>
|
||||
<ProductCategoryControl
|
||||
selected={ attributes.categories }
|
||||
onChange={ ( value = [] ) => {
|
||||
const ids = value.map( ( { id } ) => id );
|
||||
const changes = { categories: ids };
|
||||
|
||||
// Changes in the sidebar save instantly and overwrite any unsaved changes.
|
||||
setAttributes( changes );
|
||||
this.setChangedAttributes( changes );
|
||||
} }
|
||||
operator={ catOperator }
|
||||
onOperatorChange={ ( value = 'any' ) => {
|
||||
const changes = { catOperator: value };
|
||||
setAttributes( changes );
|
||||
this.setChangedAttributes( changes );
|
||||
} }
|
||||
isCompact={ true }
|
||||
/>
|
||||
</PanelBody>
|
||||
<PanelBody
|
||||
title={ __( 'Layout', 'woo-gutenberg-products-block' ) }
|
||||
initialOpen
|
||||
>
|
||||
<GridLayoutControl
|
||||
columns={ columns }
|
||||
rows={ rows }
|
||||
alignButtons={ alignButtons }
|
||||
setAttributes={ setAttributes }
|
||||
minColumns={ getSetting( 'min_columns', 1 ) }
|
||||
maxColumns={ getSetting( 'max_columns', 6 ) }
|
||||
minRows={ getSetting( 'min_rows', 1 ) }
|
||||
maxRows={ getSetting( 'max_rows', 6 ) }
|
||||
/>
|
||||
</PanelBody>
|
||||
<PanelBody
|
||||
title={ __( 'Content', 'woo-gutenberg-products-block' ) }
|
||||
initialOpen
|
||||
>
|
||||
<GridContentControl
|
||||
settings={ contentVisibility }
|
||||
onChange={ ( value ) =>
|
||||
setAttributes( { contentVisibility: value } )
|
||||
}
|
||||
/>
|
||||
</PanelBody>
|
||||
<PanelBody
|
||||
title={ __( 'Order By', 'woo-gutenberg-products-block' ) }
|
||||
initialOpen={ false }
|
||||
>
|
||||
<ProductOrderbyControl
|
||||
setAttributes={ setAttributes }
|
||||
value={ orderby }
|
||||
/>
|
||||
</PanelBody>
|
||||
<PanelBody
|
||||
title={ __(
|
||||
'Filter by stock status',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
initialOpen={ false }
|
||||
>
|
||||
<ProductStockControl
|
||||
setAttributes={ setAttributes }
|
||||
value={ stockStatus }
|
||||
/>
|
||||
</PanelBody>
|
||||
</InspectorControls>
|
||||
);
|
||||
}
|
||||
|
||||
renderEditMode() {
|
||||
const { attributes, debouncedSpeak } = this.props;
|
||||
const { changedAttributes } = this.state;
|
||||
const currentAttributes = { ...attributes, ...changedAttributes };
|
||||
const onDone = () => {
|
||||
this.save();
|
||||
debouncedSpeak(
|
||||
__(
|
||||
'Showing Products by Category block preview.',
|
||||
'woo-gutenberg-products-block'
|
||||
)
|
||||
);
|
||||
};
|
||||
const onCancel = () => {
|
||||
this.stopEditing();
|
||||
debouncedSpeak(
|
||||
__(
|
||||
'Showing Products by Category block preview.',
|
||||
'woo-gutenberg-products-block'
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Placeholder
|
||||
icon={ <Icon icon={ file } /> }
|
||||
label={ __(
|
||||
'Products by Category',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
className="wc-block-products-grid wc-block-products-category"
|
||||
>
|
||||
{ __(
|
||||
'Display a grid of products from your selected categories.',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
<div className="wc-block-products-category__selection">
|
||||
<ProductCategoryControl
|
||||
selected={ currentAttributes.categories }
|
||||
onChange={ ( value = [] ) => {
|
||||
const ids = value.map( ( { id } ) => id );
|
||||
this.setChangedAttributes( { categories: ids } );
|
||||
} }
|
||||
operator={ currentAttributes.catOperator }
|
||||
onOperatorChange={ ( value = 'any' ) =>
|
||||
this.setChangedAttributes( { catOperator: value } )
|
||||
}
|
||||
/>
|
||||
<Button isPrimary onClick={ onDone }>
|
||||
{ __( 'Done', 'woo-gutenberg-products-block' ) }
|
||||
</Button>
|
||||
<Button
|
||||
className="wc-block-products-category__cancel-button"
|
||||
isTertiary
|
||||
onClick={ onCancel }
|
||||
>
|
||||
{ __( 'Cancel', 'woo-gutenberg-products-block' ) }
|
||||
</Button>
|
||||
</div>
|
||||
</Placeholder>
|
||||
);
|
||||
}
|
||||
|
||||
renderViewMode() {
|
||||
const { attributes, name } = this.props;
|
||||
const hasCategories = attributes.categories.length;
|
||||
|
||||
return (
|
||||
<Disabled>
|
||||
{ hasCategories ? (
|
||||
<ServerSideRender
|
||||
block={ name }
|
||||
attributes={ attributes }
|
||||
EmptyResponsePlaceholder={ EmptyPlaceholder }
|
||||
/>
|
||||
) : (
|
||||
__(
|
||||
'Select at least one category to display its products.',
|
||||
'woo-gutenberg-products-block'
|
||||
)
|
||||
) }
|
||||
</Disabled>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isEditing } = this.state;
|
||||
const { attributes } = this.props;
|
||||
|
||||
if ( attributes.isPreview ) {
|
||||
return gridBlockPreview;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<BlockControls>
|
||||
<ToolbarGroup
|
||||
controls={ [
|
||||
{
|
||||
icon: 'edit',
|
||||
title: __(
|
||||
'Edit selected categories',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
onClick: () =>
|
||||
isEditing
|
||||
? this.stopEditing()
|
||||
: this.startEditing(),
|
||||
isActive: isEditing,
|
||||
},
|
||||
] }
|
||||
/>
|
||||
</BlockControls>
|
||||
{ this.getInspectorControls() }
|
||||
{ isEditing ? this.renderEditMode() : this.renderViewMode() }
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withSpokenMessages( ProductByCategoryBlock );
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"name": "woocommerce/product-category",
|
||||
"title": "Products by Category",
|
||||
"category": "woocommerce",
|
||||
"keywords": [ "WooCommerce" ],
|
||||
"description": "Display a grid of products from your selected categories.",
|
||||
"supports": {
|
||||
"align": [ "wide", "full" ],
|
||||
"html": false
|
||||
},
|
||||
"example": {
|
||||
"attributes": {
|
||||
"isPreview": true
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
"columns": {
|
||||
"type": "number",
|
||||
"default": 3
|
||||
},
|
||||
"rows": {
|
||||
"type": "number",
|
||||
"default": 3
|
||||
},
|
||||
"alignButtons": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"contentVisibility": {
|
||||
"type": "object",
|
||||
"default": {
|
||||
"image": true,
|
||||
"title": true,
|
||||
"price": true,
|
||||
"rating": true,
|
||||
"button": true
|
||||
},
|
||||
"properties": {
|
||||
"image": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"title": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"price": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"rating": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"categories": {
|
||||
"type": "array",
|
||||
"default": []
|
||||
},
|
||||
"catOperator": {
|
||||
"type": "string",
|
||||
"default": "any"
|
||||
},
|
||||
"isPreview": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"stockStatus": {
|
||||
"type": "array"
|
||||
},
|
||||
"editMode": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"orderby": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"date",
|
||||
"popularity",
|
||||
"price_asc",
|
||||
"price_desc",
|
||||
"rating",
|
||||
"title",
|
||||
"menu_order"
|
||||
],
|
||||
"default": "date"
|
||||
}
|
||||
},
|
||||
"textdomain": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2,
|
||||
"$schema": "https://schemas.wp.org/trunk/block.json"
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import ServerSideRender from '@wordpress/server-side-render';
|
||||
import { Placeholder } from '@wordpress/components';
|
||||
import { Icon, file } from '@wordpress/icons';
|
||||
import { gridBlockPreview } from '@woocommerce/resource-previews';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Props } from './types';
|
||||
|
||||
const EmptyPlaceholder = () => (
|
||||
<Placeholder
|
||||
icon={ <Icon icon={ file } /> }
|
||||
label={ __( 'Products by Category', 'woo-gutenberg-products-block' ) }
|
||||
className="wc-block-products-grid wc-block-products-category"
|
||||
>
|
||||
{ __(
|
||||
'No products were found that matched your selection.',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</Placeholder>
|
||||
);
|
||||
|
||||
export const ProductByCategoryBlock = ( props: Props ): JSX.Element => {
|
||||
const { name, attributes } = props;
|
||||
const hasCategories = attributes.categories.length;
|
||||
|
||||
if ( attributes.isPreview ) {
|
||||
return gridBlockPreview;
|
||||
}
|
||||
|
||||
return hasCategories ? (
|
||||
<ServerSideRender
|
||||
block={ name }
|
||||
attributes={ attributes }
|
||||
EmptyResponsePlaceholder={ EmptyPlaceholder }
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{ __(
|
||||
'Select at least one category to display its products.',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,104 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Button, Placeholder } from '@wordpress/components';
|
||||
import { Icon, file } from '@wordpress/icons';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import ProductCategoryControl from '@woocommerce/editor-components/product-category-control';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Attributes, Props } from './types';
|
||||
|
||||
export interface EditModeProps extends Props {
|
||||
isEditing: boolean;
|
||||
setIsEditing: ( isEditing: boolean ) => void;
|
||||
changedAttributes: Partial< Attributes >;
|
||||
setChangedAttributes: ( changedAttributes: Partial< Attributes > ) => void;
|
||||
}
|
||||
|
||||
export const ProductsByCategoryEditMode = (
|
||||
props: EditModeProps
|
||||
): JSX.Element => {
|
||||
const {
|
||||
debouncedSpeak,
|
||||
setIsEditing,
|
||||
changedAttributes,
|
||||
setChangedAttributes,
|
||||
attributes,
|
||||
} = props;
|
||||
|
||||
const currentAttributes = { ...attributes, ...changedAttributes };
|
||||
|
||||
const stopEditing = () => {
|
||||
setIsEditing( false );
|
||||
setChangedAttributes( {} );
|
||||
};
|
||||
|
||||
const save = () => {
|
||||
const { setAttributes } = props;
|
||||
|
||||
setAttributes( changedAttributes );
|
||||
stopEditing();
|
||||
};
|
||||
|
||||
const onDone = () => {
|
||||
save();
|
||||
debouncedSpeak(
|
||||
__(
|
||||
'Showing Products by Category block preview.',
|
||||
'woo-gutenberg-products-block'
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
stopEditing();
|
||||
debouncedSpeak(
|
||||
__(
|
||||
'Showing Products by Category block preview.',
|
||||
'woo-gutenberg-products-block'
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Placeholder
|
||||
icon={ <Icon icon={ file } /> }
|
||||
label={ __(
|
||||
'Products by Category',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
className="wc-block-products-grid wc-block-products-category"
|
||||
>
|
||||
{ __(
|
||||
'Display a grid of products from your selected categories.',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
<div className="wc-block-products-category__selection">
|
||||
<ProductCategoryControl
|
||||
selected={ currentAttributes.categories }
|
||||
onChange={ ( value = [] ) => {
|
||||
const ids = value.map( ( { id } ) => id );
|
||||
setChangedAttributes( { categories: ids } );
|
||||
} }
|
||||
operator={ currentAttributes.catOperator }
|
||||
onOperatorChange={ ( value = 'any' ) =>
|
||||
setChangedAttributes( { catOperator: value } )
|
||||
}
|
||||
/>
|
||||
<Button isPrimary onClick={ onDone }>
|
||||
{ __( 'Done', 'woo-gutenberg-products-block' ) }
|
||||
</Button>
|
||||
<Button
|
||||
className="wc-block-products-category__cancel-button"
|
||||
isTertiary
|
||||
onClick={ onCancel }
|
||||
>
|
||||
{ __( 'Cancel', 'woo-gutenberg-products-block' ) }
|
||||
</Button>
|
||||
</div>
|
||||
</Placeholder>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { BlockControls, useBlockProps } from '@wordpress/block-editor';
|
||||
import { useState } from '@wordpress/element';
|
||||
import {
|
||||
Disabled,
|
||||
ToolbarGroup,
|
||||
withSpokenMessages,
|
||||
} from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ProductByCategoryBlock } from './block';
|
||||
import { Attributes, Props } from './types';
|
||||
import { ProductsByCategoryInspectorControls } from './inspector-controls';
|
||||
import { ProductsByCategoryEditMode } from './edit-mode';
|
||||
|
||||
const EditBlock = ( props: Props ): JSX.Element => {
|
||||
const blockProps = useBlockProps();
|
||||
|
||||
const { attributes } = props;
|
||||
|
||||
const [ isEditing, setIsEditing ] = useState(
|
||||
! attributes.categories.length
|
||||
);
|
||||
|
||||
const [ changedAttributes, setChangedAttributes ] = useState<
|
||||
Partial< Attributes >
|
||||
>( {} );
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<BlockControls>
|
||||
<ToolbarGroup
|
||||
controls={ [
|
||||
{
|
||||
icon: 'edit',
|
||||
title: __(
|
||||
'Edit selected categories',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
onClick: () => setIsEditing( ! isEditing ),
|
||||
isActive: isEditing,
|
||||
},
|
||||
] }
|
||||
/>
|
||||
</BlockControls>
|
||||
<ProductsByCategoryInspectorControls
|
||||
isEditing={ isEditing }
|
||||
setChangedAttributes={ setChangedAttributes }
|
||||
{ ...props }
|
||||
/>
|
||||
{ isEditing ? (
|
||||
<ProductsByCategoryEditMode
|
||||
isEditing={ isEditing }
|
||||
setIsEditing={ setIsEditing }
|
||||
changedAttributes={ changedAttributes }
|
||||
setChangedAttributes={ setChangedAttributes }
|
||||
{ ...props }
|
||||
/>
|
||||
) : (
|
||||
<Disabled>
|
||||
<ProductByCategoryBlock { ...props } />
|
||||
</Disabled>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Edit = withSpokenMessages( EditBlock );
|
|
@ -1,95 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { createBlock, registerBlockType } from '@wordpress/blocks';
|
||||
import { without } from 'lodash';
|
||||
import { Icon, file } from '@wordpress/icons';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './editor.scss';
|
||||
import Block from './block';
|
||||
import sharedAttributes, {
|
||||
sharedAttributeBlockTypes,
|
||||
} from '../../utils/shared-attributes';
|
||||
|
||||
/**
|
||||
* Register and run the "Products by Category" block.
|
||||
*/
|
||||
registerBlockType( 'woocommerce/product-category', {
|
||||
title: __( 'Products by Category', 'woo-gutenberg-products-block' ),
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ file }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
category: 'woocommerce',
|
||||
keywords: [ __( 'WooCommerce', 'woo-gutenberg-products-block' ) ],
|
||||
description: __(
|
||||
'Display a grid of products from your selected categories.',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
supports: {
|
||||
align: [ 'wide', 'full' ],
|
||||
html: false,
|
||||
},
|
||||
example: {
|
||||
attributes: {
|
||||
isPreview: true,
|
||||
},
|
||||
},
|
||||
attributes: {
|
||||
...sharedAttributes,
|
||||
|
||||
/**
|
||||
* Toggle for edit mode in the block preview.
|
||||
*/
|
||||
editMode: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
},
|
||||
|
||||
/**
|
||||
* How to order the products: 'date', 'popularity', 'price_asc', 'price_desc' 'rating', 'title'.
|
||||
*/
|
||||
orderby: {
|
||||
type: 'string',
|
||||
default: 'date',
|
||||
},
|
||||
},
|
||||
|
||||
transforms: {
|
||||
from: [
|
||||
{
|
||||
type: 'block',
|
||||
blocks: without(
|
||||
sharedAttributeBlockTypes,
|
||||
'woocommerce/product-category'
|
||||
),
|
||||
transform: ( attributes ) =>
|
||||
createBlock( 'woocommerce/product-category', {
|
||||
...attributes,
|
||||
editMode: false,
|
||||
} ),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders and manages the block.
|
||||
*
|
||||
* @param {Object} props Props to pass to block.
|
||||
*/
|
||||
edit( props ) {
|
||||
return <Block { ...props } />;
|
||||
},
|
||||
|
||||
save() {
|
||||
return null;
|
||||
},
|
||||
} );
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { createBlock, registerBlockType } from '@wordpress/blocks';
|
||||
import { without } from 'lodash';
|
||||
import { Icon, file } from '@wordpress/icons';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './editor.scss';
|
||||
import metadata from './block.json';
|
||||
import sharedAttributes, {
|
||||
sharedAttributeBlockTypes,
|
||||
} from '../../utils/shared-attributes';
|
||||
import { Edit } from './edit';
|
||||
|
||||
/**
|
||||
* Register and run the "Products by Category" block.
|
||||
*/
|
||||
registerBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ file }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
attributes: {
|
||||
...metadata.attributes,
|
||||
...sharedAttributes,
|
||||
},
|
||||
|
||||
transforms: {
|
||||
from: [
|
||||
{
|
||||
type: 'block',
|
||||
blocks: without(
|
||||
sharedAttributeBlockTypes,
|
||||
'woocommerce/product-category'
|
||||
),
|
||||
transform: ( attributes ) =>
|
||||
createBlock( 'woocommerce/product-category', {
|
||||
...attributes,
|
||||
editMode: false,
|
||||
} ),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
edit: Edit,
|
||||
|
||||
save: () => {
|
||||
return null;
|
||||
},
|
||||
} );
|
|
@ -0,0 +1,116 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { InspectorControls } from '@wordpress/block-editor';
|
||||
import { PanelBody } from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import ProductCategoryControl from '@woocommerce/editor-components/product-category-control';
|
||||
import GridLayoutControl from '@woocommerce/editor-components/grid-layout-control';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
import GridContentControl from '@woocommerce/editor-components/grid-content-control';
|
||||
import ProductOrderbyControl from '@woocommerce/editor-components/product-orderby-control';
|
||||
import ProductStockControl from '@woocommerce/editor-components/product-stock-control';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Attributes, Props } from './types';
|
||||
|
||||
export interface InspectorControlsProps extends Props {
|
||||
isEditing: boolean;
|
||||
setChangedAttributes: ( changedAttributes: Partial< Attributes > ) => void;
|
||||
}
|
||||
|
||||
export const ProductsByCategoryInspectorControls = (
|
||||
props: InspectorControlsProps
|
||||
): JSX.Element => {
|
||||
const { isEditing, attributes, setAttributes, setChangedAttributes } =
|
||||
props;
|
||||
const {
|
||||
columns,
|
||||
catOperator,
|
||||
contentVisibility,
|
||||
orderby,
|
||||
rows,
|
||||
alignButtons,
|
||||
stockStatus,
|
||||
} = attributes;
|
||||
|
||||
return (
|
||||
<InspectorControls key="inspector">
|
||||
<PanelBody
|
||||
title={ __(
|
||||
'Product Category',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
initialOpen={ ! attributes.categories.length && ! isEditing }
|
||||
>
|
||||
<ProductCategoryControl
|
||||
selected={ attributes.categories }
|
||||
onChange={ ( value = [] ) => {
|
||||
const ids = value.map( ( { id } ) => id );
|
||||
const changes = { categories: ids };
|
||||
|
||||
// Changes in the sidebar save instantly and overwrite any unsaved changes.
|
||||
setAttributes( changes );
|
||||
setChangedAttributes( changes );
|
||||
} }
|
||||
operator={ catOperator }
|
||||
onOperatorChange={ ( value = 'any' ) => {
|
||||
const changes = { catOperator: value };
|
||||
setAttributes( changes );
|
||||
setChangedAttributes( changes );
|
||||
} }
|
||||
isCompact={ true }
|
||||
/>
|
||||
</PanelBody>
|
||||
<PanelBody
|
||||
title={ __( 'Layout', 'woo-gutenberg-products-block' ) }
|
||||
initialOpen
|
||||
>
|
||||
<GridLayoutControl
|
||||
columns={ columns }
|
||||
rows={ rows }
|
||||
alignButtons={ alignButtons }
|
||||
setAttributes={ setAttributes }
|
||||
minColumns={ getSetting( 'min_columns', 1 ) }
|
||||
maxColumns={ getSetting( 'max_columns', 6 ) }
|
||||
minRows={ getSetting( 'min_rows', 1 ) }
|
||||
maxRows={ getSetting( 'max_rows', 6 ) }
|
||||
/>
|
||||
</PanelBody>
|
||||
<PanelBody
|
||||
title={ __( 'Content', 'woo-gutenberg-products-block' ) }
|
||||
initialOpen
|
||||
>
|
||||
<GridContentControl
|
||||
settings={ contentVisibility }
|
||||
onChange={ ( value ) =>
|
||||
setAttributes( { contentVisibility: value } )
|
||||
}
|
||||
/>
|
||||
</PanelBody>
|
||||
<PanelBody
|
||||
title={ __( 'Order By', 'woo-gutenberg-products-block' ) }
|
||||
initialOpen={ false }
|
||||
>
|
||||
<ProductOrderbyControl
|
||||
setAttributes={ setAttributes }
|
||||
value={ orderby }
|
||||
/>
|
||||
</PanelBody>
|
||||
<PanelBody
|
||||
title={ __(
|
||||
'Filter by stock status',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
initialOpen={ false }
|
||||
>
|
||||
<ProductStockControl
|
||||
setAttributes={ setAttributes }
|
||||
value={ stockStatus }
|
||||
/>
|
||||
</PanelBody>
|
||||
</InspectorControls>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,42 @@
|
|||
export interface Attributes {
|
||||
columns: number;
|
||||
rows: number;
|
||||
alignButtons: boolean;
|
||||
contentVisibility: {
|
||||
image: boolean;
|
||||
title: boolean;
|
||||
price: boolean;
|
||||
rating: boolean;
|
||||
button: boolean;
|
||||
};
|
||||
categories: Array< number >;
|
||||
catOperator: string;
|
||||
isPreview: boolean;
|
||||
stockStatus: Array< string >;
|
||||
editMode: boolean;
|
||||
orderby:
|
||||
| 'date'
|
||||
| 'popularity'
|
||||
| 'price_asc'
|
||||
| 'price_desc'
|
||||
| 'rating'
|
||||
| 'title'
|
||||
| 'menu_order';
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
/**
|
||||
* The attributes for this block
|
||||
*/
|
||||
attributes: Attributes;
|
||||
/**
|
||||
* The register block name.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* A callback to update attributes
|
||||
*/
|
||||
setAttributes: ( attributes: Partial< Attributes > ) => void;
|
||||
// from withSpokenMessages
|
||||
debouncedSpeak: ( message: string ) => void;
|
||||
}
|
|
@ -13,7 +13,7 @@ import { RangeControl, ToggleControl } from '@wordpress/components';
|
|||
* @param {number} props.columns
|
||||
* @param {number} props.rows
|
||||
* @param {function(any):any} props.setAttributes Setter for block attributes.
|
||||
* @param {string} props.alignButtons
|
||||
* @param {boolean} props.alignButtons
|
||||
* @param {number} props.minColumns
|
||||
* @param {number} props.maxColumns
|
||||
* @param {number} props.minRows
|
||||
|
|
|
@ -17,6 +17,19 @@ import classNames from 'classnames';
|
|||
*/
|
||||
import './style.scss';
|
||||
|
||||
/**
|
||||
* @param {Object} props
|
||||
* @param {string=} props.categories
|
||||
* @param {boolean=} props.isLoading
|
||||
* @param {string=} props.error
|
||||
* @param {Function} props.onChange
|
||||
* @param {Function=} props.onOperatorChange
|
||||
* @param {string=} props.operator
|
||||
* @param {number[]} props.selected
|
||||
* @param {boolean=} props.isCompact
|
||||
* @param {boolean=} props.isSingle
|
||||
* @param {boolean=} props.showReviewCount
|
||||
*/
|
||||
const ProductCategoryControl = ( {
|
||||
categories,
|
||||
error,
|
||||
|
|
Loading…
Reference in New Issue