-
Notifications
You must be signed in to change notification settings - Fork 221
Add Single Product Details block #8225
Changes from 8 commits
d46e480
e743ebe
f87de21
561f6f8
d07b9b5
9c90f71
eb0ce4a
22be4e9
2a4baad
8e05b71
f70ab0c
4ea37cb
ad70695
fd83c09
3661133
8a9196e
edcac21
528b9ce
0ba35e1
af09b77
93bcb28
7742ffe
96fab80
43dd558
85e6247
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"name": "woocommerce/single-product-details", | ||
"version": "1.0.0", | ||
"title": "Single Product Details", | ||
"description": "A block that allows your customers to see details and reviews about the product.", | ||
"category": "woocommerce", | ||
"keywords": [ "WooCommerce" ], | ||
"supports": {}, | ||
"attributes": {}, | ||
"textdomain": "woo-gutenberg-products-block", | ||
"apiVersion": 2, | ||
"$schema": "https://schemas.wp.org/trunk/block.json" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Miss the declaration for the icon. Any idea about what icon should we pick? @vivialice |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import classnames from 'classnames'; | ||
import { __ } from '@wordpress/i18n'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
|
||
interface SingleProductTab { | ||
id: string; | ||
title: string; | ||
active: boolean; | ||
content: string | undefined; | ||
} | ||
|
||
const ProductTabTitle = ( { | ||
id, | ||
title, | ||
active, | ||
}: Pick< SingleProductTab, 'id' | 'title' | 'active' > ) => { | ||
return ( | ||
<li | ||
className={ classnames( `${ id }_tab`, { | ||
active, | ||
} ) } | ||
id={ `tab-title-${ id }` } | ||
role="tab" | ||
aria-controls={ `tab-${ id }` } | ||
> | ||
<a href={ `#tab-${ id }` }>{ title }</a> | ||
</li> | ||
); | ||
}; | ||
|
||
const ProductTabContent = ( { | ||
id, | ||
content, | ||
}: Pick< SingleProductTab, 'id' | 'content' > ) => { | ||
return ( | ||
<div | ||
className={ `${ id }_tab` } | ||
id={ `tab-title-${ id }` } | ||
role="tab" | ||
aria-controls={ `tab-${ id }` } | ||
> | ||
{ content } | ||
</div> | ||
); | ||
}; | ||
|
||
export const SingleProductDetails = () => { | ||
const productTabs = [ | ||
{ | ||
id: 'description', | ||
title: 'Description', | ||
active: true, | ||
content: __( | ||
'This block lists description, attributes and reviews for a single product.', | ||
'woo-gutenberg-products-block' | ||
), | ||
}, | ||
{ | ||
id: 'additional_information', | ||
title: 'Additional Information', | ||
active: false, | ||
}, | ||
{ id: 'reviews', title: 'Reviews', active: false }, | ||
]; | ||
const tabsTitle = productTabs.map( ( { id, title, active } ) => ( | ||
<ProductTabTitle | ||
key={ id } | ||
id={ id } | ||
title={ title } | ||
active={ active } | ||
/> | ||
) ); | ||
const tabsContent = productTabs.map( ( { id, content } ) => ( | ||
<ProductTabContent key={ id } id={ id } content={ content } /> | ||
) ); | ||
|
||
return ( | ||
<div className="woocommerce-tabs wc-tabs-wrapper"> | ||
thealexandrelara marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<ul className="tabs" role="tablist"> | ||
{ tabsTitle } | ||
</ul> | ||
{ tabsContent } | ||
</div> | ||
); | ||
}; | ||
|
||
export default SingleProductDetails; |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,35 @@ | ||||||
/** | ||||||
* External dependencies | ||||||
*/ | ||||||
import classNames from 'classnames'; | ||||||
import { useBlockProps } from '@wordpress/block-editor'; | ||||||
import { Disabled } from '@wordpress/components'; | ||||||
import type { BlockEditProps } from '@wordpress/blocks'; | ||||||
|
||||||
/** | ||||||
* Internal dependencies | ||||||
*/ | ||||||
import Block from './block'; | ||||||
import { Attributes } from './types'; | ||||||
import './editor.scss'; | ||||||
|
||||||
const Edit = ( { attributes, ...rest }: BlockEditProps< Attributes > ) => { | ||||||
console.log( { attributes } ); | ||||||
thealexandrelara marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
console.log( { rest } ); | ||||||
thealexandrelara marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
const { className } = attributes; | ||||||
const blockProps = useBlockProps( { | ||||||
className: classNames( 'wc-block-single-product-details', className ), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the same idea about "no custom class", we could refactor this code in this way:
Suggested change
|
||||||
} ); | ||||||
|
||||||
return ( | ||||||
<> | ||||||
<div { ...blockProps }> | ||||||
<Disabled> | ||||||
<Block /> | ||||||
</Disabled> | ||||||
</div> | ||||||
</> | ||||||
); | ||||||
}; | ||||||
|
||||||
export default Edit; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.wc-block-single-product-details { | ||
ul.tabs.li { | ||
list-style-type: none; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { registerBlockType } from '@wordpress/blocks'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import metadata from './block.json'; | ||
import edit from './edit'; | ||
|
||
registerBlockType( metadata, { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should make the block available only on the Single Product Template. I created a util function to do this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that function is not merged into trunk yet, right? |
||
attributes: { | ||
thealexandrelara marked this conversation as resolved.
Show resolved
Hide resolved
|
||
...metadata.attributes, | ||
}, | ||
edit, | ||
save() { | ||
return null; | ||
}, | ||
} ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
.wp-block-woocommerce-single-product-details { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we resolve #8314, is this CSS still necessary? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I hope we don't need it anymore after solving that issue; however, if we need to change some things in the UX I think we still are going to need this |
||
ul.tabs { | ||
list-style: none; | ||
padding: 0 0 0 1em; | ||
margin: 0 0 1.618em; | ||
overflow: hidden; | ||
position: relative; | ||
border-bottom: 1px solid $gray-200; | ||
|
||
li { | ||
border: 1px solid $gray-200; | ||
background-color: $white; | ||
display: inline-block; | ||
position: relative; | ||
z-index: 0; | ||
border-radius: 4px 4px 0 0; | ||
margin: 0; | ||
padding: 0.5em 1em; | ||
opacity: 0.5; | ||
|
||
a { | ||
display: inline-block; | ||
font-weight: 700; | ||
color: $black; | ||
text-decoration: none; | ||
|
||
&:hover { | ||
text-decoration: none; | ||
color: lighten($black, 10%); | ||
} | ||
} | ||
|
||
&.active { | ||
background: $gray-100; | ||
z-index: 2; | ||
border-bottom-color: $gray-200; | ||
opacity: 1; | ||
|
||
a { | ||
color: inherit; | ||
text-shadow: inherit; | ||
} | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export interface Attributes { | ||
className?: string; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<?php | ||
|
||
namespace Automattic\WooCommerce\Blocks\BlockTypes; | ||
|
||
use Automattic\WooCommerce\Blocks\Utils\StyleAttributesUtils; | ||
|
||
/** | ||
* SingleProductDetails class. | ||
*/ | ||
class SingleProductDetails extends AbstractBlock { | ||
/** | ||
* Block name. | ||
* | ||
* @var string | ||
*/ | ||
protected $block_name = 'single-product-details'; | ||
|
||
/** | ||
* Render the block. | ||
* | ||
* @param array $attributes Block attributes. | ||
* @param string $content Block content. | ||
* @param WP_Block $block Block instance. | ||
* | ||
* @return string Rendered block output. | ||
*/ | ||
protected function render( $attributes, $content, $block ) { | ||
$tabs = $this->render_tabs(); | ||
|
||
$classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); | ||
thealexandrelara marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return sprintf( | ||
'<div class="wp-block-woocommerce-single-product-details %1$s" style="%2$s"> | ||
%3$s | ||
</div>', | ||
esc_attr( $classes_and_styles['classes'] ), | ||
esc_attr( $classes_and_styles['styles'] ), | ||
$tabs, | ||
); | ||
} | ||
|
||
protected function render_tabs(){ | ||
ob_start(); | ||
|
||
while ( have_posts() ) { | ||
the_post(); | ||
woocommerce_output_product_data_tabs(); | ||
} | ||
|
||
$tabs = ob_get_clean(); | ||
|
||
return $tabs; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"title":"Single Product Details Block","pageContent":"<!-- wp:woocommerce/single-product-details /-->"} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { getAllBlocks, switchUserToAdmin } from '@wordpress/e2e-test-utils'; | ||
import { visitBlockPage } from '@woocommerce/blocks-test-utils'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { insertBlockDontWaitForInsertClose } from '../../utils.js'; | ||
|
||
const block = { | ||
name: 'Single Product Details', | ||
slug: 'woocommerce/single-product-details', | ||
class: '.wc-block-single-product-details', | ||
}; | ||
|
||
describe( `${ block.name } Block`, () => { | ||
beforeAll( async () => { | ||
await switchUserToAdmin(); | ||
await visitBlockPage( `${ block.name } Block` ); | ||
} ); | ||
|
||
it( 'can be inserted more than once', async () => { | ||
await insertBlockDontWaitForInsertClose( block.name ); | ||
expect( await getAllBlocks() ).toHaveLength( 2 ); | ||
} ); | ||
|
||
it( 'renders without crashing', async () => { | ||
await expect( page ).toRenderBlock( block ); | ||
} ); | ||
} ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we name it
Product Details
orSingle Product Details
? 🤔 cc @vivialiceThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just name it
Product Details
for now.