Skip to content
This repository was archived by the owner on Feb 23, 2024. It is now read-only.

Commit 48d93ae

Browse files
authored
Add "Featured Product" block preview (#308)
* Add classnames package * Add background image & initial styles * Only show inspector controls when not in edit-mode * Add overlay color + opacity controls * Add content alignment * Fix display of content * Add content toggles * Update styles from feedback * Display the "Featured Product" block on the frontend (#310) * Add render_callback to dynamically render product * Set a size for the product description * Remove fallback placeholder image * Reset the background color for the placeholder component Previously this was overriding storefront's custom background color CSS * Update nested conditional * Remove unnecessary style * "Featured Product" Block: Add link to the product to the block (#311) * Featured Product Block: Add link to the product to the block * Add a better link label for screen reader users * Match core button styles, fix alignment of button-link
1 parent af768b4 commit 48d93ae

File tree

8 files changed

+504
-30
lines changed

8 files changed

+504
-30
lines changed

assets/css/abstracts/_variables.scss

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,15 @@ $gap: 16px;
55
$gap-small: 12px;
66
$gap-smaller: 8px;
77
$gap-smallest: 4px;
8+
9+
// Variables pulled from Gutenberg.
10+
// Editor Widths
11+
$sidebar-width: 280px;
12+
$content-width: 610px; // For the visual width, subtract 30px (2 * $block-padding + 2px borders). This comes to 580px, which is optimized for 70 characters.
13+
14+
// Blocks
15+
$block-padding: 14px; // Space between block footprint and focus boundaries. These are drawn outside the block footprint, and do not affect the size.
16+
$block-spacing: 4px; // Vertical space between blocks.
17+
$block-side-ui-width: 28px; // Width of the movers/drag handle UI.
18+
$block-side-ui-clearance: 2px; // Space between movers/drag handle UI, and block.
19+
$block-container-side-padding: $block-side-ui-width + $block-padding + 2 * $block-side-ui-clearance; // Total space left and right of the block footprint.

assets/js/blocks/featured-product/block.js

Lines changed: 133 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,27 @@
33
*/
44
import { __ } from '@wordpress/i18n';
55
import apiFetch from '@wordpress/api-fetch';
6-
import { BlockControls, InspectorControls } from '@wordpress/editor';
6+
import {
7+
AlignmentToolbar,
8+
BlockControls,
9+
InspectorControls,
10+
PanelColorSettings,
11+
RichText,
12+
withColors,
13+
} from '@wordpress/editor';
714
import {
815
Button,
916
PanelBody,
1017
Placeholder,
18+
RangeControl,
1119
Spinner,
20+
ToggleControl,
1221
Toolbar,
1322
withSpokenMessages,
1423
} from '@wordpress/components';
24+
import classnames from 'classnames';
1525
import { Component, Fragment } from '@wordpress/element';
26+
import { compose } from '@wordpress/compose';
1627
import { debounce } from 'lodash';
1728
import PropTypes from 'prop-types';
1829

@@ -21,7 +32,21 @@ import PropTypes from 'prop-types';
2132
*/
2233
import { IconStar } from '../../components/icons';
2334
import ProductControl from '../../components/product-control';
24-
import ProductPreview from '../../components/product-preview';
35+
36+
// Copied from core/cover, updated for product.
37+
function backgroundImageStyles( { images = [] } ) {
38+
if ( images.length ) {
39+
const url = images[ 0 ].src;
40+
return { backgroundImage: `url(${ url })` };
41+
}
42+
return {};
43+
}
44+
45+
function dimRatioToClass( ratio ) {
46+
return ratio === 0 || ratio === 50 ?
47+
null :
48+
`has-background-dim-${ 10 * Math.round( ratio / 10 ) }`;
49+
}
2550

2651
/**
2752
* Component to handle edit mode of "Featured Product".
@@ -66,7 +91,12 @@ class FeaturedProduct extends Component {
6691
}
6792

6893
getInspectorControls() {
69-
const { attributes, setAttributes } = this.props;
94+
const {
95+
attributes,
96+
setAttributes,
97+
overlayColor,
98+
setOverlayColor,
99+
} = this.props;
70100

71101
return (
72102
<InspectorControls key="inspector">
@@ -82,6 +112,37 @@ class FeaturedProduct extends Component {
82112
} }
83113
/>
84114
</PanelBody>
115+
<PanelBody title={ __( 'Content', 'woo-gutenberg-products-block' ) }>
116+
<ToggleControl
117+
label="Show description"
118+
checked={ attributes.showDesc }
119+
onChange={ () => setAttributes( { showDesc: ! attributes.showDesc } ) }
120+
/>
121+
<ToggleControl
122+
label="Show price"
123+
checked={ attributes.showPrice }
124+
onChange={ () => setAttributes( { showPrice: ! attributes.showPrice } ) }
125+
/>
126+
</PanelBody>
127+
<PanelColorSettings
128+
title={ __( 'Overlay', 'woo-gutenberg-products-block' ) }
129+
colorSettings={ [
130+
{
131+
value: overlayColor.color,
132+
onChange: setOverlayColor,
133+
label: __( 'Overlay Color', 'woo-gutenberg-products-block' ),
134+
},
135+
] }
136+
>
137+
<RangeControl
138+
label={ __( 'Background Opacity', 'woo-gutenberg-products-block' ) }
139+
value={ attributes.dimRatio }
140+
onChange={ ( ratio ) => setAttributes( { dimRatio: ratio } ) }
141+
min={ 0 }
142+
max={ 100 }
143+
step={ 10 }
144+
/>
145+
</PanelColorSettings>
85146
</InspectorControls>
86147
);
87148
}
@@ -125,21 +186,41 @@ class FeaturedProduct extends Component {
125186
}
126187

127188
render() {
128-
const { attributes, setAttributes } = this.props;
129-
const { editMode } = attributes;
189+
const { attributes, setAttributes, overlayColor } = this.props;
190+
const {
191+
contentAlign,
192+
dimRatio,
193+
editMode,
194+
linkText,
195+
showDesc,
196+
showPrice,
197+
} = attributes;
130198
const { loaded, product } = this.state;
131-
const classes = [ 'wc-block-featured-product' ];
132-
if ( ! product ) {
133-
if ( ! loaded ) {
134-
classes.push( 'is-loading' );
135-
} else {
136-
classes.push( 'is-not-found' );
137-
}
199+
const classes = classnames(
200+
'wc-block-featured-product',
201+
{
202+
'is-loading': ! product && ! loaded,
203+
'is-not-found': ! product && loaded,
204+
'has-background-dim': dimRatio !== 0,
205+
},
206+
dimRatioToClass( dimRatio ),
207+
contentAlign !== 'center' && `has-${ contentAlign }-content`
208+
);
209+
210+
const style = !! product ? backgroundImageStyles( product ) : {};
211+
if ( overlayColor.color ) {
212+
style.backgroundColor = overlayColor.color;
138213
}
139214

140215
return (
141216
<Fragment>
142217
<BlockControls>
218+
<AlignmentToolbar
219+
value={ contentAlign }
220+
onChange={ ( nextAlign ) => {
221+
setAttributes( { contentAlign: nextAlign } );
222+
} }
223+
/>
143224
<Toolbar
144225
controls={ [
145226
{
@@ -151,15 +232,44 @@ class FeaturedProduct extends Component {
151232
] }
152233
/>
153234
</BlockControls>
154-
{ this.getInspectorControls() }
235+
{ ! attributes.editMode && this.getInspectorControls() }
155236
{ editMode ? (
156237
this.renderEditMode()
157238
) : (
158-
<div className={ classes.join( ' ' ) }>
239+
<Fragment>
159240
{ !! product ? (
160-
<ProductPreview product={ product } key={ product.id } />
241+
<div className={ classes } style={ style }>
242+
<h2 className="wc-block-featured-product__title">
243+
{ product.name }
244+
</h2>
245+
{ showDesc && (
246+
<div
247+
className="wc-block-featured-product__description"
248+
dangerouslySetInnerHTML={ {
249+
__html:
250+
'<p>Black cotton top with matching striped skirt. </p>\n',
251+
} }
252+
/>
253+
) }
254+
{ showPrice && (
255+
<div
256+
className="wc-block-featured-product__price"
257+
dangerouslySetInnerHTML={ { __html: product.price_html } }
258+
/>
259+
) }
260+
<div className="wc-block-featured-product__link wp-block-button">
261+
<RichText
262+
value={ linkText }
263+
onChange={ ( value ) => setAttributes( { linkText: value } ) }
264+
formattingControls={ [ 'bold', 'italic', 'strikethrough' ] }
265+
className="wp-block-button__link"
266+
keepPlaceholderOnFocus
267+
/>
268+
</div>
269+
</div>
161270
) : (
162271
<Placeholder
272+
className="wc-block-featured-product"
163273
icon={ <IconStar /> }
164274
label={ __( 'Featured Product', 'woo-gutenberg-products-block' ) }
165275
>
@@ -170,7 +280,7 @@ class FeaturedProduct extends Component {
170280
) }
171281
</Placeholder>
172282
) }
173-
</div>
283+
</Fragment>
174284
) }
175285
</Fragment>
176286
);
@@ -190,8 +300,14 @@ FeaturedProduct.propTypes = {
190300
* A callback to update attributes
191301
*/
192302
setAttributes: PropTypes.func.isRequired,
303+
// from withColors
304+
overlayColor: PropTypes.object,
305+
setOverlayColor: PropTypes.func.isRequired,
193306
// from withSpokenMessages
194307
debouncedSpeak: PropTypes.func.isRequired,
195308
};
196309

197-
export default withSpokenMessages( FeaturedProduct );
310+
export default compose( [
311+
withColors( { overlayColor: 'background-color' } ),
312+
withSpokenMessages,
313+
] )( FeaturedProduct );

assets/js/blocks/featured-product/index.js

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { registerBlockType } from '@wordpress/blocks';
77
/**
88
* Internal dependencies
99
*/
10+
import './style.scss';
1011
import Block from './block';
1112
import { IconStar } from '../../components/icons';
1213

@@ -26,6 +27,22 @@ registerBlockType( 'woocommerce/featured-product', {
2627
align: [ 'wide', 'full' ],
2728
},
2829
attributes: {
30+
/**
31+
* Alignment of content inside block.
32+
*/
33+
contentAlign: {
34+
type: 'string',
35+
default: 'center',
36+
},
37+
38+
/**
39+
* Percentage opacity of overlay.
40+
*/
41+
dimRatio: {
42+
type: 'number',
43+
default: 50,
44+
},
45+
2946
/**
3047
* Toggle for edit mode in the block preview.
3148
*/
@@ -35,11 +52,49 @@ registerBlockType( 'woocommerce/featured-product', {
3552
},
3653

3754
/**
38-
* The product ID to display
55+
* The overlay color, from the color list.
56+
*/
57+
overlayColor: {
58+
type: 'string',
59+
},
60+
61+
/**
62+
* The overlay color, if a custom color value.
63+
*/
64+
customOverlayColor: {
65+
type: 'string',
66+
},
67+
68+
/**
69+
* Text for the product link.
70+
*/
71+
linkText: {
72+
type: 'string',
73+
default: __( 'Shop now', 'woo-gutenberg-products-block' ),
74+
},
75+
76+
/**
77+
* The product ID to display.
3978
*/
4079
productId: {
4180
type: 'number',
4281
},
82+
83+
/**
84+
* Show the product description.
85+
*/
86+
showDesc: {
87+
type: 'boolean',
88+
default: true,
89+
},
90+
91+
/**
92+
* Show the product price.
93+
*/
94+
showPrice: {
95+
type: 'boolean',
96+
default: true,
97+
},
4398
},
4499

45100
/**

0 commit comments

Comments
 (0)