* Block JS

* Add block to library and configure build

* Update API to return image and link

* Limit category selection to 1

* Frontend

* Fix variable

* Add icon

* Rename to isSingle

* Standardize naming

* fix wrapping issue
This commit is contained in:
Mike Jolley 2019-07-09 12:38:44 +01:00 committed by GitHub
parent f97b5ce800
commit b528868ea1
12 changed files with 931 additions and 11 deletions

View File

@ -0,0 +1,397 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';
import {
AlignmentToolbar,
BlockControls,
InnerBlocks,
InspectorControls,
MediaUpload,
MediaUploadCheck,
PanelColorSettings,
withColors,
} from '@wordpress/editor';
import {
Button,
FocalPointPicker,
IconButton,
PanelBody,
Placeholder,
RangeControl,
ResizableBox,
Spinner,
ToggleControl,
Toolbar,
withSpokenMessages,
} from '@wordpress/components';
import classnames from 'classnames';
import { Component, Fragment } from '@wordpress/element';
import { compose } from '@wordpress/compose';
import { debounce, isObject } from 'lodash';
import PropTypes from 'prop-types';
import { IconFolderStar } from '../../components/icons';
/**
* Internal dependencies
*/
import ProductCategoryControl from '../../components/product-category-control';
/**
* The min-height for the block content.
*/
const MIN_HEIGHT = wc_product_block_data.min_height;
/**
* Get the src from a category object, unless null (no image).
*
* @param {object|null} category A product category object from the API.
* @return {string}
*/
function getCategoryImageSrc( category ) {
if ( isObject( category.image ) ) {
return category.image.src;
}
return '';
}
/**
* Get the attachment ID from a category object, unless null (no image).
*
* @param {object|null} category A product category object from the API.
* @return {int}
*/
function getCategoryImageID( category ) {
if ( isObject( category.image ) ) {
return category.image.id;
}
return 0;
}
/**
* Generate a style object given either a product category image from the API or URL to an image.
*
* @param {string} url An image URL.
* @return {object} A style object with a backgroundImage set (if a valid image is provided).
*/
function backgroundImageStyles( url ) {
if ( url ) {
return { backgroundImage: `url(${ url })` };
}
return {};
}
/**
* Convert the selected ratio to the correct background class.
*
* @param {number} ratio Selected opacity from 0 to 100.
* @return {string} The class name, if applicable (not used for ratio 0 or 50).
*/
function dimRatioToClass( ratio ) {
return ratio === 0 || ratio === 50 ?
null :
`has-background-dim-${ 10 * Math.round( ratio / 10 ) }`;
}
/**
* Component to handle edit mode of "Featured Category".
*/
class FeaturedCategory extends Component {
constructor() {
super( ...arguments );
this.state = {
category: false,
loaded: false,
};
this.debouncedGetCategory = debounce( this.getCategory.bind( this ), 200 );
}
componentDidMount() {
this.getCategory();
}
componentDidUpdate( prevProps ) {
if ( prevProps.attributes.categoryId !== this.props.attributes.categoryId ) {
this.debouncedGetCategory();
}
}
getCategory() {
const { categoryId } = this.props.attributes;
if ( ! categoryId ) {
// We've removed the selected product, or no product is selected yet.
this.setState( { category: false, loaded: true } );
return;
}
apiFetch( {
path: `/wc/blocks/products/categories/${ categoryId }`,
} )
.then( ( category ) => {
this.setState( { category, loaded: true } );
} )
.catch( () => {
this.setState( { category: false, loaded: true } );
} );
}
getInspectorControls() {
const {
attributes,
setAttributes,
overlayColor,
setOverlayColor,
} = this.props;
const url =
attributes.mediaSrc || getCategoryImageSrc( this.state.category );
const { focalPoint = { x: 0.5, y: 0.5 } } = attributes;
return (
<InspectorControls key="inspector">
<PanelBody title={ __( 'Content', 'woo-gutenberg-products-block' ) }>
<ToggleControl
label={ __( 'Show description', 'woo-gutenberg-products-block' ) }
checked={ attributes.showDesc }
onChange={ () => setAttributes( { showDesc: ! attributes.showDesc } ) }
/>
</PanelBody>
<PanelColorSettings
title={ __( 'Overlay', 'woo-gutenberg-products-block' ) }
colorSettings={ [
{
value: overlayColor.color,
onChange: setOverlayColor,
label: __( 'Overlay Color', 'woo-gutenberg-products-block' ),
},
] }
>
<RangeControl
label={ __( 'Background Opacity', 'woo-gutenberg-products-block' ) }
value={ attributes.dimRatio }
onChange={ ( ratio ) => setAttributes( { dimRatio: ratio } ) }
min={ 0 }
max={ 100 }
step={ 10 }
/>
{ !! FocalPointPicker && !! url &&
<FocalPointPicker
label={ __( 'Focal Point Picker' ) }
url={ url }
value={ focalPoint }
onChange={ ( value ) => setAttributes( { focalPoint: value } ) }
/>
}
</PanelColorSettings>
</InspectorControls>
);
}
renderEditMode() {
const { attributes, debouncedSpeak, setAttributes } = this.props;
const onDone = () => {
setAttributes( { editMode: false } );
debouncedSpeak(
__(
'Showing Featured Product block preview.',
'woo-gutenberg-products-block'
)
);
};
return (
<Placeholder
icon={ <IconFolderStar /> }
label={ __( 'Featured Category', 'woo-gutenberg-products-block' ) }
className="wc-block-featured-category"
>
{ __(
'Visually highlight a product category and encourage prompt action',
'woo-gutenberg-products-block'
) }
<div className="wc-block-featured-category__selection">
<ProductCategoryControl
selected={ [ attributes.categoryId ] }
onChange={ ( value = [] ) => {
const id = value[ 0 ] ? value[ 0 ].id : 0;
setAttributes( { categoryId: id, mediaId: 0, mediaSrc: '' } );
} }
multiple={ false }
/>
<Button isDefault onClick={ onDone }>
{ __( 'Done', 'woo-gutenberg-products-block' ) }
</Button>
</div>
</Placeholder>
);
}
render() {
const { attributes, isSelected, overlayColor, setAttributes } = this.props;
const {
className,
contentAlign,
dimRatio,
editMode,
focalPoint,
height,
showDesc,
} = attributes;
const { loaded, category } = this.state;
const classes = classnames(
'wc-block-featured-category',
{
'is-selected': isSelected,
'is-loading': ! category && ! loaded,
'is-not-found': ! category && loaded,
'has-background-dim': dimRatio !== 0,
},
dimRatioToClass( dimRatio ),
contentAlign !== 'center' && `has-${ contentAlign }-content`,
className,
);
const mediaId = attributes.mediaId || getCategoryImageID( category );
const mediaSrc = attributes.mediaSrc || getCategoryImageSrc( this.state.category );
const style = !! category ?
backgroundImageStyles( mediaSrc ) :
{};
if ( overlayColor.color ) {
style.backgroundColor = overlayColor.color;
}
if ( focalPoint ) {
style.backgroundPosition = `${ focalPoint.x * 100 }% ${ focalPoint.y *
100 }%`;
}
const onResizeStop = ( event, direction, elt ) => {
setAttributes( { height: parseInt( elt.style.height ) } );
};
return (
<Fragment>
<BlockControls>
<AlignmentToolbar
value={ contentAlign }
onChange={ ( nextAlign ) => {
setAttributes( { contentAlign: nextAlign } );
} }
/>
<MediaUploadCheck>
<Toolbar>
<MediaUpload
onSelect={ ( media ) => {
setAttributes( { mediaId: media.id, mediaSrc: media.url } );
} }
allowedTypes={ [ 'image' ] }
value={ mediaId }
render={ ( { open } ) => (
<IconButton
className="components-toolbar__control"
label={ __( 'Edit media' ) }
icon="format-image"
onClick={ open }
disabled={ ! this.state.category }
/>
) }
/>
</Toolbar>
</MediaUploadCheck>
</BlockControls>
{ ! attributes.editMode && this.getInspectorControls() }
{ editMode ? (
this.renderEditMode()
) : (
<Fragment>
{ !! category ? (
<ResizableBox
className={ classes }
size={ { height } }
minHeight={ MIN_HEIGHT }
enable={ { bottom: true } }
onResizeStop={ onResizeStop }
style={ style }
>
<div className="wc-block-featured-category__wrapper">
<h2
className="wc-block-featured-category__title"
dangerouslySetInnerHTML={ {
__html: category.name,
} }
/>
{ showDesc && (
<div
className="wc-block-featured-category__description"
dangerouslySetInnerHTML={ {
__html: category.description,
} }
/>
) }
<div className="wc-block-featured-category__link">
<InnerBlocks
template={ [
[
'core/button',
{
text: __(
'Shop now',
'woo-gutenberg-products-block'
),
url: category.permalink,
align: 'center',
},
],
] }
templateLock="all"
/>
</div>
</div>
</ResizableBox>
) : (
<Placeholder
className="wc-block-featured-category"
icon={ <IconFolderStar /> }
label={ __( 'Featured Category', 'woo-gutenberg-products-block' ) }
>
{ ! loaded ? (
<Spinner />
) : (
__( 'No product category is selected.', 'woo-gutenberg-products-block' )
) }
</Placeholder>
) }
</Fragment>
) }
</Fragment>
);
}
}
FeaturedCategory.propTypes = {
/**
* The attributes for this block.
*/
attributes: PropTypes.object.isRequired,
/**
* Whether this block is currently active.
*/
isSelected: PropTypes.bool.isRequired,
/**
* The register block name.
*/
name: PropTypes.string.isRequired,
/**
* A callback to update attributes.
*/
setAttributes: PropTypes.func.isRequired,
// from withColors
overlayColor: PropTypes.object,
setOverlayColor: PropTypes.func.isRequired,
// from withSpokenMessages
debouncedSpeak: PropTypes.func.isRequired,
};
export default compose( [
withColors( { overlayColor: 'background-color' } ),
withSpokenMessages,
] )( FeaturedCategory );

