Skip to content

Commit

Permalink
Add validation to enable Save attributes and Save variations buttons (#…
Browse files Browse the repository at this point in the history
…37046)

* Add validation for attributes and variations

* Enable save button when data is valid

# Conflicts:
#	plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-variations.php

* Add changelog

* Remove if

* Remove validation while saving

# Conflicts:
#	plugins/woocommerce/client/legacy/js/admin/meta-boxes-product-variation.js

* Rename method `is_attribute_or_variation_empty`

* Add button title when disabled

# Conflicts:
#	plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-variations.php

* Fix typo

* Fix e2e tests

* Convert functions into global fn

* Use maybe_disable_save_button

* Fix validation

* Refactor `is_attribute_or_variation_empty`

---------

Co-authored-by: Fernando Marichal <contacto@fernandomarichal.com>
  • Loading branch information
octaedro and Fernando Marichal committed Mar 9, 2023
1 parent bec3ec1 commit 3edd8f4
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 64 deletions.
@@ -0,0 +1,4 @@
Significance: patch
Type: fix

Add validation when saving attributes and variations
Expand Up @@ -34,7 +34,11 @@ jQuery( function ( $ ) {
this.open_modal_to_set_variations_price
)
.on( 'reload', this.reload )
.on( 'click', 'button.create-variations', this.create_variations);
.on(
'click',
'button.create-variations',
this.create_variations
);

$(
'input.variable_is_downloadable, input.variable_is_virtual, input.variable_manage_stock'
Expand All @@ -52,18 +56,10 @@ jQuery( function ( $ ) {
);
},

create_variations: function() {
create_variations: function () {
var new_attribute_data = $(
'.woocommerce_variation_new_attribute_data'
);
var attribute_name = new_attribute_data.find( 'input[name^="attribute_names"]' ).val();
var attribute_value = new_attribute_data
.find( 'textarea[name^="attribute_values"]' )
.val();

if ( ! attribute_name || ! attribute_value ) {
return;
}

$( '#variable_product_options' ).block( {
message: null,
Expand Down Expand Up @@ -110,9 +106,7 @@ jQuery( function ( $ ) {
'#variable_product_options_inner'
)
);
$( '#variable_product_options' ).trigger(
'reload'
);
$( '#variable_product_options' ).trigger( 'reload' );
$(
'#product_attributes > .product_attributes'
).replaceWith(
Expand Down
Expand Up @@ -482,6 +482,7 @@ jQuery( function ( $ ) {
$wrapper.unblock();

$( document.body ).trigger( 'woocommerce_added_attribute' );
jQuery.maybe_disable_save_button();
} );

if ( attribute ) {
Expand Down Expand Up @@ -664,6 +665,7 @@ jQuery( function ( $ ) {
) {
toggle_add_global_attribute_layout();
}
jQuery.maybe_disable_save_button();
}
return false;
} );
Expand Down Expand Up @@ -756,6 +758,7 @@ jQuery( function ( $ ) {
opacity: 0.6,
},
} );

var original_data = $( '.product_attributes' ).find(
'input, select, textarea'
);
Expand Down Expand Up @@ -829,7 +832,6 @@ jQuery( function ( $ ) {
);

$( document.body ).trigger( 'woocommerce_attributes_saved' );

}
} );
} );
Expand Down
183 changes: 135 additions & 48 deletions plugins/woocommerce/client/legacy/js/admin/meta-boxes.js
@@ -1,22 +1,82 @@
jQuery( function ( $ ) {
/**
* Function to check if the attribute and variation fields are empty.
*/
jQuery.is_attribute_or_variation_empty = function (
attributes_and_variations_data
) {
var has_empty_fields = false;
attributes_and_variations_data.each( function () {
var $this = $( this );
// Check if the field is checkbox or a search field.
if (
$this.hasClass( 'checkbox' ) ||
$this.filter( '[class*=search__field]' ).length
) {
return;
}

var is_empty = $this.is( 'select' )
? $this.find( ':selected' ).length === 0
: ! $this.val();
if ( is_empty ) {
has_empty_fields = true;
}
} );
return has_empty_fields;
};

/**
* Function to maybe disable the save button.
*/
jQuery.maybe_disable_save_button = function () {
var $tab = $( '.product_attributes' );
var $save_button = $( 'button.save_attributes' );
if (
$( '.woocommerce_variation_new_attribute_data' ).is( ':visible' )
) {
$tab = $( '.woocommerce_variation_new_attribute_data' );
$save_button = $( 'button.create-variations' );
}

var attributes_and_variations_data = $tab.find(
'input, select, textarea'
);
if (
jQuery.is_attribute_or_variation_empty(
attributes_and_variations_data
)
) {
if ( ! $save_button.is( ':disabled' ) ) {
$save_button.attr( 'disabled', 'disabled' );
$save_button.attr(
'title',
woocommerce_admin_meta_boxes.i18n_save_attribute_variation_tip
);
}
return;
}
$save_button.removeAttr( 'disabled' );
$save_button.removeAttr( 'title' );
};

// Run tipTip
function runTipTip() {
// Remove any lingering tooltips
$( '#tiptip_holder' ).removeAttr( 'style' );
$( '#tiptip_arrow' ).removeAttr( 'style' );
$( '.tips' ).tipTip({
'attribute': 'data-tip',
'fadeIn': 50,
'fadeOut': 50,
'delay': 200,
'keepAlive': true
});
$( '.tips' ).tipTip( {
attribute: 'data-tip',
fadeIn: 50,
fadeOut: 50,
delay: 200,
keepAlive: true,
} );
}

runTipTip();

$( '.wc-metaboxes-wrapper' ).on( 'click', '.wc-metabox > h3', function() {
$( '.wc-metaboxes-wrapper' ).on( 'click', '.wc-metabox > h3', function () {
var metabox = $( this ).parent( '.wc-metabox' );

if ( metabox.hasClass( 'closed' ) ) {
Expand All @@ -30,51 +90,78 @@ jQuery( function ( $ ) {
} else {
metabox.addClass( 'open' );
}
});
} );

// Tabbed Panels
$( document.body ).on( 'wc-init-tabbed-panels', function() {
$( 'ul.wc-tabs' ).show();
$( 'ul.wc-tabs a' ).on( 'click', function( e ) {
e.preventDefault();
var panel_wrap = $( this ).closest( 'div.panel-wrap' );
$( 'ul.wc-tabs li', panel_wrap ).removeClass( 'active' );
$( this ).parent().addClass( 'active' );
$( 'div.panel', panel_wrap ).hide();
$( $( this ).attr( 'href' ) ).show();
});
$( 'div.panel-wrap' ).each( function() {
$( this ).find( 'ul.wc-tabs li' ).eq( 0 ).find( 'a' ).trigger( 'click' );
});
}).trigger( 'wc-init-tabbed-panels' );
$( document.body )
.on( 'wc-init-tabbed-panels', function () {
$( 'ul.wc-tabs' ).show();
$( 'ul.wc-tabs a' ).on( 'click', function ( e ) {
e.preventDefault();
var panel_wrap = $( this ).closest( 'div.panel-wrap' );
$( 'ul.wc-tabs li', panel_wrap ).removeClass( 'active' );
$( this ).parent().addClass( 'active' );
$( 'div.panel', panel_wrap ).hide();
$( $( this ).attr( 'href' ) ).show();
} );
$( 'div.panel-wrap' ).each( function () {
$( this )
.find( 'ul.wc-tabs li' )
.eq( 0 )
.find( 'a' )
.trigger( 'click' );
} );
} )
.trigger( 'wc-init-tabbed-panels' );

// Date Picker
$( document.body ).on( 'wc-init-datepickers', function() {
$( '.date-picker-field, .date-picker' ).datepicker({
dateFormat: 'yy-mm-dd',
numberOfMonths: 1,
showButtonPanel: true
});
}).trigger( 'wc-init-datepickers' );
$( document.body )
.on( 'wc-init-datepickers', function () {
$( '.date-picker-field, .date-picker' ).datepicker( {
dateFormat: 'yy-mm-dd',
numberOfMonths: 1,
showButtonPanel: true,
} );
} )
.trigger( 'wc-init-datepickers' );

// Meta-Boxes - Open/close
$( '.wc-metaboxes-wrapper' ).on( 'click', '.wc-metabox h3', function( event ) {
// If the user clicks on some form input inside the h3, like a select list (for variations), the box should not be toggled
if ( $( event.target ).filter( ':input, option, .sort' ).length ) {
return;
}
$( '.wc-metaboxes-wrapper' )
.on( 'click', '.wc-metabox h3', function ( event ) {
// If the user clicks on some form input inside the h3, like a select list (for variations), the box should not be toggled
if ( $( event.target ).filter( ':input, option, .sort' ).length ) {
return;
}

$( this ).next( '.wc-metabox-content' ).stop().slideToggle();
})
.on( 'click', '.expand_all', function() {
$( this ).closest( '.wc-metaboxes-wrapper' ).find( '.wc-metabox > .wc-metabox-content' ).show();
return false;
})
.on( 'click', '.close_all', function() {
$( this ).closest( '.wc-metaboxes-wrapper' ).find( '.wc-metabox > .wc-metabox-content' ).hide();
return false;
});
$( '.wc-metabox.closed' ).each( function() {
$( this ).next( '.wc-metabox-content' ).stop().slideToggle();
} )
.on( 'click', '.expand_all', function () {
$( this )
.closest( '.wc-metaboxes-wrapper' )
.find( '.wc-metabox > .wc-metabox-content' )
.show();
return false;
} )
.on( 'click', '.close_all', function () {
$( this )
.closest( '.wc-metaboxes-wrapper' )
.find( '.wc-metabox > .wc-metabox-content' )
.hide();
return false;
} );
$( '.wc-metabox.closed' ).each( function () {
$( this ).find( '.wc-metabox-content' ).hide();
});
});
} );

$( '.product_attributes, .woocommerce_variation_new_attribute_data' ).on(
'keyup',
'input, textarea',
jQuery.maybe_disable_save_button
);

$( '#product_attributes' ).on(
'change',
'select.attribute_values',
jQuery.maybe_disable_save_button
);
} );
Expand Up @@ -419,6 +419,7 @@ public function admin_scripts() {
'i18n_product_other_tip' => __( 'Product types define available product details and attributes, such as downloadable files and variations. They’re 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. We’ll 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() ) ),
);
Expand Down
Expand Up @@ -106,7 +106,7 @@
<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>
<button type="button" class="button save_attributes button-primary"><?php esc_html_e( 'Save attributes', 'woocommerce' ); ?></button>
<button type="button" class="button save_attributes button-primary" disabled="disabled" title="<?php echo esc_html_e( 'Make sure you enter the name and values for each attribute.', 'woocommerce' ); ?>"><?php esc_html_e( 'Save attributes', 'woocommerce' ); ?></button>
</div>
<?php do_action( 'woocommerce_product_options_attributes' ); ?>
</div>
Expand Up @@ -29,7 +29,7 @@
require __DIR__ . '/html-product-attribute-inner.php';
?>
<div class="toolbar">
<button type="button" class="button button-primary create-variations"><?php esc_html_e( 'Create variations', 'woocommerce' ); ?></button>
<button type="button" class="button button-primary create-variations" disabled="disabled" title="<?php echo esc_html_e( 'Make sure you enter the name and values for each attribute.', 'woocommerce' ); ?>"><?php esc_html_e( 'Create variations', 'woocommerce' ); ?></button>
</div>
</div>
</div>
Expand Down
Expand Up @@ -64,6 +64,7 @@ test.describe.serial( 'Add New Variable Product Page', () => {
'val1 | val2'
);
}
await page.keyboard.press( 'ArrowUp' );
await page.click( 'text=Save attributes' );

// Save before going to the Variations tab to prevent variations from all attributes to be automatically created
Expand Down Expand Up @@ -138,6 +139,7 @@ test.describe.serial( 'Add New Variable Product Page', () => {
await page.fill( 'input[name="variable_length[2]"]', productLength );
await page.fill( 'input[name="variable_width[2]"]', productWidth );
await page.fill( 'input[name="variable_height[2]"]', productHeight );
await page.keyboard.press( 'ArrowUp' );
await page.click( 'button.save-variation-changes' );

// bulk-edit variations
Expand Down Expand Up @@ -211,6 +213,7 @@ test.describe.serial( 'Add New Variable Product Page', () => {
`textarea[name="attribute_values[${ i }]"]`,
'val1 | val2'
);
await page.keyboard.press( 'ArrowUp' );
await page.click( 'text=Save attributes' );
await expect(
page
Expand Down

0 comments on commit 3edd8f4

Please sign in to comment.