Enhance getInputProps to allow passing of non-overridden props (#35034)

* Allow additional props to be passed to the Form getInputProps method

* Remove getTextControlProps

* Pass additional shared props through getInputProps in shipping

* Simplify checkbox props

* Unwrap currency props

* Use onBlur event to sanitize prices

* Add changelog entry

* Add option to get checkbox props to form context helpers

* Update checkbox tracks handler naming and typing

* Fix up usage of getInputProps

* Add helper sanitize method

* Use sanitize helper method for product input fields

* Fix inventory input props after rebase

* Fix shipping typo

* Fix up form types after rebase

* Align all checkboxes on product page

* Rename new checkbox helper to getCheckboxControlProps

* Add helper method to get select control props

* Add data changelog entry

* Check for product name length on blur

* Add initial value for name to prevent uncontrolled value

* Add initial value for sku
This commit is contained in:
Joshua T Flowers 2022-10-21 07:03:49 -07:00 committed by GitHub
parent be6c26b671
commit 28f8e7f996
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 292 additions and 238 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Allow passing of additional props to form inputs

View File

@ -1,23 +1,22 @@
/**
* External dependencies
*/
import { ChangeEvent } from 'react';
import { createContext, useContext } from '@wordpress/element';
/**
* Internal dependencies
*/
import {
CheckboxProps,
ConsumerInputProps,
InputProps,
SelectControlProps,
} from './form';
export type FormErrors< Values > = {
[ P in keyof Values ]?: FormErrors< Values[ P ] > | string;
};
export type InputProps< Value > = {
value: Value;
checked: boolean;
selected?: boolean;
onChange: ( value: ChangeEvent< HTMLInputElement > | Value ) => void;
onBlur: () => void;
className: string | undefined;
help: string | null | undefined;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type FormContext< Values extends Record< string, any > > = {
values: Values;
@ -31,9 +30,18 @@ export type FormContext< Values extends Record< string, any > > = {
setValue: ( name: string, value: any ) => void;
setValues: ( valuesToSet: Values ) => void;
handleSubmit: () => Promise< Values >;
getCheckboxControlProps< Value extends Values[ keyof Values ] >(
name: string,
inputProps?: ConsumerInputProps< Values >
): CheckboxProps< Values, Value >;
getSelectControlProps< Value extends Values[ keyof Values ] >(
name: string,
inputProps?: ConsumerInputProps< Values >
): SelectControlProps< Values, Value >;
getInputProps< Value extends Values[ keyof Values ] >(
name: string
): InputProps< Value >;
name: string,
inputProps?: ConsumerInputProps< Values >
): InputProps< Values, Value >;
isValidForm: boolean;
resetForm: (
initialValues: Values,

View File

@ -1,6 +1,7 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import {
cloneElement,
useState,
@ -17,11 +18,12 @@ import _setWith from 'lodash/setWith';
import _get from 'lodash/get';
import _clone from 'lodash/clone';
import _isEqual from 'lodash/isEqual';
import _omit from 'lodash/omit';
/**
* Internal dependencies
*/
import { FormContext, FormErrors, InputProps } from './form-context';
import { FormContext, FormErrors } from './form-context';
type FormProps< Values > = {
/**
@ -87,6 +89,40 @@ export type FormRef< Values > = {
resetForm: ( initialValues: Values ) => void;
};
export type InputProps< Values, Value > = {
value: Value;
checked: boolean;
selected?: boolean;
onChange: (
value: ChangeEvent< HTMLInputElement > | Values[ keyof Values ]
) => void;
onBlur: () => void;
className: string | undefined;
help: string | null | undefined;
};
export type CheckboxProps< Values, Value > = Omit<
InputProps< Values, Value >,
'value' | 'selected'
>;
export type SelectControlProps< Values, Value > = Omit<
InputProps< Values, Value >,
'value'
> & {
value: string | undefined;
};
export type ConsumerInputProps< Values > = {
className?: string;
onChange?: (
value: ChangeEvent< HTMLInputElement > | Values[ keyof Values ]
) => void;
onBlur?: () => void;
[ key: string ]: unknown;
sanitize?: ( value: Values[ keyof Values ] ) => Values[ keyof Values ];
};
/**
* A form component to handle form state and provide input helper props.
*/
@ -268,21 +304,70 @@ function FormComponent< Values extends Record< string, any > >(
};
function getInputProps< Value = Values[ keyof Values ] >(
name: string
): InputProps< Value > {
name: string,
inputProps: ConsumerInputProps< Values > = {}
): InputProps< Values, Value > {
const inputValue = _get( values, name );
const isTouched = touched[ name ];
const inputError = _get( errors, name );
const {
className: classNameProp,
onBlur: onBlurProp,
onChange: onChangeProp,
sanitize,
...additionalProps
} = inputProps;
return {
value: inputValue,
checked: Boolean( inputValue ),
selected: inputValue,
onChange: ( value: ChangeEvent< HTMLInputElement > | Value ) =>
handleChange( name, value as Values[ keyof Values ] ),
onBlur: () => handleBlur( name ),
className: isTouched && inputError ? 'has-error' : undefined,
onChange: (
value: ChangeEvent< HTMLInputElement > | Values[ keyof Values ]
) => {
handleChange( name, value );
if ( onChangeProp ) {
onChangeProp( value );
}
},
onBlur: () => {
if ( sanitize ) {
handleChange( name, sanitize( inputValue ) );
}
handleBlur( name );
if ( onBlurProp ) {
onBlurProp();
}
},
className: classnames( classNameProp, {
'has-error': isTouched && inputError,
} ),
help: isTouched ? ( inputError as string ) : null,
...additionalProps,
};
}
function getCheckboxControlProps< Value = Values[ keyof Values ] >(
name: string,
inputProps: ConsumerInputProps< Values > = {}
): CheckboxProps< Values, Value > {
return _omit( getInputProps( name, inputProps ), [
'selected',
'value',
] );
}
function getSelectControlProps< Value = Values[ keyof Values ] >(
name: string,
inputProps: ConsumerInputProps< Values > = {}
): SelectControlProps< Values, Value > {
const selectControlProps = getInputProps( name, inputProps );
return {
...selectControlProps,
value:
selectControlProps.value === undefined
? undefined
: String( selectControlProps.value ),
};
}
@ -301,7 +386,9 @@ function FormComponent< Values extends Record< string, any > >(
setValue,
setValues,
handleSubmit,
getCheckboxControlProps,
getInputProps,
getSelectControlProps,
isValidForm: ! Object.keys( errors ).length,
resetForm,
};

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Add missing shipping class property

View File

@ -86,6 +86,7 @@ export type Product< Status = ProductStatus, Type = ProductType > = Omit<
backordered: boolean;
shipping_required: boolean;
shipping_taxable: boolean;
shipping_class: string;
shipping_class_id: number;
average_rating: string;
rating_count: number;

View File

@ -28,7 +28,12 @@ const AddProductPage: React.FC = () => {
return (
<div className="woocommerce-add-product">
<Form< Partial< Product > >
initialValues={ { stock_quantity: 0, stock_status: 'instock' } }
initialValues={ {
name: '',
sku: '',
stock_quantity: 0,
stock_status: 'instock',
} }
errors={ {} }
validate={ validate }
>

View File

@ -3,7 +3,7 @@
.woocommerce-product-form-actions {
margin-top: $gap-largest + $gap-smaller;
}
.woocommerce-product__checkbox,
.components-checkbox-control,
.components-toggle-control {
& > * {
margin-bottom: 0;
@ -17,8 +17,19 @@
margin-right: $gap-smaller;
}
}
.components-checkbox-control__label {
align-items: center;
.components-checkbox-control {
&__label {
display: flex;
align-items: center;
}
&__input-container {
align-self: center;
}
.components-base-control__field {
display: flex;
}
}
.woocommerce-tooltip {
margin-left: $gap-smaller;

View File

@ -20,8 +20,8 @@ import {
* Internal dependencies
*/
import './pricing-section.scss';
import { formatCurrencyDisplayValue, getCurrencySymbolProps } from './utils';
import { ProductSectionLayout } from '../layout/product-section-layout';
import { getInputControlProps } from './utils';
import { ADMIN_URL } from '../../utils/admin-settings';
import { CurrencyContext } from '../../lib/currency-context';
import { useProductHelper } from '../use-product-helper';
@ -44,6 +44,8 @@ export const PricingSection: React.FC = () => {
const pricesIncludeTax =
taxSettings.woocommerce_prices_include_tax === 'yes';
const context = useContext( CurrencyContext );
const { getCurrencyConfig, formatAmount } = context;
const currencyConfig = getCurrencyConfig();
const taxIncludedInPriceText = __(
'Per your {{link}}store settings{{/link}}, tax is {{strong}}included{{/strong}} in the price.',
@ -87,14 +89,17 @@ export const PricingSection: React.FC = () => {
},
} );
const regularPriceProps = getInputControlProps( {
...getInputProps( 'regular_price' ),
context,
} );
const salePriceProps = getInputControlProps( {
...getInputProps( 'sale_price' ),
context,
} );
const currencyInputProps = {
...getCurrencySymbolProps( currencyConfig ),
sanitize: ( value: Product[ keyof Product ] ) => {
return sanitizePrice( String( value ) );
},
};
const regularPriceProps = getInputProps(
'regular_price',
currencyInputProps
);
const salePriceProps = getInputProps( 'sale_price', currencyInputProps );
return (
<ProductSectionLayout
@ -135,10 +140,11 @@ export const PricingSection: React.FC = () => {
{ ...regularPriceProps }
label={ __( 'List price', 'woocommerce' ) }
placeholder={ __( '10.59', 'woocommerce' ) }
onChange={ ( value: string ) => {
const sanitizedValue = sanitizePrice( value );
regularPriceProps?.onChange( sanitizedValue );
} }
value={ formatCurrencyDisplayValue(
String( regularPriceProps?.value ),
currencyConfig,
formatAmount
) }
/>
</BaseControl>
{ ! isTaxSettingsResolving && (
@ -156,10 +162,11 @@ export const PricingSection: React.FC = () => {
{ ...salePriceProps }
label={ salePriceTitle }
placeholder={ __( '8.59', 'woocommerce' ) }
onChange={ ( value: string ) => {
const sanitizedValue = sanitizePrice( value );
salePriceProps?.onChange( sanitizedValue );
} }
value={ formatCurrencyDisplayValue(
String( salePriceProps?.value ),
currencyConfig,
formatAmount
) }
/>
</BaseControl>
</CardBody>

View File

@ -12,19 +12,4 @@
margin-left: $gap-smaller;
}
}
&__feature-checkbox {
.components-base-control__field {
display: flex;
.components-checkbox-control {
&__label {
display: flex;
}
&__input-container {
align-self: center;
}
}
}
}
}

View File

@ -34,14 +34,20 @@ import { BlockInstance, serialize, parse } from '@wordpress/blocks';
import './product-details-section.scss';
import { CategoryField } from '../fields/category-field';
import { EditProductLinkModal } from '../shared/edit-product-link-modal';
import { getCheckboxProps, getTextControlProps } from './utils';
import { getCheckboxTracks } from './utils';
import { ProductSectionLayout } from '../layout/product-section-layout';
const PRODUCT_DETAILS_SLUG = 'product-details';
export const ProductDetailsSection: React.FC = () => {
const { getInputProps, values, setValue, touched, errors } =
useFormContext< Product >();
const {
getCheckboxControlProps,
getInputProps,
values,
touched,
errors,
setValue,
} = useFormContext< Product >();
const [ showProductLinkEditModal, setShowProductLinkEditModal ] =
useState( false );
const [ descriptionBlocks, setDescriptionBlocks ] = useState<
@ -66,7 +72,7 @@ export const ProductDetailsSection: React.FC = () => {
};
const setSkuIfEmpty = () => {
if ( values.sku || ! values.name.length ) {
if ( values.sku || ! values.name?.length ) {
return;
}
setValue( 'sku', cleanForSlug( values.name ) );
@ -90,13 +96,9 @@ export const ProductDetailsSection: React.FC = () => {
'e.g. 12 oz Coffee Mug',
'woocommerce'
) }
{ ...getTextControlProps(
getInputProps( 'name' )
) }
onBlur={ () => {
setSkuIfEmpty();
getInputProps( 'name' ).onBlur();
} }
{ ...getInputProps( 'name', {
onBlur: setSkuIfEmpty,
} ) }
/>
{ values.id && ! hasNameError() && permalinkPrefix && (
<span className="woocommerce-product-form__secondary-text product-details-section__product-link">
@ -170,12 +172,10 @@ export const ProductDetailsSection: React.FC = () => {
/>
</>
}
{ ...getCheckboxProps( {
...getInputProps( 'featured' ),
name: 'featured',
className:
'product-details-section__feature-checkbox',
} ) }
{ ...getCheckboxControlProps(
'featured',
getCheckboxTracks( 'featured' )
) }
/>
{ showProductLinkEditModal && (
<EditProductLinkModal

View File

@ -13,7 +13,6 @@ import { recordEvent } from '@woocommerce/tracks';
* Internal dependencies
*/
import { getAdminSetting } from '~/utils/admin-settings';
import { getTextControlProps } from '../utils';
export const ManageStockSection: React.FC = () => {
const { getInputProps } = useFormContext< Product >();
@ -25,9 +24,7 @@ export const ManageStockSection: React.FC = () => {
<TextControl
type="number"
label={ __( 'Current quantity', 'woocommerce' ) }
{ ...getTextControlProps( {
...getInputProps( 'stock_quantity' ),
} ) }
{ ...getInputProps( 'stock_quantity' ) }
min={ 0 }
/>
<TextControl
@ -38,9 +35,7 @@ export const ManageStockSection: React.FC = () => {
__( '%d (store default)', 'woocommerce' ),
notifyLowStockAmount
) }
{ ...getTextControlProps( {
...getInputProps( 'low_stock_amount' ),
} ) }
{ ...getInputProps( 'low_stock_amount' ) }
min={ 0 }
/>
<span className="woocommerce-product-form__secondary-text">

View File

@ -16,14 +16,15 @@ import { recordEvent } from '@woocommerce/tracks';
/**
* Internal dependencies
*/
import { getCheckboxProps, getTextControlProps } from '../utils';
import { getCheckboxTracks } from '../utils';
import { getAdminSetting } from '~/utils/admin-settings';
import { ProductSectionLayout } from '../../layout/product-section-layout';
import { ManageStockSection } from './manage-stock-section';
import { ManualStockSection } from './manual-stock-section';
export const ProductInventorySection: React.FC = () => {
const { getInputProps, values } = useFormContext< Product >();
const { getCheckboxControlProps, getInputProps, values } =
useFormContext< Product >();
const canManageStock = getAdminSetting( 'manageStock', 'yes' ) === 'yes';
return (
@ -67,7 +68,7 @@ export const ProductInventorySection: React.FC = () => {
'washed-oxford-button-down-shirt',
'woocommerce'
) }
{ ...getTextControlProps( getInputProps( 'sku' ) ) }
{ ...getInputProps( 'sku' ) }
/>
{ canManageStock && (
<>
@ -76,8 +77,9 @@ export const ProductInventorySection: React.FC = () => {
'Track quantity for this product',
'woocommerce'
) }
{ ...getCheckboxProps(
getInputProps( 'manage_stock' )
{ ...getCheckboxControlProps(
'manage_stock',
getCheckboxTracks( 'manage_stock' )
) }
/>
{ values.manage_stock && <ManageStockSection /> }

View File

@ -32,7 +32,6 @@ import {
} from '../fields/shipping-dimensions-image';
import { useProductHelper } from '../use-product-helper';
import { AddNewShippingClassModal } from '../shared/add-new-shipping-class-modal';
import { getTextControlProps } from './utils';
import './product-shipping-section.scss';
import {
ADD_NEW_SHIPPING_CLASS_OPTION_VALUE,
@ -74,7 +73,7 @@ function getInterpolatedSizeLabel( mixedString: string ) {
* the first category different to `Uncategorized`.
*
* @see https://github.com/woocommerce/woocommerce/issues/34657
* @param product The product
* @param product The product
* @return The default shipping class
*/
function extractDefaultShippingClassFromProduct(
@ -94,7 +93,8 @@ function extractDefaultShippingClassFromProduct(
export function ProductShippingSection( {
product,
}: ProductShippingSectionProps ) {
const { getInputProps, setValue } = useFormContext< PartialProduct >();
const { getInputProps, getSelectControlProps, setValue } =
useFormContext< PartialProduct >();
const { formatNumber, parseNumber } = useProductHelper();
const [ highlightSide, setHighlightSide ] =
useState< ShippingDimensionsImageProps[ 'highlight' ] >();
@ -140,19 +140,29 @@ export function ProductShippingSection( {
EXPERIMENTAL_PRODUCT_SHIPPING_CLASSES_STORE_NAME
);
const selectShippingClassProps = getTextControlProps(
getInputProps( 'shipping_class' )
const dimensionProps = {
onBlur: () => {
setHighlightSide( undefined );
},
sanitize: ( value: PartialProduct[ keyof PartialProduct ] ) =>
parseNumber( String( value ) ),
suffix: dimensionUnit,
};
const inputWidthProps = getInputProps( 'dimensions.width', dimensionProps );
const inputLengthProps = getInputProps(
'dimensions.length',
dimensionProps
);
const inputWidthProps = getTextControlProps(
getInputProps( 'dimensions.width' )
const inputHeightProps = getInputProps(
'dimensions.height',
dimensionProps
);
const inputLengthProps = getTextControlProps(
getInputProps( 'dimensions.length' )
);
const inputHeightProps = getTextControlProps(
getInputProps( 'dimensions.height' )
);
const inputWeightProps = getTextControlProps( getInputProps( 'weight' ) );
const inputWeightProps = getInputProps( 'weight', {
sanitize: ( value: PartialProduct[ keyof PartialProduct ] ) =>
parseNumber( String( value ) ),
} );
const shippingClassProps = getInputProps( 'shipping_class' );
return (
<ProductSectionLayout
@ -167,14 +177,8 @@ export function ProductShippingSection( {
{ hasResolvedShippingClasses ? (
<>
<SelectControl
{ ...selectShippingClassProps }
label={ __( 'Shipping class', 'woocommerce' ) }
options={ [
...DEFAULT_SHIPPING_CLASS_OPTIONS,
...mapShippingClassToSelectOption(
shippingClasses ?? []
),
] }
{ ...getSelectControlProps( 'shipping_class' ) }
onChange={ ( value: string ) => {
if (
value ===
@ -183,8 +187,14 @@ export function ProductShippingSection( {
setShowShippingClassModal( true );
return;
}
selectShippingClassProps?.onChange( value );
shippingClassProps.onChange( value );
} }
options={ [
...DEFAULT_SHIPPING_CLASS_OPTIONS,
...mapShippingClassToSelectOption(
shippingClasses ?? []
),
] }
/>
<span className="woocommerce-product-form__secondary-text">
{ interpolateComponents( {
@ -235,7 +245,7 @@ export function ProductShippingSection( {
<InputControl
{ ...inputWidthProps }
value={ formatNumber(
inputWidthProps.value
String( inputWidthProps.value )
) }
label={ getInterpolatedSizeLabel(
__(
@ -243,19 +253,9 @@ export function ProductShippingSection( {
'woocommerce'
)
) }
onChange={ ( value: string ) =>
inputWidthProps?.onChange(
parseNumber( value )
)
}
onFocus={ () => {
setHighlightSide( 'A' );
} }
onBlur={ () => {
setHighlightSide( undefined );
inputWidthProps?.onBlur();
} }
suffix={ dimensionUnit }
/>
</BaseControl>
@ -267,7 +267,7 @@ export function ProductShippingSection( {
<InputControl
{ ...inputLengthProps }
value={ formatNumber(
inputLengthProps.value
String( inputLengthProps.value )
) }
label={ getInterpolatedSizeLabel(
__(
@ -275,19 +275,9 @@ export function ProductShippingSection( {
'woocommerce'
)
) }
onChange={ ( value: string ) =>
inputLengthProps?.onChange(
parseNumber( value )
)
}
onFocus={ () => {
setHighlightSide( 'B' );
} }
onBlur={ () => {
setHighlightSide( undefined );
inputLengthProps?.onBlur();
} }
suffix={ dimensionUnit }
/>
</BaseControl>
@ -299,7 +289,7 @@ export function ProductShippingSection( {
<InputControl
{ ...inputHeightProps }
value={ formatNumber(
inputHeightProps.value
String( inputHeightProps.value )
) }
label={ getInterpolatedSizeLabel(
__(
@ -307,19 +297,9 @@ export function ProductShippingSection( {
'woocommerce'
)
) }
onChange={ ( value: string ) =>
inputHeightProps?.onChange(
parseNumber( value )
)
}
onFocus={ () => {
setHighlightSide( 'C' );
} }
onBlur={ () => {
setHighlightSide( undefined );
inputHeightProps?.onBlur();
} }
suffix={ dimensionUnit }
/>
</BaseControl>
@ -331,17 +311,12 @@ export function ProductShippingSection( {
<InputControl
{ ...inputWeightProps }
value={ formatNumber(
inputWeightProps.value
String( inputWeightProps.value )
) }
label={ __(
'Weight',
'woocommerce'
) }
onChange={ ( value: string ) =>
inputWeightProps?.onChange(
parseNumber( value )
)
}
suffix={ weightUnit }
/>
</BaseControl>
@ -368,10 +343,10 @@ export function ProductShippingSection( {
product &&
extractDefaultShippingClassFromProduct( product )
}
onAdd={ ( values ) =>
onAdd={ ( shippingClassValues ) =>
createProductShippingClass<
Promise< ProductShippingClass >
>( values ).then( ( value ) => {
>( shippingClassValues ).then( ( value ) => {
invalidateResolution( 'getProductShippingClasses' );
setValue( 'shipping_class', value.slug );
return value;

View File

@ -1,7 +1,8 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import { ChangeEvent } from 'react';
import { Product } from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks';
/**
@ -9,85 +10,67 @@ import { recordEvent } from '@woocommerce/tracks';
*/
import { NUMBERS_AND_ALLOWED_CHARS } from '../constants';
type gettersProps = {
context?: {
formatAmount: ( number: number | string ) => string;
getCurrencyConfig: () => {
code: string;
symbol: string;
symbolPosition: string;
decimalSeparator: string;
priceFormat: string;
thousandSeparator: string;
precision: number;
};
};
value: string;
name?: string;
checked: boolean;
selected?: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onChange: ( value: any ) => void;
onBlur: () => void;
className: string | undefined;
help: string | null | undefined;
type CurrencyConfig = {
code: string;
symbol: string;
symbolPosition: string;
decimalSeparator: string;
priceFormat: string;
thousandSeparator: string;
precision: number;
};
export const getCheckboxProps = ( {
checked = false,
className,
name,
onBlur,
onChange,
}: gettersProps ) => {
/**
* Get additional props to be passed to all checkbox inputs.
*
* @param {string} name Name of the checkbox
* @return {Object} Props.
*/
export const getCheckboxTracks = ( name: string ) => {
return {
checked,
className: classnames( 'woocommerce-product__checkbox', className ),
onChange: ( isChecked: boolean ) => {
onChange: (
isChecked:
| ChangeEvent< HTMLInputElement >
| Product[ keyof Product ]
) => {
recordEvent( `product_checkbox_${ name }`, {
checked: isChecked,
} );
return onChange( isChecked );
},
onBlur,
};
};
export const getTextControlProps = ( {
className,
onBlur,
onChange,
value = '',
help,
}: gettersProps ) => {
return {
value,
className: classnames( 'woocommerce-product__text', className ),
onChange,
onBlur,
help,
};
};
export const getInputControlProps = ( {
className,
context,
onBlur,
onChange,
value = '',
help,
}: gettersProps ) => {
if ( ! context ) {
return;
}
const { formatAmount, getCurrencyConfig } = context;
const { decimalSeparator, symbol, symbolPosition, thousandSeparator } =
getCurrencyConfig();
/**
* Get input props for currency related values and symbol positions.
*
* @param {Object} currencyConfig - Currency context
* @return {Object} Props.
*/
export const getCurrencySymbolProps = ( currencyConfig: CurrencyConfig ) => {
const { symbol, symbolPosition } = currencyConfig;
const currencyPosition = symbolPosition.includes( 'left' )
? 'prefix'
: 'suffix';
// Cleans the value to show.
return {
[ currencyPosition ]: symbol,
};
};
/**
* Cleans and formats the currency value shown to the user.
*
* @param {string} value Form value.
* @param {Object} currencyConfig Currency context.
* @return {string} Display value.
*/
export const formatCurrencyDisplayValue = (
value: string,
currencyConfig: CurrencyConfig,
format: ( number: number | string ) => string
) => {
const { decimalSeparator, thousandSeparator } = currencyConfig;
const regex = new RegExp(
NUMBERS_AND_ALLOWED_CHARS.replace( '%s1', decimalSeparator ).replace(
'%s2',
@ -95,16 +78,6 @@ export const getInputControlProps = ( {
),
'g'
);
const currencyString =
value === undefined
? value
: formatAmount( value ).replace( regex, '' );
return {
value: currencyString,
[ currencyPosition ]: symbol,
className: classnames( 'woocommerce-product__input', className ),
onChange,
onBlur,
help,
};
return value === undefined ? value : format( value ).replace( regex, '' );
};

View File

@ -11,7 +11,6 @@ import { ProductShippingClass } from '@woocommerce/data';
/**
* Internal dependencies
*/
import { getTextControlProps } from '../../sections/utils';
import './add-new-shipping-class-modal.scss';
export type ShippingClassFormProps = {
@ -20,16 +19,10 @@ export type ShippingClassFormProps = {
};
function ShippingClassForm( { onAdd, onCancel }: ShippingClassFormProps ) {
const { getInputProps, isValidForm } =
const { errors, getInputProps, isValidForm } =
useFormContext< ProductShippingClass >();
const [ isLoading, setIsLoading ] = useState( false );
const inputNameProps = getTextControlProps( getInputProps( 'name' ) );
const inputSlugProps = getTextControlProps( getInputProps( 'slug' ) );
const inputDescriptionProps = getTextControlProps(
getInputProps( 'description' )
);
function handleAdd() {
setIsLoading( true );
onAdd()
@ -45,11 +38,11 @@ function ShippingClassForm( { onAdd, onCancel }: ShippingClassFormProps ) {
return (
<div className="woocommerce-add-new-shipping-class-modal__wrapper">
<TextControl
{ ...inputNameProps }
{ ...getInputProps( 'name' ) }
label={ __( 'Name', 'woocommerce' ) }
/>
<TextControl
{ ...inputSlugProps }
{ ...getInputProps( 'slug' ) }
label={ interpolateComponents( {
mixedString: __(
'Slug {{span}}(optional){{/span}}',
@ -63,7 +56,7 @@ function ShippingClassForm( { onAdd, onCancel }: ShippingClassFormProps ) {
} ) }
/>
<TextControl
{ ...inputDescriptionProps }
{ ...getInputProps( 'description' ) }
label={ interpolateComponents( {
mixedString: __(
'Description {{span}}(optional){{/span}}',
@ -76,7 +69,7 @@ function ShippingClassForm( { onAdd, onCancel }: ShippingClassFormProps ) {
},
} ) }
help={
inputDescriptionProps?.help ??
errors?.description ??
__(
'Describe how you and other store administrators can use this shipping class.',
'woocommerce'

View File

@ -0,0 +1,4 @@
Significance: minor
Type: tweak
Unwrap product page input props and pass via getInputProps