diff --git a/packages/js/product-editor/changelog/dev-47357_request_terms_conditionally b/packages/js/product-editor/changelog/dev-47357_request_terms_conditionally
new file mode 100644
index 00000000000..9dd17454990
--- /dev/null
+++ b/packages/js/product-editor/changelog/dev-47357_request_terms_conditionally
@@ -0,0 +1,4 @@
+Significance: minor
+Type: dev
+
+Request attributes conditionally #47361
diff --git a/packages/js/product-editor/src/blocks/product-fields/attributes/block.json b/packages/js/product-editor/src/blocks/product-fields/attributes/block.json
index 9e4ebdf00a1..aed5dd2c121 100644
--- a/packages/js/product-editor/src/blocks/product-fields/attributes/block.json
+++ b/packages/js/product-editor/src/blocks/product-fields/attributes/block.json
@@ -22,5 +22,6 @@
"lock": false,
"__experimentalToolbar": false
},
+ "usesContext": [ "isInSelectedTab" ],
"editorStyle": "file:./editor.css"
}
diff --git a/packages/js/product-editor/src/blocks/product-fields/attributes/edit.tsx b/packages/js/product-editor/src/blocks/product-fields/attributes/edit.tsx
index d3cc42cc3ed..50ae61bde27 100644
--- a/packages/js/product-editor/src/blocks/product-fields/attributes/edit.tsx
+++ b/packages/js/product-editor/src/blocks/product-fields/attributes/edit.tsx
@@ -2,9 +2,11 @@
* External dependencies
*/
import { BlockAttributes } from '@wordpress/blocks';
-import { createElement } from '@wordpress/element';
+import { createElement, useEffect } from '@wordpress/element';
import { useWooBlockProps } from '@woocommerce/block-templates';
import { ProductProductAttribute } from '@woocommerce/data';
+import { __ } from '@wordpress/i18n';
+import { recordEvent } from '@woocommerce/tracks';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore No types for this exist yet.
// eslint-disable-next-line @woocommerce/dependency-group
@@ -13,11 +15,13 @@ import { useEntityProp, useEntityId } from '@wordpress/core-data';
/**
* Internal dependencies
*/
-import { Attributes as AttributesContainer } from '../../../components/attributes/attributes';
+import { AttributeControl } from '../../../components/attribute-control';
import { ProductEditorBlockEditProps } from '../../../types';
+import { useProductAttributes } from '../../../hooks/use-product-attributes';
export function AttributesBlockEdit( {
attributes,
+ context: { isInSelectedTab },
}: ProductEditorBlockEditProps< BlockAttributes > ) {
const [ entityAttributes, setEntityAttributes ] = useEntityProp<
ProductProductAttribute[]
@@ -27,12 +31,77 @@ export function AttributesBlockEdit( {
const blockProps = useWooBlockProps( attributes );
+ const {
+ attributes: attributeList,
+ fetchAttributes,
+ handleChange,
+ } = useProductAttributes( {
+ allAttributes: entityAttributes,
+ onChange: setEntityAttributes,
+ productId,
+ } );
+
+ useEffect( () => {
+ if ( isInSelectedTab ) {
+ fetchAttributes();
+ }
+ }, [ entityAttributes, isInSelectedTab ] );
+
return (
-
!! attr.variation )
+ .map( ( attr ) => attr.id ) }
+ uiStrings={ {
+ disabledAttributeMessage: __(
+ 'Already used in Variations',
+ 'woocommerce'
+ ),
+ } }
+ onAdd={ () => {
+ recordEvent(
+ 'product_add_attributes_modal_add_button_click'
+ );
+ } }
+ onChange={ handleChange }
+ onNewModalCancel={ () => {
+ recordEvent(
+ 'product_add_attributes_modal_cancel_button_click'
+ );
+ } }
+ onNewModalOpen={ () => {
+ if ( ! attributeList.length ) {
+ recordEvent(
+ 'product_add_first_attribute_button_click'
+ );
+ return;
+ }
+ recordEvent( 'product_add_attribute_button' );
+ } }
+ onAddAnother={ () => {
+ recordEvent(
+ 'product_add_attributes_modal_add_another_attribute_button_click'
+ );
+ } }
+ onRemoveItem={ () => {
+ recordEvent(
+ 'product_add_attributes_modal_remove_attribute_button_click'
+ );
+ } }
+ onRemove={ () =>
+ recordEvent(
+ 'product_remove_attribute_confirmation_confirm_click'
+ )
+ }
+ onRemoveCancel={ () =>
+ recordEvent(
+ 'product_remove_attribute_confirmation_cancel_click'
+ )
+ }
+ termsAutoSelection="first"
+ defaultVisibility={ true }
/>
);
diff --git a/packages/js/product-editor/src/blocks/product-fields/variation-options/block.json b/packages/js/product-editor/src/blocks/product-fields/variation-options/block.json
index c7aa348db3d..4b2f7c70d33 100644
--- a/packages/js/product-editor/src/blocks/product-fields/variation-options/block.json
+++ b/packages/js/product-editor/src/blocks/product-fields/variation-options/block.json
@@ -22,5 +22,6 @@
"lock": false,
"__experimentalToolbar": false
},
+ "usesContext": [ "postType", "isInSelectedTab" ],
"editorStyle": "file:./editor.css"
}
diff --git a/packages/js/product-editor/src/blocks/product-fields/variation-options/edit.tsx b/packages/js/product-editor/src/blocks/product-fields/variation-options/edit.tsx
index e276ac22c39..f13b70cd0c1 100644
--- a/packages/js/product-editor/src/blocks/product-fields/variation-options/edit.tsx
+++ b/packages/js/product-editor/src/blocks/product-fields/variation-options/edit.tsx
@@ -7,6 +7,7 @@ import { Button } from '@wordpress/components';
import {
createElement,
createInterpolateElement,
+ useEffect,
useMemo,
} from '@wordpress/element';
import { useWooBlockProps } from '@woocommerce/block-templates';
@@ -37,7 +38,7 @@ import { ProductTShirt } from './images';
export function Edit( {
attributes: blockAttributes,
- context,
+ context: { postType, isInSelectedTab },
}: ProductEditorBlockEditProps< BlockAttributes > ) {
const blockProps = useWooBlockProps( blockAttributes );
const { generateProductVariations } = useProductVariationsHelper();
@@ -57,19 +58,26 @@ export function Edit( {
'default_attributes'
);
- const { postType } = context;
const productId = useEntityId( 'postType', postType );
- const { attributes, handleChange } = useProductAttributes( {
- allAttributes: entityAttributes,
- isVariationAttributes: true,
- productId: useEntityId( 'postType', 'product' ),
- onChange( values, defaultAttributes ) {
- setEntityAttributes( values );
- setEntityDefaultAttributes( defaultAttributes );
- generateProductVariations( values, defaultAttributes );
- },
- } );
+ const { attributes, fetchAttributes, handleChange } = useProductAttributes(
+ {
+ allAttributes: entityAttributes,
+ isVariationAttributes: true,
+ productId,
+ onChange( values, defaultAttributes ) {
+ setEntityAttributes( values );
+ setEntityDefaultAttributes( defaultAttributes );
+ generateProductVariations( values, defaultAttributes );
+ },
+ }
+ );
+
+ useEffect( () => {
+ if ( isInSelectedTab ) {
+ fetchAttributes();
+ }
+ }, [ isInSelectedTab, entityAttributes ] );
const localAttributeNames = attributes
.filter( ( attr ) => attr.id === 0 )
diff --git a/packages/js/product-editor/src/components/attributes/attributes.tsx b/packages/js/product-editor/src/components/attributes/attributes.tsx
index b9af12717d9..264d7256d08 100644
--- a/packages/js/product-editor/src/components/attributes/attributes.tsx
+++ b/packages/js/product-editor/src/components/attributes/attributes.tsx
@@ -10,28 +10,26 @@ import { recordEvent } from '@woocommerce/tracks';
* Internal dependencies
*/
import { AttributeControl } from '../attribute-control';
-import { useProductAttributes } from '../../hooks/use-product-attributes';
type AttributesProps = {
+ attributeList?: ProductProductAttribute[];
value: ProductProductAttribute[];
onChange: ( value: ProductProductAttribute[] ) => void;
- productId?: number;
};
+/**
+ * This component is no longer in active use.
+ * It is kept here for backward compatibility because is being used in the `AttributesField` component, under
+ * `plugins/woocommerce-admin/client/products/fills/attributes-section/attributes-field.tsx`.
+ */
export const Attributes: React.FC< AttributesProps > = ( {
value,
onChange,
- productId,
+ attributeList = [],
} ) => {
- const { attributes, handleChange } = useProductAttributes( {
- allAttributes: value,
- onChange,
- productId,
- } );
-
return (
!! attr.variation )
.map( ( attr ) => attr.id ) }
@@ -44,14 +42,14 @@ export const Attributes: React.FC< AttributesProps > = ( {
onAdd={ () => {
recordEvent( 'product_add_attributes_modal_add_button_click' );
} }
- onChange={ handleChange }
+ onChange={ onChange }
onNewModalCancel={ () => {
recordEvent(
'product_add_attributes_modal_cancel_button_click'
);
} }
onNewModalOpen={ () => {
- if ( ! attributes.length ) {
+ if ( ! attributeList.length ) {
recordEvent( 'product_add_first_attribute_button_click' );
return;
}
diff --git a/packages/js/product-editor/src/hooks/test/use-product-attributes.test.ts b/packages/js/product-editor/src/hooks/test/use-product-attributes.test.ts
index 2ed6d4c8d73..4db09e1d783 100644
--- a/packages/js/product-editor/src/hooks/test/use-product-attributes.test.ts
+++ b/packages/js/product-editor/src/hooks/test/use-product-attributes.test.ts
@@ -152,6 +152,7 @@ describe( 'useProductAttributes', () => {
},
}
);
+ result.current.fetchAttributes();
await waitForNextUpdate();
expect( resolveSelect ).not.toHaveBeenCalled();
expect( result.current.attributes ).toEqual( [] );
@@ -175,6 +176,7 @@ describe( 'useProductAttributes', () => {
},
}
);
+ result.current.fetchAttributes();
jest.runOnlyPendingTimers();
await waitForNextUpdate();
result.current.handleChange( [
@@ -210,6 +212,7 @@ describe( 'useProductAttributes', () => {
}
);
jest.runOnlyPendingTimers();
+ result.current.fetchAttributes();
await waitForNextUpdate();
result.current.handleChange( [
{ ...testAttributes[ 0 ], isDefault: false },
@@ -242,6 +245,7 @@ describe( 'useProductAttributes', () => {
}
);
jest.runOnlyPendingTimers();
+ result.current.fetchAttributes();
await waitForNextUpdate();
result.current.handleChange( [
{ ...testAttributes[ 0 ], isDefault: false },
@@ -274,6 +278,7 @@ describe( 'useProductAttributes', () => {
}
);
jest.runOnlyPendingTimers();
+ result.current.fetchAttributes();
await waitForNextUpdate();
result.current.handleChange( [
{ ...testAttributes[ 1 ], isDefault: false },
@@ -305,6 +310,7 @@ describe( 'useProductAttributes', () => {
}
);
jest.runOnlyPendingTimers();
+ result.current.fetchAttributes();
await waitForNextUpdate();
result.current.handleChange( [
{ ...testAttributes[ 0 ], isDefault: false },
@@ -336,6 +342,7 @@ describe( 'useProductAttributes', () => {
}
);
jest.runOnlyPendingTimers();
+ result.current.fetchAttributes();
await waitForNextUpdate();
result.current.handleChange( [ { ...testAttributes[ 0 ] } ] );
expect( onChange ).toHaveBeenCalledWith(
@@ -371,6 +378,7 @@ describe( 'useProductAttributes', () => {
}
);
jest.runOnlyPendingTimers();
+ result.current.fetchAttributes();
await waitForNextUpdate();
result.current.handleChange( [
{ ...testAttributes[ 0 ], isDefault: true },
@@ -415,6 +423,7 @@ describe( 'useProductAttributes', () => {
},
}
);
+ result.current.fetchAttributes();
jest.runOnlyPendingTimers();
await waitForNextUpdate();
expect( result.current.attributes.length ).toBe( 2 );
@@ -445,6 +454,7 @@ describe( 'useProductAttributes', () => {
},
}
);
+ result.current.fetchAttributes();
jest.runOnlyPendingTimers();
await waitForNextUpdate();
expect( result.current.attributes.length ).toBe( 2 );
@@ -460,6 +470,7 @@ describe( 'useProductAttributes', () => {
isVariationAttributes: false,
productId: 123,
} );
+ result.current.fetchAttributes();
jest.runOnlyPendingTimers();
await waitForNextUpdate();
expect( result.current.attributes.length ).toBe( 1 );
@@ -486,6 +497,7 @@ describe( 'useProductAttributes', () => {
},
}
);
+ result.current.fetchAttributes();
jest.runOnlyPendingTimers();
await waitForNextUpdate();
expect( result.current.attributes.length ).toBe( 3 );
diff --git a/packages/js/product-editor/src/hooks/use-product-attributes.ts b/packages/js/product-editor/src/hooks/use-product-attributes.ts
index d5f9dc3c732..8fb176a1ccb 100644
--- a/packages/js/product-editor/src/hooks/use-product-attributes.ts
+++ b/packages/js/product-editor/src/hooks/use-product-attributes.ts
@@ -9,7 +9,7 @@ import {
ProductDefaultAttribute,
} from '@woocommerce/data';
import { resolveSelect } from '@wordpress/data';
-import { useCallback, useEffect, useState } from '@wordpress/element';
+import { useCallback, useState } from '@wordpress/element';
/**
* Internal dependencies
@@ -167,7 +167,7 @@ export function useProductAttributes( {
}
};
- useEffect( () => {
+ const fetchAttributes = useCallback( () => {
const [
localAttributes,
globalAttributes,
@@ -190,6 +190,7 @@ export function useProductAttributes( {
return {
attributes,
+ fetchAttributes,
handleChange,
setAttributes,
};