View File

@ -0,0 +1,18 @@
.wc-block-featured-category {
&.components-placeholder {
// Reset the background for the placeholders.
background-color: rgba( 139, 139, 150, .1 );
}
.components-resizable-box__handle {
z-index: 10;
}
.components-placeholder__label svg {
fill: currentColor;
margin-right: 1ch;
}
}
.wc-block-featured-category__selection {
width: 100%;
}

View File

@ -0,0 +1,141 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { InnerBlocks } from '@wordpress/editor';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import './style.scss';
import './editor.scss';
import Block from './block';
import { IconFolderStar } from '../../components/icons';
/**
* Register and run the "Featured Category" block.
*/
registerBlockType( 'woocommerce/featured-category', {
title: __( 'Featured Category', 'woo-gutenberg-products-block' ),
icon: {
src: <IconFolderStar />,
foreground: '#96588a',
},
category: 'woocommerce',
keywords: [ __( 'WooCommerce', 'woo-gutenberg-products-block' ) ],
description: __(
'Visually highlight a product category and encourage prompt action.',
'woo-gutenberg-products-block'
),
supports: {
align: [ 'wide', 'full' ],
},
attributes: {
/**
* Alignment of content inside block.
*/
contentAlign: {
type: 'string',
default: 'center',
},
/**
* Percentage opacity of overlay.
*/
dimRatio: {
type: 'number',
default: 50,
},
/**
* Toggle for edit mode in the block preview.
*/
editMode: {
type: 'boolean',
default: true,
},
/**
* Focus point for the background image
*/
focalPoint: {
type: 'object',
},
/**
* A fixed height for the block.
*/
height: {
type: 'number',
default: wc_product_block_data.default_height,
},
/**
* ID for a custom image, overriding the product's featured image.
*/
mediaId: {
type: 'number',
default: 0,
},
/**
* URL for a custom image, overriding the product's featured image.
*/
mediaSrc: {
type: 'string',
default: '',
},
/**
* The overlay color, from the color list.
*/
overlayColor: {
type: 'string',
},
/**
* The overlay color, if a custom color value.
*/
customOverlayColor: {
type: 'string',
},
/**
* Text for the category link.
*/
linkText: {
type: 'string',
default: __( 'Shop now', 'woo-gutenberg-products-block' ),
},
/**
* The category ID to display.
*/
categoryId: {
type: 'number',
},
/**
* Show the category description.
*/
showDesc: {
type: 'boolean',
default: true,
},
},
/**
* Renders and manages the block.
*/
edit( props ) {
return <Block { ...props } />;
},
/**
* Block content is rendered in PHP, not via save function.
*/
save() {
return <InnerBlocks.Content />;
},
} );

