diff --git a/includes/admin/views/acf-field-group/location-rule.php b/includes/admin/views/acf-field-group/location-rule.php
index a0caae22..44413909 100644
--- a/includes/admin/views/acf-field-group/location-rule.php
+++ b/includes/admin/views/acf-field-group/location-rule.php
@@ -2,7 +2,6 @@
// vars
$prefix = 'acf_field_group[location][' . $rule['group'] . '][' . $rule['id'] . ']';
-
?>
diff --git a/includes/admin/views/acf-field-group/locations.php b/includes/admin/views/acf-field-group/locations.php
index fe00fb95..25df9992 100644
--- a/includes/admin/views/acf-field-group/locations.php
+++ b/includes/admin/views/acf-field-group/locations.php
@@ -2,7 +2,6 @@
// global
global $field_group;
-
?>
diff --git a/includes/admin/views/acf-field-group/options.php b/includes/admin/views/acf-field-group/options.php
index 77c61360..a816c7ab 100644
--- a/includes/admin/views/acf-field-group/options.php
+++ b/includes/admin/views/acf-field-group/options.php
@@ -72,6 +72,8 @@
case 'location_rules':
echo ' ';
acf_get_view( 'acf-field-group/locations' );
+
+ do_action( 'acf/field_group/render_additional_location_settings', $field_group );
echo ' ';
break;
case 'presentation':
@@ -164,6 +166,8 @@
'field'
);
+ do_action( 'acf/field_group/render_additional_presentation_settings', $field_group );
+
echo ' ';
echo ' ';
@@ -221,8 +225,6 @@
'prefix' => 'acf_field_group',
'value' => $field_group['active'],
'ui' => 1,
- // 'ui_on_text' => __('Active', 'secure-custom-fields'),
- // 'ui_off_text' => __('Inactive', 'secure-custom-fields'),
)
);
@@ -237,8 +239,6 @@
'prefix' => 'acf_field_group',
'value' => $field_group['show_in_rest'],
'ui' => 1,
- // 'ui_on_text' => __('Active', 'secure-custom-fields'),
- // 'ui_off_text' => __('Inactive', 'secure-custom-fields'),
)
);
}
@@ -257,6 +257,21 @@
'field'
);
+ acf_render_field_wrap(
+ array(
+ 'label' => __( 'Display Title', 'secure-custom-fields' ),
+ 'instructions' => __( 'Title shown on the edit screen for the field group meta box to use instead of the field group title', 'secure-custom-fields' ),
+ 'type' => 'text',
+ 'name' => 'display_title',
+ 'prefix' => 'acf_field_group',
+ 'value' => $field_group['display_title'],
+ ),
+ 'div',
+ 'field'
+ );
+
+ do_action( 'acf/field_group/render_additional_group_settings', $field_group );
+
/* translators: 1: Post creation date 2: Post creation time */
$acf_created_on = sprintf( __( 'Created on %1$s at %2$s', 'secure-custom-fields' ), get_the_date(), get_the_time() );
?>
diff --git a/includes/admin/views/acf-post-type/list-empty.php b/includes/admin/views/acf-post-type/list-empty.php
index f12219fd..0bfc2cca 100644
--- a/includes/admin/views/acf-post-type/list-empty.php
+++ b/includes/admin/views/acf-post-type/list-empty.php
@@ -5,7 +5,8 @@
* @package wordpress/secure-custom-fields
*/
-?>
+?>
+
 ); ?>)
diff --git a/includes/admin/views/upgrade/network.php b/includes/admin/views/upgrade/network.php
index 49ab0457..643d4b1f 100644
--- a/includes/admin/views/upgrade/network.php
+++ b/includes/admin/views/upgrade/network.php
@@ -60,7 +60,7 @@
?>
class="alternate">
|
@@ -73,8 +73,16 @@ class="alternate">
|
-
-
+
+
+
diff --git a/includes/admin/views/upgrade/notice.php b/includes/admin/views/upgrade/notice.php
index bb14df96..603d8f2e 100644
--- a/includes/admin/views/upgrade/notice.php
+++ b/includes/admin/views/upgrade/notice.php
@@ -19,7 +19,7 @@
}
?>
-
+
 ); ?>)
