Update empty state for product attributes tab (#38126)
* Remove empty state HTML * Add empty attribute when product has no attributes * Remove unused woocommerce_admin_meta_boxes.has_local_attributes * Remove unused toggle_add_global_attribute_layout * Remove unused button.add_attribute click handling (button doesn't exist anymore) * Fix positioning of Expand / Close * Remove unnecessary add-attribute-container div * Refactor attribute search selection handling * Remove empty attribute if adding an existing attribute * Update e2e test clicking of "Add new" attribute button * Update Tracks handling for "Add new" attribute button * Changelog * Fix action recorded when "Add new" button is clicked * Remove console.log statements * Allow propagation of click event on "Create value" button * Move Tracks wcadmin_product_attributes_buttons action: 'add_existing' to product-tracking TS * Make function names more descriptive. Add comment to clarify why event.preventDefault is used.
This commit is contained in:
parent
227695386d
commit
d11f50b5a3
|
@ -412,29 +412,44 @@ const attachProductTagsTracks = () => {
|
|||
* Attaches attributes tracks.
|
||||
*/
|
||||
const attachAttributesTracks = () => {
|
||||
function addNewTermEventHandler() {
|
||||
recordEvent( 'product_attributes_add_term', {
|
||||
page: 'product',
|
||||
} );
|
||||
}
|
||||
|
||||
function addNewAttributeTermTracks() {
|
||||
const addNewTermButtons = document.querySelectorAll(
|
||||
'.woocommerce_attribute .add_new_attribute'
|
||||
);
|
||||
addNewTermButtons.forEach( ( button ) => {
|
||||
button.removeEventListener( 'click', addNewTermEventHandler );
|
||||
button.addEventListener( 'click', addNewTermEventHandler );
|
||||
} );
|
||||
}
|
||||
addNewAttributeTermTracks();
|
||||
attachEventListenerToParentForChildren( '#product_attributes', [
|
||||
{
|
||||
eventName: 'click',
|
||||
childQuery: '.add_new_attribute',
|
||||
callback: () => {
|
||||
recordEvent( 'product_attributes_add_term', {
|
||||
page: 'product',
|
||||
} );
|
||||
},
|
||||
},
|
||||
] );
|
||||
};
|
||||
|
||||
/**
|
||||
* Attaches Tracks event for when a new custom attribute is added to a product.
|
||||
*/
|
||||
const attachAddCustomAttributeTracks = () => {
|
||||
document
|
||||
.querySelector( '.add_attribute' )
|
||||
.querySelector( '#product_attributes .add_custom_attribute' )
|
||||
?.addEventListener( 'click', () => {
|
||||
setTimeout( () => {
|
||||
addNewAttributeTermTracks();
|
||||
}, 1000 );
|
||||
recordEvent( 'product_attributes_buttons', {
|
||||
action: 'add_new',
|
||||
} );
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* Attaches Tracks event for when an existing global attribute is added to a product.
|
||||
*/
|
||||
const attachAddExistingAttributeTracks = () => {
|
||||
window
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore Need to use jQuery to hook up to the select2:select event since the select2 component is jQuery-based
|
||||
?.jQuery( 'select.wc-attribute-search' )
|
||||
.on( 'select2:select', function () {
|
||||
recordEvent( 'product_attributes_buttons', {
|
||||
action: 'add_existing',
|
||||
} );
|
||||
} );
|
||||
};
|
||||
|
||||
|
@ -442,29 +457,8 @@ const attachAttributesTracks = () => {
|
|||
* Attaches product attributes tracks.
|
||||
*/
|
||||
const attachProductAttributesTracks = () => {
|
||||
document
|
||||
.querySelector( '#product_attributes .add_custom_attribute' )
|
||||
?.addEventListener( 'click', () => {
|
||||
recordEvent( 'product_attributes_buttons', {
|
||||
action: 'add_first_attribute',
|
||||
} );
|
||||
} );
|
||||
document
|
||||
.querySelector( '#product_attributes .add_attribute' )
|
||||
?.addEventListener( 'click', () => {
|
||||
// We verify that we are not adding an existing attribute to not
|
||||
// duplicate the recorded event.
|
||||
const selectElement = document.querySelector(
|
||||
'.attribute_taxonomy'
|
||||
) as HTMLSelectElement;
|
||||
// Get the index of the selected option
|
||||
const selectedIndex = selectElement.selectedIndex;
|
||||
if ( selectElement.options[ selectedIndex ]?.value === '' ) {
|
||||
recordEvent( 'product_attributes_buttons', {
|
||||
action: 'add_new',
|
||||
} );
|
||||
}
|
||||
} );
|
||||
attachAddCustomAttributeTracks();
|
||||
attachAddExistingAttributeTracks();
|
||||
|
||||
const attributesSection = '#product_attributes';
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: update
|
||||
|
||||
Update empty state for product attributes tab.
|
|
@ -57,12 +57,6 @@ jQuery( function ( $ ) {
|
|||
} );
|
||||
} );
|
||||
|
||||
$( function () {
|
||||
if ( ! woocommerce_admin_meta_boxes.has_local_attributes ) {
|
||||
$( 'button.add_attribute' ).trigger( 'click' );
|
||||
}
|
||||
} );
|
||||
|
||||
// Catalog Visibility.
|
||||
$( '#catalog-visibility' )
|
||||
.find( '.edit-catalog-visibility' )
|
||||
|
@ -400,6 +394,14 @@ jQuery( function ( $ ) {
|
|||
.find( '.woocommerce_attribute' )
|
||||
.get();
|
||||
|
||||
// If the product has no attributes, add an empty attribute to be filled out by the user.
|
||||
$( function add_blank_custom_attribute_if_no_attributes() {
|
||||
|
||||
if ( woocommerce_attribute_items.length === 0 ) {
|
||||
$( 'button.add_custom_attribute' ).trigger( 'click' );
|
||||
}
|
||||
} );
|
||||
|
||||
woocommerce_attribute_items.sort( function ( a, b ) {
|
||||
var compA = parseInt( $( a ).attr( 'rel' ), 10 );
|
||||
var compB = parseInt( $( b ).attr( 'rel' ), 10 );
|
||||
|
@ -445,12 +447,6 @@ jQuery( function ( $ ) {
|
|||
selectedAttributes
|
||||
);
|
||||
|
||||
function toggle_add_global_attribute_layout() {
|
||||
$( 'div.add-attribute-container' ).toggle();
|
||||
$( 'div.add-global-attribute-container' ).toggle();
|
||||
$( '#product_attributes > .toolbar-buttons' ).toggle();
|
||||
}
|
||||
|
||||
function add_attribute( element, attribute ) {
|
||||
var size = $( '.product_attributes .woocommerce_attribute' ).length;
|
||||
var $wrapper = $( element ).closest( '#product_attributes' );
|
||||
|
@ -504,64 +500,52 @@ jQuery( function ( $ ) {
|
|||
}
|
||||
}
|
||||
|
||||
$( 'select.wc-attribute-search' ).on( 'select2:select', function ( e ) {
|
||||
if ( e.params && e.params.data && e.params.data.id ) {
|
||||
add_attribute( this, e.params.data.id );
|
||||
if ( ! selectedAttributes.includes( e.params.data.id ) ) {
|
||||
selectedAttributes.push( e.params.data.id );
|
||||
$( 'select.wc-attribute-search' ).data(
|
||||
'disabled-items',
|
||||
selectedAttributes
|
||||
);
|
||||
function add_if_not_exists( arr, item ) {
|
||||
return arr.includes( item ) ? attr : [ ...arr, item ];
|
||||
}
|
||||
|
||||
function disable_in_attribute_search( selectedAttributes ) {
|
||||
$( 'select.wc-attribute-search' ).data( 'disabled-items', selectedAttributes );
|
||||
}
|
||||
|
||||
function remove_blank_custom_attribute_if_no_other_attributes() {
|
||||
const $attributes = $( '.product_attributes .woocommerce_attribute' );
|
||||
|
||||
if ( $attributes.length === 1 ) {
|
||||
const $attribute = $attributes.first();
|
||||
|
||||
const $attributeName = $attribute.find( 'input[name="attribute_names[0]"]' );
|
||||
const $attributeValue = $attribute.find( 'input[name="attribute_values[0]"]' );
|
||||
|
||||
if ( ! $attributeName.val() && ! $attributeValue.val() ) {
|
||||
$attribute.remove();
|
||||
}
|
||||
window.wcTracks.recordEvent( 'product_attributes_buttons', {
|
||||
action: 'add_existing',
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
$( 'select.wc-attribute-search' ).on( 'select2:select', function ( e ) {
|
||||
const attributeId = e?.params?.data?.id;
|
||||
|
||||
if ( attributeId ) {
|
||||
remove_blank_custom_attribute_if_no_other_attributes();
|
||||
|
||||
add_attribute( this, attributeId );
|
||||
|
||||
selectedAttributes = add_if_not_exists( selectedAttributes, attributeId );
|
||||
disable_in_attribute_search( selectedAttributes );
|
||||
}
|
||||
|
||||
$( this ).val( null );
|
||||
$( this ).trigger( 'change' );
|
||||
if (
|
||||
$( 'div.add-attribute-container' ).hasClass( 'hidden' ) &&
|
||||
! $( 'div.add-global-attribute-container' ).hasClass( 'hidden' )
|
||||
) {
|
||||
toggle_add_global_attribute_layout();
|
||||
}
|
||||
|
||||
return false;
|
||||
} );
|
||||
|
||||
// Add rows.
|
||||
$( 'button.add_attribute' ).on( 'click', function () {
|
||||
var attribute = $( 'select.attribute_taxonomy' ).val();
|
||||
if (
|
||||
! attribute &&
|
||||
$( 'select.attribute_taxonomy' ).hasClass( 'wc-attribute-search' )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
add_attribute( this, attribute );
|
||||
$( 'select.attribute_taxonomy' ).val( null );
|
||||
$( 'select.attribute_taxonomy' ).trigger( 'change' );
|
||||
|
||||
// We record the event only when an existing attribute is added.
|
||||
if ( attribute !== '' ) {
|
||||
window.wcTracks.recordEvent( 'product_attributes_buttons', {
|
||||
action: 'add_existing',
|
||||
} );
|
||||
}
|
||||
|
||||
return false;
|
||||
} );
|
||||
|
||||
$( 'button.add_custom_attribute' ).on( 'click', function () {
|
||||
add_attribute( this, '' );
|
||||
|
||||
if (
|
||||
$( 'div.add-attribute-container' ).hasClass( 'hidden' ) &&
|
||||
! $( 'div.add-global-attribute-container' ).hasClass( 'hidden' )
|
||||
) {
|
||||
toggle_add_global_attribute_layout();
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
|
@ -694,16 +678,6 @@ jQuery( function ( $ ) {
|
|||
action: 'remove_attribute',
|
||||
} );
|
||||
|
||||
if (
|
||||
! $( '.woocommerce_attribute_data' ).is( ':visible' ) &&
|
||||
! $( 'div.add-global-attribute-container' ).hasClass(
|
||||
'hidden'
|
||||
) &&
|
||||
$( '.product_attributes' ).find( 'input, select, textarea' )
|
||||
.length === 0
|
||||
) {
|
||||
toggle_add_global_attribute_layout();
|
||||
}
|
||||
jQuery.maybe_disable_save_button();
|
||||
}
|
||||
return false;
|
||||
|
@ -734,7 +708,10 @@ jQuery( function ( $ ) {
|
|||
$( '.product_attributes' ).on(
|
||||
'click',
|
||||
'button.add_new_attribute',
|
||||
function () {
|
||||
function ( event ) {
|
||||
// prevent form submission but allow event propagation
|
||||
event.preventDefault();
|
||||
|
||||
$( '.product_attributes' ).block( {
|
||||
message: null,
|
||||
overlayCSS: {
|
||||
|
@ -784,8 +761,6 @@ jQuery( function ( $ ) {
|
|||
} else {
|
||||
$( '.product_attributes' ).unblock();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -416,7 +416,6 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
|
|||
'rounding_precision' => wc_get_rounding_precision(),
|
||||
'tax_rounding_mode' => wc_get_tax_rounding_mode(),
|
||||
'product_types' => array_unique( array_merge( array( 'simple', 'grouped', 'variable', 'external' ), array_keys( wc_get_product_types() ) ) ),
|
||||
'has_local_attributes' => ! empty( wc_get_attribute_taxonomies() ),
|
||||
'i18n_download_permission_fail' => __( 'Could not grant access - the user may already have permission for this file or billing email is not set. Ensure the billing email is set, and the order has been saved.', 'woocommerce' ),
|
||||
'i18n_permission_revoke' => __( 'Are you sure you want to revoke access to this download?', 'woocommerce' ),
|
||||
'i18n_tax_rate_already_exists' => __( 'You cannot add the same tax rate twice!', 'woocommerce' ),
|
||||
|
|
|
@ -13,78 +13,27 @@ global $wc_product_attributes;
|
|||
// Array of defined attribute taxonomies.
|
||||
$attribute_taxonomies = wc_get_attribute_taxonomies();
|
||||
// Product attributes - taxonomies and custom, ordered, with visibility and variation attributes set.
|
||||
$product_attributes = $product_object->get_attributes( 'edit' );
|
||||
$has_local_attributes = empty( $attribute_taxonomies );
|
||||
$has_global_attributes = empty( $product_attributes );
|
||||
$is_add_global_attribute_visible = ! $has_local_attributes && $has_global_attributes;
|
||||
$icon_url = WC_ADMIN_IMAGES_FOLDER_URL . '/icons/global-attributes-icon.svg';
|
||||
$product_attributes = $product_object->get_attributes( 'edit' );
|
||||
?>
|
||||
<div id="product_attributes" class="panel wc-metaboxes-wrapper hidden">
|
||||
<div class="toolbar toolbar-top <?php echo $is_add_global_attribute_visible ? ' expand-close-hidden' : ''; ?>">
|
||||
<div class="add-global-attribute-container<?php echo $is_add_global_attribute_visible ? '' : ' hidden'; ?>">
|
||||
<div class="actions">
|
||||
<button type="button" class="button add_custom_attribute"><?php esc_html_e( 'Add new', 'woocommerce' ); ?></button>
|
||||
<select class="wc-attribute-search" data-placeholder="<?php esc_attr_e( 'Add existing', 'woocommerce' ); ?>" data-minimum-input-length="0">
|
||||
</select>
|
||||
</div>
|
||||
<div class="message">
|
||||
<img src="<?php echo esc_url( $icon_url ); ?>" />
|
||||
<p>
|
||||
<?php
|
||||
esc_html_e(
|
||||
'Add descriptive pieces of information that customers can use to search for this product on your store, such as “Material” or “Brand”.',
|
||||
'woocommerce'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="add-attribute-container<?php echo $is_add_global_attribute_visible ? ' hidden' : ' '; ?>">
|
||||
<?php
|
||||
if ( $has_local_attributes && $has_global_attributes ) :
|
||||
?>
|
||||
<div id="message" class="inline notice woocommerce-message">
|
||||
<p>
|
||||
<?php
|
||||
esc_html_e(
|
||||
'Add descriptive pieces of information that customers can use to search for this product on your store, such as “Material” or “Brand”.',
|
||||
'woocommerce'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<span class="expand-close">
|
||||
<a href="#" class="expand_all"><?php esc_html_e( 'Expand', 'woocommerce' ); ?></a> / <a href="#" class="close_all"><?php esc_html_e( 'Close', 'woocommerce' ); ?></a>
|
||||
</span>
|
||||
|
||||
<?php
|
||||
/**
|
||||
* Filter for the attribute taxonomy filter dropdown threshold.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @param number $threshold The threshold for showing the simple dropdown.
|
||||
*/
|
||||
if ( count( $attribute_taxonomies ) <= apply_filters( 'woocommerce_attribute_taxonomy_filter_threshold', 20 ) ) :
|
||||
?>
|
||||
<select name="attribute_taxonomy" class="attribute_taxonomy">
|
||||
<option value=""><?php esc_html_e( 'Custom product attribute', 'woocommerce' ); ?></option>
|
||||
<div class="toolbar toolbar-top">
|
||||
<div id="message" class="inline notice woocommerce-message">
|
||||
<p>
|
||||
<?php
|
||||
if ( ! $has_local_attributes ) {
|
||||
foreach ( $attribute_taxonomies as $attr_taxonomy ) {
|
||||
$attribute_taxonomy_name = wc_attribute_taxonomy_name( $attr_taxonomy->attribute_name );
|
||||
$label = $attr_taxonomy->attribute_label ? $attr_taxonomy->attribute_label : $attr_taxonomy->attribute_name;
|
||||
echo '<option value="' . esc_attr( $attribute_taxonomy_name ) . '">' . esc_html( $label ) . '</option>';
|
||||
}
|
||||
}
|
||||
esc_html_e(
|
||||
'Add descriptive pieces of information that customers can use to search for this product on your store, such as “Material” or “Brand”.',
|
||||
'woocommerce'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
<span class="expand-close">
|
||||
<a href="#" class="expand_all"><?php esc_html_e( 'Expand', 'woocommerce' ); ?></a> / <a href="#" class="close_all"><?php esc_html_e( 'Close', 'woocommerce' ); ?></a>
|
||||
</span>
|
||||
<div class="actions">
|
||||
<button type="button" class="button add_custom_attribute"><?php esc_html_e( 'Add new', 'woocommerce' ); ?></button>
|
||||
<select class="wc-attribute-search" data-placeholder="<?php esc_attr_e( 'Add existing', 'woocommerce' ); ?>" data-minimum-input-length="0">
|
||||
</select>
|
||||
<button type="button" class="button add_attribute"><?php esc_html_e( 'Add', 'woocommerce' ); ?></button>
|
||||
<?php else : ?>
|
||||
<button type="button" class="button add_custom_attribute"><?php esc_html_e( 'Add custom attribute', 'woocommerce' ); ?></button>
|
||||
<select class="wc-attribute-search attribute_taxonomy" id="attribute_taxonomy" name="attribute_taxonomy" data-placeholder="<?php esc_attr_e( 'Add existing attribute', 'woocommerce' ); ?>" data-minimum-input-length="0">
|
||||
</select>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product_attributes wc-metaboxes">
|
||||
|
@ -104,7 +53,7 @@ $icon_url = WC_ADMIN_IMAGES_FOLDER_URL . '/icons/global-a
|
|||
}
|
||||
?>
|
||||
</div>
|
||||
<div class="toolbar toolbar-buttons<?php echo $is_add_global_attribute_visible ? ' hidden' : ''; ?>">
|
||||
<div class="toolbar toolbar-buttons">
|
||||
<span class="expand-close">
|
||||
<a href="#" class="expand_all"><?php esc_html_e( 'Expand', 'woocommerce' ); ?></a> / <a href="#" class="close_all"><?php esc_html_e( 'Close', 'woocommerce' ); ?></a>
|
||||
</span>
|
||||
|
|
|
@ -292,8 +292,8 @@ test.describe( 'Add New Variable Product Page', () => {
|
|||
if ( i > 0 ) {
|
||||
await test.step( "Click 'Add'.", async () => {
|
||||
await page
|
||||
.locator( '.add-attribute-container' )
|
||||
.getByRole( 'button', { name: 'Add' } )
|
||||
.locator( '#product_attributes .toolbar-top' )
|
||||
.getByRole( 'button', { name: 'Add new' } )
|
||||
.click();
|
||||
} );
|
||||
}
|
||||
|
@ -746,7 +746,7 @@ test.describe( 'Add New Variable Product Page', () => {
|
|||
// add 3 attributes
|
||||
for ( let i = 0; i < 3; i++ ) {
|
||||
if ( i > 0 ) {
|
||||
await page.click( 'button.add_attribute' );
|
||||
await page.click( 'button.add_custom_attribute' );
|
||||
}
|
||||
await page.waitForSelector(
|
||||
`input[name="attribute_names[${ i }]"]`
|
||||
|
|
Loading…
Reference in New Issue