View File

@ -0,0 +1,130 @@
.wc-block-featured-category {
position: relative;
background-color: $black;
background-size: cover;
background-position: center center;
width: 100%;
margin: 0 0 1.5em 0;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
align-content: center;
.wc-block-featured-category__wrapper {
overflow: hidden;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
align-content: center;
}
&.has-left-content {
justify-content: flex-start;
.wc-block-featured-category__title,
.wc-block-featured-category__description,
.wc-block-featured-category__price {
margin-left: 0;
text-align: left;
}
}
&.has-right-content {
justify-content: flex-end;
.wc-block-featured-category__title,
.wc-block-featured-category__description,
.wc-block-featured-category__price {
margin-right: 0;
text-align: right;
}
}
.wc-block-featured-category__title,
.wc-block-featured-category__description,
.wc-block-featured-category__price {
color: $white;
line-height: 1.25;
margin-bottom: 0;
text-align: center;
a,
a:hover,
a:focus,
a:active {
color: $white;
}
}
.wc-block-featured-category__title,
.wc-block-featured-category__description,
.wc-block-featured-category__price,
.wc-block-featured-category__link {
width: 100%;
padding: 0 48px 16px 48px;
z-index: 1;
}
.wc-block-featured-category__title {
margin-top: 0;
&:before {
display: none;
}
}
.wc-block-featured-category__description {
p {
margin: 0;
}
}
&.has-background-dim::before {
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: inherit;
opacity: 0.5;
z-index: 1;
}
@for $i from 1 through 10 {
&.has-background-dim.has-background-dim-#{ $i * 10 }::before {
opacity: $i * 0.1;
}
}
// Apply max-width to floated items that have no intrinsic width
&.alignleft,
&.alignright {
max-width: $content-width / 2;
width: 100%;
}
// Using flexbox without an assigned height property breaks vertical center alignment in IE11.
// Appending an empty ::after element tricks IE11 into giving the cover image an implicit height, which sidesteps this issue.
&::after {
display: block;
content: "";
font-size: 0;
min-height: inherit;
// IE doesn't support flex so omit that.
@supports (position: sticky) {
content: none;
}
}
// Aligned cover blocks should not use our global alignment rules
&.aligncenter,
&.alignleft,
&.alignright {
display: flex;
}
}

View File

@ -0,0 +1,20 @@
/**
* External dependencies
*/
import { Icon } from '@wordpress/components';
export default () => (
<Icon
icon={
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path d="M22 7.5H2c-1.2 0-2.1 1-2 2.2l.7 11.1c.1 1.1 1 1.9 2 1.9h18.5c1.1 0 2-.8 2-1.9L24 9.6c.1-1.1-.9-2.1-2-2.1zM13.1 2.8v-.4c0-1.1-.9-2-2-2H4.8c-1.1 0-2 .9-2 2v3.4h18.4v-1c0-1.1-.9-2-2-2h-6.1z" />
<path fill="#fff" d="M14.4 18.7L12 17.4l-2.4 1.3.5-2.6-1.9-1.9 2.6-.4 1.2-2.4 1.2 2.4 2.6.4-1.9 1.9z" />
</svg>
}
/>
);

View File

@ -2,6 +2,7 @@
export { default as IconCheckChecked } from './checkbox-checked';
export { default as IconCheckUnchecked } from './checkbox-unchecked';
export { default as IconFolder } from './folder';
export { default as IconFolderStar } from './folder-star';
export { default as IconNewReleases } from './new-releases';
export { default as IconRadioSelected } from './radio-selected';
export { default as IconRadioUnselected } from './radio-unselected';

View File

@ -74,7 +74,7 @@ class ProductCategoryControl extends Component {
render() {
const { list, loading } = this.state;
const { onChange, onOperatorChange, operator, selected } = this.props;
const { onChange, onOperatorChange, operator, selected, isSingle } = this.props;
const messages = {
clear: __( 'Clear all product categories', 'woo-gutenberg-products-block' ),
@ -114,6 +114,7 @@ class ProductCategoryControl extends Component {
renderItem={ this.renderItem }
messages={ messages }
isHierarchical
isSingle={ isSingle }
/>
{ ( !! onOperatorChange ) && (
<div className={ selected.length < 2 ? 'screen-reader-text' : '' }>
@ -158,10 +159,15 @@ ProductCategoryControl.propTypes = {
* The list of currently selected category IDs.
*/
selected: PropTypes.array.isRequired,
/**
* Allow only a single selection. Defaults to false.
*/
isSingle: PropTypes.bool,
};
ProductCategoryControl.defaultProps = {
operator: 'any',
isSingle: false,
};
export default ProductCategoryControl;

View File

@ -47,6 +47,7 @@ class Assets {
self::register_script( 'wc-product-top-rated', plugins_url( 'build/product-top-rated.js', __DIR__ ), array( 'wc-vendors', 'wc-packages', 'wc-blocks' ) );
self::register_script( 'wc-products-by-attribute', plugins_url( 'build/products-by-attribute.js', __DIR__ ), array( 'wc-vendors', 'wc-packages', 'wc-blocks' ) );
self::register_script( 'wc-featured-product', plugins_url( 'build/featured-product.js', __DIR__ ), array( 'wc-vendors', 'wc-packages', 'wc-blocks' ) );
self::register_script( 'wc-featured-category', plugins_url( 'build/featured-category.js', __DIR__ ), array( 'wc-vendors', 'wc-packages', 'wc-blocks' ) );
self::register_script( 'wc-product-categories', plugins_url( 'build/product-categories.js', __DIR__ ), array( 'wc-vendors', 'wc-packages', 'wc-blocks' ) );
}

View File

@ -0,0 +1,175 @@
<?php
/**
* Featured category block.
*
* @package WooCommerce\Blocks
*/
namespace Automattic\WooCommerce\Blocks\BlockTypes;
defined( 'ABSPATH' ) || exit;
/**
* FeaturedCategory class.
*/
class FeaturedCategory extends AbstractDynamicBlock {
/**
* Block name.
*
* @var string
*/
protected $block_name = 'featured-category';
/**
* Default attribute values, should match what's set in JS `registerBlockType`.
*
* @var array
*/
protected $defaults = array(
'align' => 'none',
'contentAlign' => 'center',
'dimRatio' => 50,
'focalPoint' => false,
'height' => false,
'mediaId' => 0,
'mediaSrc' => '',
'showDesc' => true,
);
/**
* Render the Featured Category block.
*
* @param array $attributes Block attributes. Default empty array.
* @param string $content Block content. Default empty string.
* @return string Rendered block type output.
*/
public function render( $attributes = array(), $content = '' ) {
$id = isset( $attributes['categoryId'] ) ? (int) $attributes['categoryId'] : 0;
$category = get_term( $id, 'product_cat' );
if ( ! $category ) {
return '';
}
$attributes = wp_parse_args( $attributes, $this->defaults );
if ( ! $attributes['height'] ) {
$attributes['height'] = wc_get_theme_support( 'featured_block::default_height', 500 );
}
$title = sprintf(
'<h2 class="wc-block-featured-category__title">%s</h2>',
wp_kses_post( $category->name )
);
$desc_str = sprintf(
'<div class="wc-block-featured-category__description">%s</div>',
wc_format_content( $category->description )
);
$output = sprintf( '<div class="%1$s" style="%2$s">', $this->get_classes( $attributes ), $this->get_styles( $attributes, $category ) );
$output .= $title;
if ( $attributes['showDesc'] ) {
$output .= $desc_str;
}
$output .= '<div class="wc-block-featured-category__link">' . $content . '</div>';
$output .= '</div>';
return $output;
}
/**
* Get the styles for the wrapper element (background image, color).
*
* @param array $attributes Block attributes. Default empty array.
* @param \WP_Term $category Term object.
* @return string
*/
public function get_styles( $attributes, $category ) {
$style = '';
$image_size = 'large';
if ( 'none' !== $attributes['align'] || $attributes['height'] > 800 ) {
$image_size = 'full';
}
if ( $attributes['mediaId'] ) {
$image = wp_get_attachment_image_url( $attributes['mediaId'], $image_size );
} else {
$image = $this->get_image( $category, $image_size );
}
if ( ! empty( $image ) ) {
$style .= sprintf( 'background-image:url(%s);', esc_url( $image ) );
}
if ( isset( $attributes['customOverlayColor'] ) ) {
$style .= sprintf( 'background-color:%s;', esc_attr( $attributes['customOverlayColor'] ) );
}
if ( isset( $attributes['height'] ) ) {
$style .= sprintf( 'min-height:%dpx;', intval( $attributes['height'] ) );
}
if ( is_array( $attributes['focalPoint'] ) && 2 === count( $attributes['focalPoint'] ) ) {
$style .= sprintf(
'background-position: %s%% %s%%',
$attributes['focalPoint']['x'] * 100,
$attributes['focalPoint']['y'] * 100
);
}
return $style;
}
/**
* Get class names for the block container.
*
* @param array $attributes Block attributes. Default empty array.
* @return string
*/
public function get_classes( $attributes ) {
$classes = array( 'wc-block-' . $this->block_name );
if ( isset( $attributes['align'] ) ) {
$classes[] = "align{$attributes['align']}";
}
if ( isset( $attributes['dimRatio'] ) && ( 0 !== $attributes['dimRatio'] ) ) {
$classes[] = 'has-background-dim';
if ( 50 !== $attributes['dimRatio'] ) {
$classes[] = 'has-background-dim-' . 10 * round( $attributes['dimRatio'] / 10 );
}
}
if ( isset( $attributes['contentAlign'] ) && 'center' !== $attributes['contentAlign'] ) {
$classes[] = "has-{$attributes['contentAlign']}-content";
}
if ( isset( $attributes['overlayColor'] ) ) {
$classes[] = "has-{$attributes['overlayColor']}-background-color";
}
if ( isset( $attributes['className'] ) ) {
$classes[] = $attributes['className'];
}
return implode( $classes, ' ' );
}
/**
* Returns the main product image URL.
*
* @param \WP_Term $category Term object.
* @param string $size Image size, defaults to 'full'.
* @return string
*/
public function get_image( $category, $size = 'full' ) {
$image = '';
$image_id = get_term_meta( $category->term_id, 'thumbnail_id', true );
if ( $image_id ) {
$image = wp_get_attachment_image_url( $image_id, $size );
}
return $image;
}
}

View File

@ -26,6 +26,7 @@ class Library {
*/
public static function register_blocks() {
$blocks = [
'FeaturedCategory',
'FeaturedProduct',
'HandpickedProducts',
'ProductBestSellers',

View File

@ -107,13 +107,33 @@ class ProductCategories extends WC_REST_Product_Categories_Controller {
*/
public function prepare_item_for_response( $item, $request ) {
$data = array(
'id' => (int) $item->term_id,
'name' => $item->name,
'slug' => $item->slug,
'parent' => (int) $item->parent,
'count' => (int) $item->count,
'id' => (int) $item->term_id,
'name' => $item->name,
'slug' => $item->slug,
'parent' => (int) $item->parent,
'count' => (int) $item->count,
'description' => $item->description,
'image' => null,
'permalink' => get_term_link( $item->term_id, 'product_cat' ),
);
$image_id = get_term_meta( $item->term_id, 'thumbnail_id', true );
if ( $image_id ) {
$attachment = get_post( $image_id );
$data['image'] = array(
'id' => (int) $image_id,
'date_created' => wc_rest_prepare_date_response( $attachment->post_date ),
'date_created_gmt' => wc_rest_prepare_date_response( $attachment->post_date_gmt ),
'date_modified' => wc_rest_prepare_date_response( $attachment->post_modified ),
'date_modified_gmt' => wc_rest_prepare_date_response( $attachment->post_modified_gmt ),
'src' => wp_get_attachment_url( $image_id ),
'name' => get_the_title( $attachment ),
'alt' => get_post_meta( $image_id, '_wp_attachment_image_alt', true ),
);
}
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
$data = $this->add_additional_fields_to_object( $data, $request );
$data = $this->filter_response_by_context( $data, $context );
@ -138,11 +158,20 @@ class ProductCategories extends WC_REST_Product_Categories_Controller {
'properties' => array(),
);
$schema['properties']['id'] = $raw_schema['properties']['id'];
$schema['properties']['name'] = $raw_schema['properties']['name'];
$schema['properties']['slug'] = $raw_schema['properties']['slug'];
$schema['properties']['parent'] = $raw_schema['properties']['parent'];
$schema['properties']['count'] = $raw_schema['properties']['count'];
$schema['properties']['id'] = $raw_schema['properties']['id'];
$schema['properties']['name'] = $raw_schema['properties']['name'];
$schema['properties']['slug'] = $raw_schema['properties']['slug'];
$schema['properties']['parent'] = $raw_schema['properties']['parent'];
$schema['properties']['count'] = $raw_schema['properties']['count'];
$schema['properties']['description'] = $raw_schema['properties']['description'];
$schema['properties']['image'] = $raw_schema['properties']['image'];
$schema['properties']['permalink'] = array(
'description' => __( 'Category URL.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'format' => 'uri',
'context' => array( 'view', 'edit', 'embed' ),
'readonly' => true,
);
return $this->add_additional_fields_schema( $schema );
}

View File

@ -38,6 +38,7 @@ const GutenbergBlocksConfig = {
'product-top-rated': './assets/js/blocks/product-top-rated/index.js',
'products-by-attribute': './assets/js/blocks/products-by-attribute/index.js',
'featured-product': './assets/js/blocks/featured-product/index.js',
'featured-category': './assets/js/blocks/featured-category/index.js',
},
output: {
path: path.resolve( __dirname, './build/' ),