[Experimental] Handle adding attributes during fields registration (#43379)
Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
parent
f7e8b676d6
commit
23db3eff69
|
@ -113,6 +113,20 @@ const Form = < T extends AddressFormValues | ContactFormValues >( {
|
|||
if ( field.hidden ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fieldProps = {
|
||||
id: `${ id }-${ field.key }`,
|
||||
errorId: `${ addressType }_${ field.key }`,
|
||||
label: field.required ? field.label : field.optionalLabel,
|
||||
// These may come from core locale settings or the attributes option on custom fields.
|
||||
autoCapitalize: field.autocapitalize,
|
||||
autoComplete: field.autocomplete,
|
||||
errorMessage: field.errorMessage,
|
||||
required: field.required,
|
||||
className: `wc-block-components-address-form__${ field.key }`,
|
||||
...field.attributes,
|
||||
};
|
||||
|
||||
if ( field.type === 'checkbox' ) {
|
||||
return (
|
||||
<CheckboxControl
|
||||
|
@ -126,19 +140,10 @@ const Form = < T extends AddressFormValues | ContactFormValues >( {
|
|||
[ field.key ]: checked,
|
||||
} );
|
||||
} }
|
||||
{ ...fieldProps }
|
||||
/>
|
||||
);
|
||||
}
|
||||
const fieldProps = {
|
||||
id: `${ id }-${ field.key }`,
|
||||
errorId: `${ addressType }_${ field.key }`,
|
||||
label: field.required ? field.label : field.optionalLabel,
|
||||
autoCapitalize: field.autocapitalize,
|
||||
autoComplete: field.autocomplete,
|
||||
errorMessage: field.errorMessage,
|
||||
required: field.required,
|
||||
className: `wc-block-components-address-form__${ field.key }`,
|
||||
};
|
||||
|
||||
if (
|
||||
field.key === 'country' &&
|
||||
|
|
|
@ -2,12 +2,25 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { ComboboxControlOption } from '@woocommerce/base-components/combobox';
|
||||
import type { AllHTMLAttributes, AriaAttributes } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { getSetting } from './utils';
|
||||
|
||||
// A list of attributes that can be added to a custom field when registering it.
|
||||
type CustomFieldAttributes = Pick<
|
||||
AllHTMLAttributes< HTMLInputElement >,
|
||||
| 'maxLength'
|
||||
| 'readOnly'
|
||||
| 'pattern'
|
||||
| 'title'
|
||||
| 'autoCapitalize'
|
||||
| 'autoComplete'
|
||||
> &
|
||||
AriaAttributes;
|
||||
|
||||
export interface FormField {
|
||||
// The label for the field.
|
||||
label: string;
|
||||
|
@ -27,6 +40,8 @@ export interface FormField {
|
|||
type?: string;
|
||||
// The options if this is a select field
|
||||
options?: ComboboxControlOption[];
|
||||
// Additional attributes added when registering a field. String in key is required for data attributes.
|
||||
attributes?: Record< keyof CustomFieldAttributes, string >;
|
||||
}
|
||||
|
||||
export interface LocaleSpecificFormField extends Partial< FormField > {
|
||||
|
|
|
@ -17,7 +17,7 @@ export type CheckboxControlProps = {
|
|||
children?: React.ReactChildren;
|
||||
hasError?: boolean;
|
||||
checked?: boolean;
|
||||
disabled?: boolean;
|
||||
disabled?: string | boolean | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -33,7 +33,7 @@ export const CheckboxControl = ( {
|
|||
checked = false,
|
||||
disabled = false,
|
||||
...rest
|
||||
}: CheckboxControlProps ): JSX.Element => {
|
||||
}: CheckboxControlProps & Record< string, unknown > ): JSX.Element => {
|
||||
const instanceId = useInstanceId( CheckboxControl );
|
||||
const checkboxId = id || `checkbox-control-${ instanceId }`;
|
||||
|
||||
|
@ -55,7 +55,7 @@ export const CheckboxControl = ( {
|
|||
onChange={ ( event ) => onChange( event.target.checked ) }
|
||||
aria-invalid={ hasError === true }
|
||||
checked={ checked }
|
||||
disabled={ disabled }
|
||||
disabled={ !! disabled }
|
||||
{ ...rest }
|
||||
/>
|
||||
<svg
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: add
|
||||
Comment: Allow adding attributes when registering custom checkout fields in the Checkout block.
|
||||
|
|
@ -304,19 +304,18 @@ class CheckoutFields {
|
|||
}
|
||||
|
||||
$field_data = array(
|
||||
'label' => $options['label'],
|
||||
'hidden' => false,
|
||||
'type' => $type,
|
||||
'optionalLabel' => empty( $options['optionalLabel'] ) ? sprintf(
|
||||
/* translators: %s Field label. */
|
||||
'label' => $options['label'],
|
||||
'hidden' => false,
|
||||
'type' => $type,
|
||||
'optionalLabel' => empty( $options['optionalLabel'] ) ? sprintf(
|
||||
/* translators: %s Field label. */
|
||||
__( '%s (optional)', 'woocommerce' ),
|
||||
$options['label']
|
||||
) : $options['optionalLabel'],
|
||||
'required' => empty( $options['required'] ) ? false : $options['required'],
|
||||
'autocomplete' => empty( $options['autocomplete'] ) ? '' : $options['autocomplete'],
|
||||
'autocapitalize' => empty( $options['autocapitalize'] ) ? '' : $options['autocapitalize'],
|
||||
'required' => empty( $options['required'] ) ? false : $options['required'],
|
||||
);
|
||||
|
||||
$field_data['attributes'] = $this->register_field_attributes( $id, $options['attributes'] ?? [] );
|
||||
/**
|
||||
* Handle Checkbox fields.
|
||||
*/
|
||||
|
@ -383,6 +382,64 @@ class CheckoutFields {
|
|||
$this->fields_locations[ $location ][] = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the attributes supplied during field registration.
|
||||
*
|
||||
* @param array $id The field ID.
|
||||
* @param array $attributes The attributes supplied during field registration.
|
||||
*
|
||||
* @return array The processed attributes.
|
||||
*/
|
||||
private function register_field_attributes( $id, $attributes ) {
|
||||
|
||||
// We check if attributes are valid. This is done to prevent too much nesting and also to allow field registration
|
||||
// even if the attributes property is invalid. We can just skip it and register the field without attributes.
|
||||
$has_attributes = false;
|
||||
|
||||
if ( empty( $attributes ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ( ! is_array( $attributes ) || 0 === count( $attributes ) ) {
|
||||
$message = sprintf( 'An invalid attributes value was supplied when registering field with id: "%s". %s', $id, 'Attributes must be a non-empty array.' );
|
||||
_doing_it_wrong( 'woocommerce_blocks_register_checkout_field', esc_html( $message ), '8.6.0' );
|
||||
return [];
|
||||
}
|
||||
|
||||
// These are formatted in camelCase because React components expect them that way.
|
||||
$allowed_attributes = array(
|
||||
'maxLength',
|
||||
'readOnly',
|
||||
'pattern',
|
||||
'autocomplete',
|
||||
'autocapitalize',
|
||||
'title',
|
||||
);
|
||||
|
||||
$valid_attributes = array_filter(
|
||||
$attributes,
|
||||
function( $_, $key ) use ( $allowed_attributes ) {
|
||||
return in_array( $key, $allowed_attributes, true ) || strpos( $key, 'aria-' ) === 0 || strpos( $key, 'data-' ) === 0;
|
||||
},
|
||||
ARRAY_FILTER_USE_BOTH
|
||||
);
|
||||
|
||||
// Any invalid attributes should show a doing_it_wrong warning. It shouldn't stop field registration, though.
|
||||
if ( count( $attributes ) !== count( $valid_attributes ) ) {
|
||||
$invalid_attributes = array_keys( array_diff_key( $attributes, $valid_attributes ) );
|
||||
$message = sprintf( 'Invalid attribute found when registering field with id: "%s". Attributes: %s are not allowed.', $id, implode( ', ', $invalid_attributes ) );
|
||||
_doing_it_wrong( 'woocommerce_blocks_register_checkout_field', esc_html( $message ), '8.6.0' );
|
||||
}
|
||||
|
||||
// Escape attributes to remove any malicious code and return them.
|
||||
return array_map(
|
||||
function( $value ) {
|
||||
return esc_attr( $value );
|
||||
},
|
||||
$valid_attributes
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all core fields.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue