Commit ba8854342d for woocommerce
commit ba8854342d6a1c37b3d9d4099fb137600096930f
Author: Amit Raj <77401999+amitraj2203@users.noreply.github.com>
Date: Thu Feb 5 21:28:42 2026 +0530
Refactor Product Specifications Block to use ToolsPanel (#62903)
* Refactor product specifications block to use ToolsPanel
* Add changelog file
* Remove redundant resetAllFilter from all three ToolsPanelItem components
* Refactor Product Specifications block tests to ensure ToolsPanel initialization and section visibility toggling
diff --git a/plugins/woocommerce/changelog/59464-use-toolspanel-in-product-specifications-block b/plugins/woocommerce/changelog/59464-use-toolspanel-in-product-specifications-block
new file mode 100644
index 0000000000..ad7ff3a151
--- /dev/null
+++ b/plugins/woocommerce/changelog/59464-use-toolspanel-in-product-specifications-block
@@ -0,0 +1,4 @@
+Significance: minor
+Type: update
+
+Removes PanelBody and uses ToolsPanel in Product Specifications block
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-specifications/edit.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/product-specifications/edit.tsx
index fa6a6564e6..0e14dad5e8 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-specifications/edit.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-specifications/edit.tsx
@@ -6,13 +6,25 @@ import { __ } from '@wordpress/i18n';
import { useQueryLoopProductContextValidation } from '@woocommerce/base-hooks';
import { useSelect } from '@wordpress/data';
import { optionsStore, Product, productsStore } from '@woocommerce/data';
-import { PanelBody, ToggleControl } from '@wordpress/components';
+import {
+ ToggleControl,
+ // eslint-disable-next-line @wordpress/no-unsafe-wp-apis
+ __experimentalToolsPanel as ToolsPanel,
+ // eslint-disable-next-line @wordpress/no-unsafe-wp-apis
+ __experimentalToolsPanelItem as ToolsPanelItem,
+} from '@wordpress/components';
/**
* Internal dependencies
*/
import { ProductSpecificationsEditProps } from './types';
+const DEFAULT_ATTRIBUTES = {
+ showWeight: true,
+ showDimensions: true,
+ showAttributes: true,
+};
+
const getFormattedDimensions = (
dimensions: Product[ 'dimensions' ],
dimensionUnit: string
@@ -180,33 +192,79 @@ const Edit = ( {
return (
<>
<InspectorControls>
- <PanelBody title={ __( 'Display Settings', 'woocommerce' ) }>
- <ToggleControl
+ <ToolsPanel
+ label={ __( 'Display Settings', 'woocommerce' ) }
+ resetAll={ () => {
+ setAttributes( DEFAULT_ATTRIBUTES );
+ } }
+ >
+ <ToolsPanelItem
label={ __( 'Show Weight', 'woocommerce' ) }
- checked={ showWeight }
- onChange={ () =>
- setAttributes( { showWeight: ! showWeight } )
+ hasValue={ () =>
+ showWeight !== DEFAULT_ATTRIBUTES.showWeight
}
- />
- <ToggleControl
+ onDeselect={ () =>
+ setAttributes( {
+ showWeight: DEFAULT_ATTRIBUTES.showWeight,
+ } )
+ }
+ isShownByDefault
+ >
+ <ToggleControl
+ label={ __( 'Show Weight', 'woocommerce' ) }
+ checked={ showWeight }
+ onChange={ () =>
+ setAttributes( { showWeight: ! showWeight } )
+ }
+ />
+ </ToolsPanelItem>
+ <ToolsPanelItem
label={ __( 'Show Dimensions', 'woocommerce' ) }
- checked={ showDimensions }
- onChange={ () =>
+ hasValue={ () =>
+ showDimensions !== DEFAULT_ATTRIBUTES.showDimensions
+ }
+ onDeselect={ () =>
setAttributes( {
- showDimensions: ! showDimensions,
+ showDimensions:
+ DEFAULT_ATTRIBUTES.showDimensions,
} )
}
- />
- <ToggleControl
+ isShownByDefault
+ >
+ <ToggleControl
+ label={ __( 'Show Dimensions', 'woocommerce' ) }
+ checked={ showDimensions }
+ onChange={ () =>
+ setAttributes( {
+ showDimensions: ! showDimensions,
+ } )
+ }
+ />
+ </ToolsPanelItem>
+ <ToolsPanelItem
label={ __( 'Show Attributes', 'woocommerce' ) }
- checked={ showAttributes }
- onChange={ () =>
+ hasValue={ () =>
+ showAttributes !== DEFAULT_ATTRIBUTES.showAttributes
+ }
+ onDeselect={ () =>
setAttributes( {
- showAttributes: ! showAttributes,
+ showAttributes:
+ DEFAULT_ATTRIBUTES.showAttributes,
} )
}
- />
- </PanelBody>
+ isShownByDefault
+ >
+ <ToggleControl
+ label={ __( 'Show Attributes', 'woocommerce' ) }
+ checked={ showAttributes }
+ onChange={ () =>
+ setAttributes( {
+ showAttributes: ! showAttributes,
+ } )
+ }
+ />
+ </ToolsPanelItem>
+ </ToolsPanel>
</InspectorControls>
<figure { ...blockProps }>
<table>
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-specifications/test/block.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-specifications/test/block.ts
index 12f5f6d357..6f8d535654 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-specifications/test/block.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-specifications/test/block.ts
@@ -43,6 +43,13 @@ describe( 'Product Specifications block', () => {
if ( displaySettings.getAttribute( 'aria-expanded' ) !== 'true' ) {
fireEvent.click( displaySettings );
}
+
+ // Wait for ToolsPanel to fully initialize and settle.
+ await waitFor( () => {
+ expect(
+ screen.getByRole( 'checkbox', { name: /Show Weight/i } )
+ ).toBeVisible();
+ } );
} );
test( 'should show all sections by default', () => {
@@ -70,58 +77,70 @@ describe( 'Product Specifications block', () => {
).toBeChecked();
} );
- test( 'should hide weight section when toggled off', () => {
+ test( 'should hide weight section when toggled off', async () => {
const block = within(
screen.getByLabelText( /Block: Product Specifications/i )
);
- fireEvent.click(
- screen.getByRole( 'checkbox', { name: /Show Weight/i } )
- );
+ const weightCheckbox = screen.getByRole( 'checkbox', {
+ name: /Show Weight/i,
+ } );
+
+ fireEvent.click( weightCheckbox );
+
+ await waitFor( () => {
+ expect(
+ block.queryByText( /Weight/i )
+ ).not.toBeInTheDocument();
+ } );
- expect( block.queryByText( /Weight/i ) ).not.toBeInTheDocument();
expect( block.getByText( /Dimensions/i ) ).toBeInTheDocument();
expect( block.getByText( /Test Attribute/i ) ).toBeInTheDocument();
} );
- test( 'should hide dimensions section when toggled off', () => {
+ test( 'should hide dimensions section when toggled off', async () => {
const block = within(
screen.getByLabelText( /Block: Product Specifications/i )
);
- fireEvent.click(
- screen.getByRole( 'checkbox', {
- name: /Show Dimensions/i,
- } )
- );
+ const dimensionsCheckbox = screen.getByRole( 'checkbox', {
+ name: /Show Dimensions/i,
+ } );
- expect( block.getByText( /Weight/i ) ).toBeInTheDocument();
+ fireEvent.click( dimensionsCheckbox );
- expect(
- block.queryByText( /Dimensions/i )
- ).not.toBeInTheDocument();
+ await waitFor( () => {
+ expect(
+ block.queryByText( /Dimensions/i )
+ ).not.toBeInTheDocument();
+ } );
+
+ expect( block.getByText( /Weight/i ) ).toBeInTheDocument();
expect( block.getByText( /Test Attribute/i ) ).toBeInTheDocument();
} );
- test( 'should hide attributes section when toggled off', () => {
+ test( 'should hide attributes section when toggled off', async () => {
const block = within(
screen.getByLabelText( /Block: Product Specifications/i )
);
- fireEvent.click(
- screen.getByRole( 'checkbox', {
- name: /Show Attributes/i,
- } )
- );
+ const attributesCheckbox = screen.getByRole( 'checkbox', {
+ name: /Show Attributes/i,
+ } );
+
+ fireEvent.click( attributesCheckbox );
+
+ await waitFor( () => {
+ expect(
+ block.queryByText( /Test Attribute/i )
+ ).not.toBeInTheDocument();
+ } );
expect( block.getByText( /Weight/i ) ).toBeInTheDocument();
expect( block.getByText( /Dimensions/i ) ).toBeInTheDocument();
- expect(
- block.queryByText( /Test Attribute/i )
- ).not.toBeInTheDocument();
} );
- test( 'should restore visibility when sections are toggled back on', () => {
+ test( 'should restore visibility when sections are toggled back on', async () => {
const block = within(
screen.getByLabelText( /Block: Product Specifications/i )
);
@@ -137,6 +156,19 @@ describe( 'Product Specifications block', () => {
screen.getByRole( 'checkbox', { name: /Show Attributes/i } )
);
+ // Wait for all items to be hidden.
+ await waitFor( () => {
+ expect(
+ block.queryByText( /Weight/i )
+ ).not.toBeInTheDocument();
+ expect(
+ block.queryByText( /Dimensions/i )
+ ).not.toBeInTheDocument();
+ expect(
+ block.queryByText( /Test Attribute/i )
+ ).not.toBeInTheDocument();
+ } );
+
// Then show them all again
fireEvent.click(
screen.getByRole( 'checkbox', { name: /Show Weight/i } )
@@ -148,9 +180,14 @@ describe( 'Product Specifications block', () => {
screen.getByRole( 'checkbox', { name: /Show Attributes/i } )
);
- expect( block.getByText( /Weight/i ) ).toBeInTheDocument();
- expect( block.getByText( /Dimensions/i ) ).toBeInTheDocument();
- expect( block.getByText( /Test Attribute/i ) ).toBeInTheDocument();
+ // Wait for all items to be shown.
+ await waitFor( () => {
+ expect( block.getByText( /Weight/i ) ).toBeInTheDocument();
+ expect( block.getByText( /Dimensions/i ) ).toBeInTheDocument();
+ expect(
+ block.getByText( /Test Attribute/i )
+ ).toBeInTheDocument();
+ } );
} );
} );
} );