Improved UI/UX of the Attributes tab (v2) (#39987)

This commit is contained in:
Jason Kytros 2023-09-05 09:24:34 +02:00 committed by GitHub
commit 283b1a673c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 139 additions and 71 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: enhancement
Design enhancements for the Attributes tab.

View File

@ -5303,6 +5303,14 @@ img.help_tip {
visibility: visible; visibility: visible;
} }
} }
&.wc-metabox {
&.postbox {
border-top: 0px;
border-left: 0px;
border-right: 0px;
}
}
} }
.woocommerce_options_panel { .woocommerce_options_panel {
@ -5517,7 +5525,8 @@ img.help_tip {
* WooCommerce meta boxes * WooCommerce meta boxes
*/ */
.wc-metaboxes-wrapper { .wc-metaboxes-wrapper {
:not(#variable_product_options_inner) { :not(#variable_product_options_inner),
&#product_attributes {
.toolbar:not(.expand-close-hidden) { .toolbar:not(.expand-close-hidden) {
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
} }

View File

@ -463,6 +463,14 @@ jQuery( function ( $ ) {
.find( 'option[value="' + $( el ).data( 'taxonomy' ) + '"]' ) .find( 'option[value="' + $( el ).data( 'taxonomy' ) + '"]' )
.attr( 'disabled', 'disabled' ); .attr( 'disabled', 'disabled' );
} }
if ( 'undefined' === $(el).attr( 'data-taxonomy' ) || false === $(el).attr( 'data-taxonomy' ) || '' === $(el).attr( 'data-taxonomy' ) ) {
add_placeholder_to_attribute_values_field( $(el) );
$( '.woocommerce_attribute input.woocommerce_attribute_used_for_variations' ).on( 'change', function() {
add_placeholder_to_attribute_values_field( $(el) );
} );
}
} ); } );
$( 'select.wc-attribute-search' ).data( $( 'select.wc-attribute-search' ).data(
'disabled-items', 'disabled-items',
@ -514,6 +522,26 @@ jQuery( function ( $ ) {
$attributeListItem.find( 'h3' ).trigger( 'click' ); $attributeListItem.find( 'h3' ).trigger( 'click' );
} }
function toggle_selection_of_attribute_list_item_terms( $attributeListItem ) {
var $attributeListItemSelectAllButton = $attributeListItem.find( 'button.select_all_attributes' );
if ( $attributeListItemSelectAllButton.length ) {
$attributeListItemSelectAllButton.trigger( 'click' );
}
}
function add_placeholder_to_attribute_values_field( $attributeListItem ) {
var $used_for_variations_checkbox = $attributeListItem.find( 'input.woocommerce_attribute_used_for_variations' );
if ( $used_for_variations_checkbox.length && $used_for_variations_checkbox.is( ':checked' ) ) {
$attributeListItem.find( 'textarea' ).attr( 'placeholder', woocommerce_admin_meta_boxes.i18n_attributes_used_for_variations_placeholder );
} else {
$attributeListItem.find( 'textarea' ).attr( 'placeholder', woocommerce_admin_meta_boxes.i18n_attributes_default_placeholder );
}
}
function init_select_controls() { function init_select_controls() {
$( document.body ).trigger( 'wc-enhanced-select-init' ); $( document.body ).trigger( 'wc-enhanced-select-init' );
} }
@ -547,6 +575,18 @@ jQuery( function ( $ ) {
toggle_expansion_of_attribute_list_item( $attributeListItem ); toggle_expansion_of_attribute_list_item( $attributeListItem );
// Automatically pre-select all terms when a global Attribute is chosen.
toggle_selection_of_attribute_list_item_terms( $attributeListItem );
// Conditionally change the placeholder of product-level Attributes depending on the value of the "Use for variations" checkbox.
if ( 'undefined' === typeof globalAttributeId ) {
add_placeholder_to_attribute_values_field( $attributeListItem );
$( '.woocommerce_attribute input.woocommerce_attribute_used_for_variations' ).on( 'change', function() {
add_placeholder_to_attribute_values_field( $(this).closest( '.woocommerce_attribute' ) );
} );
}
$( document.body ).trigger( 'woocommerce_added_attribute' ); $( document.body ).trigger( 'woocommerce_added_attribute' );
jQuery.maybe_disable_save_button(); jQuery.maybe_disable_save_button();
@ -602,6 +642,17 @@ jQuery( function ( $ ) {
} }
} }
// Handle the Attributes onboarding dismissible notice.
// If users dismiss the notice, never show it again.
if ( localStorage.getItem('attributes-notice-dismissed' ) ) {
$( '#product_attributes .notice' ).hide();
}
$( '#product_attributes .notice.woocommerce-message button' ).on( 'click', function( e ) {
$( '#product_attributes .notice' ).hide();
localStorage.setItem( 'attributes-notice-dismissed', 'true');
} );
$( 'select.wc-attribute-search' ).on( 'select2:select', function ( e ) { $( 'select.wc-attribute-search' ).on( 'select2:select', function ( e ) {
const attributeId = e && e.params && e.params.data && e.params.data.id; const attributeId = e && e.params && e.params.data && e.params.data.id;

View File

@ -369,74 +369,77 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
} }
$params = array( $params = array(
'remove_item_notice' => $remove_item_notice, 'remove_item_notice' => $remove_item_notice,
'remove_fee_notice' => $remove_fee_notice, 'remove_fee_notice' => $remove_fee_notice,
'remove_shipping_notice' => $remove_shipping_notice, 'remove_shipping_notice' => $remove_shipping_notice,
'i18n_select_items' => __( 'Please select some items.', 'woocommerce' ), 'i18n_select_items' => __( 'Please select some items.', 'woocommerce' ),
'i18n_do_refund' => __( 'Are you sure you wish to process this refund? This action cannot be undone.', 'woocommerce' ), 'i18n_do_refund' => __( 'Are you sure you wish to process this refund? This action cannot be undone.', 'woocommerce' ),
'i18n_delete_refund' => __( 'Are you sure you wish to delete this refund? This action cannot be undone.', 'woocommerce' ), 'i18n_delete_refund' => __( 'Are you sure you wish to delete this refund? This action cannot be undone.', 'woocommerce' ),
'i18n_delete_tax' => __( 'Are you sure you wish to delete this tax column? This action cannot be undone.', 'woocommerce' ), 'i18n_delete_tax' => __( 'Are you sure you wish to delete this tax column? This action cannot be undone.', 'woocommerce' ),
'remove_item_meta' => __( 'Remove this item meta?', 'woocommerce' ), 'remove_item_meta' => __( 'Remove this item meta?', 'woocommerce' ),
'name_label' => __( 'Name', 'woocommerce' ), 'name_label' => __( 'Name', 'woocommerce' ),
'remove_label' => __( 'Remove', 'woocommerce' ), 'remove_label' => __( 'Remove', 'woocommerce' ),
'click_to_toggle' => __( 'Click to toggle', 'woocommerce' ), 'click_to_toggle' => __( 'Click to toggle', 'woocommerce' ),
'values_label' => __( 'Value(s)', 'woocommerce' ), 'values_label' => __( 'Value(s)', 'woocommerce' ),
'text_attribute_tip' => __( 'Enter some text, or some attributes by pipe (|) separating values.', 'woocommerce' ), 'text_attribute_tip' => __( 'Enter some text, or some attributes by pipe (|) separating values.', 'woocommerce' ),
'visible_label' => __( 'Visible on the product page', 'woocommerce' ), 'visible_label' => __( 'Visible on the product page', 'woocommerce' ),
'used_for_variations_label' => __( 'Used for variations', 'woocommerce' ), 'used_for_variations_label' => __( 'Used for variations', 'woocommerce' ),
'new_attribute_prompt' => __( 'Enter a name for the new attribute term:', 'woocommerce' ), 'new_attribute_prompt' => __( 'Enter a name for the new attribute term:', 'woocommerce' ),
'calc_totals' => __( 'Recalculate totals? This will calculate taxes based on the customers country (or the store base country) and update totals.', 'woocommerce' ), 'calc_totals' => __( 'Recalculate totals? This will calculate taxes based on the customers country (or the store base country) and update totals.', 'woocommerce' ),
'copy_billing' => __( 'Copy billing information to shipping information? This will remove any currently entered shipping information.', 'woocommerce' ), 'copy_billing' => __( 'Copy billing information to shipping information? This will remove any currently entered shipping information.', 'woocommerce' ),
'load_billing' => __( "Load the customer's billing information? This will remove any currently entered billing information.", 'woocommerce' ), 'load_billing' => __( "Load the customer's billing information? This will remove any currently entered billing information.", 'woocommerce' ),
'load_shipping' => __( "Load the customer's shipping information? This will remove any currently entered shipping information.", 'woocommerce' ), 'load_shipping' => __( "Load the customer's shipping information? This will remove any currently entered shipping information.", 'woocommerce' ),
'featured_label' => __( 'Featured', 'woocommerce' ), 'featured_label' => __( 'Featured', 'woocommerce' ),
'prices_include_tax' => esc_attr( get_option( 'woocommerce_prices_include_tax' ) ), 'prices_include_tax' => esc_attr( get_option( 'woocommerce_prices_include_tax' ) ),
'tax_based_on' => esc_attr( get_option( 'woocommerce_tax_based_on' ) ), 'tax_based_on' => esc_attr( get_option( 'woocommerce_tax_based_on' ) ),
'round_at_subtotal' => esc_attr( get_option( 'woocommerce_tax_round_at_subtotal' ) ), 'round_at_subtotal' => esc_attr( get_option( 'woocommerce_tax_round_at_subtotal' ) ),
'no_customer_selected' => __( 'No customer selected', 'woocommerce' ), 'no_customer_selected' => __( 'No customer selected', 'woocommerce' ),
'plugin_url' => WC()->plugin_url(), 'plugin_url' => WC()->plugin_url(),
'ajax_url' => admin_url( 'admin-ajax.php' ), 'ajax_url' => admin_url( 'admin-ajax.php' ),
'order_item_nonce' => wp_create_nonce( 'order-item' ), 'order_item_nonce' => wp_create_nonce( 'order-item' ),
'add_attribute_nonce' => wp_create_nonce( 'add-attribute' ), 'add_attribute_nonce' => wp_create_nonce( 'add-attribute' ),
'save_attributes_nonce' => wp_create_nonce( 'save-attributes' ), 'save_attributes_nonce' => wp_create_nonce( 'save-attributes' ),
'add_attributes_and_variations' => wp_create_nonce( 'add-attributes-and-variations' ), 'add_attributes_and_variations' => wp_create_nonce( 'add-attributes-and-variations' ),
'calc_totals_nonce' => wp_create_nonce( 'calc-totals' ), 'calc_totals_nonce' => wp_create_nonce( 'calc-totals' ),
'get_customer_details_nonce' => wp_create_nonce( 'get-customer-details' ), 'get_customer_details_nonce' => wp_create_nonce( 'get-customer-details' ),
'search_products_nonce' => wp_create_nonce( 'search-products' ), 'search_products_nonce' => wp_create_nonce( 'search-products' ),
'grant_access_nonce' => wp_create_nonce( 'grant-access' ), 'grant_access_nonce' => wp_create_nonce( 'grant-access' ),
'revoke_access_nonce' => wp_create_nonce( 'revoke-access' ), 'revoke_access_nonce' => wp_create_nonce( 'revoke-access' ),
'add_order_note_nonce' => wp_create_nonce( 'add-order-note' ), 'add_order_note_nonce' => wp_create_nonce( 'add-order-note' ),
'delete_order_note_nonce' => wp_create_nonce( 'delete-order-note' ), 'delete_order_note_nonce' => wp_create_nonce( 'delete-order-note' ),
'calendar_image' => WC()->plugin_url() . '/assets/images/calendar.png', 'calendar_image' => WC()->plugin_url() . '/assets/images/calendar.png',
'post_id' => $this->is_order_meta_box_screen( $screen_id ) && isset( $order_or_post_object ) ? \Automattic\WooCommerce\Utilities\OrderUtil::get_post_or_order_id( $order_or_post_object ) : $post_id, 'post_id' => $this->is_order_meta_box_screen( $screen_id ) && isset( $order_or_post_object ) ? \Automattic\WooCommerce\Utilities\OrderUtil::get_post_or_order_id( $order_or_post_object ) : $post_id,
'base_country' => WC()->countries->get_base_country(), 'base_country' => WC()->countries->get_base_country(),
'currency_format_num_decimals' => wc_get_price_decimals(), 'currency_format_num_decimals' => wc_get_price_decimals(),
'currency_format_symbol' => get_woocommerce_currency_symbol( $currency ), 'currency_format_symbol' => get_woocommerce_currency_symbol( $currency ),
'currency_format_decimal_sep' => esc_attr( wc_get_price_decimal_separator() ), 'currency_format_decimal_sep' => esc_attr( wc_get_price_decimal_separator() ),
'currency_format_thousand_sep' => esc_attr( wc_get_price_thousand_separator() ), 'currency_format_thousand_sep' => esc_attr( wc_get_price_thousand_separator() ),
'currency_format' => esc_attr( str_replace( array( '%1$s', '%2$s' ), array( '%s', '%v' ), get_woocommerce_price_format() ) ), // For accounting JS. 'currency_format' => esc_attr( str_replace( array( '%1$s', '%2$s' ), array( '%s', '%v' ), get_woocommerce_price_format() ) ), // For accounting JS.
'rounding_precision' => wc_get_rounding_precision(), 'rounding_precision' => wc_get_rounding_precision(),
'tax_rounding_mode' => wc_get_tax_rounding_mode(), '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() ) ) ), 'product_types' => array_unique( array_merge( array( 'simple', 'grouped', 'variable', 'external' ), array_keys( wc_get_product_types() ) ) ),
'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_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_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' ), 'i18n_tax_rate_already_exists' => __( 'You cannot add the same tax rate twice!', 'woocommerce' ),
'i18n_delete_note' => __( 'Are you sure you wish to delete this note? This action cannot be undone.', 'woocommerce' ), 'i18n_delete_note' => __( 'Are you sure you wish to delete this note? This action cannot be undone.', 'woocommerce' ),
'i18n_apply_coupon' => __( 'Enter a coupon code to apply. Discounts are applied to line totals, before taxes.', 'woocommerce' ), 'i18n_apply_coupon' => __( 'Enter a coupon code to apply. Discounts are applied to line totals, before taxes.', 'woocommerce' ),
'i18n_add_fee' => __( 'Enter a fixed amount or percentage to apply as a fee.', 'woocommerce' ), 'i18n_add_fee' => __( 'Enter a fixed amount or percentage to apply as a fee.', 'woocommerce' ),
'i18n_attribute_name_placeholder' => __( 'New attribute', 'woocommerce' ), 'i18n_attribute_name_placeholder' => __( 'New attribute', 'woocommerce' ),
'i18n_product_simple_tip' => __( '<b>Simple </b> covers the vast majority of any products you may sell. Simple products are shipped and have no options. For example, a book.', 'woocommerce' ), 'i18n_product_simple_tip' => __( '<b>Simple </b> covers the vast majority of any products you may sell. Simple products are shipped and have no options. For example, a book.', 'woocommerce' ),
'i18n_product_grouped_tip' => __( '<b>Grouped </b> a collection of related products that can be purchased individually and only consist of simple products. For example, a set of six drinking glasses.', 'woocommerce' ), 'i18n_product_grouped_tip' => __( '<b>Grouped </b> a collection of related products that can be purchased individually and only consist of simple products. For example, a set of six drinking glasses.', 'woocommerce' ),
'i18n_product_external_tip' => __( '<b>External or Affiliate </b> one that you list and describe on your website but is sold elsewhere.', 'woocommerce' ), 'i18n_product_external_tip' => __( '<b>External or Affiliate </b> one that you list and describe on your website but is sold elsewhere.', 'woocommerce' ),
'i18n_product_variable_tip' => __( '<b>Variable </b> a product with variations, each of which may have a different SKU, price, stock option, etc. For example, a t-shirt available in different colors and/or sizes.', 'woocommerce' ), 'i18n_product_variable_tip' => __( '<b>Variable </b> a product with variations, each of which may have a different SKU, price, stock option, etc. For example, a t-shirt available in different colors and/or sizes.', 'woocommerce' ),
'i18n_product_other_tip' => __( 'Product types define available product details and attributes, such as downloadable files and variations. Theyre also used for analytics and inventory management.', 'woocommerce' ), 'i18n_product_other_tip' => __( 'Product types define available product details and attributes, such as downloadable files and variations. Theyre also used for analytics and inventory management.', 'woocommerce' ),
'i18n_product_description_tip' => __( 'Describe this product. What makes it unique? What are its most important features?', 'woocommerce' ), 'i18n_product_description_tip' => __( 'Describe this product. What makes it unique? What are its most important features?', 'woocommerce' ),
'i18n_product_short_description_tip' => __( 'Summarize this product in 1-2 short sentences. Well show it at the top of the page.', 'woocommerce' ), 'i18n_product_short_description_tip' => __( 'Summarize this product in 1-2 short sentences. Well show it at the top of the page.', 'woocommerce' ),
'i18n_save_attribute_variation_tip' => __( 'Make sure you enter the name and values for each attribute.', 'woocommerce' ), 'i18n_save_attribute_variation_tip' => __( 'Make sure you enter the name and values for each attribute.', 'woocommerce' ),
/* translators: %1$s: maximum file size */ /* translators: %1$s: maximum file size */
'i18n_product_image_tip' => sprintf( __( 'For best results, upload JPEG or PNG files that are 1000 by 1000 pixels or larger. Maximum upload file size: %1$s.', 'woocommerce' ) , size_format( wp_max_upload_size() ) ), 'i18n_product_image_tip' => sprintf( __( 'For best results, upload JPEG or PNG files that are 1000 by 1000 pixels or larger. Maximum upload file size: %1$s.', 'woocommerce' ), size_format( wp_max_upload_size() ) ),
'i18n_remove_used_attribute_confirmation_message' => __( 'If you remove this attribute, customers will no longer be able to purchase some variations of this product.', 'woocommerce' ), 'i18n_remove_used_attribute_confirmation_message' => __( 'If you remove this attribute, customers will no longer be able to purchase some variations of this product.', 'woocommerce' ),
'i18n_add_attribute_error_notice' => __( 'Adding new attribute failed.', 'woocommerce' ), 'i18n_add_attribute_error_notice' => __( 'Adding new attribute failed.', 'woocommerce' ),
/* translators: %s: WC_DELIMITER */
'i18n_attributes_default_placeholder' => sprintf( esc_attr__( 'Enter some descriptive text. Use “%s” to separate different values.', 'woocommerce' ), esc_attr( WC_DELIMITER ) ),
'i18n_attributes_used_for_variations_placeholder' => sprintf( esc_attr__( 'Enter options for customers to choose from, f.e. “Blue” or “Large”. Use “%s” to separate different options.', 'woocommerce' ), esc_attr( WC_DELIMITER ) )
); );
wp_localize_script( 'wc-admin-meta-boxes', 'woocommerce_admin_meta_boxes', $params ); wp_localize_script( 'wc-admin-meta-boxes', 'woocommerce_admin_meta_boxes', $params );

View File

@ -41,7 +41,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<select multiple="multiple" <select multiple="multiple"
data-minimum_input_length="0" data-minimum_input_length="0"
data-limit="50" data-return_id="id" data-limit="50" data-return_id="id"
data-placeholder="<?php esc_attr_e( 'Select terms', 'woocommerce' ); ?>" data-placeholder="<?php esc_attr_e( 'Select values', 'woocommerce' ); ?>"
data-orderby="<?php echo esc_attr( $attribute_orderby ); ?>" data-orderby="<?php echo esc_attr( $attribute_orderby ); ?>"
class="multiselect attribute_values wc-taxonomy-term-search" class="multiselect attribute_values wc-taxonomy-term-search"
name="attribute_values[<?php echo esc_attr( $i ); ?>][]" name="attribute_values[<?php echo esc_attr( $i ); ?>][]"
@ -79,7 +79,7 @@ if ( ! defined( 'ABSPATH' ) ) {
do_action( 'woocommerce_product_option_terms', $attribute_taxonomy, $i, $attribute ); do_action( 'woocommerce_product_option_terms', $attribute_taxonomy, $i, $attribute );
} else { } else {
?> ?>
<textarea name="attribute_values[<?php echo esc_attr( $i ); ?>]" cols="5" rows="5" placeholder="<?php /* translators: %s: WC_DELIMITER */ printf( esc_attr__( 'Enter options for customers to choose from, f.e. “Blue” or “Large”. Use “%s” to separate different options.', 'woocommerce' ), esc_attr( WC_DELIMITER ) ); ?>"><?php echo esc_textarea( wc_implode_text_attributes( $attribute->get_options() ) ); ?></textarea> <textarea name="attribute_values[<?php echo esc_attr( $i ); ?>]" cols="5" rows="5"><?php echo esc_textarea( wc_implode_text_attributes( $attribute->get_options() ) ); ?></textarea>
<?php <?php
} }
?> ?>

View File

@ -17,14 +17,15 @@ $product_attributes = $product_object->get_attributes( 'edit' );
?> ?>
<div id="product_attributes" class="panel wc-metaboxes-wrapper hidden"> <div id="product_attributes" class="panel wc-metaboxes-wrapper hidden">
<div class="toolbar toolbar-top"> <div class="toolbar toolbar-top">
<div id="message" class="inline notice woocommerce-message"> <div id="message" class="inline notice woocommerce-message is-dismissible">
<p> <p class="help">
<?php <?php
esc_html_e( 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”.', 'Add descriptive pieces of information that customers can use to search for this product on your store, such as “Material” or “Brand”.',
'woocommerce' 'woocommerce'
); );
?> ?>
<button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'woocommerce' ); ?></span></button>
</p> </p>
</div> </div>
<span class="expand-close"> <span class="expand-close">