Commit c0f025744b for woocommerce
commit c0f025744b778ca725f380fd8a729483af7c7e87
Author: Amit Raj <77401999+amitraj2203@users.noreply.github.com>
Date: Fri May 30 17:49:58 2025 +0530
Fix: WooCommerce Blocks Storybook bugs (#58053)
diff --git a/plugins/woocommerce/changelog/58053-fix-woocommerce-blocks-storybook-bugs b/plugins/woocommerce/changelog/58053-fix-woocommerce-blocks-storybook-bugs
new file mode 100644
index 0000000000..b7ab967114
--- /dev/null
+++ b/plugins/woocommerce/changelog/58053-fix-woocommerce-blocks-storybook-bugs
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix various component functionality and styling issues in Storybook implementation.
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/country-input/stories/index.stories.tsx b/plugins/woocommerce/client/blocks/assets/js/base/components/country-input/stories/index.stories.tsx
index 7ab78ae523..0276b5311b 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/country-input/stories/index.stories.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/country-input/stories/index.stories.tsx
@@ -21,7 +21,7 @@ export default {
countries,
autoComplete: 'off',
id: 'country',
- label: 'Countries: ',
+ label: 'Country: ',
required: false,
},
argTypes: {
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/quantity-selector/index.tsx b/plugins/woocommerce/client/blocks/assets/js/base/components/quantity-selector/index.tsx
index ee83e68874..6b8d8ec2a2 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/quantity-selector/index.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/quantity-selector/index.tsx
@@ -52,8 +52,10 @@ export interface QuantitySelectorProps {
disabled: boolean;
/**
* Whether the component should be editable
+ *
+ * @default true
*/
- editable: boolean;
+ editable?: boolean;
}
const QuantitySelector = ( {
@@ -65,7 +67,7 @@ const QuantitySelector = ( {
step = 1,
itemName = '',
disabled,
- editable,
+ editable = true,
}: QuantitySelectorProps ): JSX.Element => {
const classes = clsx( 'wc-block-components-quantity-selector', className );
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/quantity-selector/stories/index.stories.tsx b/plugins/woocommerce/client/blocks/assets/js/base/components/quantity-selector/stories/index.stories.tsx
index 321c8404ae..2b7daad2ea 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/quantity-selector/stories/index.stories.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/quantity-selector/stories/index.stories.tsx
@@ -32,7 +32,24 @@ const Template: Story< QuantitySelectorProps > = ( args ) => {
export const Default = Template.bind( {} );
Default.args = {};
+export const NonEditable = Template.bind( {} );
+NonEditable.args = {
+ editable: false,
+};
+
export const Disabled = Template.bind( {} );
Disabled.args = {
disabled: true,
};
+
+export const WithMinimum = Template.bind( {} );
+WithMinimum.args = {
+ minimum: 2,
+ quantity: 2,
+};
+
+export const WithMaximum = Template.bind( {} );
+WithMaximum.args = {
+ maximum: 5,
+ quantity: 3,
+};
diff --git a/plugins/woocommerce/client/blocks/packages/components/chip/stories/removable-chip.stories.tsx b/plugins/woocommerce/client/blocks/packages/components/chip/stories/removable-chip.stories.tsx
index 211c48f80f..e3b0ce79bf 100644
--- a/plugins/woocommerce/client/blocks/packages/components/chip/stories/removable-chip.stories.tsx
+++ b/plugins/woocommerce/client/blocks/packages/components/chip/stories/removable-chip.stories.tsx
@@ -2,6 +2,7 @@
* External dependencies
*/
import type { Meta, StoryFn } from '@storybook/react';
+import { useState } from '@wordpress/element';
/**
* Internal dependencies
@@ -21,9 +22,22 @@ export default {
},
} as Meta< RemovableChipProps >;
-const Template: StoryFn< RemovableChipProps > = ( args ) => (
- <RemovableChip { ...args } />
-);
+const Template: StoryFn< RemovableChipProps > = ( args ) => {
+ const [ isVisible, setIsVisible ] = useState( true );
+ const handleRemove = () => {
+ setIsVisible( false );
+ };
+
+ if ( ! isVisible ) {
+ return (
+ <button onClick={ () => setIsVisible( true ) }>
+ <em>Chip was removed, click to reset</em>
+ </button>
+ );
+ }
+
+ return <RemovableChip { ...args } onRemove={ handleRemove } />;
+};
export const Default: StoryFn< RemovableChipProps > = Template.bind( {} );
Default.args = {
@@ -31,3 +45,19 @@ Default.args = {
text: 'Take me to the casino',
screenReaderText: "I'm a removable chip, me",
};
+
+export const Disabled: StoryFn< RemovableChipProps > = Template.bind( {} );
+Disabled.args = {
+ element: 'li',
+ text: 'Disabled chip',
+ disabled: true,
+};
+
+export const RemoveOnAnyClick: StoryFn< RemovableChipProps > = Template.bind(
+ {}
+);
+RemoveOnAnyClick.args = {
+ element: 'li',
+ text: 'Click anywhere to remove',
+ removeOnAnyClick: true,
+};
diff --git a/plugins/woocommerce/client/blocks/packages/components/radio-control-accordion/stories/index.stories.tsx b/plugins/woocommerce/client/blocks/packages/components/radio-control-accordion/stories/index.stories.tsx
index db29cf22e3..5014bea20f 100644
--- a/plugins/woocommerce/client/blocks/packages/components/radio-control-accordion/stories/index.stories.tsx
+++ b/plugins/woocommerce/client/blocks/packages/components/radio-control-accordion/stories/index.stories.tsx
@@ -8,6 +8,7 @@ import { useArgs } from '@storybook/preview-api';
* Internal dependencies
*/
import { RadioControlAccordion, RadioControlAccordionProps } from '..';
+import './style.stories.scss';
export default {
title: 'External Components/RadioControlAccordion',
diff --git a/plugins/woocommerce/client/blocks/packages/components/radio-control-accordion/stories/style.stories.scss b/plugins/woocommerce/client/blocks/packages/components/radio-control-accordion/stories/style.stories.scss
new file mode 100644
index 0000000000..a09819e08f
--- /dev/null
+++ b/plugins/woocommerce/client/blocks/packages/components/radio-control-accordion/stories/style.stories.scss
@@ -0,0 +1,8 @@
+.wc-block-components-radio-control-accordion-option {
+ margin: em($gap) 0;
+ margin-top: 0;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+}
diff --git a/plugins/woocommerce/client/blocks/packages/components/text-input/stories/text-input.stories.tsx b/plugins/woocommerce/client/blocks/packages/components/text-input/stories/text-input.stories.tsx
index 73e77d6f6d..276ce9cb31 100644
--- a/plugins/woocommerce/client/blocks/packages/components/text-input/stories/text-input.stories.tsx
+++ b/plugins/woocommerce/client/blocks/packages/components/text-input/stories/text-input.stories.tsx
@@ -168,12 +168,24 @@ Default.args = {
id: 'unique-id',
label: 'Enter your value',
value: 'Lorem ipsum',
+};
+
+export const WithError: StoryFn< TextInputProps > = Template.bind( {} );
+WithError.args = {
+ id: 'unique-id',
+ label: 'Enter your value',
+ value: 'Lorem ipsum',
feedback: (
<div className="wc-block-components-validation-error">
- <p>
- This is a feedback element, usually it would be used as an error
- message.
- </p>
+ <p>This is an error message.</p>
</div>
),
};
+
+export const WithHelp: StoryFn< TextInputProps > = Template.bind( {} );
+WithHelp.args = {
+ id: 'unique-id',
+ label: 'Enter your value',
+ value: 'Lorem ipsum',
+ help: 'This is help text for the input.',
+};
diff --git a/plugins/woocommerce/client/blocks/packages/components/text-input/stories/validated-text-input.stories.tsx b/plugins/woocommerce/client/blocks/packages/components/text-input/stories/validated-text-input.stories.tsx
index a0e7cd72b0..f58fdcdc60 100644
--- a/plugins/woocommerce/client/blocks/packages/components/text-input/stories/validated-text-input.stories.tsx
+++ b/plugins/woocommerce/client/blocks/packages/components/text-input/stories/validated-text-input.stories.tsx
@@ -4,6 +4,9 @@
import type { StoryFn, Meta } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { useArgs } from '@storybook/client-api';
+import { useDispatch } from '@wordpress/data';
+import { validationStore } from '@woocommerce/block-data';
+import { useState, useEffect } from '@wordpress/element';
/**
* Internal dependencies
@@ -214,12 +217,70 @@ export default {
const Template: StoryFn< ValidatedTextInputProps > = ( args ) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [ _, updateArgs ] = useArgs();
- const onChange = ( value: string ) => {
- action( 'change' )( value || '' );
- updateArgs( { value } );
+ const getFormattedValue = (
+ val: string | number | readonly string[] | undefined
+ ) => {
+ const stringVal = typeof val === 'string' ? val : String( val || '' );
+ return args.customFormatter
+ ? args.customFormatter( stringVal )
+ : stringVal;
};
- return <ValidatedTextInput { ...args } onChange={ onChange } />;
+ const [ inputValue, setInputValue ] = useState(
+ getFormattedValue( args.value )
+ );
+ const { setValidationErrors, showValidationError } =
+ useDispatch( validationStore );
+
+ useEffect( () => {
+ setInputValue( getFormattedValue( args.value ) );
+ }, [ args.value, args.customFormatter ] );
+
+ const onChange = ( newValue: string ) => {
+ const formattedValue = args.customFormatter
+ ? args.customFormatter( newValue )
+ : newValue;
+
+ setInputValue( formattedValue );
+
+ action( 'change' )( newValue || '' );
+ updateArgs( { value: newValue } );
+
+ // Always show error for WithError story.
+ if ( args.id === 'with-error-id' ) {
+ setValidationErrors( {
+ [ args.id ]: {
+ message: 'This field cannot be empty',
+ hidden: true,
+ },
+ } );
+ showValidationError( args.id );
+ return;
+ }
+
+ // Default: only show error if input is empty and showError is true.
+ if (
+ args.showError &&
+ ! newValue.trim() &&
+ args.id !== 'with-error-id'
+ ) {
+ setValidationErrors( {
+ [ args.id || 'unique-id' ]: {
+ message: 'This field cannot be empty',
+ hidden: true,
+ },
+ } );
+ showValidationError( args.id || 'unique-id' );
+ }
+ };
+
+ return (
+ <ValidatedTextInput
+ { ...args }
+ value={ inputValue }
+ onChange={ onChange }
+ />
+ );
};
export const Default: StoryFn< ValidatedTextInputProps > = Template.bind( {} );
@@ -233,11 +294,11 @@ export const WithError: StoryFn< ValidatedTextInputProps > = Template.bind(
{}
);
WithError.args = {
- id: 'unique-id',
- showError: true,
- errorMessage: 'This is an error message',
+ id: 'with-error-id',
label: 'Enter your value',
- value: 'Lorem ipsum',
+ value: '',
+ errorMessage: 'This is an error message',
+ showError: true,
};
export const WithCustomFormatter: StoryFn< ValidatedTextInputProps > =
diff --git a/plugins/woocommerce/client/blocks/packages/components/title/stories/index.stories.tsx b/plugins/woocommerce/client/blocks/packages/components/title/stories/index.stories.tsx
index 42da665b0f..2fbc587afd 100644
--- a/plugins/woocommerce/client/blocks/packages/components/title/stories/index.stories.tsx
+++ b/plugins/woocommerce/client/blocks/packages/components/title/stories/index.stories.tsx
@@ -8,6 +8,7 @@ import type { StoryFn, Meta } from '@storybook/react';
*/
import Title, { type TitleProps } from '..';
import '../style.scss';
+import './style.stories.scss';
export default {
title: 'External Components/Title',
@@ -47,8 +48,16 @@ export default {
} as Meta< TitleProps >;
const Template: StoryFn< TitleProps > = ( args ) => {
- const { children, ...rest } = args;
- return <Title { ...rest }>{ children }</Title>;
+ const { children, headingLevel, ...rest } = args;
+ return (
+ <Title
+ { ...rest }
+ headingLevel={ headingLevel }
+ className={ `h${ headingLevel }` }
+ >
+ { children }
+ </Title>
+ );
};
export const Default: StoryFn< TitleProps > = Template.bind( {} );
diff --git a/plugins/woocommerce/client/blocks/packages/components/title/stories/style.stories.scss b/plugins/woocommerce/client/blocks/packages/components/title/stories/style.stories.scss
new file mode 100644
index 0000000000..064dd9495c
--- /dev/null
+++ b/plugins/woocommerce/client/blocks/packages/components/title/stories/style.stories.scss
@@ -0,0 +1,20 @@
+.wc-block-components-title {
+ &[class*="h1"] {
+ font-size: 2em;
+ }
+ &[class*="h2"] {
+ font-size: 1.5em;
+ }
+ &[class*="h3"] {
+ font-size: 1.17em;
+ }
+ &[class*="h4"] {
+ font-size: 1em;
+ }
+ &[class*="h5"] {
+ font-size: 0.83em;
+ }
+ &[class*="h6"] {
+ font-size: 0.67em;
+ }
+}