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

View File

@ -463,6 +463,14 @@ jQuery( function ( $ ) {
.find( 'option[value="' + $( el ).data( 'taxonomy' ) + '"]' )
.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(
'disabled-items',
@ -514,6 +522,26 @@ jQuery( function ( $ ) {
$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() {
$( document.body ).trigger( 'wc-enhanced-select-init' );
}
@ -547,6 +575,18 @@ jQuery( function ( $ ) {
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' );
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 ) {
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(
'remove_item_notice' => $remove_item_notice,
'remove_fee_notice' => $remove_fee_notice,
'remove_shipping_notice' => $remove_shipping_notice,
'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_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' ),
'remove_item_meta' => __( 'Remove this item meta?', 'woocommerce' ),
'name_label' => __( 'Name', 'woocommerce' ),
'remove_label' => __( 'Remove', 'woocommerce' ),
'click_to_toggle' => __( 'Click to toggle', 'woocommerce' ),
'values_label' => __( 'Value(s)', 'woocommerce' ),
'text_attribute_tip' => __( 'Enter some text, or some attributes by pipe (|) separating values.', 'woocommerce' ),
'visible_label' => __( 'Visible on the product page', 'woocommerce' ),
'used_for_variations_label' => __( 'Used for variations', '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' ),
'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_shipping' => __( "Load the customer's shipping information? This will remove any currently entered shipping information.", 'woocommerce' ),
'featured_label' => __( 'Featured', 'woocommerce' ),
'prices_include_tax' => esc_attr( get_option( 'woocommerce_prices_include_tax' ) ),
'tax_based_on' => esc_attr( get_option( 'woocommerce_tax_based_on' ) ),
'round_at_subtotal' => esc_attr( get_option( 'woocommerce_tax_round_at_subtotal' ) ),
'no_customer_selected' => __( 'No customer selected', 'woocommerce' ),
'plugin_url' => WC()->plugin_url(),
'ajax_url' => admin_url( 'admin-ajax.php' ),
'order_item_nonce' => wp_create_nonce( 'order-item' ),
'add_attribute_nonce' => wp_create_nonce( 'add-attribute' ),
'save_attributes_nonce' => wp_create_nonce( 'save-attributes' ),
'add_attributes_and_variations' => wp_create_nonce( 'add-attributes-and-variations' ),
'calc_totals_nonce' => wp_create_nonce( 'calc-totals' ),
'get_customer_details_nonce' => wp_create_nonce( 'get-customer-details' ),
'search_products_nonce' => wp_create_nonce( 'search-products' ),
'grant_access_nonce' => wp_create_nonce( 'grant-access' ),
'revoke_access_nonce' => wp_create_nonce( 'revoke-access' ),
'add_order_note_nonce' => wp_create_nonce( 'add-order-note' ),
'delete_order_note_nonce' => wp_create_nonce( 'delete-order-note' ),
'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,
'base_country' => WC()->countries->get_base_country(),
'currency_format_num_decimals' => wc_get_price_decimals(),
'currency_format_symbol' => get_woocommerce_currency_symbol( $currency ),
'currency_format_decimal_sep' => esc_attr( wc_get_price_decimal_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.
'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() ) ) ),
'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' ),
'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_add_fee' => __( 'Enter a fixed amount or percentage to apply as a fee.', '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_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_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_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_save_attribute_variation_tip' => __( 'Make sure you enter the name and values for each attribute.', 'woocommerce' ),
'remove_item_notice' => $remove_item_notice,
'remove_fee_notice' => $remove_fee_notice,
'remove_shipping_notice' => $remove_shipping_notice,
'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_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' ),
'remove_item_meta' => __( 'Remove this item meta?', 'woocommerce' ),
'name_label' => __( 'Name', 'woocommerce' ),
'remove_label' => __( 'Remove', 'woocommerce' ),
'click_to_toggle' => __( 'Click to toggle', 'woocommerce' ),
'values_label' => __( 'Value(s)', 'woocommerce' ),
'text_attribute_tip' => __( 'Enter some text, or some attributes by pipe (|) separating values.', 'woocommerce' ),
'visible_label' => __( 'Visible on the product page', 'woocommerce' ),
'used_for_variations_label' => __( 'Used for variations', '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' ),
'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_shipping' => __( "Load the customer's shipping information? This will remove any currently entered shipping information.", 'woocommerce' ),
'featured_label' => __( 'Featured', 'woocommerce' ),
'prices_include_tax' => esc_attr( get_option( 'woocommerce_prices_include_tax' ) ),
'tax_based_on' => esc_attr( get_option( 'woocommerce_tax_based_on' ) ),
'round_at_subtotal' => esc_attr( get_option( 'woocommerce_tax_round_at_subtotal' ) ),
'no_customer_selected' => __( 'No customer selected', 'woocommerce' ),
'plugin_url' => WC()->plugin_url(),
'ajax_url' => admin_url( 'admin-ajax.php' ),
'order_item_nonce' => wp_create_nonce( 'order-item' ),
'add_attribute_nonce' => wp_create_nonce( 'add-attribute' ),
'save_attributes_nonce' => wp_create_nonce( 'save-attributes' ),
'add_attributes_and_variations' => wp_create_nonce( 'add-attributes-and-variations' ),
'calc_totals_nonce' => wp_create_nonce( 'calc-totals' ),
'get_customer_details_nonce' => wp_create_nonce( 'get-customer-details' ),
'search_products_nonce' => wp_create_nonce( 'search-products' ),
'grant_access_nonce' => wp_create_nonce( 'grant-access' ),
'revoke_access_nonce' => wp_create_nonce( 'revoke-access' ),
'add_order_note_nonce' => wp_create_nonce( 'add-order-note' ),
'delete_order_note_nonce' => wp_create_nonce( 'delete-order-note' ),
'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,
'base_country' => WC()->countries->get_base_country(),
'currency_format_num_decimals' => wc_get_price_decimals(),
'currency_format_symbol' => get_woocommerce_currency_symbol( $currency ),
'currency_format_decimal_sep' => esc_attr( wc_get_price_decimal_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.
'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() ) ) ),
'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' ),
'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_add_fee' => __( 'Enter a fixed amount or percentage to apply as a fee.', '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_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_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_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_save_attribute_variation_tip' => __( 'Make sure you enter the name and values for each attribute.', 'woocommerce' ),
/* 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_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 );

View File

@ -41,7 +41,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<select multiple="multiple"
data-minimum_input_length="0"
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 ); ?>"
class="multiselect attribute_values wc-taxonomy-term-search"
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 );
} 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
}
?>

View File

@ -17,14 +17,15 @@ $product_attributes = $product_object->get_attributes( 'edit' );
?>
<div id="product_attributes" class="panel wc-metaboxes-wrapper hidden">
<div class="toolbar toolbar-top">
<div id="message" class="inline notice woocommerce-message">
<p>
<div id="message" class="inline notice woocommerce-message is-dismissible">
<p class="help">
<?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'
);
?>
<button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'woocommerce' ); ?></span></button>
</p>
</div>
<span class="expand-close">