WooCommerce Gutenberg Products Block - Version 2.0.0

Version Description

  • 2019-04-18 =

  • BREAKING: Requires WordPress 5.0+, WooCommerce 3.6+

  • BREAKING: Remove the legacy block entirely

  • BREAKING: Remove the wc-pb/v3/* endpoints in favor of new core wc-blocks/v1/* endpoints

  • Feature: Add content visibility settings to show/hide title, price, rating, button

  • Feature: Add transforms between basic product grid blocks

  • Fix: Add product rating display to preview, to better match front end

  • Fix: Product titles render HTML correctly in preview

  • Fix: Icons are now aligned correctly in placeholders

  • Fix: Grid block preview column width now matches the front-end

  • Fix: Webpack now builds using a custom jsonp callback, fixing possible collisions with other projects

  • API: Change namespace, endpoints now accessed at /wc-blocks/v1/*

  • API: Add catalog_visibility parameter for fetching products

  • API: Update structure of attribute term endpoint to return attribute.slug, attribute.name etc

  • API: Update parameters to use full names, category_operator, attribute_operator

  • Components: Move SearchListControl to @woocommerce/components library

  • Components: Added new control component GridContentControl to manage content visibility

  • Build: Reorganize CSS into one file for editor preview, and one file for front-end styles

  • Build: Move registration code to a new class

  • Build: Update packages

Download this release

Release Info

Developer ryelle
Plugin Icon 128x128 WooCommerce Gutenberg Products Block
Version 2.0.0
Comparing to
See all releases

Code changes from version 1.4.0 to 2.0.0

Files changed (106) hide show
  1. assets/css/abstracts/_mixins.scss +0 -12
  2. assets/css/{products-grid.scss → editor.scss} +21 -12
  3. assets/css/style.scss +31 -0
  4. assets/js/blocks/featured-product/block.js +7 -4
  5. assets/js/blocks/featured-product/editor.scss +10 -0
  6. assets/js/blocks/featured-product/index.js +1 -0
  7. assets/js/blocks/featured-product/style.scss +0 -9
  8. assets/js/blocks/handpicked-products/block.js +28 -17
  9. assets/js/blocks/handpicked-products/{style.scss → editor.scss} +0 -0
  10. assets/js/blocks/handpicked-products/index.js +26 -2
  11. assets/js/blocks/product-best-sellers/block.js +34 -20
  12. assets/js/blocks/product-best-sellers/index.js +27 -3
  13. assets/js/blocks/product-category/block.js +33 -17
  14. assets/js/blocks/product-category/{style.scss → editor.scss} +0 -0
  15. assets/js/blocks/product-category/index.js +28 -4
  16. assets/js/blocks/product-new/block.js +40 -20
  17. assets/js/blocks/product-new/index.js +27 -3
  18. assets/js/blocks/product-on-sale/block.js +35 -17
  19. assets/js/blocks/product-on-sale/index.js +27 -3
  20. assets/js/blocks/product-top-rated/block.js +40 -20
  21. assets/js/blocks/product-top-rated/index.js +27 -3
  22. assets/js/blocks/products-by-attribute/block.js +34 -16
  23. assets/js/blocks/products-by-attribute/{style.scss → editor.scss} +0 -0
  24. assets/js/blocks/products-by-attribute/index.js +26 -2
  25. assets/js/components/grid-content-control/index.js +81 -0
  26. assets/js/components/icons/new-releases.js +1 -0
  27. assets/js/components/icons/widgets.js +1 -0
  28. assets/js/components/product-attribute-control/index.js +4 -5
  29. assets/js/components/product-category-control/index.js +2 -3
  30. assets/js/components/product-control/index.js +3 -6
  31. assets/js/components/product-preview/index.js +40 -5
  32. assets/js/components/product-preview/style.scss +72 -2
  33. assets/js/components/products-control/index.js +6 -6
  34. assets/js/components/search-list-control/hierarchy.js +0 -48
  35. assets/js/components/search-list-control/index.js +0 -312
  36. assets/js/components/search-list-control/item.js +0 -132
  37. assets/js/components/search-list-control/style.scss +0 -208
  38. assets/js/legacy/products-block.jsx +0 -1030
  39. assets/js/legacy/products-block.scss +0 -827
  40. assets/js/legacy/views/attribute-select.jsx +0 -459
  41. assets/js/legacy/views/category-select.jsx +0 -327
  42. assets/js/legacy/views/specific-select.jsx +0 -497
  43. assets/js/utils/get-query.js +3 -2
  44. assets/js/utils/shared-attributes.js +22 -0
  45. assets/js/vendor/react-transition-group.js +0 -2517
  46. includes/blocks/class-wc-block-featured-product.php → assets/php/class-wgpb-block-featured-product.php +5 -3
  47. assets/php/class-wgpb-block-library.php +296 -0
  48. bin/generate-translation-json.js +71 -0
  49. bin/merge-extract-files-webpack-plugin.js +50 -0
  50. build/editor.css +9 -0
  51. build/featured-product.css +0 -2
  52. build/featured-product.js +69 -1
  53. build/handpicked-products.css +0 -3
  54. build/handpicked-products.js +69 -1
  55. build/product-best-sellers.css +0 -3
  56. build/product-best-sellers.js +69 -1
  57. build/product-category.css +0 -4
  58. build/product-category.js +69 -1
  59. build/product-new.css +0 -3
  60. build/product-new.js +69 -1
  61. build/product-on-sale.css +0 -3
  62. build/product-on-sale.js +69 -1
  63. build/product-top-rated.css +0 -3
  64. build/product-top-rated.js +69 -1
  65. build/products-attribute.css +0 -4
  66. build/products-attribute.js +69 -1
  67. build/products-block.css +0 -1
  68. build/products-block.js +0 -1
  69. build/products-grid.css +0 -1
  70. build/products-grid.js +0 -1
  71. build/style.css +2 -0
  72. build/vendors.css +0 -1
  73. build/vendors.js +7 -69
  74. includes/class-wgpb-product-attribute-terms-controller.php +0 -169
  75. includes/class-wgpb-product-attributes-controller.php +0 -186
  76. includes/class-wgpb-product-categories-controller.php +0 -149
  77. includes/class-wgpb-products-controller.php +0 -359
  78. languages/woo-gutenberg-products-block-da_DK-15e4e3df51632e1ca1000ea405c14b95.json +1 -1
  79. languages/woo-gutenberg-products-block-da_DK-2674aecf6bb0c5be4f2454ea842be5f6.json +1 -1
  80. languages/woo-gutenberg-products-block-da_DK-3f1f6467892f2a342fddcfecdceeb2c3.json +1 -1
  81. languages/woo-gutenberg-products-block-da_DK-6e3d95eaf70621f822b845d5bcb2b3cf.json +1 -1
  82. languages/woo-gutenberg-products-block-da_DK-84d4253d132f6c5c53fe25d03f5e9200.json +1 -1
  83. languages/woo-gutenberg-products-block-da_DK-91f7cd8fa1b168630fc2b3a54cbe410f.json +1 -1
  84. languages/woo-gutenberg-products-block-da_DK-a4caf5c468bc583ff7fc9a943d3dc348.json +1 -1
  85. languages/woo-gutenberg-products-block-da_DK-d748c5738d57b7518bef14f74456090e.json +1 -1
  86. languages/woo-gutenberg-products-block-da_DK-f9f323a7db976a4eafb44c35cc6d5e32.json +1 -1
  87. languages/woo-gutenberg-products-block-es_ES-15e4e3df51632e1ca1000ea405c14b95.json +1 -1
  88. languages/woo-gutenberg-products-block-es_ES-2674aecf6bb0c5be4f2454ea842be5f6.json +1 -1
  89. languages/woo-gutenberg-products-block-es_ES-3f1f6467892f2a342fddcfecdceeb2c3.json +1 -1
  90. languages/woo-gutenberg-products-block-es_ES-6e3d95eaf70621f822b845d5bcb2b3cf.json +1 -1
  91. languages/woo-gutenberg-products-block-es_ES-84d4253d132f6c5c53fe25d03f5e9200.json +1 -1
  92. languages/woo-gutenberg-products-block-es_ES-91f7cd8fa1b168630fc2b3a54cbe410f.json +1 -1
  93. languages/woo-gutenberg-products-block-es_ES-a4caf5c468bc583ff7fc9a943d3dc348.json +1 -1
  94. languages/woo-gutenberg-products-block-es_ES-d748c5738d57b7518bef14f74456090e.json +1 -1
  95. languages/woo-gutenberg-products-block-es_ES-f9f323a7db976a4eafb44c35cc6d5e32.json +1 -1
  96. languages/woo-gutenberg-products-block-ru_RU-15e4e3df51632e1ca1000ea405c14b95.json +1 -1
  97. languages/woo-gutenberg-products-block-ru_RU-2674aecf6bb0c5be4f2454ea842be5f6.json +1 -1
  98. languages/woo-gutenberg-products-block-ru_RU-3f1f6467892f2a342fddcfecdceeb2c3.json +1 -1
  99. languages/woo-gutenberg-products-block-ru_RU-6e3d95eaf70621f822b845d5bcb2b3cf.json +1 -1
  100. languages/woo-gutenberg-products-block-ru_RU-84d4253d132f6c5c53fe25d03f5e9200.json +1 -1
  101. languages/woo-gutenberg-products-block-ru_RU-91f7cd8fa1b168630fc2b3a54cbe410f.json +1 -1
  102. languages/woo-gutenberg-products-block-ru_RU-a4caf5c468bc583ff7fc9a943d3dc348.json +1 -1
  103. languages/woo-gutenberg-products-block-ru_RU-d748c5738d57b7518bef14f74456090e.json +1 -1
  104. languages/woo-gutenberg-products-block-ru_RU-f9f323a7db976a4eafb44c35cc6d5e32.json +1 -1
  105. readme.txt +28 -14
  106. woocommerce-gutenberg-products-block.php +14 -515
assets/css/abstracts/_mixins.scss CHANGED
@@ -57,15 +57,3 @@
57
margin: unset;
58
overflow: hidden;
59
}
60
-
61
- // Create a string-repeat function
62
- @function repeat($character, $n) {
63
- @if $n == 0 {
64
- @return "";
65
- }
66
- $c: "";
67
- @for $i from 1 through $n {
68
- $c: $c + $character;
69
- }
70
- @return $c;
71
- }
57
margin: unset;
58
overflow: hidden;
59
}
assets/css/{products-grid.scss → editor.scss} RENAMED
@@ -6,35 +6,44 @@
6
overflow: hidden;
7
}
8
9
.wc-block-products-grid {
10
overflow: hidden;
11
display: flex;
12
flex-wrap: wrap;
13
justify-content: flex-start;
14
15
&.is-loading,
16
- &.is-not-found,
17
- &.cols-1 {
18
display: block;
19
}
20
21
- .wc-product-preview {
22
- flex: 1;
23
- padding: $gap/2;
24
}
25
26
@for $i from 2 to 7 {
27
&.cols-#{$i} .wc-product-preview {
28
- max-width: calc(#{ 100% / $i });
29
- min-width: calc(#{ 100% / $i });
30
- flex: 1;
31
}
32
}
33
34
- &.components-placeholder {
35
- padding: 2em 1em;
36
- }
37
-
38
// Styles for "resuable block" preview.
39
.editor-block-preview & {
40
min-width: 5em;
6
overflow: hidden;
7
}
8
9
+ // Align the block icons in edit mode
10
+ .components-placeholder__label .gridicon,
11
+ .components-placeholder__label .material-icon {
12
+ margin-right: 1ch;
13
+ fill: currentColor;
14
+ }
15
+
16
.wc-block-products-grid {
17
overflow: hidden;
18
display: flex;
19
flex-wrap: wrap;
20
justify-content: flex-start;
21
22
+ &.components-placeholder {
23
+ padding: 2em 1em;
24
+ }
25
+
26
&.is-loading,
27
+ &.is-not-found {
28
display: block;
29
}
30
31
+ &.cols-1 {
32
+ display: block;
33
+
34
+ .wc-product-preview {
35
+ margin-left: auto;
36
+ margin-right: auto;
37
+ }
38
}
39
40
@for $i from 2 to 7 {
41
&.cols-#{$i} .wc-product-preview {
42
+ flex: 1 0 calc(#{ 100% / $i });
43
+ max-width: 100% / $i !important;
44
}
45
}
46
47
// Styles for "resuable block" preview.
48
.editor-block-preview & {
49
min-width: 5em;
assets/css/style.scss ADDED
@@ -0,0 +1,31 @@
1
+ .wp-block-woocommerce-handpicked-products,
2
+ .wp-block-woocommerce-product-best-sellers,
3
+ .wp-block-woocommerce-product-category,
4
+ .wp-block-woocommerce-product-new,
5
+ .wp-block-woocommerce-product-on-sale,
6
+ .wp-block-woocommerce-product-top-rated,
7
+ .wp-block-woocommerce-products-by-attribute {
8
+ &.is-hidden-title {
9
+ .woocommerce-loop-product__title {
10
+ display: none !important;
11
+ }
12
+ }
13
+
14
+ &.is-hidden-price {
15
+ .price {
16
+ display: none !important;
17
+ }
18
+ }
19
+
20
+ &.is-hidden-rating {
21
+ .star-rating {
22
+ display: none;
23
+ }
24
+ }
25
+
26
+ &.is-hidden-button {
27
+ .button[data-product_sku] {
28
+ display: none !important;
29
+ }
30
+ }
31
+ }
assets/js/blocks/featured-product/block.js CHANGED
@@ -106,7 +106,7 @@ class FeaturedProduct extends Component {
106
return;
107
}
108
apiFetch( {
109
- path: `/wc-pb/v3/products/${ productId }`,
110
} )
111
.then( ( product ) => {
112
this.setState( { product, loaded: true } );
@@ -278,9 +278,12 @@ class FeaturedProduct extends Component {
278
style={ style }
279
>
280
<div className="wc-block-featured-product__wrapper">
281
- <h2 className="wc-block-featured-product__title">
282
- { product.name }
283
- </h2>
284
{ showDesc && (
285
<div
286
className="wc-block-featured-product__description"
106
return;
107
}
108
apiFetch( {
109
+ path: `/wc-blocks/v1/products/${ productId }`,
110
} )
111
.then( ( product ) => {
112
this.setState( { product, loaded: true } );
278
style={ style }
279
>
280
<div className="wc-block-featured-product__wrapper">
281
+ <h2
282
+ className="wc-block-featured-product__title"
283
+ dangerouslySetInnerHTML={ {
284
+ __html: product.name,
285
+ } }
286
+ />
287
{ showDesc && (
288
<div
289
className="wc-block-featured-product__description"
assets/js/blocks/featured-product/editor.scss ADDED
@@ -0,0 +1,10 @@
1
+ .wc-block-featured-product {
2
+ &.components-placeholder {
3
+ // Reset the background for the placeholders.
4
+ background-color: rgba( 139, 139, 150, .1 );
5
+ }
6
+
7
+ .components-resizable-box__handle {
8
+ z-index: 10;
9
+ }
10
+ }
assets/js/blocks/featured-product/index.js CHANGED
@@ -9,6 +9,7 @@ import { registerBlockType } from '@wordpress/blocks';
9
* Internal dependencies
10
*/
11
import './style.scss';
12
import Block from './block';
13
14
/**
9
* Internal dependencies
10
*/
11
import './style.scss';
12
+ import './editor.scss';
13
import Block from './block';
14
15
/**
assets/js/blocks/featured-product/style.scss CHANGED
@@ -21,15 +21,6 @@
21
align-content: center;
22
}
23
24
- &.components-placeholder {
25
- // Reset the background for the placeholders.
26
- background-color: rgba( 139, 139, 150, .1 );
27
- }
28
-
29
- .components-resizable-box__handle {
30
- z-index: 10;
31
- }
32
-
33
&.has-left-content {
34
justify-content: flex-start;
35
21
align-content: center;
22
}
23
24
&.has-left-content {
25
justify-content: flex-start;
26
assets/js/blocks/handpicked-products/block.js CHANGED
@@ -14,6 +14,7 @@ import {
14
Toolbar,
15
withSpokenMessages,
16
} from '@wordpress/components';
17
import { Component, Fragment } from '@wordpress/element';
18
import { debounce } from 'lodash';
19
import PropTypes from 'prop-types';
@@ -22,6 +23,7 @@ import PropTypes from 'prop-types';
22
* Internal dependencies
23
*/
24
import getQuery from '../../utils/get-query';
25
import { IconWidgets } from '../../components/icons';
26
import ProductsControl from '../../components/products-control';
27
import ProductOrderbyControl from '../../components/product-orderby-control';
@@ -62,7 +64,7 @@ class ProductsBlock extends Component {
62
}
63
apiFetch( {
64
path: addQueryArgs(
65
- '/wc-pb/v3/products',
66
getQuery( this.props.attributes, this.props.name )
67
),
68
} )
@@ -76,7 +78,7 @@ class ProductsBlock extends Component {
76
77
getInspectorControls() {
78
const { attributes, setAttributes } = this.props;
79
- const { columns, orderby } = attributes;
80
81
return (
82
<InspectorControls key="inspector">
@@ -92,6 +94,15 @@ class ProductsBlock extends Component {
92
max={ wc_product_block_data.max_columns }
93
/>
94
</PanelBody>
95
<PanelBody
96
title={ __( 'Order By', 'woo-gutenberg-products-block' ) }
97
initialOpen={ false }
@@ -157,20 +168,20 @@ class ProductsBlock extends Component {
157
158
render() {
159
const { setAttributes } = this.props;
160
- const { columns, editMode } = this.props.attributes;
161
- const { loaded, products } = this.state;
162
- const hasSelectedProducts = products && products.length;
163
- const classes = [ 'wc-block-products-grid', 'wc-block-handpicked-products' ];
164
- if ( columns ) {
165
- classes.push( `cols-${ columns }` );
166
- }
167
- if ( ! hasSelectedProducts ) {
168
- if ( ! loaded ) {
169
- classes.push( 'is-loading' );
170
- } else {
171
- classes.push( 'is-not-found' );
172
- }
173
- }
174
175
return (
176
<Fragment>
@@ -190,7 +201,7 @@ class ProductsBlock extends Component {
190
{ editMode ? (
191
this.renderEditMode()
192
) : (
193
- <div className={ classes.join( ' ' ) }>
194
{ hasSelectedProducts ? (
195
products.map( ( product ) => (
196
<ProductPreview product={ product } key={ product.id } />
14
Toolbar,
15
withSpokenMessages,
16
} from '@wordpress/components';
17
+ import classnames from 'classnames';
18
import { Component, Fragment } from '@wordpress/element';
19
import { debounce } from 'lodash';
20
import PropTypes from 'prop-types';
23
* Internal dependencies
24
*/
25
import getQuery from '../../utils/get-query';
26
+ import GridContentControl from '../../components/grid-content-control';
27
import { IconWidgets } from '../../components/icons';
28
import ProductsControl from '../../components/products-control';
29
import ProductOrderbyControl from '../../components/product-orderby-control';
64
}
65
apiFetch( {
66
path: addQueryArgs(
67
+ '/wc-blocks/v1/products',
68
getQuery( this.props.attributes, this.props.name )
69
),
70
} )
78
79
getInspectorControls() {
80
const { attributes, setAttributes } = this.props;
81
+ const { columns, contentVisibility, orderby } = attributes;
82
83
return (
84
<InspectorControls key="inspector">
94
max={ wc_product_block_data.max_columns }
95
/>
96
</PanelBody>
97
+ <PanelBody
98
+ title={ __( 'Content', 'woo-gutenberg-products-block' ) }
99
+ initialOpen
100
+ >
101
+ <GridContentControl
102
+ settings={ contentVisibility }
103
+ onChange={ ( value ) => setAttributes( { contentVisibility: value } ) }
104
+ />
105
+ </PanelBody>
106
<PanelBody
107
title={ __( 'Order By', 'woo-gutenberg-products-block' ) }
108
initialOpen={ false }
168
169
render() {
170
const { setAttributes } = this.props;
171
+ const { columns, contentVisibility, editMode } = this.props.attributes;
172
+ const { loaded, products = [] } = this.state;
173
+ const hasSelectedProducts = products.length > 0;
174
+ const classes = classnames( {
175
+ 'wc-block-products-grid': true,
176
+ 'wc-block-handpicked-products': true,
177
+ [ `cols-${ columns }` ]: columns,
178
+ 'is-loading': ! loaded,
179
+ 'is-not-found': loaded && ! hasSelectedProducts,
180
+ 'is-hidden-title': ! contentVisibility.title,
181
+ 'is-hidden-price': ! contentVisibility.price,
182
+ 'is-hidden-rating': ! contentVisibility.rating,
183
+ 'is-hidden-button': ! contentVisibility.button,
184
+ } );
185
186
return (
187
<Fragment>
201
{ editMode ? (
202
this.renderEditMode()
203
) : (
204
+ <div className={ classes }>
205
{ hasSelectedProducts ? (
206
products.map( ( product ) => (
207
<ProductPreview product={ product } key={ product.id } />
assets/js/blocks/handpicked-products/{style.scss → editor.scss} RENAMED
File without changes
assets/js/blocks/handpicked-products/index.js CHANGED
@@ -2,13 +2,14 @@
2
* External dependencies
3
*/
4
import { __ } from '@wordpress/i18n';
5
import { registerBlockType } from '@wordpress/blocks';
6
import { RawHTML } from '@wordpress/element';
7
8
/**
9
* Internal dependencies
10
*/
11
- import './style.scss';
12
import Block from './block';
13
import getShortcode from '../../utils/get-shortcode';
14
import { IconWidgets } from '../../components/icons';
@@ -49,6 +50,19 @@ registerBlockType( 'woocommerce/handpicked-products', {
49
default: true,
50
},
51
52
/**
53
* How to order the products: 'date', 'popularity', 'price_asc', 'price_desc' 'rating', 'title'.
54
*/
@@ -81,9 +95,19 @@ registerBlockType( 'woocommerce/handpicked-products', {
81
save( props ) {
82
const {
83
align,
84
} = props.attributes; /* eslint-disable-line react/prop-types */
85
return (
86
- <RawHTML className={ align ? `align${ align }` : '' }>
87
{ getShortcode( props, 'woocommerce/handpicked-products' ) }
88
</RawHTML>
89
);
2
* External dependencies
3
*/
4
import { __ } from '@wordpress/i18n';
5
+ import classnames from 'classnames';
6
import { registerBlockType } from '@wordpress/blocks';
7
import { RawHTML } from '@wordpress/element';
8
9
/**
10
* Internal dependencies
11
*/
12
+ import './editor.scss';
13
import Block from './block';
14
import getShortcode from '../../utils/get-shortcode';
15
import { IconWidgets } from '../../components/icons';
50
default: true,
51
},
52
53
+ /**
54
+ * Content visibility setting
55
+ */
56
+ contentVisibility: {
57
+ type: 'object',
58
+ default: {
59
+ title: true,
60
+ price: true,
61
+ rating: true,
62
+ button: true,
63
+ },
64
+ },
65
+
66
/**
67
* How to order the products: 'date', 'popularity', 'price_asc', 'price_desc' 'rating', 'title'.
68
*/
95
save( props ) {
96
const {
97
align,
98
+ contentVisibility,
99
} = props.attributes; /* eslint-disable-line react/prop-types */
100
+ const classes = classnames(
101
+ align ? `align${ align }` : '',
102
+ {
103
+ 'is-hidden-title': ! contentVisibility.title,
104
+ 'is-hidden-price': ! contentVisibility.price,
105
+ 'is-hidden-rating': ! contentVisibility.rating,
106
+ 'is-hidden-button': ! contentVisibility.button,
107
+ }
108
+ );
109
return (
110
+ <RawHTML className={ classes }>
111
{ getShortcode( props, 'woocommerce/handpicked-products' ) }
112
</RawHTML>
113
);
assets/js/blocks/product-best-sellers/block.js CHANGED
@@ -4,10 +4,11 @@
4
import { __ } from '@wordpress/i18n';
5
import { addQueryArgs } from '@wordpress/url';
6
import apiFetch from '@wordpress/api-fetch';
7
- import { InspectorControls } from '@wordpress/editor';
8
import { Component, Fragment } from '@wordpress/element';
9
import { debounce } from 'lodash';
10
import Gridicon from 'gridicons';
11
import { PanelBody, Placeholder, Spinner } from '@wordpress/components';
12
import PropTypes from 'prop-types';
13
@@ -15,6 +16,7 @@ import PropTypes from 'prop-types';
15
* Internal dependencies
16
*/
17
import getQuery from '../../utils/get-query';
18
import GridLayoutControl from '../../components/grid-layout-control';
19
import ProductCategoryControl from '../../components/product-category-control';
20
import ProductPreview from '../../components/product-preview';
@@ -52,7 +54,7 @@ class ProductBestSellersBlock extends Component {
52
getProducts() {
53
apiFetch( {
54
path: addQueryArgs(
55
- '/wc-pb/v3/products',
56
getQuery( this.props.attributes, this.props.name )
57
),
58
} )
@@ -66,7 +68,13 @@ class ProductBestSellersBlock extends Component {
66
67
getInspectorControls() {
68
const { attributes, setAttributes } = this.props;
69
- const { categories, catOperator, columns, rows } = attributes;
70
71
return (
72
<InspectorControls key="inspector">
@@ -80,6 +88,15 @@ class ProductBestSellersBlock extends Component {
80
setAttributes={ setAttributes }
81
/>
82
</PanelBody>
83
<PanelBody
84
title={ __(
85
'Filter by Product Category',
@@ -104,27 +121,24 @@ class ProductBestSellersBlock extends Component {
104
}
105
106
render() {
107
- const { columns } = this.props.attributes;
108
- const { loaded, products } = this.state;
109
- const classes = [
110
- 'wc-block-products-grid',
111
- 'wc-block-best-selling-products',
112
- ];
113
- if ( columns ) {
114
- classes.push( `cols-${ columns }` );
115
- }
116
- if ( products && ! products.length ) {
117
- if ( ! loaded ) {
118
- classes.push( 'is-loading' );
119
- } else {
120
- classes.push( 'is-not-found' );
121
- }
122
- }
123
124
return (
125
<Fragment>
126
{ this.getInspectorControls() }
127
- <div className={ classes.join( ' ' ) }>
128
{ products.length ? (
129
products.map( ( product ) => (
130
<ProductPreview product={ product } key={ product.id } />
4
import { __ } from '@wordpress/i18n';
5
import { addQueryArgs } from '@wordpress/url';
6
import apiFetch from '@wordpress/api-fetch';
7
+ import classnames from 'classnames';
8
import { Component, Fragment } from '@wordpress/element';
9
import { debounce } from 'lodash';
10
import Gridicon from 'gridicons';
11
+ import { InspectorControls } from '@wordpress/editor';
12
import { PanelBody, Placeholder, Spinner } from '@wordpress/components';
13
import PropTypes from 'prop-types';
14
16
* Internal dependencies
17
*/
18
import getQuery from '../../utils/get-query';
19
+ import GridContentControl from '../../components/grid-content-control';
20
import GridLayoutControl from '../../components/grid-layout-control';
21
import ProductCategoryControl from '../../components/product-category-control';
22
import ProductPreview from '../../components/product-preview';
54
getProducts() {
55
apiFetch( {
56
path: addQueryArgs(
57
+ '/wc-blocks/v1/products',
58
getQuery( this.props.attributes, this.props.name )
59
),
60
} )
68
69
getInspectorControls() {
70
const { attributes, setAttributes } = this.props;
71
+ const {
72
+ categories,
73
+ catOperator,
74
+ columns,
75
+ contentVisibility,
76
+ rows,
77
+ } = attributes;
78
79
return (
80
<InspectorControls key="inspector">
88
setAttributes={ setAttributes }
89
/>
90
</PanelBody>
91
+ <PanelBody
92
+ title={ __( 'Content', 'woo-gutenberg-products-block' ) }
93
+ initialOpen
94
+ >
95
+ <GridContentControl
96
+ settings={ contentVisibility }
97
+ onChange={ ( value ) => setAttributes( { contentVisibility: value } ) }
98
+ />
99
+ </PanelBody>
100
<PanelBody
101
title={ __(
102
'Filter by Product Category',
121
}
122
123
render() {
124
+ const { columns, contentVisibility } = this.props.attributes;
125
+ const { loaded, products = [] } = this.state;
126
+ const classes = classnames( {
127
+ 'wc-block-products-grid': true,
128
+ 'wc-block-best-selling-products': true,
129
+ [ `cols-${ columns }` ]: columns,
130
+ 'is-loading': ! loaded,
131
+ 'is-not-found': loaded && ! products.length,
132
+ 'is-hidden-title': ! contentVisibility.title,
133
+ 'is-hidden-price': ! contentVisibility.price,
134
+ 'is-hidden-rating': ! contentVisibility.rating,
135
+ 'is-hidden-button': ! contentVisibility.button,
136
+ } );
137
138
return (
139
<Fragment>
140
{ this.getInspectorControls() }
141
+ <div className={ classes }>
142
{ products.length ? (
143
products.map( ( product ) => (
144
<ProductPreview product={ product } key={ product.id } />
assets/js/blocks/product-best-sellers/index.js CHANGED
@@ -2,8 +2,10 @@
2
* External dependencies
3
*/
4
import { __ } from '@wordpress/i18n';
5
import Gridicon from 'gridicons';
6
- import { registerBlockType } from '@wordpress/blocks';
7
import { RawHTML } from '@wordpress/element';
8
9
/**
@@ -11,7 +13,7 @@ import { RawHTML } from '@wordpress/element';
11
*/
12
import Block from './block';
13
import getShortcode from '../../utils/get-shortcode';
14
- import sharedAttributes from '../../utils/shared-attributes';
15
16
registerBlockType( 'woocommerce/product-best-sellers', {
17
title: __( 'Best Selling Products', 'woo-gutenberg-products-block' ),
@@ -28,6 +30,18 @@ registerBlockType( 'woocommerce/product-best-sellers', {
28
attributes: {
29
...sharedAttributes,
30
},
31
32
/**
33
* Renders and manages the block.
@@ -44,9 +58,19 @@ registerBlockType( 'woocommerce/product-best-sellers', {
44
save( props ) {
45
const {
46
align,
47
} = props.attributes; /* eslint-disable-line react/prop-types */
48
return (
49
- <RawHTML className={ align ? `align${ align }` : '' }>
50
{ getShortcode( props, 'woocommerce/product-best-sellers' ) }
51
</RawHTML>
52
);
2
* External dependencies
3
*/
4
import { __ } from '@wordpress/i18n';
5
+ import classnames from 'classnames';
6
+ import { createBlock, registerBlockType } from '@wordpress/blocks';
7
+ import { without } from 'lodash';
8
import Gridicon from 'gridicons';
9
import { RawHTML } from '@wordpress/element';
10
11
/**
13
*/
14
import Block from './block';
15
import getShortcode from '../../utils/get-shortcode';
16
+ import sharedAttributes, { sharedAttributeBlockTypes } from '../../utils/shared-attributes';
17
18
registerBlockType( 'woocommerce/product-best-sellers', {
19
title: __( 'Best Selling Products', 'woo-gutenberg-products-block' ),
30
attributes: {
31
...sharedAttributes,
32
},
33
+ transforms: {
34
+ from: [
35
+ {
36
+ type: 'block',
37
+ blocks: without( sharedAttributeBlockTypes, 'woocommerce/product-best-sellers' ),
38
+ transform: ( attributes ) => createBlock(
39
+ 'woocommerce/product-best-sellers',
40
+ attributes
41
+ ),
42
+ },
43
+ ],
44
+ },
45
46
/**
47
* Renders and manages the block.
58
save( props ) {
59
const {
60
align,
61
+ contentVisibility,
62
} = props.attributes; /* eslint-disable-line react/prop-types */
63
+ const classes = classnames(
64
+ align ? `align${ align }` : '',
65
+ {
66
+ 'is-hidden-title': ! contentVisibility.title,
67
+ 'is-hidden-price': ! contentVisibility.price,
68
+ 'is-hidden-rating': ! contentVisibility.rating,
69
+ 'is-hidden-button': ! contentVisibility.button,
70
+ }
71
+ );
72
return (
73
+ <RawHTML className={ classes }>
74
{ getShortcode( props, 'woocommerce/product-best-sellers' ) }
75
</RawHTML>
76
);
assets/js/blocks/product-category/block.js CHANGED
@@ -13,6 +13,7 @@ import {
13
Toolbar,
14
withSpokenMessages,
15
} from '@wordpress/components';
16
import { Component, Fragment } from '@wordpress/element';
17
import { debounce } from 'lodash';
18
import PropTypes from 'prop-types';
@@ -21,6 +22,7 @@ import PropTypes from 'prop-types';
21
* Internal dependencies
22
*/
23
import getQuery from '../../utils/get-query';
24
import GridLayoutControl from '../../components/grid-layout-control';
25
import ProductCategoryControl from '../../components/product-category-control';
26
import ProductOrderbyControl from '../../components/product-orderby-control';
@@ -69,7 +71,7 @@ class ProductByCategoryBlock extends Component {
69
}
70
apiFetch( {
71
path: addQueryArgs(
72
- '/wc-pb/v3/products',
73
getQuery( this.props.attributes, this.props.name )
74
),
75
} )
@@ -83,13 +85,13 @@ class ProductByCategoryBlock extends Component {
83
84
getInspectorControls() {
85
const { attributes, setAttributes } = this.props;
86
- const { columns, catOperator, orderby, rows } = attributes;
87
88
return (
89
<InspectorControls key="inspector">
90
<PanelBody
91
title={ __( 'Product Category', 'woo-gutenberg-products-block' ) }
92
- initialOpen={ false }
93
>
94
<ProductCategoryControl
95
selected={ attributes.categories }
@@ -113,6 +115,15 @@ class ProductByCategoryBlock extends Component {
113
setAttributes={ setAttributes }
114
/>
115
</PanelBody>
116
<PanelBody
117
title={ __( 'Order By', 'woo-gutenberg-products-block' ) }
118
initialOpen={ false }
@@ -170,19 +181,24 @@ class ProductByCategoryBlock extends Component {
170
171
render() {
172
const { setAttributes } = this.props;
173
- const { categories, columns, editMode } = this.props.attributes;
174
- const { loaded, products } = this.state;
175
- const classes = [ 'wc-block-products-grid', 'wc-block-products-category' ];
176
- if ( columns ) {
177
- classes.push( `cols-${ columns }` );
178
- }
179
- if ( products && ! products.length ) {
180
- if ( ! loaded ) {
181
- classes.push( 'is-loading' );
182
- } else {
183
- classes.push( 'is-not-found' );
184
- }
185
- }
186
187
const nothingFound = ! categories.length ?
188
__(
@@ -214,7 +230,7 @@ class ProductByCategoryBlock extends Component {
214
{ editMode ? (
215
this.renderEditMode()
216
) : (
217
- <div className={ classes.join( ' ' ) }>
218
{ products.length ? (
219
products.map( ( product ) => (
220
<ProductPreview product={ product } key={ product.id } />
13
Toolbar,
14
withSpokenMessages,
15
} from '@wordpress/components';
16
+ import classnames from 'classnames';
17
import { Component, Fragment } from '@wordpress/element';
18
import { debounce } from 'lodash';
19
import PropTypes from 'prop-types';
22
* Internal dependencies
23
*/
24
import getQuery from '../../utils/get-query';
25
+ import GridContentControl from '../../components/grid-content-control';
26
import GridLayoutControl from '../../components/grid-layout-control';
27
import ProductCategoryControl from '../../components/product-category-control';
28
import ProductOrderbyControl from '../../components/product-orderby-control';
71
}
72
apiFetch( {
73
path: addQueryArgs(
74
+ '/wc-blocks/v1/products',
75
getQuery( this.props.attributes, this.props.name )
76
),
77
} )
85
86
getInspectorControls() {
87
const { attributes, setAttributes } = this.props;
88
+ const { columns, catOperator, contentVisibility, editMode, orderby, rows } = attributes;
89
90
return (
91
<InspectorControls key="inspector">
92
<PanelBody
93
title={ __( 'Product Category', 'woo-gutenberg-products-block' ) }
94
+ initialOpen={ ! attributes.categories.length && ! editMode }
95
>
96
<ProductCategoryControl
97
selected={ attributes.categories }
115
setAttributes={ setAttributes }
116
/>
117
</PanelBody>
118
+ <PanelBody
119
+ title={ __( 'Content', 'woo-gutenberg-products-block' ) }
120
+ initialOpen
121
+ >
122
+ <GridContentControl
123
+ settings={ contentVisibility }
124
+ onChange={ ( value ) => setAttributes( { contentVisibility: value } ) }
125
+ />
126
+ </PanelBody>
127
<PanelBody
128
title={ __( 'Order By', 'woo-gutenberg-products-block' ) }
129
initialOpen={ false }
181
182
render() {
183
const { setAttributes } = this.props;
184
+ const {
185
+ categories,
186
+ columns,
187
+ contentVisibility,
188
+ editMode,
189
+ } = this.props.attributes;
190
+ const { loaded, products = [] } = this.state;
191
+ const classes = classnames( {
192
+ 'wc-block-products-grid': true,
193
+ 'wc-block-products-category': true,
194
+ [ `cols-${ columns }` ]: columns,
195
+ 'is-loading': ! loaded,
196
+ 'is-not-found': loaded && ! products.length,
197
+ 'is-hidden-title': ! contentVisibility.title,
198
+ 'is-hidden-price': ! contentVisibility.price,
199
+ 'is-hidden-rating': ! contentVisibility.rating,
200
+ 'is-hidden-button': ! contentVisibility.button,
201
+ } );
202
203
const nothingFound = ! categories.length ?
204
__(
230
{ editMode ? (
231
this.renderEditMode()
232
) : (
233
+ <div className={ classes }>
234
{ products.length ? (
235
products.map( ( product ) => (
236
<ProductPreview product={ product } key={ product.id } />
assets/js/blocks/product-category/{style.scss → editor.scss} RENAMED
File without changes
assets/js/blocks/product-category/index.js CHANGED
@@ -2,16 +2,18 @@
2
* External dependencies
3
*/
4
import { __ } from '@wordpress/i18n';
5
- import { registerBlockType } from '@wordpress/blocks';
6
import { RawHTML } from '@wordpress/element';
7
8
/**
9
* Internal dependencies
10
*/
11
- import './style.scss';
12
import Block from './block';
13
import getShortcode from '../../utils/get-shortcode';
14
- import sharedAttributes from '../../utils/shared-attributes';
15
16
/**
17
* Register and run the "Products by Category" block.
@@ -47,6 +49,18 @@ registerBlockType( 'woocommerce/product-category', {
47
default: 'date',
48
},
49
},
50
51
/**
52
* Renders and manages the block.
@@ -63,9 +77,19 @@ registerBlockType( 'woocommerce/product-category', {
63
save( props ) {
64
const {
65
align,
66
} = props.attributes; /* eslint-disable-line react/prop-types */
67
return (
68
- <RawHTML className={ align ? `align${ align }` : '' }>
69
{ getShortcode( props, 'woocommerce/product-category' ) }
70
</RawHTML>
71
);
2
* External dependencies
3
*/
4
import { __ } from '@wordpress/i18n';
5
+ import classnames from 'classnames';
6
+ import { createBlock, registerBlockType } from '@wordpress/blocks';
7
+ import { without } from 'lodash';
8
import { RawHTML } from '@wordpress/element';
9
10
/**
11
* Internal dependencies
12
*/
13
+ import './editor.scss';
14
import Block from './block';
15
import getShortcode from '../../utils/get-shortcode';
16
+ import sharedAttributes, { sharedAttributeBlockTypes } from '../../utils/shared-attributes';
17
18
/**
19
* Register and run the "Products by Category" block.
49
default: 'date',
50
},
51
},
52
+ transforms: {
53
+ from: [
54
+ {
55
+ type: 'block',
56
+ blocks: without( sharedAttributeBlockTypes, 'woocommerce/product-category' ),
57
+ transform: ( attributes ) => createBlock(
58
+ 'woocommerce/product-category',
59
+ { ...attributes, editMode: false }
60
+ ),
61
+ },
62
+ ],
63
+ },
64
65
/**
66
* Renders and manages the block.
77
save( props ) {
78
const {
79
align,
80
+ contentVisibility,
81
} = props.attributes; /* eslint-disable-line react/prop-types */
82
+ const classes = classnames(
83
+ align ? `align${ align }` : '',
84
+ {
85
+ 'is-hidden-title': ! contentVisibility.title,
86
+ 'is-hidden-price': ! contentVisibility.price,
87
+ 'is-hidden-rating': ! contentVisibility.rating,
88
+ 'is-hidden-button': ! contentVisibility.button,
89
+ }
90
+ );
91
return (
92
+ <RawHTML className={ classes }>
93
{ getShortcode( props, 'woocommerce/product-category' ) }
94
</RawHTML>
95
);
assets/js/blocks/product-new/block.js CHANGED
@@ -4,9 +4,10 @@
4
import { __ } from '@wordpress/i18n';
5
import { addQueryArgs } from '@wordpress/url';
6
import apiFetch from '@wordpress/api-fetch';
7
- import { InspectorControls } from '@wordpress/editor';
8
import { Component, Fragment } from '@wordpress/element';
9
import { debounce } from 'lodash';
10
import { PanelBody, Placeholder, Spinner } from '@wordpress/components';
11
import PropTypes from 'prop-types';
12
@@ -14,6 +15,7 @@ import PropTypes from 'prop-types';
14
* Internal dependencies
15
*/
16
import getQuery from '../../utils/get-query';
17
import GridLayoutControl from '../../components/grid-layout-control';
18
import { IconNewReleases } from '../../components/icons';
19
import ProductCategoryControl from '../../components/product-category-control';
@@ -40,9 +42,12 @@ class ProductNewestBlock extends Component {
40
}
41
42
componentDidUpdate( prevProps ) {
43
- const hasChange = [ 'rows', 'columns', 'categories', 'catOperator' ].reduce( ( acc, key ) => {
44
- return acc || prevProps.attributes[ key ] !== this.props.attributes[ key ];
45
- }, false );
46
if ( hasChange ) {
47
this.debouncedGetProducts();
48
}
@@ -51,7 +56,7 @@ class ProductNewestBlock extends Component {
51
getProducts() {
52
apiFetch( {
53
path: addQueryArgs(
54
- '/wc-pb/v3/products',
55
getQuery( this.props.attributes, this.props.name )
56
),
57
} )
@@ -65,7 +70,13 @@ class ProductNewestBlock extends Component {
65
66
getInspectorControls() {
67
const { attributes, setAttributes } = this.props;
68
- const { categories, catOperator, columns, rows } = attributes;
69
70
return (
71
<InspectorControls key="inspector">
@@ -79,6 +90,15 @@ class ProductNewestBlock extends Component {
79
setAttributes={ setAttributes }
80
/>
81
</PanelBody>
82
<PanelBody
83
title={ __(
84
'Filter by Product Category',
@@ -103,24 +123,24 @@ class ProductNewestBlock extends Component {
103
}
104
105
render() {
106
- const { columns } = this.props.attributes;
107
- const { loaded, products } = this.state;
108
- const classes = [ 'wc-block-products-grid', 'wc-block-newest-products' ];
109
- if ( columns ) {
110
- classes.push( `cols-${ columns }` );
111
- }
112
- if ( products && ! products.length ) {
113
- if ( ! loaded ) {
114
- classes.push( 'is-loading' );
115
- } else {
116
- classes.push( 'is-not-found' );
117
- }
118
- }
119
120
return (
121
<Fragment>
122
{ this.getInspectorControls() }
123
- <div className={ classes.join( ' ' ) }>
124
{ products.length ? (
125
products.map( ( product ) => (
126
<ProductPreview product={ product } key={ product.id } />
4
import { __ } from '@wordpress/i18n';
5
import { addQueryArgs } from '@wordpress/url';
6
import apiFetch from '@wordpress/api-fetch';
7
+ import classnames from 'classnames';
8
import { Component, Fragment } from '@wordpress/element';
9
import { debounce } from 'lodash';
10
+ import { InspectorControls } from '@wordpress/editor';
11
import { PanelBody, Placeholder, Spinner } from '@wordpress/components';
12
import PropTypes from 'prop-types';
13
15
* Internal dependencies
16
*/
17
import getQuery from '../../utils/get-query';
18
+ import GridContentControl from '../../components/grid-content-control';
19
import GridLayoutControl from '../../components/grid-layout-control';
20
import { IconNewReleases } from '../../components/icons';
21
import ProductCategoryControl from '../../components/product-category-control';
42
}
43
44
componentDidUpdate( prevProps ) {
45
+ const hasChange = [ 'rows', 'columns', 'categories', 'catOperator' ].reduce(
46
+ ( acc, key ) => {
47
+ return acc || prevProps.attributes[ key ] !== this.props.attributes[ key ];
48
+ },
49
+ false
50
+ );
51
if ( hasChange ) {
52
this.debouncedGetProducts();
53
}
56
getProducts() {
57
apiFetch( {
58
path: addQueryArgs(
59
+ '/wc-blocks/v1/products',
60
getQuery( this.props.attributes, this.props.name )
61
),
62
} )
70
71
getInspectorControls() {
72
const { attributes, setAttributes } = this.props;
73
+ const {
74
+ categories,
75
+ catOperator,
76
+ columns,
77
+ contentVisibility,
78
+ rows,
79
+ } = attributes;
80
81
return (
82
<InspectorControls key="inspector">
90
setAttributes={ setAttributes }
91
/>
92
</PanelBody>
93
+ <PanelBody
94
+ title={ __( 'Content', 'woo-gutenberg-products-block' ) }
95
+ initialOpen
96
+ >
97
+ <GridContentControl
98
+ settings={ contentVisibility }
99
+ onChange={ ( value ) => setAttributes( { contentVisibility: value } ) }
100
+ />
101
+ </PanelBody>
102
<PanelBody
103
title={ __(
104
'Filter by Product Category',
123
}
124
125
render() {
126
+ const { columns, contentVisibility } = this.props.attributes;
127
+ const { loaded, products = [] } = this.state;
128
+ const classes = classnames( {
129
+ 'wc-block-products-grid': true,
130
+ 'wc-block-newest-products': true,
131
+ [ `cols-${ columns }` ]: columns,
132
+ 'is-loading': ! loaded,
133
+ 'is-not-found': loaded && ! products.length,
134
+ 'is-hidden-title': ! contentVisibility.title,
135
+ 'is-hidden-price': ! contentVisibility.price,
136
+ 'is-hidden-rating': ! contentVisibility.rating,
137
+ 'is-hidden-button': ! contentVisibility.button,
138
+ } );
139
140
return (
141
<Fragment>
142
{ this.getInspectorControls() }
143
+ <div className={ classes }>
144
{ products.length ? (
145
products.map( ( product ) => (
146
<ProductPreview product={ product } key={ product.id } />
assets/js/blocks/product-new/index.js CHANGED
@@ -2,7 +2,9 @@
2
* External dependencies
3
*/
4
import { __ } from '@wordpress/i18n';
5
- import { registerBlockType } from '@wordpress/blocks';
6
import { RawHTML } from '@wordpress/element';
7
8
/**
@@ -11,7 +13,7 @@ import { RawHTML } from '@wordpress/element';
11
import Block from './block';
12
import getShortcode from '../../utils/get-shortcode';
13
import { IconNewReleases } from '../../components/icons';
14
- import sharedAttributes from '../../utils/shared-attributes';
15
16
registerBlockType( 'woocommerce/product-new', {
17
title: __( 'Newest Products', 'woo-gutenberg-products-block' ),
@@ -28,6 +30,18 @@ registerBlockType( 'woocommerce/product-new', {
28
attributes: {
29
...sharedAttributes,
30
},
31
32
/**
33
* Renders and manages the block.
@@ -44,9 +58,19 @@ registerBlockType( 'woocommerce/product-new', {
44
save( props ) {
45
const {
46
align,
47
} = props.attributes; /* eslint-disable-line react/prop-types */
48
return (
49
- <RawHTML className={ align ? `align${ align }` : '' }>
50
{ getShortcode( props, 'woocommerce/product-new' ) }
51
</RawHTML>
52
);
2
* External dependencies
3
*/
4
import { __ } from '@wordpress/i18n';
5
+ import classnames from 'classnames';
6
+ import { createBlock, registerBlockType } from '@wordpress/blocks';
7
+ import { without } from 'lodash';
8
import { RawHTML } from '@wordpress/element';
9
10
/**
13
import Block from './block';
14
import getShortcode from '../../utils/get-shortcode';
15
import { IconNewReleases } from '../../components/icons';
16
+ import sharedAttributes, { sharedAttributeBlockTypes } from '../../utils/shared-attributes';
17
18
registerBlockType( 'woocommerce/product-new', {
19
title: __( 'Newest Products', 'woo-gutenberg-products-block' ),
30
attributes: {
31
...sharedAttributes,
32
},
33
+ transforms: {
34
+ from: [
35
+ {
36
+ type: 'block',
37
+ blocks: without( sharedAttributeBlockTypes, 'woocommerce/product-new' ),
38
+ transform: ( attributes ) => createBlock(
39
+ 'woocommerce/product-new',
40
+ attributes
41
+ ),
42
+ },
43
+ ],
44
+ },
45
46
/**
47
* Renders and manages the block.
58
save( props ) {
59
const {
60
align,
61
+ contentVisibility,
62
} = props.attributes; /* eslint-disable-line react/prop-types */
63
+ const classes = classnames(
64
+ align ? `align${ align }` : '',
65
+ {
66
+ 'is-hidden-title': ! contentVisibility.title,
67
+ 'is-hidden-price': ! contentVisibility.price,
68
+ 'is-hidden-rating': ! contentVisibility.rating,
69
+ 'is-hidden-button': ! contentVisibility.button,
70
+ }
71
+ );
72
return (
73
+ <RawHTML className={ classes }>
74
{ getShortcode( props, 'woocommerce/product-new' ) }
75
</RawHTML>
76
);
assets/js/blocks/product-on-sale/block.js CHANGED
@@ -4,10 +4,11 @@
4
import { __ } from '@wordpress/i18n';
5
import { addQueryArgs } from '@wordpress/url';
6
import apiFetch from '@wordpress/api-fetch';
7
- import { InspectorControls } from '@wordpress/editor';
8
import { Component, Fragment } from '@wordpress/element';
9
import { debounce } from 'lodash';
10
import Gridicon from 'gridicons';
11
import { PanelBody, Placeholder, Spinner } from '@wordpress/components';
12
import PropTypes from 'prop-types';
13
@@ -15,6 +16,7 @@ import PropTypes from 'prop-types';
15
* Internal dependencies
16
*/
17
import getQuery from '../../utils/get-query';
18
import GridLayoutControl from '../../components/grid-layout-control';
19
import ProductCategoryControl from '../../components/product-category-control';
20
import ProductOrderbyControl from '../../components/product-orderby-control';
@@ -56,7 +58,7 @@ class ProductOnSaleBlock extends Component {
56
getProducts() {
57
apiFetch( {
58
path: addQueryArgs(
59
- '/wc-pb/v3/products',
60
getQuery( this.props.attributes, this.props.name )
61
),
62
} )
@@ -70,7 +72,14 @@ class ProductOnSaleBlock extends Component {
70
71
getInspectorControls() {
72
const { attributes, setAttributes } = this.props;
73
- const { categories, catOperator, columns, rows, orderby } = attributes;
74
75
return (
76
<InspectorControls key="inspector">
@@ -84,6 +93,15 @@ class ProductOnSaleBlock extends Component {
84
setAttributes={ setAttributes }
85
/>
86
</PanelBody>
87
<PanelBody
88
title={ __( 'Order By', 'woo-gutenberg-products-block' ) }
89
initialOpen={ false }
@@ -117,24 +135,24 @@ class ProductOnSaleBlock extends Component {
117
}
118
119
render() {
120
- const { columns } = this.props.attributes;
121
- const { loaded, products } = this.state;
122
- const classes = [ 'wc-block-products-grid', 'wc-block-on-sale-products' ];
123
- if ( columns ) {
124
- classes.push( `cols-${ columns }` );
125
- }
126
- if ( products && ! products.length ) {
127
- if ( ! loaded ) {
128
- classes.push( 'is-loading' );
129
- } else {
130
- classes.push( 'is-not-found' );
131
- }
132
- }
133
134
return (
135
<Fragment>
136
{ this.getInspectorControls() }
137
- <div className={ classes.join( ' ' ) }>
138
{ products.length ? (
139
products.map( ( product ) => (
140
<ProductPreview product={ product } key={ product.id } />
4
import { __ } from '@wordpress/i18n';
5
import { addQueryArgs } from '@wordpress/url';
6
import apiFetch from '@wordpress/api-fetch';
7
+ import classnames from 'classnames';
8
import { Component, Fragment } from '@wordpress/element';
9
import { debounce } from 'lodash';
10
import Gridicon from 'gridicons';
11
+ import { InspectorControls } from '@wordpress/editor';
12
import { PanelBody, Placeholder, Spinner } from '@wordpress/components';
13
import PropTypes from 'prop-types';
14
16
* Internal dependencies
17
*/
18
import getQuery from '../../utils/get-query';
19
+ import GridContentControl from '../../components/grid-content-control';
20
import GridLayoutControl from '../../components/grid-layout-control';
21
import ProductCategoryControl from '../../components/product-category-control';
22
import ProductOrderbyControl from '../../components/product-orderby-control';
58
getProducts() {
59
apiFetch( {
60
path: addQueryArgs(
61
+ '/wc-blocks/v1/products',
62
getQuery( this.props.attributes, this.props.name )
63
),
64
} )
72
73
getInspectorControls() {
74
const { attributes, setAttributes } = this.props;
75
+ const {
76
+ categories,
77
+ catOperator,
78
+ columns,
79
+ contentVisibility,
80
+ rows,
81
+ orderby,
82
+ } = attributes;
83
84
return (
85
<InspectorControls key="inspector">
93
setAttributes={ setAttributes }
94
/>
95
</PanelBody>
96
+ <PanelBody
97
+ title={ __( 'Content', 'woo-gutenberg-products-block' ) }
98
+ initialOpen
99
+ >
100
+ <GridContentControl
101
+ settings={ contentVisibility }
102
+ onChange={ ( value ) => setAttributes( { contentVisibility: value } ) }
103
+ />
104
+ </PanelBody>
105
<PanelBody
106
title={ __( 'Order By', 'woo-gutenberg-products-block' ) }
107
initialOpen={ false }
135
}
136
137
render() {
138
+ const { columns, contentVisibility } = this.props.attributes;
139
+ const { loaded, products = [] } = this.state;
140
+ const classes = classnames( {
141
+ 'wc-block-products-grid': true,
142
+ 'wc-block-on-sale-products': true,
143
+ [ `cols-${ columns }` ]: columns,
144
+ 'is-loading': ! loaded,
145
+ 'is-not-found': loaded && ! products.length,
146
+ 'is-hidden-title': ! contentVisibility.title,
147
+ 'is-hidden-price': ! contentVisibility.price,
148
+ 'is-hidden-rating': ! contentVisibility.rating,
149
+ 'is-hidden-button': ! contentVisibility.button,
150
+ } );
151
152
return (
153
<Fragment>
154
{ this.getInspectorControls() }
155
+ <div className={ classes }>
156
{ products.length ? (
157
products.map( ( product ) => (
158
<ProductPreview product={ product } key={ product.id } />
assets/js/blocks/product-on-sale/index.js CHANGED
@@ -2,8 +2,10 @@
2
* External dependencies
3
*/
4
import { __ } from '@wordpress/i18n';
5
import Gridicon from 'gridicons';
6
- import { registerBlockType } from '@wordpress/blocks';
7
import { RawHTML } from '@wordpress/element';
8
9
/**
@@ -11,7 +13,7 @@ import { RawHTML } from '@wordpress/element';
11
*/
12
import Block from './block';
13
import getShortcode from '../../utils/get-shortcode';
14
- import sharedAttributes from '../../utils/shared-attributes';
15
16
registerBlockType( 'woocommerce/product-on-sale', {
17
title: __( 'On Sale Products', 'woo-gutenberg-products-block' ),
@@ -36,6 +38,18 @@ registerBlockType( 'woocommerce/product-on-sale', {
36
default: 'date',
37
},
38
},
39
40
/**
41
* Renders and manages the block.
@@ -52,9 +66,19 @@ registerBlockType( 'woocommerce/product-on-sale', {
52
save( props ) {
53
const {
54
align,
55
} = props.attributes; /* eslint-disable-line react/prop-types */
56
return (
57
- <RawHTML className={ align ? `align${ align }` : '' }>
58
{ getShortcode( props, 'woocommerce/product-on-sale' ) }
59
</RawHTML>
60
);
2
* External dependencies
3
*/
4
import { __ } from '@wordpress/i18n';
5
+ import classnames from 'classnames';
6
+ import { createBlock, registerBlockType } from '@wordpress/blocks';
7
+ import { without } from 'lodash';
8
import Gridicon from 'gridicons';
9
import { RawHTML } from '@wordpress/element';
10
11
/**
13
*/
14
import Block from './block';
15
import getShortcode from '../../utils/get-shortcode';
16
+ import sharedAttributes, { sharedAttributeBlockTypes } from '../../utils/shared-attributes';
17
18
registerBlockType( 'woocommerce/product-on-sale', {
19
title: __( 'On Sale Products', 'woo-gutenberg-products-block' ),
38
default: 'date',
39
},
40
},
41
+ transforms: {
42
+ from: [
43
+ {
44
+ type: 'block',
45
+ blocks: without( sharedAttributeBlockTypes, 'woocommerce/product-on-sale' ),
46
+ transform: ( attributes ) => createBlock(
47
+ 'woocommerce/product-on-sale',
48
+ attributes
49
+ ),
50
+ },
51
+ ],
52
+ },
53
54
/**
55
* Renders and manages the block.
66
save( props ) {
67
const {
68
align,
69
+ contentVisibility,
70
} = props.attributes; /* eslint-disable-line react/prop-types */
71
+ const classes = classnames(
72
+ align ? `align${ align }` : '',
73
+ {
74
+ 'is-hidden-title': ! contentVisibility.title,
75
+ 'is-hidden-price': ! contentVisibility.price,
76
+ 'is-hidden-rating': ! contentVisibility.rating,
77
+ 'is-hidden-button': ! contentVisibility.button,
78
+ }
79
+ );
80
return (
81
+ <RawHTML className={ classes }>
82
{ getShortcode( props, 'woocommerce/product-on-sale' ) }
83
</RawHTML>
84
);
assets/js/blocks/product-top-rated/block.js CHANGED
@@ -4,10 +4,11 @@
4
import { __ } from '@wordpress/i18n';
5
import { addQueryArgs } from '@wordpress/url';
6
import apiFetch from '@wordpress/api-fetch';
7
- import { InspectorControls } from '@wordpress/editor';
8
import { Component, Fragment } from '@wordpress/element';
9
import { debounce } from 'lodash';
10
import Gridicon from 'gridicons';
11
import { PanelBody, Placeholder, Spinner } from '@wordpress/components';
12
import PropTypes from 'prop-types';
13
@@ -15,6 +16,7 @@ import PropTypes from 'prop-types';
15
* Internal dependencies
16
*/
17
import getQuery from '../../utils/get-query';
18
import GridLayoutControl from '../../components/grid-layout-control';
19
import ProductCategoryControl from '../../components/product-category-control';
20
import ProductPreview from '../../components/product-preview';
@@ -40,9 +42,12 @@ class ProductTopRatedBlock extends Component {
40
}
41
42
componentDidUpdate( prevProps ) {
43
- const hasChange = [ 'rows', 'columns', 'categories', 'catOperator' ].reduce( ( acc, key ) => {
44
- return acc || prevProps.attributes[ key ] !== this.props.attributes[ key ];
45
- }, false );
46
if ( hasChange ) {
47
this.debouncedGetProducts();
48
}
@@ -51,7 +56,7 @@ class ProductTopRatedBlock extends Component {
51
getProducts() {
52
apiFetch( {
53
path: addQueryArgs(
54
- '/wc-pb/v3/products',
55
getQuery( this.props.attributes, this.props.name )
56
),
57
} )
@@ -65,7 +70,13 @@ class ProductTopRatedBlock extends Component {
65
66
getInspectorControls() {
67
const { attributes, setAttributes } = this.props;
68
- const { categories, catOperator, columns, rows } = attributes;
69
70
return (
71
<InspectorControls key="inspector">
@@ -79,6 +90,15 @@ class ProductTopRatedBlock extends Component {
79
setAttributes={ setAttributes }
80
/>
81
</PanelBody>
82
<PanelBody
83
title={ __(
84
'Filter by Product Category',
@@ -103,24 +123,24 @@ class ProductTopRatedBlock extends Component {
103
}
104
105
render() {
106
- const { columns } = this.props.attributes;
107
- const { loaded, products } = this.state;
108
- const classes = [ 'wc-block-products-grid', 'wc-block-top-rated-products' ];
109
- if ( columns ) {
110
- classes.push( `cols-${ columns }` );
111
- }
112
- if ( products && ! products.length ) {
113
- if ( ! loaded ) {
114
- classes.push( 'is-loading' );
115
- } else {
116
- classes.push( 'is-not-found' );
117
- }
118
- }
119
120
return (
121
<Fragment>
122
{ this.getInspectorControls() }
123
- <div className={ classes.join( ' ' ) }>
124
{ products.length ? (
125
products.map( ( product ) => (
126
<ProductPreview product={ product } key={ product.id } />
4
import { __ } from '@wordpress/i18n';
5
import { addQueryArgs } from '@wordpress/url';
6
import apiFetch from '@wordpress/api-fetch';
7
+ import classnames from 'classnames';
8
import { Component, Fragment } from '@wordpress/element';
9
import { debounce } from 'lodash';
10
import Gridicon from 'gridicons';
11
+ import { InspectorControls } from '@wordpress/editor';
12
import { PanelBody, Placeholder, Spinner } from '@wordpress/components';
13
import PropTypes from 'prop-types';
14
16
* Internal dependencies
17
*/
18
import getQuery from '../../utils/get-query';
19
+ import GridContentControl from '../../components/grid-content-control';
20
import GridLayoutControl from '../../components/grid-layout-control';
21
import ProductCategoryControl from '../../components/product-category-control';
22
import ProductPreview from '../../components/product-preview';
42
}
43
44
componentDidUpdate( prevProps ) {
45
+ const hasChange = [ 'rows', 'columns', 'categories', 'catOperator' ].reduce(
46
+ ( acc, key ) => {
47
+ return acc || prevProps.attributes[ key ] !== this.props.attributes[ key ];
48
+ },
49
+ false
50
+ );
51
if ( hasChange ) {
52
this.debouncedGetProducts();
53
}
56
getProducts() {
57
apiFetch( {
58
path: addQueryArgs(
59
+ '/wc-blocks/v1/products',
60
getQuery( this.props.attributes, this.props.name )
61
),
62
} )
70
71
getInspectorControls() {
72
const { attributes, setAttributes } = this.props;
73
+ const {
74
+ categories,
75
+ catOperator,
76
+ columns,
77
+ contentVisibility,
78
+ rows,
79
+ } = attributes;
80
81
return (
82
<InspectorControls key="inspector">
90
setAttributes={ setAttributes }
91
/>
92
</PanelBody>
93
+ <PanelBody
94
+ title={ __( 'Content', 'woo-gutenberg-products-block' ) }
95
+ initialOpen
96
+ >
97
+ <GridContentControl
98
+ settings={ contentVisibility }
99
+ onChange={ ( value ) => setAttributes( { contentVisibility: value } ) }
100
+ />
101
+ </PanelBody>
102
<PanelBody
103
title={ __(
104
'Filter by Product Category',
123
}
124
125
render() {
126
+ const { columns, contentVisibility } = this.props.attributes;
127
+ const { loaded, products = [] } = this.state;
128
+ const classes = classnames( {
129
+ 'wc-block-products-grid': true,
130
+ 'wc-block-top-rated-products': true,
131
+ [ `cols-${ columns }` ]: columns,
132
+ 'is-loading': ! loaded,
133
+ 'is-not-found': loaded && ! products.length,
134
+ 'is-hidden-title': ! contentVisibility.title,
135
+ 'is-hidden-price': ! contentVisibility.price,
136
+ 'is-hidden-rating': ! contentVisibility.rating,
137
+ 'is-hidden-button': ! contentVisibility.button,
138
+ } );
139
140
return (
141
<Fragment>
142
{ this.getInspectorControls() }
143
+ <div className={ classes }>
144
{ products.length ? (
145
products.map( ( product ) => (
146
<ProductPreview product={ product } key={ product.id } />
assets/js/blocks/product-top-rated/index.js CHANGED
@@ -2,8 +2,10 @@
2
* External dependencies
3
*/
4
import { __ } from '@wordpress/i18n';
5
import Gridicon from 'gridicons';
6
- import { registerBlockType } from '@wordpress/blocks';
7
import { RawHTML } from '@wordpress/element';
8
9
/**
@@ -11,7 +13,7 @@ import { RawHTML } from '@wordpress/element';
11
*/
12
import Block from './block';
13
import getShortcode from '../../utils/get-shortcode';
14
- import sharedAttributes from '../../utils/shared-attributes';
15
16
registerBlockType( 'woocommerce/product-top-rated', {
17
title: __( 'Top Rated Products', 'woo-gutenberg-products-block' ),
@@ -28,6 +30,18 @@ registerBlockType( 'woocommerce/product-top-rated', {
28
attributes: {
29
...sharedAttributes,
30
},
31
32
/**
33
* Renders and manages the block.
@@ -44,9 +58,19 @@ registerBlockType( 'woocommerce/product-top-rated', {
44
save( props ) {
45
const {
46
align,
47
} = props.attributes; /* eslint-disable-line react/prop-types */
48
return (
49
- <RawHTML className={ align ? `align${ align }` : '' }>
50
{ getShortcode( props, 'woocommerce/product-top-rated' ) }
51
</RawHTML>
52
);
2
* External dependencies
3
*/
4
import { __ } from '@wordpress/i18n';
5
+ import classnames from 'classnames';
6
+ import { createBlock, registerBlockType } from '@wordpress/blocks';
7
+ import { without } from 'lodash';
8
import Gridicon from 'gridicons';
9
import { RawHTML } from '@wordpress/element';
10
11
/**
13
*/
14
import Block from './block';
15
import getShortcode from '../../utils/get-shortcode';
16
+ import sharedAttributes, { sharedAttributeBlockTypes } from '../../utils/shared-attributes';
17
18
registerBlockType( 'woocommerce/product-top-rated', {
19
title: __( 'Top Rated Products', 'woo-gutenberg-products-block' ),
30
attributes: {
31
...sharedAttributes,
32
},
33
+ transforms: {
34
+ from: [
35
+ {
36
+ type: 'block',
37
+ blocks: without( sharedAttributeBlockTypes, 'woocommerce/product-top-rated' ),
38
+ transform: ( attributes ) => createBlock(
39
+ 'woocommerce/product-top-rated',
40
+ attributes
41
+ ),
42
+ },
43
+ ],
44
+ },
45
46
/**
47
* Renders and manages the block.
58
save( props ) {
59
const {
60
align,
61
+ contentVisibility,
62
} = props.attributes; /* eslint-disable-line react/prop-types */
63
+ const classes = classnames(
64
+ align ? `align${ align }` : '',
65
+ {
66
+ 'is-hidden-title': ! contentVisibility.title,
67
+ 'is-hidden-price': ! contentVisibility.price,
68
+ 'is-hidden-rating': ! contentVisibility.rating,
69
+ 'is-hidden-button': ! contentVisibility.button,
70
+ }
71
+ );
72
return (
73
+ <RawHTML className={ classes }>
74
{ getShortcode( props, 'woocommerce/product-top-rated' ) }
75
</RawHTML>
76
);
assets/js/blocks/products-by-attribute/block.js CHANGED
@@ -13,6 +13,7 @@ import {
13
Toolbar,
14
withSpokenMessages,
15
} from '@wordpress/components';
16
import { Component, Fragment } from '@wordpress/element';
17
import { debounce } from 'lodash';
18
import Gridicon from 'gridicons';
@@ -22,6 +23,7 @@ import PropTypes from 'prop-types';
22
* Internal dependencies
23
*/
24
import getQuery from '../../utils/get-query';
25
import GridLayoutControl from '../../components/grid-layout-control';
26
import ProductAttributeControl from '../../components/product-attribute-control';
27
import ProductOrderbyControl from '../../components/product-orderby-control';
@@ -71,7 +73,7 @@ class ProductsByAttributeBlock extends Component {
71
}
72
apiFetch( {
73
path: addQueryArgs(
74
- '/wc-pb/v3/products',
75
getQuery( blockAttributes, this.props.name )
76
),
77
} )
@@ -85,7 +87,14 @@ class ProductsByAttributeBlock extends Component {
85
86
getInspectorControls() {
87
const { setAttributes } = this.props;
88
- const { attributes, attrOperator, columns, orderby, rows } = this.props.attributes;
89
90
return (
91
<InspectorControls key="inspector">
@@ -99,6 +108,15 @@ class ProductsByAttributeBlock extends Component {
99
setAttributes={ setAttributes }
100
/>
101
</PanelBody>
102
<PanelBody
103
title={ __(
104
'Filter by Product Attribute',
@@ -182,19 +200,19 @@ class ProductsByAttributeBlock extends Component {
182
183
render() {
184
const { setAttributes } = this.props;
185
- const { columns, editMode } = this.props.attributes;
186
- const { loaded, products } = this.state;
187
- const classes = [ 'wc-block-products-grid', 'wc-block-products-attribute' ];
188
- if ( columns ) {
189
- classes.push( `cols-${ columns }` );
190
- }
191
- if ( products && ! products.length ) {
192
- if ( ! loaded ) {
193
- classes.push( 'is-loading' );
194
- } else {
195
- classes.push( 'is-not-found' );
196
- }
197
- }
198
199
return (
200
<Fragment>
@@ -214,7 +232,7 @@ class ProductsByAttributeBlock extends Component {
214
{ editMode ? (
215
this.renderEditMode()
216
) : (
217
- <div className={ classes.join( ' ' ) }>
218
{ products.length ? (
219
products.map( ( product ) => (
220
<ProductPreview product={ product } key={ product.id } />
13
Toolbar,
14
withSpokenMessages,
15
} from '@wordpress/components';
16
+ import classnames from 'classnames';
17
import { Component, Fragment } from '@wordpress/element';
18
import { debounce } from 'lodash';
19
import Gridicon from 'gridicons';
23
* Internal dependencies
24
*/
25
import getQuery from '../../utils/get-query';
26
+ import GridContentControl from '../../components/grid-content-control';
27
import GridLayoutControl from '../../components/grid-layout-control';
28
import ProductAttributeControl from '../../components/product-attribute-control';
29
import ProductOrderbyControl from '../../components/product-orderby-control';
73
}
74
apiFetch( {
75
path: addQueryArgs(
76
+ '/wc-blocks/v1/products',
77
getQuery( blockAttributes, this.props.name )
78
),
79
} )
87
88
getInspectorControls() {
89
const { setAttributes } = this.props;
90
+ const {
91
+ attributes,
92
+ attrOperator,
93
+ columns,
94
+ contentVisibility,
95
+ orderby,
96
+ rows,
97
+ } = this.props.attributes;
98
99
return (
100
<InspectorControls key="inspector">
108
setAttributes={ setAttributes }
109
/>
110
</PanelBody>
111
+ <PanelBody
112
+ title={ __( 'Content', 'woo-gutenberg-products-block' ) }
113
+ initialOpen
114
+ >
115
+ <GridContentControl
116
+ settings={ contentVisibility }
117
+ onChange={ ( value ) => setAttributes( { contentVisibility: value } ) }
118
+ />
119
+ </PanelBody>
120
<PanelBody
121
title={ __(
122
'Filter by Product Attribute',
200
201
render() {
202
const { setAttributes } = this.props;
203
+ const { columns, editMode, contentVisibility } = this.props.attributes;
204
+ const { loaded, products = [] } = this.state;
205
+ const classes = classnames( {
206
+ 'wc-block-products-grid': true,
207
+ 'wc-block-products-attribute': true,
208
+ [ `cols-${ columns }` ]: columns,
209
+ 'is-loading': ! loaded,
210
+ 'is-not-found': loaded && ! products.length,
211
+ 'is-hidden-title': ! contentVisibility.title,
212
+ 'is-hidden-price': ! contentVisibility.price,
213
+ 'is-hidden-rating': ! contentVisibility.rating,
214
+ 'is-hidden-button': ! contentVisibility.button,
215
+ } );
216
217
return (
218
<Fragment>
232
{ editMode ? (
233
this.renderEditMode()
234
) : (
235
+ <div className={ classes }>
236
{ products.length ? (
237
products.map( ( product ) => (
238
<ProductPreview product={ product } key={ product.id } />
assets/js/blocks/products-by-attribute/{style.scss → editor.scss} RENAMED
File without changes
assets/js/blocks/products-by-attribute/index.js CHANGED
@@ -2,6 +2,7 @@
2
* External dependencies
3
*/
4
import { __ } from '@wordpress/i18n';
5
import Gridicon from 'gridicons';
6
import { RawHTML } from '@wordpress/element';
7
import { registerBlockType } from '@wordpress/blocks';
@@ -9,7 +10,7 @@ import { registerBlockType } from '@wordpress/blocks';
9
/**
10
* Internal dependencies
11
*/
12
- import './style.scss';
13
import Block from './block';
14
import getShortcode from '../../utils/get-shortcode';
15
@@ -58,6 +59,19 @@ registerBlockType( 'woocommerce/products-by-attribute', {
58
default: true,
59
},
60
61
/**
62
* How to order the products: 'date', 'popularity', 'price_asc', 'price_desc' 'rating', 'title'.
63
*/
@@ -90,9 +104,19 @@ registerBlockType( 'woocommerce/products-by-attribute', {
90
save( props ) {
91
const {
92
align,
93
} = props.attributes; /* eslint-disable-line react/prop-types */
94
return (
95
- <RawHTML className={ align ? `align${ align }` : '' }>
96
{ getShortcode( props, 'woocommerce/products-by-attribute' ) }
97
</RawHTML>
98
);
2
* External dependencies
3
*/
4
import { __ } from '@wordpress/i18n';
5
+ import classnames from 'classnames';
6
import Gridicon from 'gridicons';
7
import { RawHTML } from '@wordpress/element';
8
import { registerBlockType } from '@wordpress/blocks';
10
/**
11
* Internal dependencies
12
*/
13
+ import './editor.scss';
14
import Block from './block';
15
import getShortcode from '../../utils/get-shortcode';
16
59
default: true,
60
},
61
62
+ /**
63
+ * Content visibility setting
64
+ */
65
+ contentVisibility: {
66
+ type: 'object',
67
+ default: {
68
+ title: true,
69
+ price: true,
70
+ rating: true,
71
+ button: true,
72
+ },
73
+ },
74
+
75
/**
76
* How to order the products: 'date', 'popularity', 'price_asc', 'price_desc' 'rating', 'title'.
77
*/
104
save( props ) {
105
const {
106
align,
107
+ contentVisibility,
108
} = props.attributes; /* eslint-disable-line react/prop-types */
109
+ const classes = classnames(
110
+ align ? `align${ align }` : '',
111
+ {
112
+ 'is-hidden-title': ! contentVisibility.title,
113
+ 'is-hidden-price': ! contentVisibility.price,
114
+ 'is-hidden-rating': ! contentVisibility.rating,
115
+ 'is-hidden-button': ! contentVisibility.button,
116
+ }
117
+ );
118
return (
119
+ <RawHTML className={ classes }>
120
{ getShortcode( props, 'woocommerce/products-by-attribute' ) }
121
</RawHTML>
122
);
assets/js/components/grid-content-control/index.js ADDED
@@ -0,0 +1,81 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { __ } from '@wordpress/i18n';
5
+ import { Fragment } from '@wordpress/element';
6
+ import PropTypes from 'prop-types';
7
+ import { ToggleControl } from '@wordpress/components';
8
+
9
+ /**
10
+ * A combination of range controls for product grid layout settings.
11
+ */
12
+ const GridContentControl = ( { onChange, settings } ) => {
13
+ const { button, price, rating, title } = settings;
14
+ return (
15
+ <Fragment>
16
+ <ToggleControl
17
+ label={ __( 'Product title', 'woo-gutenberg-products-block' ) }
18
+ help={
19
+ title ?
20
+ __( 'Product title is visible.', 'woo-gutenberg-products-block' ) :
21
+ __( 'Product title is hidden.', 'woo-gutenberg-products-block' )
22
+ }
23
+ checked={ title }
24
+ onChange={ () => onChange( { ...settings, title: ! title } ) }
25
+ />
26
+ <ToggleControl
27
+ label={ __( 'Product price', 'woo-gutenberg-products-block' ) }
28
+ help={
29
+ price ?
30
+ __( 'Product price is visible.', 'woo-gutenberg-products-block' ) :
31
+ __( 'Product price is hidden.', 'woo-gutenberg-products-block' )
32
+ }
33
+ checked={ price }
34
+ onChange={ () => onChange( { ...settings, price: ! price } ) }
35
+ />
36
+ <ToggleControl
37
+ label={ __( 'Product rating', 'woo-gutenberg-products-block' ) }
38
+ help={
39
+ rating ?
40
+ __( 'Product rating is visible.', 'woo-gutenberg-products-block' ) :
41
+ __( 'Product rating is hidden.', 'woo-gutenberg-products-block' )
42
+ }
43
+ checked={ rating }
44
+ onChange={ () => onChange( { ...settings, rating: ! rating } ) }
45
+ />
46
+ <ToggleControl
47
+ label={ __( 'Add to Cart button', 'woo-gutenberg-products-block' ) }
48
+ help={
49
+ button ?
50
+ __(
51
+ 'Add to Cart button is visible.',
52
+ 'woo-gutenberg-products-block'
53
+ ) :
54
+ __(
55
+ 'Add to Cart button is hidden.',
56
+ 'woo-gutenberg-products-block'
57
+ )
58
+ }
59
+ checked={ button }
60
+ onChange={ () => onChange( { ...settings, button: ! button } ) }
61
+ />
62
+ </Fragment>
63
+ );
64
+ };
65
+
66
+ GridContentControl.propTypes = {
67
+ /**
68
+ * The current title visibility.
69
+ */
70
+ settings: PropTypes.shape( {
71
+ button: PropTypes.bool.isRequired,
72
+ price: PropTypes.bool.isRequired,
73
+ title: PropTypes.bool.isRequired,
74
+ } ).isRequired,
75
+ /**
76
+ * Callback to update the layout settings.
77
+ */
78
+ onChange: PropTypes.func.isRequired,
79
+ };
80
+
81
+ export default GridContentControl;
assets/js/components/icons/new-releases.js CHANGED
@@ -5,6 +5,7 @@ import { Icon } from '@wordpress/components';
5
6
export default () => (
7
<Icon
8
icon={
9
<svg
10
xmlns="http://www.w3.org/2000/svg"
5
6
export default () => (
7
<Icon
8
+ className="material-icon"
9
icon={
10
<svg
11
xmlns="http://www.w3.org/2000/svg"
assets/js/components/icons/widgets.js CHANGED
@@ -5,6 +5,7 @@ import { Icon } from '@wordpress/components';
5
6
export default () => (
7
<Icon
8
icon={
9
<svg
10
xmlns="http://www.w3.org/2000/svg"
5
6
export default () => (
7
<Icon
8
+ className="material-icon"
9
icon={
10
<svg
11
xmlns="http://www.w3.org/2000/svg"
assets/js/components/product-attribute-control/index.js CHANGED
@@ -7,14 +7,13 @@ import apiFetch from '@wordpress/api-fetch';
7
import { Component, Fragment } from '@wordpress/element';
8
import { debounce, find } from 'lodash';
9
import PropTypes from 'prop-types';
10
import { SelectControl, Spinner } from '@wordpress/components';
11
12
/**
13
* Internal dependencies
14
*/
15
import './style.scss';
16
- import SearchListControl from '../search-list-control';
17
- import SearchListItem from '../search-list-control/item';
18
19
class ProductAttributeControl extends Component {
20
constructor() {
@@ -35,7 +34,7 @@ class ProductAttributeControl extends Component {
35
componentDidMount() {
36
const { selected } = this.props;
37
apiFetch( {
38
- path: addQueryArgs( '/wc-pb/v3/products/attributes', { per_page: -1 } ),
39
} )
40
.then( ( list ) => {
41
list = list.map( ( item ) => ( { ...item, parent: 0 } ) );
@@ -68,12 +67,12 @@ class ProductAttributeControl extends Component {
68
}
69
70
apiFetch( {
71
- path: addQueryArgs( `/wc-pb/v3/products/attributes/${ attribute }/terms`, {
72
per_page: -1,
73
} ),
74
} )
75
.then( ( terms ) => {
76
- terms = terms.map( ( term ) => ( { ...term, parent: attribute } ) );
77
this.setState( ( prevState ) => ( {
78
termsList: { ...prevState.termsList, [ attribute ]: terms },
79
termsLoading: false,
7
import { Component, Fragment } from '@wordpress/element';
8
import { debounce, find } from 'lodash';
9
import PropTypes from 'prop-types';
10
+ import { SearchListControl, SearchListItem } from '@woocommerce/components';
11
import { SelectControl, Spinner } from '@wordpress/components';
12
13
/**
14
* Internal dependencies
15
*/
16
import './style.scss';
17
18
class ProductAttributeControl extends Component {
19
constructor() {
34
componentDidMount() {
35
const { selected } = this.props;
36
apiFetch( {
37
+ path: addQueryArgs( '/wc-blocks/v1/products/attributes', { per_page: -1 } ),
38
} )
39
.then( ( list ) => {
40
list = list.map( ( item ) => ( { ...item, parent: 0 } ) );
67
}
68
69
apiFetch( {
70
+ path: addQueryArgs( `/wc-blocks/v1/products/attributes/${ attribute }/terms`, {
71
per_page: -1,
72
} ),
73
} )
74
.then( ( terms ) => {
75
+ terms = terms.map( ( term ) => ( { ...term, parent: attribute, attr_slug: term.attribute.slug } ) );
76
this.setState( ( prevState ) => ( {
77
termsList: { ...prevState.termsList, [ attribute ]: terms },
78
termsLoading: false,
assets/js/components/product-category-control/index.js CHANGED
@@ -7,14 +7,13 @@ import apiFetch from '@wordpress/api-fetch';
7
import { Component, Fragment } from '@wordpress/element';
8
import { find } from 'lodash';
9
import PropTypes from 'prop-types';
10
import { SelectControl } from '@wordpress/components';
11
12
/**
13
* Internal dependencies
14
*/
15
import './style.scss';
16
- import SearchListControl from '../search-list-control';
17
- import SearchListItem from '../search-list-control/item';
18
19
class ProductCategoryControl extends Component {
20
constructor() {
@@ -28,7 +27,7 @@ class ProductCategoryControl extends Component {
28
29
componentDidMount() {
30
apiFetch( {
31
- path: addQueryArgs( '/wc-pb/v3/products/categories', { per_page: -1 } ),
32
} )
33
.then( ( list ) => {
34
this.setState( { list, loading: false } );
7
import { Component, Fragment } from '@wordpress/element';
8
import { find } from 'lodash';
9
import PropTypes from 'prop-types';
10
+ import { SearchListControl, SearchListItem } from '@woocommerce/components';
11
import { SelectControl } from '@wordpress/components';
12
13
/**
14
* Internal dependencies
15
*/
16
import './style.scss';
17
18
class ProductCategoryControl extends Component {
19
constructor() {
27
28
componentDidMount() {
29
apiFetch( {
30
+ path: addQueryArgs( '/wc-blocks/v1/products/categories', { per_page: -1 } ),
31
} )
32
.then( ( list ) => {
33
this.setState( { list, loading: false } );
assets/js/components/product-control/index.js CHANGED
@@ -7,11 +7,7 @@ import apiFetch from '@wordpress/api-fetch';
7
import { Component, Fragment } from '@wordpress/element';
8
import { find } from 'lodash';
9
import PropTypes from 'prop-types';
10
-
11
- /**
12
- * Internal dependencies
13
- */
14
- import SearchListControl from '../search-list-control';
15
16
class ProductControl extends Component {
17
constructor() {
@@ -24,8 +20,9 @@ class ProductControl extends Component {
24
25
componentDidMount() {
26
apiFetch( {
27
- path: addQueryArgs( '/wc-pb/v3/products', {
28
per_page: -1,
29
status: 'publish',
30
} ),
31
} )
7
import { Component, Fragment } from '@wordpress/element';
8
import { find } from 'lodash';
9
import PropTypes from 'prop-types';
10
+ import { SearchListControl } from '@woocommerce/components';
11
12
class ProductControl extends Component {
13
constructor() {
20
21
componentDidMount() {
22
apiFetch( {
23
+ path: addQueryArgs( '/wc-blocks/v1/products', {
24
per_page: -1,
25
+ catalog_visibility: 'visible',
26
status: 'publish',
27
} ),
28
} )
assets/js/components/product-preview/index.js CHANGED
@@ -13,23 +13,58 @@ import './style.scss';
13
* Display a preview for a given product.
14
*/
15
const ProductPreview = ( { product } ) => {
16
- const { placeholderImgSrc } = wc_product_block_data; /* eslint-disable-line camelcase */
17
18
let image = null;
19
if ( product.images.length ) {
20
- image = <img src={ product.images[ 0 ].src } alt="" />;
21
} else {
22
- image = <img src={ placeholderImgSrc } alt="" />;
23
}
24
25
return (
26
- <div className="wc-product-preview">
27
{ image }
28
- <div className="wc-product-preview__title">{ product.name }</div>
29
<div
30
className="wc-product-preview__price"
31
dangerouslySetInnerHTML={ { __html: product.price_html } }
32
/>
33
<span className="wp-block-button">
34
<span className="wc-product-preview__add-to-cart wp-block-button__link">
35
{ __( 'Add to cart', 'woo-gutenberg-products-block' ) }
13
* Display a preview for a given product.
14
*/
15
const ProductPreview = ( { product } ) => {
16
+ const {
17
+ placeholderImgSrc,
18
+ } = wc_product_block_data; /* eslint-disable-line camelcase */
19
20
let image = null;
21
if ( product.images.length ) {
22
+ image = (
23
+ <img
24
+ className="wc-product-preview__image"
25
+ src={ product.images[ 0 ].src }
26
+ alt=""
27
+ style={ { width: `${ wc_product_block_data.thumbnail_size }px` } }
28
+ />
29
+ );
30
} else {
31
+ image = (
32
+ <img
33
+ className="wc-product-preview__image"
34
+ src={ placeholderImgSrc }
35
+ alt=""
36
+ style={ { width: `${ wc_product_block_data.thumbnail_size }px` } }
37
+ />
38
+ );
39
+ }
40
+
41
+ const rating = Number( product.average_rating );
42
+ let displayRating = false;
43
+ if ( rating > 0 ) {
44
+ displayRating = ( rating / 5 ) * 100;
45
}
46
47
return (
48
+ <div
49
+ className="wc-product-preview"
50
+ style={ { maxWidth: `${ wc_product_block_data.thumbnail_size }px` } }
51
+ >
52
{ image }
53
+ <div
54
+ className="wc-product-preview__title"
55
+ dangerouslySetInnerHTML={ { __html: product.name } }
56
+ />
57
<div
58
className="wc-product-preview__price"
59
dangerouslySetInnerHTML={ { __html: product.price_html } }
60
/>
61
+
62
+ { displayRating && (
63
+ <div className="wc-product-preview__rating star-rating" role="img">
64
+ <span style={ { width: `${ displayRating }%` } } />
65
+ </div>
66
+ ) }
67
+
68
<span className="wp-block-button">
69
<span className="wc-product-preview__add-to-cart wp-block-button__link">
70
{ __( 'Add to cart', 'woo-gutenberg-products-block' ) }
assets/js/components/product-preview/style.scss CHANGED
@@ -1,12 +1,58 @@
1
.wc-product-preview {
2
- text-align: center;
3
margin-bottom: $gap;
4
5
.wc-product-preview__title,
6
- .wc-product-preview__price {
7
margin-top: $gap-smallest;
8
}
9
10
.wp-block-button {
11
margin-bottom: 0;
12
}
@@ -32,6 +78,30 @@
32
}
33
}
34
35
.editor-block-preview & {
36
.wc-product-preview__title {
37
font-size: 0.7em;
1
.wc-product-preview {
2
margin-bottom: $gap;
3
+ padding: $gap/2;
4
+ text-align: center;
5
6
.wc-product-preview__title,
7
+ .wc-product-preview__price,
8
+ .wc-product-preview__rating {
9
margin-top: $gap-smallest;
10
}
11
12
+ .wc-product-preview__image {
13
+ margin-left: auto;
14
+ margin-right: auto;
15
+ }
16
+
17
+ .star-rating {
18
+ overflow: hidden;
19
+ position: relative;
20
+ margin-left: auto;
21
+ margin-right: auto;
22
+ width: 5.3em;
23
+ height: 1.618em;
24
+ line-height: 1.618;
25
+ font-size: 1em;
26
+ font-family: star;
27
+ font-weight: 400;
28
+
29
+ &::before {
30
+ content: '\53\53\53\53\53';
31
+ top: 0;
32
+ left: 0;
33
+ right: 0;
34
+ position: absolute;
35
+ opacity: 0.25;
36
+ }
37
+
38
+ span {
39
+ overflow: hidden;
40
+ top: 0;
41
+ left: 0;
42
+ right: 0;
43
+ position: absolute;
44
+ padding-top: 1.5em;
45
+ }
46
+
47
+ span::before {
48
+ content: '\53\53\53\53\53';
49
+ top: 0;
50
+ left: 0;
51
+ right: 0;
52
+ position: absolute;
53
+ }
54
+ }
55
+
56
.wp-block-button {
57
margin-bottom: 0;
58
}
78
}
79
}
80
81
+ .is-hidden-title & {
82
+ .wc-product-preview__title {
83
+ display: none;
84
+ }
85
+ }
86
+
87
+ .is-hidden-price & {
88
+ .wc-product-preview__price {
89
+ display: none;
90
+ }
91
+ }
92
+
93
+ .is-hidden-rating & {
94
+ .wc-product-preview__rating {
95
+ display: none;
96
+ }
97
+ }
98
+
99
+ .is-hidden-button & {
100
+ .wp-block-button {
101
+ display: none;
102
+ }
103
+ }
104
+
105
.editor-block-preview & {
106
.wc-product-preview__title {
107
font-size: 0.7em;
assets/js/components/products-control/index.js CHANGED
@@ -7,11 +7,7 @@ import apiFetch from '@wordpress/api-fetch';
7
import { Component, Fragment } from '@wordpress/element';
8
import { find } from 'lodash';
9
import PropTypes from 'prop-types';
10
-
11
- /**
12
- * Internal dependencies
13
- */
14
- import SearchListControl from '../search-list-control';
15
16
class ProductsControl extends Component {
17
constructor() {
@@ -24,7 +20,11 @@ class ProductsControl extends Component {
24
25
componentDidMount() {
26
apiFetch( {
27
- path: addQueryArgs( '/wc-pb/v3/products', { per_page: -1, status: 'publish' } ),
28
} )
29
.then( ( list ) => {
30
this.setState( { list, loading: false } );
7
import { Component, Fragment } from '@wordpress/element';
8
import { find } from 'lodash';
9
import PropTypes from 'prop-types';
10
+ import { SearchListControl } from '@woocommerce/components';
11
12
class ProductsControl extends Component {
13
constructor() {
20
21
componentDidMount() {
22
apiFetch( {
23
+ path: addQueryArgs( '/wc-blocks/v1/products', {
24
+ per_page: -1,
25
+ catalog_visibility: 'visible',
26
+ status: 'publish',
27
+ } ),
28
} )
29
.then( ( list ) => {
30
this.setState( { list, loading: false } );
assets/js/components/search-list-control/hierarchy.js DELETED
@@ -1,48 +0,0 @@
1
- /**
2
- * External dependencies
3
- */
4
- import { forEach, groupBy, keyBy } from 'lodash';
5
-
6
- /**
7
- * Returns terms in a tree form.
8
- *
9
- * @param {Array} filteredList Array of terms, possibly a subset of all terms, in flat format.
10
- * @param {Array} list Array of the full list of terms, defaults to the filteredList.
11
- *
12
- * @return {Array} Array of terms in tree format.
13
- */
14
- export function buildTermsTree( filteredList, list = filteredList ) {
15
- const termsByParent = groupBy( filteredList, 'parent' );
16
- const listById = keyBy( list, 'id' );
17
-
18
- const getParentsName = ( term = {} ) => {
19
- if ( ! term.parent ) {
20
- return term.name ? [ term.name ] : [];
21
- }
22
-
23
- const parentName = getParentsName( listById[ term.parent ] );
24
- return [ ...parentName, term.name ];
25
- };
26
-
27
- const fillWithChildren = ( terms ) => {
28
- return terms.map( ( term ) => {
29
- const children = termsByParent[ term.id ];
30
- delete termsByParent[ term.id ];
31
- return {
32
- ...term,
33
- breadcrumbs: getParentsName( listById[ term.parent ] ),
34
- children: children && children.length ? fillWithChildren( children ) : [],
35
- };
36
- } );
37
- };
38
-
39
- const tree = fillWithChildren( termsByParent[ '0' ] || [] );
40
- delete termsByParent[ '0' ];
41
-
42
- // anything left in termsByParent has no visible parent
43
- forEach( termsByParent, ( terms ) => {
44
- tree.push( ...fillWithChildren( terms || [] ) );
45
- } );
46
-
47
- return tree;
48
- }