Manage default variation option (#39570)
* Add default attributes property to the Product type * Add change log file * Set default attributes when click add from the attribute options modal * Add Set default value checkbox to the edit attribute modal * Manage default attributes when update or delete an attribute option * Show Set default value only when the editing attribute is used for variations * Add change log file
This commit is contained in:
parent
9c13c21ec8
commit
b546e4fb17
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Add default attributes property to the Product type
|
|
@ -36,6 +36,24 @@ export type ProductAttribute = {
|
|||
options: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Product - Default attributes properties
|
||||
*/
|
||||
export type ProductDefaultAttribute = {
|
||||
/**
|
||||
* Attribute ID.
|
||||
*/
|
||||
id: number;
|
||||
/**
|
||||
* Attribute name.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Selected attribute term name.
|
||||
*/
|
||||
option: string;
|
||||
};
|
||||
|
||||
export type ProductDimensions = {
|
||||
width: string;
|
||||
height: string;
|
||||
|
@ -66,6 +84,7 @@ export type Product< Status = ProductStatus, Type = ProductType > = Omit<
|
|||
date_modified_gmt: string;
|
||||
date_on_sale_from_gmt: string | null;
|
||||
date_on_sale_to_gmt: string | null;
|
||||
default_attributes: ProductDefaultAttribute[];
|
||||
description: string;
|
||||
dimensions: ProductDimensions;
|
||||
download_expiry: number;
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Add set default value checkbox to the edit attribute options modal
|
|
@ -3,8 +3,12 @@
|
|||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useBlockProps } from '@wordpress/block-editor';
|
||||
import { createElement, createInterpolateElement } from '@wordpress/element';
|
||||
import { ProductAttribute } from '@woocommerce/data';
|
||||
import {
|
||||
createElement,
|
||||
createInterpolateElement,
|
||||
useMemo,
|
||||
} from '@wordpress/element';
|
||||
import { Product, ProductAttribute } from '@woocommerce/data';
|
||||
import { Link } from '@woocommerce/components';
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore No types for this exist yet.
|
||||
|
@ -14,9 +18,31 @@ import { useEntityProp, useEntityId } from '@wordpress/core-data';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { useProductAttributes } from '../../hooks/use-product-attributes';
|
||||
import {
|
||||
EnhancedProductAttribute,
|
||||
useProductAttributes,
|
||||
} from '../../hooks/use-product-attributes';
|
||||
import { AttributeControl } from '../../components/attribute-control';
|
||||
|
||||
function manageDefaultAttributes( values: EnhancedProductAttribute[] ) {
|
||||
return values.reduce< Product[ 'default_attributes' ] >(
|
||||
( prevDefaultAttributes, currentAttribute ) => {
|
||||
if ( currentAttribute.isDefault ) {
|
||||
return [
|
||||
...prevDefaultAttributes,
|
||||
{
|
||||
id: currentAttribute.id,
|
||||
name: currentAttribute.name,
|
||||
option: currentAttribute.options[ 0 ],
|
||||
},
|
||||
];
|
||||
}
|
||||
return prevDefaultAttributes;
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
export function Edit() {
|
||||
const blockProps = useBlockProps();
|
||||
|
||||
|
@ -24,17 +50,41 @@ export function Edit() {
|
|||
ProductAttribute[]
|
||||
>( 'postType', 'product', 'attributes' );
|
||||
|
||||
const [ entityDefaultAttributes, setEntityDefaultAttributes ] =
|
||||
useEntityProp< Product[ 'default_attributes' ] >(
|
||||
'postType',
|
||||
'product',
|
||||
'default_attributes'
|
||||
);
|
||||
|
||||
const { attributes, handleChange } = useProductAttributes( {
|
||||
allAttributes: entityAttributes,
|
||||
onChange: setEntityAttributes,
|
||||
isVariationAttributes: true,
|
||||
productId: useEntityId( 'postType', 'product' ),
|
||||
onChange( values ) {
|
||||
setEntityAttributes( values );
|
||||
setEntityDefaultAttributes( manageDefaultAttributes( values ) );
|
||||
},
|
||||
} );
|
||||
|
||||
function mapDefaultAttributes() {
|
||||
return attributes.map( ( attribute ) => ( {
|
||||
...attribute,
|
||||
isDefault: entityDefaultAttributes.some(
|
||||
( defaultAttribute ) =>
|
||||
defaultAttribute.id === attribute.id ||
|
||||
defaultAttribute.name === attribute.name
|
||||
),
|
||||
} ) );
|
||||
}
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<AttributeControl
|
||||
value={ attributes }
|
||||
value={ useMemo( mapDefaultAttributes, [
|
||||
attributes,
|
||||
entityDefaultAttributes,
|
||||
] ) }
|
||||
onChange={ handleChange }
|
||||
uiStrings={ {
|
||||
globalAttributeHelperMessage: '',
|
||||
|
|
|
@ -41,6 +41,16 @@ function hasAttributesUsedForVariations(
|
|||
return productAttributes.some( ( { variation } ) => variation );
|
||||
}
|
||||
|
||||
function getFirstOptionFromEachAttribute(
|
||||
attributes: Product[ 'attributes' ]
|
||||
): Product[ 'default_attributes' ] {
|
||||
return attributes.map( ( attribute ) => ( {
|
||||
id: attribute.id,
|
||||
name: attribute.name,
|
||||
option: attribute.options[ 0 ],
|
||||
} ) );
|
||||
}
|
||||
|
||||
export function Edit( {
|
||||
attributes,
|
||||
}: BlockEditProps< VariationsBlockAttributes > ) {
|
||||
|
@ -50,13 +60,21 @@ export function Edit( {
|
|||
const [ productAttributes, setProductAttributes ] = useEntityProp<
|
||||
Product[ 'attributes' ]
|
||||
>( 'postType', 'product', 'attributes' );
|
||||
const [ , setDefaultProductAttributes ] = useEntityProp<
|
||||
Product[ 'default_attributes' ]
|
||||
>( 'postType', 'product', 'default_attributes' );
|
||||
|
||||
const { attributes: variationOptions, handleChange } = useProductAttributes(
|
||||
{
|
||||
allAttributes: productAttributes,
|
||||
onChange: setProductAttributes,
|
||||
isVariationAttributes: true,
|
||||
productId: useEntityId( 'postType', 'product' ),
|
||||
onChange( values ) {
|
||||
setProductAttributes( values );
|
||||
setDefaultProductAttributes(
|
||||
getFirstOptionFromEachAttribute( values )
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ import { AttributeListItem } from '../attribute-list-item';
|
|||
import { NewAttributeModal } from './new-attribute-modal';
|
||||
|
||||
type AttributeControlProps = {
|
||||
value: ProductAttribute[];
|
||||
value: EnhancedProductAttribute[];
|
||||
onAdd?: ( attribute: EnhancedProductAttribute[] ) => void;
|
||||
onChange: ( value: ProductAttribute[] ) => void;
|
||||
onEdit?: ( attribute: ProductAttribute ) => void;
|
||||
|
@ -189,7 +189,7 @@ export const AttributeControl: React.FC< AttributeControlProps > = ( {
|
|||
|
||||
const currentAttribute = value.find(
|
||||
( attr ) => getAttributeId( attr ) === currentAttributeId
|
||||
) as EnhancedProductAttribute;
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="woocommerce-attribute-field">
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
width: 500px;
|
||||
max-width: 100%;
|
||||
|
||||
.woocommerce-experimental-select-control + .woocommerce-experimental-select-control {
|
||||
.woocommerce-experimental-select-control
|
||||
+ .woocommerce-experimental-select-control {
|
||||
margin-top: 1.3em;
|
||||
}
|
||||
|
||||
|
@ -30,6 +31,10 @@
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.components-checkbox-control .components-base-control__field {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-attribute-term-field {
|
||||
|
|
|
@ -27,6 +27,8 @@ type EditAttributeModalProps = {
|
|||
customAttributeHelperMessage?: string;
|
||||
termsLabel?: string;
|
||||
termsPlaceholder?: string;
|
||||
isDefaultLabel?: string;
|
||||
isDefaultTooltip?: string;
|
||||
visibleLabel?: string;
|
||||
visibleTooltip?: string;
|
||||
cancelAccessibleLabel?: string;
|
||||
|
@ -48,6 +50,11 @@ export const EditAttributeModal: React.FC< EditAttributeModalProps > = ( {
|
|||
),
|
||||
termsLabel = __( 'Values', 'woocommerce' ),
|
||||
termsPlaceholder = __( 'Search or create value', 'woocommerce' ),
|
||||
isDefaultLabel = __( 'Set default value', 'woocommerce' ),
|
||||
isDefaultTooltip = __(
|
||||
'Check to preselect the first choice when customers enter the product page.',
|
||||
'woocommerce'
|
||||
),
|
||||
visibleLabel = __( 'Visible to customers', 'woocommerce' ),
|
||||
visibleTooltip = __(
|
||||
'Show or hide this attribute on the product page',
|
||||
|
@ -120,18 +127,36 @@ export const EditAttributeModal: React.FC< EditAttributeModalProps > = ( {
|
|||
/>
|
||||
) }
|
||||
|
||||
<div className="woocommerce-edit-attribute-modal__option-container">
|
||||
<CheckboxControl
|
||||
onChange={ ( val ) =>
|
||||
setEditableAttribute( {
|
||||
...( editableAttribute as EnhancedProductAttribute ),
|
||||
visible: val,
|
||||
} )
|
||||
}
|
||||
checked={ editableAttribute?.visible }
|
||||
label={ visibleLabel }
|
||||
/>
|
||||
<Tooltip text={ visibleTooltip } />
|
||||
<div className="woocommerce-edit-attribute-modal__options">
|
||||
{ attribute.variation && (
|
||||
<div className="woocommerce-edit-attribute-modal__option-container">
|
||||
<CheckboxControl
|
||||
onChange={ ( checked ) =>
|
||||
setEditableAttribute( {
|
||||
...( editableAttribute as EnhancedProductAttribute ),
|
||||
isDefault: checked,
|
||||
} )
|
||||
}
|
||||
checked={ editableAttribute?.isDefault }
|
||||
label={ isDefaultLabel }
|
||||
/>
|
||||
<Tooltip text={ isDefaultTooltip } />
|
||||
</div>
|
||||
) }
|
||||
|
||||
<div className="woocommerce-edit-attribute-modal__option-container">
|
||||
<CheckboxControl
|
||||
onChange={ ( val ) =>
|
||||
setEditableAttribute( {
|
||||
...( editableAttribute as EnhancedProductAttribute ),
|
||||
visible: val,
|
||||
} )
|
||||
}
|
||||
checked={ editableAttribute?.visible }
|
||||
label={ visibleLabel }
|
||||
/>
|
||||
<Tooltip text={ visibleTooltip } />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="woocommerce-edit-attribute-modal__buttons">
|
||||
|
|
|
@ -14,18 +14,19 @@ import { useCallback, useEffect, useState } from '@wordpress/element';
|
|||
*/
|
||||
import { sift } from '../utils';
|
||||
|
||||
export type EnhancedProductAttribute = ProductAttribute & {
|
||||
isDefault?: boolean;
|
||||
terms?: ProductAttributeTerm[];
|
||||
visible?: boolean;
|
||||
};
|
||||
|
||||
type useProductAttributesProps = {
|
||||
allAttributes: ProductAttribute[];
|
||||
isVariationAttributes?: boolean;
|
||||
onChange: ( attributes: ProductAttribute[] ) => void;
|
||||
onChange: ( attributes: EnhancedProductAttribute[] ) => void;
|
||||
productId?: number;
|
||||
};
|
||||
|
||||
export type EnhancedProductAttribute = ProductAttribute & {
|
||||
terms?: ProductAttributeTerm[];
|
||||
visible?: boolean;
|
||||
};
|
||||
|
||||
const getFilteredAttributes = (
|
||||
attr: ProductAttribute[],
|
||||
isVariationAttributes: boolean
|
||||
|
@ -89,7 +90,7 @@ export function useProductAttributes( {
|
|||
} ) );
|
||||
};
|
||||
|
||||
const handleChange = ( newAttributes: ProductAttribute[] ) => {
|
||||
const handleChange = ( newAttributes: EnhancedProductAttribute[] ) => {
|
||||
let otherAttributes = isVariationAttributes
|
||||
? allAttributes.filter( ( attribute ) => ! attribute.variation )
|
||||
: allAttributes.filter( ( attribute ) => !! attribute.variation );
|
||||
|
|
Loading…
Reference in New Issue