@@ -33,7 +33,7 @@
diff --git a/includes/ajax/class-acf-ajax-check-screen.php b/includes/ajax/class-acf-ajax-check-screen.php
index 6ee8cba8..0fe6ef7d 100644
--- a/includes/ajax/class-acf-ajax-check-screen.php
+++ b/includes/ajax/class-acf-ajax-check-screen.php
@@ -53,7 +53,7 @@ public function get_response( $request ) {
$item = array(
'id' => esc_attr( 'acf-' . $field_group['key'] ),
'key' => esc_attr( $field_group['key'] ),
- 'title' => esc_html( $field_group['title'] ),
+ 'title' => acf_esc_html( acf_get_field_group_title( $field_group ) ),
'position' => esc_attr( $field_group['position'] ),
'classes' => postbox_classes( 'acf-' . $field_group['key'], $args['screen'] ),
'style' => esc_attr( $field_group['style'] ),
diff --git a/includes/blocks.php b/includes/blocks.php
index c8765dd0..baea5c0d 100644
--- a/includes/blocks.php
+++ b/includes/blocks.php
@@ -51,6 +51,17 @@ function acf_handle_json_block_registration( $settings, $metadata ) {
return $settings;
}
+ /**
+ * Filters the default ACF block version for blocks registered via block.json.
+ *
+ * @since 6.6.0
+ *
+ * @param integer $default_acf_block_version The default ACF block version.
+ * @param array $settings An array of block settings.
+ * @return integer
+ */
+ $default_acf_block_version = apply_filters( 'acf/blocks/default_block_version', 2, $settings );
+
// Setup SCF defaults.
$settings = wp_parse_args(
$settings,
@@ -64,8 +75,7 @@ function acf_handle_json_block_registration( $settings, $metadata ) {
'uses_context' => array(),
'supports' => array(),
'attributes' => array(),
- 'acf_block_version' => 2,
- 'api_version' => 2,
+ 'acf_block_version' => $default_acf_block_version,
'validate' => true,
'validate_on_load' => true,
'use_post_meta' => false,
@@ -105,14 +115,15 @@ function acf_handle_json_block_registration( $settings, $metadata ) {
// Map custom SCF properties from the SCF key, with localization.
$property_mappings = array(
- 'renderCallback' => 'render_callback',
- 'renderTemplate' => 'render_template',
- 'mode' => 'mode',
- 'blockVersion' => 'acf_block_version',
- 'postTypes' => 'post_types',
- 'validate' => 'validate',
- 'validateOnLoad' => 'validate_on_load',
- 'usePostMeta' => 'use_post_meta',
+ 'renderCallback' => 'render_callback',
+ 'renderTemplate' => 'render_template',
+ 'mode' => 'mode',
+ 'blockVersion' => 'acf_block_version',
+ 'postTypes' => 'post_types',
+ 'validate' => 'validate',
+ 'validateOnLoad' => 'validate_on_load',
+ 'usePostMeta' => 'use_post_meta',
+ 'hideFieldsInSidebar' => 'hide_fields_in_sidebar',
);
$textdomain = ! empty( $metadata['textdomain'] ) ? $metadata['textdomain'] : 'secure-custom-fields';
$i18n_schema = get_block_metadata_i18n_schema();
@@ -127,6 +138,17 @@ function acf_handle_json_block_registration( $settings, $metadata ) {
}
}
+ if ( isset( $metadata['apiVersion'] ) ) {
+ // Use the apiVersion defined in block.json if it exists.
+ $settings['api_version'] = $metadata['apiVersion'];
+ } elseif ( $settings['acf_block_version'] >= 3 && version_compare( get_bloginfo( 'version' ), '6.3', '>=' ) ) {
+ // Otherwise, if we're on WP 6.3+ and the block is ACF block version 3 or greater, use apiVersion 3.
+ $settings['api_version'] = 3;
+ } else {
+ // Otherwise, default to apiVersion 2.
+ $settings['api_version'] = 2;
+ }
+
// Add the block name and registration path to settings.
$settings['name'] = $metadata['name'];
$settings['path'] = dirname( $metadata['file'] );
@@ -198,11 +220,28 @@ function acf_register_block_type( $block ) {
// Set ACF required attributes.
$block['attributes'] = acf_get_block_type_default_attributes( $block );
- if ( ! isset( $block['api_version'] ) ) {
- $block['api_version'] = 2;
- }
+
+ /**
+ * Filters the default ACF block version for blocks registered via acf_register_block_type().
+ *
+ * @since 6.6.0
+ *
+ * @param integer $default_acf_block_version The default ACF block version.
+ * @param array $block An array of block settings.
+ * @return integer
+ */
+ $default_acf_block_version = apply_filters( 'acf/blocks/default_block_version', 1, $block );
+
if ( ! isset( $block['acf_block_version'] ) ) {
- $block['acf_block_version'] = 1;
+ $block['acf_block_version'] = $default_acf_block_version;
+ }
+
+ if ( ! isset( $block['api_version'] ) ) {
+ if ( $block['acf_block_version'] >= 3 && version_compare( get_bloginfo( 'version' ), '6.3', '>=' ) ) {
+ $block['api_version'] = 3;
+ } else {
+ $block['api_version'] = 2;
+ }
}
// Add to storage.
@@ -559,8 +598,16 @@ function acf_render_block_callback( $attributes, $content = '', $wp_block = null
* @return string The block HTML.
*/
function acf_rendered_block( $attributes, $content = '', $is_preview = false, $post_id = 0, $wp_block = null, $context = false, $is_ajax_render = false ) {
- $mode = isset( $attributes['mode'] ) ? $attributes['mode'] : 'auto';
- $form = ( 'edit' === $mode && $is_preview );
+ $registry = WP_Block_Type_Registry::get_instance();
+ $wp_block_type = $registry->get_registered( $attributes['name'] );
+
+ if ( isset( $wp_block_type->acf_block_version ) && $wp_block_type->acf_block_version >= 3 ) {
+ $mode = 'preview';
+ $form = false;
+ } else {
+ $mode = isset( $attributes['mode'] ) ? $attributes['mode'] : 'auto';
+ $form = ( 'edit' === $mode && $is_preview );
+ }
// If context is available from the WP_Block class object and we have no context of our own, use that.
if ( empty( $context ) && ! empty( $wp_block->context ) ) {
@@ -754,12 +801,16 @@ function acf_block_render_template( $block, $content, $is_preview, $post_id, $wp
$path = locate_template( $block['render_template'] );
}
+ do_action( 'acf/blocks/pre_block_template_render', $block, $content, $is_preview, $post_id, $wp_block, $context );
+
// Include template.
if ( file_exists( $path ) ) {
include $path;
} elseif ( $is_preview ) {
echo acf_esc_html( apply_filters( 'acf/blocks/template_not_found_message', ' ' . __( 'The render template for this ACF Block was not found', 'secure-custom-fields' ) . ' ' ) );
}
+
+ do_action( 'acf/blocks/post_block_template_render', $block, $content, $is_preview, $post_id, $wp_block, $context );
}
/**
@@ -808,14 +859,17 @@ function acf_enqueue_block_assets() {
// Localize text.
acf_localize_text(
array(
- 'Switch to Edit' => __( 'Switch to Edit', 'secure-custom-fields' ),
- 'Switch to Preview' => __( 'Switch to Preview', 'secure-custom-fields' ),
- 'Change content alignment' => __( 'Change content alignment', 'secure-custom-fields' ),
- 'Error previewing block' => __( 'An error occurred when loading the preview for this block.', 'secure-custom-fields' ),
- 'Error loading block form' => __( 'An error occurred when loading the block in edit mode.', 'secure-custom-fields' ),
-
+ 'Switch to Edit' => __( 'Switch to Edit', 'secure-custom-fields' ),
+ 'Switch to Preview' => __( 'Switch to Preview', 'secure-custom-fields' ),
+ 'Change content alignment' => __( 'Change content alignment', 'secure-custom-fields' ),
+ 'Error previewing block' => __( 'An error occurred when loading the preview for this block.', 'secure-custom-fields' ),
+ 'Error loading block form' => __( 'An error occurred when loading the block in edit mode.', 'secure-custom-fields' ),
+ 'Edit Block' => __( 'Edit Block', 'secure-custom-fields' ),
+ 'Open Expanded Editor' => __( 'Open Expanded Editor', 'secure-custom-fields' ),
+ 'Error previewing block v3' => __( 'The preview for this block couldn’t be loaded. Review its content or settings for issues.', 'secure-custom-fields' ),
+ 'ACF Block' => __( 'ACF Block', 'secure-custom-fields' ),
/* translators: %s: Block type title */
- '%s settings' => __( '%s settings', 'secure-custom-fields' ),
+ '%s settings' => __( '%s settings', 'secure-custom-fields' ),
)
);
diff --git a/includes/class-acf-internal-post-type.php b/includes/class-acf-internal-post-type.php
index 8808511d..67925f8c 100644
--- a/includes/class-acf-internal-post-type.php
+++ b/includes/class-acf-internal-post-type.php
@@ -166,7 +166,7 @@ public function get_raw_post( $id = 0 ) {
* @since ACF 6.1
*
* @param integer|string $id The post ID, key, or name.
- * @return WP_Post|bool The post object, or false on failure.
+ * @return WP_Post|boolean The post object, or false on failure.
*/
public function get_post_object( $id = 0 ) {
if ( is_numeric( $id ) ) {
diff --git a/includes/fields/class-acf-field-button-group.php b/includes/fields/class-acf-field-button-group.php
index d1000551..36e480fc 100644
--- a/includes/fields/class-acf-field-button-group.php
+++ b/includes/fields/class-acf-field-button-group.php
@@ -67,10 +67,11 @@ public function render_field( $field ) {
// append
$buttons[] = array(
- 'name' => $field['name'],
- 'value' => $_value,
- 'label' => $_label,
- 'checked' => $checked,
+ 'name' => $field['name'],
+ 'value' => $_value,
+ 'label' => $_label,
+ 'checked' => $checked,
+ 'button_group' => true,
);
}
@@ -79,16 +80,29 @@ public function render_field( $field ) {
$buttons[0]['checked'] = true;
}
+ // Ensure roving tabindex when allow_null is enabled and no selection yet.
+ if ( $field['allow_null'] && null === $selected && ! empty( $buttons ) ) {
+ $buttons[0]['tabindex'] = '0';
+ }
+
// div
- $div = array( 'class' => 'acf-button-group' );
+ $div = array(
+ 'class' => 'acf-button-group',
+ 'role' => 'radiogroup',
+ );
+
+ // Add aria-labelledby if field has an ID for proper screen reader announcement
+ if ( ! empty( $field['id'] ) ) {
+ $div['aria-labelledby'] = $field['id'] . '-label';
+ }
- if ( 'vertical' === acf_maybe_get( $field, 'layout' ) ) {
+ if ( 'vertical' === $field['layout'] ) {
$div['class'] .= ' -vertical';
}
- if ( acf_maybe_get( $field, 'class' ) ) {
- $div['class'] .= ' ' . acf_maybe_get( $field, 'class' );
+ if ( $field['class'] ) {
+ $div['class'] .= ' ' . $field['class'];
}
- if ( acf_maybe_get( $field, 'allow_null' ) ) {
+ if ( $field['allow_null'] ) {
$div['data-allow_null'] = 1;
}
diff --git a/includes/fields/class-acf-field-checkbox.php b/includes/fields/class-acf-field-checkbox.php
index 582672be..5935a041 100644
--- a/includes/fields/class-acf-field-checkbox.php
+++ b/includes/fields/class-acf-field-checkbox.php
@@ -80,8 +80,14 @@ function render_field( $field ) {
$li = '';
$ul = array(
'class' => 'acf-checkbox-list',
+ 'role' => 'group',
);
+ // Add aria-labelledby if field has an ID for proper screen reader announcement
+ if ( ! empty( $field['id'] ) ) {
+ $ul['aria-labelledby'] = $field['id'] . '-label';
+ }
+
// append to class
$ul['class'] .= ' ' . ( 'horizontal' === acf_maybe_get( $field, 'layout' ) ? 'acf-hl' : 'acf-bl' );
$ul['class'] .= ' ' . acf_maybe_get( $field, 'class', '' );
diff --git a/includes/fields/class-acf-field-color_picker.php b/includes/fields/class-acf-field-color_picker.php
index 98fd1e0f..fd3d6ed9 100644
--- a/includes/fields/class-acf-field-color_picker.php
+++ b/includes/fields/class-acf-field-color_picker.php
@@ -26,9 +26,12 @@ function initialize() {
$this->doc_url = 'https://developer.wordpress.org/secure-custom-fields/features/fields/color-picker/';
$this->tutorial_url = 'https://developer.wordpress.org/secure-custom-fields/features/fields/color-picker/color-picker-tutorial/';
$this->defaults = array(
- 'default_value' => '',
- 'enable_opacity' => false,
- 'return_format' => 'string', // 'string'|'array'
+ 'default_value' => '',
+ 'enable_opacity' => false,
+ 'custom_palette_source' => '',
+ 'palette_colors' => '',
+ 'show_color_wheel' => true,
+ 'return_format' => 'string', // Possible values: 'string' or 'array'.
);
}
@@ -106,7 +109,7 @@ function input_admin_enqueue_scripts() {
* @since ACF 3.6
* @date 23/01/13
*/
- function render_field( $field ) {
+ public function render_field( $field ) {
$text_input = acf_get_sub_array( $field, array( 'id', 'class', 'name', 'value' ) );
$hidden_input = acf_get_sub_array( $field, array( 'name', 'value' ) );
$text_input['data-alpha-skip-debounce'] = true;
@@ -116,9 +119,55 @@ function render_field( $field ) {
$text_input['data-alpha-enabled'] = true;
}
+ // Handle color palette when the theme supports theme.json.
+ if ( wp_theme_has_theme_json() ) {
+ // If the field was set to use themejson.
+ if ( 'themejson' === $field['custom_palette_source'] ) {
+ $text_input['data-acf-palette-type'] = 'custom';
+
+ // Get the palette (theme + custom).
+ $global_settings = wp_get_global_settings();
+ $palette = $global_settings['color']['palette']['theme'] ?? array();
+
+ // Extract only the color values.
+ $color_values = array_map(
+ fn( $c ) => $c['color'] ?? null,
+ $palette
+ );
+
+ // Remove nulls (in case any entries are missing 'color')
+ $color_values = array_filter( $color_values );
+
+ $hex_string = implode( ',', $color_values );
+
+ $text_input['data-acf-palette-colors'] = $hex_string;
+ } elseif ( 'custom' === $field['custom_palette_source'] && ! empty( $field['palette_colors'] ) ) {
+ // If the field was set to use a custom palette.
+ $text_input['data-acf-palette-type'] = 'custom';
+ $text_input['data-acf-palette-colors'] = $field['palette_colors'];
+ } elseif ( '' === $field['custom_palette_source'] && ! empty( $field['palette_colors'] ) ) {
+ // This state can happen if they switched from a classic theme to a themejson theme without resaving the field.
+ $text_input['data-acf-palette-type'] = 'custom';
+ $text_input['data-acf-palette-colors'] = $field['palette_colors'];
+ } else {
+ // Fallback to use the default color palette for the iris color picker.
+ $text_input['data-acf-palette-type'] = 'default';
+ }
+ // phpcs:disable Universal.ControlStructures.DisallowLonelyIf.Found
+ } else {
+ // Handle color palette for themes that do not support themejson.
+ if ( ! empty( $field['palette_colors'] ) ) {
+ $text_input['data-acf-palette-type'] = 'custom';
+ $text_input['data-acf-palette-colors'] = $field['palette_colors'];
+ } else {
+ // Fallback to use the default color palette for the iris color picker.
+ $text_input['data-acf-palette-type'] = 'default';
+ }
+ }
+
// html
?>
-
+
@@ -179,6 +228,86 @@ function render_field_settings( $field ) {
);
}
+
+ /**
+ * Renders the field settings used in the "Presentation" tab.
+ *
+ * @since 6.0
+ *
+ * @param array $field The field settings array.
+ * @return void
+ */
+ public function render_field_presentation_settings( $field ) {
+ acf_render_field_setting(
+ $field,
+ array(
+ 'label' => __( 'Show Custom Palette', 'secure-custom-fields' ),
+ 'instructions' => '',
+ 'type' => 'true_false',
+ 'name' => 'show_custom_palette',
+ 'ui' => 1,
+ )
+ );
+
+ $custom_palette_conditions = array(
+ 'field' => 'show_custom_palette',
+ 'operator' => '==',
+ 'value' => 1,
+ );
+
+ if ( wp_theme_has_theme_json() ) {
+ acf_render_field_setting(
+ $field,
+ array(
+ 'label' => __( 'Custom Palette Source', 'secure-custom-fields' ),
+ 'instructions' => '',
+ 'type' => 'radio',
+ 'name' => 'custom_palette_source',
+ 'layout' => 'vertical',
+ 'choices' => array(
+ 'custom' => __( 'Specify custom colors', 'secure-custom-fields' ),
+ 'themejson' => __( 'Use colors from theme.json', 'secure-custom-fields' ),
+ ),
+ 'conditions' => array(
+ 'field' => 'show_custom_palette',
+ 'operator' => '==',
+ 'value' => 1,
+ ),
+ )
+ );
+
+ $custom_palette_conditions = array(
+ 'field' => 'custom_palette_source',
+ 'operator' => '==',
+ 'value' => 'custom',
+ );
+ }
+
+ acf_render_field_setting(
+ $field,
+ array(
+ 'label' => __( 'Custom Palette', 'secure-custom-fields' ),
+ 'instructions' => __( 'Use a custom color palette by entering comma separated hex or rgba values', 'secure-custom-fields' ),
+ 'type' => 'text',
+ 'name' => 'palette_colors',
+ 'conditions' => $custom_palette_conditions,
+ )
+ );
+
+ acf_render_field_setting(
+ $field,
+ array(
+ 'label' => __( 'Show Color Wheel', 'secure-custom-fields' ),
+ 'instructions' => '',
+ 'type' => 'true_false',
+ 'name' => 'show_color_wheel',
+ 'default_value' => 1,
+ 'ui' => 1,
+ )
+ );
+ }
+
+
/**
* Format the value for use in templates. At this stage, the value has been loaded from the
* database and is being returned by an API function such as get_field(), the_field(), etc.
diff --git a/includes/fields/class-acf-field-file.php b/includes/fields/class-acf-field-file.php
index 37fe0dad..59c40187 100644
--- a/includes/fields/class-acf-field-file.php
+++ b/includes/fields/class-acf-field-file.php
@@ -130,7 +130,7 @@ function render_field( $field ) {
)
);
?>
-
+
@@ -148,14 +148,14 @@ function render_field( $field ) {
-
+
diff --git a/includes/fields/class-acf-field-icon_picker.php b/includes/fields/class-acf-field-icon_picker.php
index d7746ad2..31b4bb17 100644
--- a/includes/fields/class-acf-field-icon_picker.php
+++ b/includes/fields/class-acf-field-icon_picker.php
@@ -334,11 +334,16 @@ public function input_admin_enqueue_scripts() {
* @return boolean true If the value is valid, false if not.
*/
public function validate_value( $valid, $value, $field, $input ) {
- // If the value is empty, return true. You're allowed to save nothing.
+ // If the value is empty and it's not required, return true. You're allowed to save nothing.
if ( empty( $value ) && empty( $field['required'] ) ) {
return true;
}
+ // Validate required.
+ if ( $field['required'] && ( empty( $value ) || empty( $value['value'] ) ) ) {
+ return false;
+ }
+
// If the value is not an array, return $valid status.
if ( ! is_array( $value ) ) {
return $valid;
diff --git a/includes/fields/class-acf-field-image.php b/includes/fields/class-acf-field-image.php
index d437965c..560fe737 100644
--- a/includes/fields/class-acf-field-image.php
+++ b/includes/fields/class-acf-field-image.php
@@ -127,17 +127,17 @@ function render_field( $field ) {
)
);
?>
-
+
![]() />
-
+
diff --git a/includes/fields/class-acf-field-radio.php b/includes/fields/class-acf-field-radio.php
index f0100200..28d33385 100644
--- a/includes/fields/class-acf-field-radio.php
+++ b/includes/fields/class-acf-field-radio.php
@@ -57,10 +57,16 @@ function render_field( $field ) {
'class' => 'acf-radio-list',
'data-allow_null' => $field['allow_null'],
'data-other_choice' => $field['other_choice'],
+ 'role' => 'radiogroup',
);
+ // Add aria-labelledby if field has an ID for proper screen reader announcement
+ if ( ! empty( $field['id'] ) ) {
+ $ul['aria-labelledby'] = $field['id'] . '-label';
+ }
+
// append to class
- $ul['class'] .= ' ' . ( $field['layout'] == 'horizontal' ? 'acf-hl' : 'acf-bl' );
+ $ul['class'] .= ' ' . ( 'horizontal' === $field['layout'] ? 'acf-hl' : 'acf-bl' );
$ul['class'] .= ' ' . $field['class'];
// Determine selected value.
diff --git a/includes/fields/class-acf-field-repeater.php b/includes/fields/class-acf-field-repeater.php
index 31b1743b..443dcfff 100644
--- a/includes/fields/class-acf-field-repeater.php
+++ b/includes/fields/class-acf-field-repeater.php
@@ -1016,24 +1016,55 @@ public function get_field_name_from_input_name( $input_name ) {
$name_parts = array();
foreach ( $field_keys as $field_key ) {
- if ( ! acf_is_field_key( $field_key ) ) {
- if ( 'acfcloneindex' === $field_key ) {
- $name_parts[] = 'acfcloneindex';
- continue;
- }
+ // Preserve acfcloneindex
+ if ( 'acfcloneindex' === $field_key ) {
+ $name_parts[] = 'acfcloneindex';
+ continue;
+ }
- $row_num = str_replace( 'row-', '', $field_key );
+ // Handle row numbers (row-0, row-1, etc.)
+ if ( strpos( $field_key, 'row-' ) === 0 ) {
+ $row_num = substr( $field_key, 4 );
if ( is_numeric( $row_num ) ) {
$name_parts[] = (int) $row_num;
continue;
}
}
- $field = acf_get_field( $field_key );
+ // Handle compound keys (field_..._field_...)
+ $compound_keys = preg_split( '/_field_/', $field_key );
+ if ( count( $compound_keys ) > 1 ) {
+ foreach ( $compound_keys as $i => $sub_key ) {
+ if ( $i > 0 ) {
+ $sub_key = 'field_' . $sub_key;
+ }
+
+ // Seamless clone fields use compound keys which can be skipped.
+ $field = acf_get_field( $sub_key );
+ if ( $field && 'clone' === $field['type'] && 'seamless' === $field['display'] ) {
+ continue;
+ }
+
+ $name_parts[] = $field && ! empty( $field['name'] ) ? $field['name'] : $sub_key;
+ }
+ continue;
+ }
+
+ // Handle standard field keys
+ if ( strpos( $field_key, 'field_' ) === 0 ) {
+
+ // Skip clone fields with prefix_name disabled.
+ $field = acf_get_field( $field_key );
+ if ( $field && 'clone' === $field['type'] && empty( $field['prefix_name'] ) ) {
+ continue;
+ }
- if ( $field ) {
- $name_parts[] = $field['name'];
+ $name_parts[] = $field && ! empty( $field['name'] ) ? $field['name'] : $field_key;
+ continue;
}
+
+ // Fallback: just add as is
+ $name_parts[] = $field_key;
}
return implode( '_', $name_parts );
@@ -1093,17 +1124,19 @@ public function ajax_get_rows() {
* We have to swap out the field name with the one sent via JS,
* as the repeater could be inside a subfield.
*/
- $field['name'] = $args['field_name'];
+ $field['name'] = $args['field_name'];
+ $field['prefix'] = $args['field_prefix'];
+ $field['value'] = acf_get_value( $post_id, $field );
- $field['value'] = acf_get_value( $post_id, $field );
+ if ( $args['refresh'] ) {
+ $response['total_rows'] = (int) acf_get_metadata_by_field( $post_id, $field );
+ }
+
+ // Render the rows to be sent back via AJAX.
$field = acf_prepare_field( $field );
$repeater_table = new ACF_Repeater_Table( $field );
$response['rows'] = $repeater_table->rows( true );
- if ( $args['refresh'] ) {
- $response['total_rows'] = (int) acf_get_metadata( $post_id, $args['field_name'] );
- }
-
wp_send_json_success( $response );
}
}
diff --git a/includes/fields/class-acf-field-taxonomy.php b/includes/fields/class-acf-field-taxonomy.php
index 84eeb94c..5f990f9d 100644
--- a/includes/fields/class-acf-field-taxonomy.php
+++ b/includes/fields/class-acf-field-taxonomy.php
@@ -576,7 +576,7 @@ public function render_field_checkbox( $field ) {
);
// checkbox saves an array.
- if ( $field['field_type'] == 'checkbox' ) {
+ if ( 'checkbox' === $field['field_type'] ) {
$field['name'] .= '[]';
}
@@ -601,9 +601,18 @@ public function render_field_checkbox( $field ) {
$args = apply_filters( 'acf/fields/taxonomy/wp_list_categories/name=' . $field['_name'], $args, $field );
$args = apply_filters( 'acf/fields/taxonomy/wp_list_categories/key=' . $field['key'], $args, $field );
+ // Build UL attributes for accessibility and consistency.
+ $ul = array(
+ 'class' => 'acf-checkbox-list acf-bl',
+ 'role' => 'radio' === $field['field_type'] ? 'radiogroup' : 'group',
+ );
+
+ if ( ! empty( $field['id'] ) ) {
+ $ul['aria-labelledby'] = $field['id'] . '-label';
+ }
?>
diff --git a/includes/forms/WC_Order.php b/includes/forms/WC_Order.php
index 9eb2a6c9..77161428 100644
--- a/includes/forms/WC_Order.php
+++ b/includes/forms/WC_Order.php
@@ -73,7 +73,6 @@ public function add_meta_boxes( $post_type, $post ) {
if ( $field_groups ) {
foreach ( $field_groups as $field_group ) {
$id = "acf-{$field_group['key']}"; // acf-group_123
- $title = $field_group['title']; // Group 1
$context = $field_group['position']; // normal, side, acf_after_title
$priority = 'core'; // high, core, default, low
@@ -104,7 +103,7 @@ public function add_meta_boxes( $post_type, $post ) {
// Add the meta box.
add_meta_box(
$id,
- esc_html( $title ),
+ acf_esc_html( acf_get_field_group_title( $field_group ) ),
array( $this, 'render_meta_box' ),
$screen,
$context,
diff --git a/includes/forms/form-comment.php b/includes/forms/form-comment.php
index 923c72d9..63dd3321 100644
--- a/includes/forms/form-comment.php
+++ b/includes/forms/form-comment.php
@@ -148,7 +148,7 @@ function edit_comment( $comment ) {
?>
-
+
';
+ return global.acf.parseJSX( html );
+ };
+
+ const mockSetModalOpen = jest.fn();
+
+ render(
+ (
+
+ ) }
+ >
+
+
+ );
+
+ expect( screen.getByTestId( 'block-placeholder' ) ).toBeInTheDocument();
+ expect( global.acf.debug ).toHaveBeenCalled();
+ } );
+
+ test( 'renders successfully with valid HTML', () => {
+ global.acf.parseJSX.mockImplementation( ( html ) => {
+ return ;
+ } );
+
+ const ValidHTMLComponent = () => {
+ const html = ' This is valid HTML ';
+ return global.acf.parseJSX( html );
+ };
+
+ render(
+ (
+
+ ) }
+ >
+
+
+ );
+
+ // Should not show error placeholder
+ expect(
+ screen.queryByTestId( 'block-placeholder' )
+ ).not.toBeInTheDocument();
+ } );
+} );
diff --git a/tests/js/setup-tests.js b/tests/js/setup-tests.js
new file mode 100644
index 00000000..81f07e0e
--- /dev/null
+++ b/tests/js/setup-tests.js
@@ -0,0 +1,18 @@
+/**
+ * Jest test setup file
+ * Runs before all tests to set up the testing environment
+ */
+
+// Add React to global scope
+import React from 'react';
+global.React = React;
+
+// Mock jQuery for parseJSX tests
+global.jQuery = jest.fn( ( html ) => {
+ if ( typeof html === 'string' ) {
+ // Simple mock that returns an array-like object
+ return [ { innerHTML: html, tagName: 'DIV' } ];
+ }
+ return [];
+} );
+global.$ = global.jQuery;
| |