Skip to content

Commit

Permalink
Merge pull request #2511 from woocommerce/fix/2507-snackbar-error-mes…
Browse files Browse the repository at this point in the history
…sage

Update Block validation to support error context
  • Loading branch information
eason9487 authored Aug 14, 2024
2 parents 9f669d4 + ea781f6 commit 0d6e636
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 36 deletions.
20 changes: 15 additions & 5 deletions js/src/blocks/product-date-time-field/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
useValidation,
} from '@woocommerce/product-editor';
import { Flex, FlexBlock } from '@wordpress/components';
import { isWcVersion } from '@woocommerce/settings'; // eslint-disable-line import/no-unresolved

/**
* Internal dependencies
Expand All @@ -21,11 +22,19 @@ import styles from './editor.module.scss';
* @typedef {import('../types.js').ProductBasicAttributes} ProductBasicAttributes
*/

async function resolveValidationMessage( inputRef ) {
async function resolveValidationMessage( inputRef, context ) {
const input = inputRef.current;

if ( ! input.validity.valid ) {
return input.validationMessage;
// compatibility-code "WC < 9.2"
if ( isWcVersion( '9.2.0', '<' ) ) {
return input.validationMessage;
}

return {
message: input.validationMessage,
context,
};
}
}

Expand All @@ -35,8 +44,9 @@ async function resolveValidationMessage( inputRef ) {
* @param {Object} props React props.
* @param {ProductBasicAttributes} props.attributes
* @param {ProductEditorBlockContext} props.context
* @param {string} props.clientId
*/
export default function Edit( { attributes, context } ) {
export default function Edit( { attributes, context, clientId } ) {
const { property } = attributes;
const blockProps = useWooBlockProps( attributes );
const [ value, setValue ] = useProductEntityProp( property, {
Expand Down Expand Up @@ -76,11 +86,11 @@ export default function Edit( { attributes, context } ) {
};

const dateValidation = useValidation( `${ property }-date`, () =>
resolveValidationMessage( dateInputRef )
resolveValidationMessage( dateInputRef, clientId )
);

const timeValidation = useValidation( `${ property }-time`, () =>
resolveValidationMessage( timeInputRef )
resolveValidationMessage( timeInputRef, clientId )
);

return (
Expand Down
7 changes: 6 additions & 1 deletion src/Admin/Input/Integer.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public function __construct() {
// the text field block to work around it.
parent::__construct( 'integer', 'woocommerce/product-text-field' );

$this->set_block_attribute( 'type', [ 'value' => 'number' ] );
$this->set_block_attribute(
'pattern',
[
'value' => '0|[1-9]\d*',
]
);
}
}
7 changes: 6 additions & 1 deletion tests/Unit/Admin/Input/InputCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,12 @@ public function test_integer() {

$this->assertEquals( 'integer', $input->get_type() );
$this->assertEquals( 'woocommerce/product-text-field', $input->get_block_name() );
$this->assertEquals( [ 'value' => 'number' ], $input->get_block_attributes()['type'] );
$this->assertEquals(
[
'value' => '0|[1-9]\d*',
],
$input->get_block_attributes()['pattern']
);
}

public function test_select() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,9 @@ public function test_multipack_input() {
'property' => 'meta_data._wc_gla_multipack',
'label' => 'Multipack',
'tooltip' => 'The number of identical products in a multipack. Use this attribute to indicate that you\'ve grouped multiple identical products for sale as one item.',
'type' => [ 'value' => 'number' ],
'pattern' => [
'value' => '0|[1-9]\d*',
],
'min' => [ 'value' => 0 ],
],
],
Expand Down
61 changes: 40 additions & 21 deletions tests/e2e/specs/product-editor/block-integration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,23 +93,18 @@ test.describe( 'Product Block Editor integration', () => {
await expect( panel.getByRole( 'combobox' ) ).toHaveCount( 9 );

/*
* 8 <input type="text|date|time">:
* 9 <input type="text|date|time">:
* - GTIN
* - MPN
* - Size
* - Color
* - Material
* - Pattern
* - Multipack
* - Availability date
* - Availability time
*/
await expect( panel.getByRole( 'textbox' ) ).toHaveCount( 8 );

/*
* 1 <input type="number">:
* - Multipack
*/
await expect( panel.getByRole( 'spinbutton' ) ).toHaveCount( 1 );
await expect( panel.getByRole( 'textbox' ) ).toHaveCount( 9 );

/*
* 16 pairs of <label> and help icon buttons:
Expand Down Expand Up @@ -243,16 +238,11 @@ test.describe( 'Product Block Editor integration', () => {
* - Color
* - Material
* - Pattern
* - Multipack
* - Availability date
* - Availability time
*/
await expect( panel.getByRole( 'textbox' ) ).toHaveCount( 8 );

/*
* 1 <input type="number"> for variation product:
* - Multipack
*/
await expect( panel.getByRole( 'spinbutton' ) ).toHaveCount( 1 );
await expect( panel.getByRole( 'textbox' ) ).toHaveCount( 9 );
} );

test( 'Channel visibility is disabled when hiding in product catalog', async () => {
Expand Down Expand Up @@ -455,7 +445,9 @@ test.describe( 'Product Block Editor integration', () => {
*/
await dateInput.pressSequentially( '9' );

await editorUtils.assertUnableSave();
await editorUtils.assertUnableSave(
'Please enter a valid value. The field is incomplete or has an invalid date.'
);
await expect( dateHelp ).toBeVisible();
await expect( dateHelp ).toHaveText(
await editorUtils.evaluateValidationMessage( dateInput )
Expand All @@ -465,7 +457,9 @@ test.describe( 'Product Block Editor integration', () => {

await timeInput.pressSequentially( '9' );

await editorUtils.assertUnableSave();
await editorUtils.assertUnableSave(
'Please enter a valid value. The field is incomplete or has an invalid date.'
);
await expect( timeHelp ).toBeVisible();
await expect( timeHelp ).toHaveText(
await editorUtils.evaluateValidationMessage( timeInput )
Expand Down Expand Up @@ -527,24 +521,49 @@ test.describe( 'Product Block Editor integration', () => {
await expect( input ).toHaveValue( '' );
await expect( help ).toHaveCount( 0 );

// Assert invalid values

await input.fill( '-1' );

await editorUtils.assertUnableSave(
'The minimum value of the field is 0'
);
await editorUtils.assertUnableSave( 'Invalid value for the field.' );
await expect( help ).toBeVisible();
await expect( help ).toHaveText(
await editorUtils.evaluateValidationMessage( input )
);

await input.fill( '9.5' );

await editorUtils.assertUnableSave();
await editorUtils.assertUnableSave( 'Invalid value for the field.' );
await expect( help ).toBeVisible();
await expect( help ).toHaveText(
await editorUtils.evaluateValidationMessage( input )
);

await input.fill( '00' );

await editorUtils.assertUnableSave( 'Invalid value for the field.' );
await expect( help ).toBeVisible();
await expect( help ).toHaveText(
await editorUtils.evaluateValidationMessage( input )
);

await input.fill( '01' );

await editorUtils.assertUnableSave( 'Invalid value for the field.' );
await expect( help ).toBeVisible();
await expect( help ).toHaveText(
await editorUtils.evaluateValidationMessage( input )
);

await input.fill( '2e5' );

await editorUtils.assertUnableSave( 'Invalid value for the field.' );
await expect( help ).toBeVisible();
await expect( help ).toHaveText(
await editorUtils.evaluateValidationMessage( input )
);

// Assert valid values
await input.fill( '0' );
await editorUtils.save();

Expand Down
20 changes: 13 additions & 7 deletions tests/e2e/utils/product-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -577,17 +577,23 @@ export function getProductBlockEditorUtils( page ) {
async assertUnableSave( message = 'Please enter a valid value.' ) {
await this.clickSave();

const failureNotice = page
.locator( '.components-snackbar__content' )
.filter( { hasText: new RegExp( message ) } );

const failureNoticeDismissButton =
failureNotice.getByRole( 'button' );
const failureNotice = page.locator(
'.components-snackbar__content'
);

await expect( failureNotice ).toBeVisible();
await expect( failureNotice ).toContainText( message );

const failureNoticeButton = failureNotice.getByRole( 'button' );

// Dismiss the notice.
await failureNoticeDismissButton.click();
if ( await failureNoticeButton.isVisible() ) {
// compatibility-code "WC < 9.2" -- Dismiss by its inner close button.
await failureNoticeButton.click();
} else {
failureNotice.click();
}

await expect( failureNotice ).toHaveCount( 0 );
},
};
Expand Down

0 comments on commit 0d6e636

Please sign in to comment.