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

Commit

Permalink
Add Single Product Details block (#8225)
Browse files Browse the repository at this point in the history
* Add minimum structure for Single Product Details block

* Add tests for Single Product Details block

* wip: create block structure and add initial styles

* Add block details to the SingleProductDetails.php file

* Render tabs title with empty content

* Use woocommerce_output_product_data_tabs function to retrieve tabs data

* Remove customizations for the Single Product Details block

* Remove unnecessary console.log from the Edit.tsx file

* Remove block classname from block wrapper

* Remove unnecessary WooCommerce tabs filter from the BlockTemplatesController

* Remove attributes property from the block registration

* Remove isExperimental flag for the Single Product Details block

* Remove get_classes_and_styles_by_attributes method from SingleProductDetails block

* Prevent Single Product Details block from apppearing in Pages or Posts

* Fix PHP Coding Standards warnings

* update block name

* fix SCSS linter error

* move blocks into product-elements folder and rename to product-details

* avoid 404 error

* disable js asset enqueue

---------

Co-authored-by: Luigi Teschio <gigitux@gmail.com>
  • Loading branch information
thealexandrelara and gigitux authored Feb 16, 2023
1 parent 9ff32cc commit 9095720
Show file tree
Hide file tree
Showing 13 changed files with 319 additions and 1 deletion.
1 change: 1 addition & 0 deletions assets/js/atomic/blocks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ import './product-elements/tag-list';
import './product-elements/stock-indicator';
import './product-elements/add-to-cart';
import './product-elements/product-image-gallery';
import './product-elements/product-details';
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "woocommerce/product-details",
"version": "1.0.0",
"icon": "info",
"title": "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"
}
95 changes: 95 additions & 0 deletions assets/js/atomic/blocks/product-elements/product-details/block.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';

/**
* 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 blockProps = useBlockProps();
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 { ...blockProps }>
<ul className="tabs" role="tablist">
{ tabsTitle }
</ul>
{ tabsContent }
</div>
);
};

export default SingleProductDetails;
31 changes: 31 additions & 0 deletions assets/js/atomic/blocks/product-elements/product-details/edit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* External dependencies
*/
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';

const Edit = ( { attributes }: BlockEditProps< Attributes > ) => {
const { className } = attributes;
const blockProps = useBlockProps( {
className,
} );

return (
<>
<div { ...blockProps }>
<Disabled>
<Block />
</Disabled>
</div>
</>
);
};

export default Edit;
24 changes: 24 additions & 0 deletions assets/js/atomic/blocks/product-elements/product-details/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* External dependencies
*/
import { registerBlockSingleProductTemplate } from '@woocommerce/atomic-utils';
import { registerBlockType, unregisterBlockType } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import metadata from './block.json';
import edit from './edit';

registerBlockSingleProductTemplate( {
registerBlockFn: () => {
// @ts-expect-error: `registerBlockType` is a function that is typed in WordPress core.
registerBlockType( metadata, {
edit,
} );
},
unregisterBlockFn: () => {
unregisterBlockType( metadata.name );
},
blockName: metadata.name,
} );
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.wp-block-woocommerce-product-details {
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: color.adjust($black, $lightness: 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;
}
1 change: 0 additions & 1 deletion src/BlockTemplatesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ protected function init() {
add_filter( 'get_block_templates', array( $this, 'add_block_templates' ), 10, 3 );
add_filter( 'current_theme_supports-block-templates', array( $this, 'remove_block_template_support_for_shop_page' ) );
add_filter( 'taxonomy_template_hierarchy', array( $this, 'add_archive_product_to_eligible_for_fallback_templates' ), 10, 1 );

if ( $this->package->is_experimental_build() ) {
add_action( 'after_switch_theme', array( $this, 'check_should_use_blockified_product_grid_templates' ), 10, 2 );
}
Expand Down
63 changes: 63 additions & 0 deletions src/BlockTypes/ProductDetails.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace Automattic\WooCommerce\Blocks\BlockTypes;

/**
* ProductDetails class.
*/
class ProductDetails extends AbstractBlock {
/**
* Block name.
*
* @var string
*/
protected $block_name = 'product-details';

/**
* It isn't necessary register block assets because it is a server side block.
*/
protected function register_block_type_assets() {
return null;
}

/**
* 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();

$classname = $attributes['className'] ?? '';

return sprintf(
'<div class="wp-block-woocommerce-product-details %1$s">
%2$s
</div>',
esc_attr( $classname ),
$tabs
);
}

/**
* Gets the tabs with their content to be rendered by the block.
*
* @return string The tabs html to be rendered by the block
*/
protected function render_tabs() {
ob_start();

while ( have_posts() ) {
the_post();
woocommerce_output_product_data_tabs();
}

$tabs = ob_get_clean();

return $tabs;
}
}
7 changes: 7 additions & 0 deletions src/BlockTypes/ProductImageGallery.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ protected function get_block_type_uses_context() {
return [ 'query', 'queryId', 'postId' ];
}

/**
* It isn't necessary register block assets because it is a server side block.
*/
protected function register_block_type_assets() {
return null;
}


/**
* Include and render the block.
Expand Down
2 changes: 2 additions & 0 deletions src/BlockTypesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ protected function get_block_types() {
'RatingFilter',
'ReviewsByCategory',
'ReviewsByProduct',
'ProductDetails',
'StockFilter',
];

Expand Down Expand Up @@ -255,6 +256,7 @@ protected function get_block_types() {
'CatalogSorting',
'ClassicTemplate',
'ProductResultsCount',
'ProductDetails',
'StoreNotices',
]
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"title":"Product Details Block","pageContent":"<!-- wp:woocommerce/single-product-details /-->"}
32 changes: 32 additions & 0 deletions tests/e2e/specs/backend/single-produt-details.test.js
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: '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 );
} );
} );

0 comments on commit 9095720

Please sign in to comment.