Skip to content

Commit dcef658

Browse files
authored
Forms: Fix renderable status of image select field (#45542)
* fix field renderable status * changelog * fix test
1 parent 6e8bf39 commit dcef658

File tree

3 files changed

+161
-125
lines changed

3 files changed

+161
-125
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: patch
2+
Type: fixed
3+
4+
Forms: Fix renderable status of image select field

projects/packages/forms/src/contact-form/class-contact-form-field.php

Lines changed: 111 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -2020,138 +2020,136 @@ public function render_image_select_field( $id, $label, $value, $class, $require
20202020

20212021
$used_html_ids = array();
20222022

2023-
if ( ! empty( $options_data ) ) {
2024-
// Create a separate array of original letters in sequence (A, B, C...)
2025-
$perceived_letters = array();
2023+
// Create a separate array of original letters in sequence (A, B, C...)
2024+
$perceived_letters = array();
20262025

2027-
foreach ( $options_data as $option ) {
2028-
$perceived_letters[] = Contact_Form_Plugin::strip_tags( $option['letter'] );
2029-
}
2026+
foreach ( $options_data as $option ) {
2027+
$perceived_letters[] = Contact_Form_Plugin::strip_tags( $option['letter'] );
2028+
}
20302029

2031-
// Create a working copy of options for potential randomization
2032-
$working_options = $options_data;
2030+
// Create a working copy of options for potential randomization
2031+
$working_options = $options_data;
20332032

2034-
// Randomize options if requested, but preserve original letter values
2035-
if ( $randomize_options ) {
2036-
shuffle( $working_options );
2033+
// Randomize options if requested, but preserve original letter values
2034+
if ( $randomize_options ) {
2035+
shuffle( $working_options );
20372036

2038-
// Trims options after randomization to ensure the last option has a label or image.
2039-
$working_options = $this->trim_image_select_options( $working_options );
2040-
}
2037+
// Trims options after randomization to ensure the last option has a label or image.
2038+
$working_options = $this->trim_image_select_options( $working_options );
2039+
}
20412040

2042-
// Calculate row options count for CSS variable
2043-
$total_options_count = count( $options_data );
2044-
// Those values are halved on mobile via CSS media query
2045-
$max_images_per_row = $is_supersized ? 2 : 4;
2046-
$row_options_count = min( $total_options_count, $max_images_per_row );
2041+
// Calculate row options count for CSS variable
2042+
$total_options_count = count( $working_options );
2043+
// Those values are halved on mobile via CSS media query
2044+
$max_images_per_row = $is_supersized ? 2 : 4;
2045+
$row_options_count = min( $total_options_count, $max_images_per_row );
20472046

2048-
foreach ( $working_options as $option_index => $option ) {
2049-
$option_label = Contact_Form_Plugin::strip_tags( $option['label'] );
2050-
$option_letter = Contact_Form_Plugin::strip_tags( $option['letter'] );
2051-
$image_block = $option['image'];
2047+
foreach ( $working_options as $option_index => $option ) {
2048+
$option_label = Contact_Form_Plugin::strip_tags( $option['label'] );
2049+
$option_letter = Contact_Form_Plugin::strip_tags( $option['letter'] );
2050+
$image_block = $option['image'];
20522051

2053-
// Extract image src from rendered block
2054-
$rendered_image_block = render_block( $image_block );
2055-
$image_src = '';
2052+
// Extract image src from rendered block
2053+
$rendered_image_block = render_block( $image_block );
2054+
$image_src = '';
20562055

2057-
if ( ! empty( $rendered_image_block ) ) {
2058-
if ( preg_match( '/<img[^>]+src=["\']([^"\']+)["\'][^>]*>/i', $rendered_image_block, $matches ) ) {
2059-
$extracted_src = $matches[1];
2056+
if ( ! empty( $rendered_image_block ) ) {
2057+
if ( preg_match( '/<img[^>]+src=["\']([^"\']+)["\'][^>]*>/i', $rendered_image_block, $matches ) ) {
2058+
$extracted_src = $matches[1];
20602059

2061-
if ( filter_var( $extracted_src, FILTER_VALIDATE_URL ) || str_starts_with( $extracted_src, 'data:' ) ) {
2062-
$image_src = $extracted_src;
2063-
}
2060+
if ( filter_var( $extracted_src, FILTER_VALIDATE_URL ) || str_starts_with( $extracted_src, 'data:' ) ) {
2061+
$image_src = $extracted_src;
20642062
}
2065-
} else {
2066-
$rendered_image_block = '<figure class="wp-block-image jetpack-input-image-option__empty-image"><img src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" alt="" style="aspect-ratio:1;object-fit:cover"/></figure>';
20672063
}
2064+
} else {
2065+
$rendered_image_block = '<figure class="wp-block-image jetpack-input-image-option__empty-image"><img src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" alt="" style="aspect-ratio:1;object-fit:cover"/></figure>';
2066+
}
20682067

2069-
$option_value = wp_json_encode(
2070-
array(
2071-
'perceived' => $perceived_letters[ $option_index ],
2072-
'selected' => $option_letter,
2073-
'label' => $option_label,
2074-
'showLabels' => $show_labels,
2075-
'image' => array(
2076-
'id' => $image_block['attrs']['id'] ?? null,
2077-
'src' => $image_src ?? null,
2078-
),
2079-
)
2080-
);
2081-
$option_id = $id . '-' . $option_letter;
2082-
$used_html_ids[ $option_id ] = true;
2068+
$option_value = wp_json_encode(
2069+
array(
2070+
'perceived' => $perceived_letters[ $option_index ],
2071+
'selected' => $option_letter,
2072+
'label' => $option_label,
2073+
'showLabels' => $show_labels,
2074+
'image' => array(
2075+
'id' => $image_block['attrs']['id'] ?? null,
2076+
'src' => $image_src ?? null,
2077+
),
2078+
)
2079+
);
2080+
$option_id = $id . '-' . $option_letter;
2081+
$used_html_ids[ $option_id ] = true;
20832082

2084-
// To be able to apply the backdrop-filter for the hover effect, we need to separate the background into an outer div.
2085-
// This outer div needs the color styles separately, and also the border radius to match the inner div without sticking out.
2086-
$option_outer_classes = 'jetpack-input-image-option__outer ' . ( isset( $option['classcolor'] ) ? $option['classcolor'] : '' );
2083+
// To be able to apply the backdrop-filter for the hover effect, we need to separate the background into an outer div.
2084+
// This outer div needs the color styles separately, and also the border radius to match the inner div without sticking out.
2085+
$option_outer_classes = 'jetpack-input-image-option__outer ' . ( isset( $option['classcolor'] ) ? $option['classcolor'] : '' );
20872086

2088-
if ( $is_supersized ) {
2089-
$option_outer_classes .= ' is-supersized';
2090-
}
2087+
if ( $is_supersized ) {
2088+
$option_outer_classes .= ' is-supersized';
2089+
}
20912090

2092-
$border_styles = '';
2093-
if ( ! empty( $option['style'] ) ) {
2094-
preg_match( '/border-radius:([^;]+)/', $option['style'], $radius_match );
2095-
preg_match( '/border-width:([^;]+)/', $option['style'], $width_match );
2091+
$border_styles = '';
2092+
if ( ! empty( $option['style'] ) ) {
2093+
preg_match( '/border-radius:([^;]+)/', $option['style'], $radius_match );
2094+
preg_match( '/border-width:([^;]+)/', $option['style'], $width_match );
20962095

2097-
if ( ! empty( $radius_match[1] ) ) {
2098-
$radius_value = trim( $radius_match[1] );
2096+
if ( ! empty( $radius_match[1] ) ) {
2097+
$radius_value = trim( $radius_match[1] );
20992098

2100-
if ( ! empty( $width_match[1] ) ) {
2101-
$width_value = trim( $width_match[1] );
2102-
$border_styles = "border-radius:calc({$radius_value} + {$width_value});";
2103-
} else {
2104-
$border_styles = "border-radius:{$radius_value};";
2105-
}
2099+
if ( ! empty( $width_match[1] ) ) {
2100+
$width_value = trim( $width_match[1] );
2101+
$border_styles = "border-radius:calc({$radius_value} + {$width_value});";
2102+
} else {
2103+
$border_styles = "border-radius:{$radius_value};";
21062104
}
21072105
}
2106+
}
21082107

2109-
$option_outer_styles = ( empty( $option['stylecolor'] ) ? '' : $option['stylecolor'] ) . $border_styles;
2110-
$option_outer_styles .= "--row-options-count: {$row_options_count};";
2111-
$option_outer_styles = empty( $option_outer_styles ) ? '' : "style='" . esc_attr( $option_outer_styles ) . "'";
2108+
$option_outer_styles = ( empty( $option['stylecolor'] ) ? '' : $option['stylecolor'] ) . $border_styles;
2109+
$option_outer_styles .= "--row-options-count: {$row_options_count};";
2110+
$option_outer_styles = empty( $option_outer_styles ) ? '' : "style='" . esc_attr( $option_outer_styles ) . "'";
21122111

2113-
$field .= "<div class='{$option_outer_classes}' {$option_outer_styles}>";
2112+
$field .= "<div class='{$option_outer_classes}' {$option_outer_styles}>";
21142113

2115-
$default_classes = 'jetpack-field jetpack-input-image-option';
2116-
$option_styles = empty( $option['style'] ) ? '' : "style='" . esc_attr( $option['style'] ) . "'";
2117-
$option_classes = "class='" . ( empty( $option['class'] ) ? $default_classes : $default_classes . ' ' . $option['class'] ) . "'";
2114+
$default_classes = 'jetpack-field jetpack-input-image-option';
2115+
$option_styles = empty( $option['style'] ) ? '' : "style='" . esc_attr( $option['style'] ) . "'";
2116+
$option_classes = "class='" . ( empty( $option['class'] ) ? $default_classes : $default_classes . ' ' . $option['class'] ) . "'";
21182117

2119-
$field .= "<div {$option_classes} {$option_styles} data-wp-on--click='actions.onImageOptionClick'>";
2118+
$field .= "<div {$option_classes} {$option_styles} data-wp-on--click='actions.onImageOptionClick'>";
21202119

2121-
$input_id = esc_attr( $option_id );
2120+
$input_id = esc_attr( $option_id );
21222121

2123-
$context = array(
2124-
'inputId' => $input_id,
2125-
);
2126-
$interactivity_attrs = ' data-wp-interactive="jetpack/form" ' . wp_interactivity_data_wp_context( $context ) . ' ';
2127-
2128-
$field .= "<div class='jetpack-input-image-option__wrapper'>";
2129-
$field .= "<input
2130-
id='" . $input_id . "'
2131-
class='jetpack-input-image-option__input'
2132-
type='" . esc_attr( $input_type ) . "'
2133-
name='" . esc_attr( $input_name ) . "'
2134-
value='" . esc_attr( $option_value ) . "'
2135-
" . $interactivity_attrs . "
2136-
data-wp-init='callbacks.setImageOptionCheckColor'
2137-
data-wp-on--keydown='actions.onKeyDownImageOption'
2138-
data-wp-on--change='" . ( $is_multiple ? 'actions.onMultipleFieldChange' : 'actions.onFieldChange' ) . "' "
2139-
. $class
2140-
. ( $is_multiple ? checked( in_array( $option_value, (array) $value, true ), true, false ) : checked( $option_value, $value, false ) ) . ' '
2141-
. ( $required ? "required aria-required='true'" : '' )
2142-
. '/> ';
2143-
2144-
$field .= $rendered_image_block;
2145-
$field .= '</div>';
2146-
2147-
$field .= "<div class='jetpack-input-image-option__label-wrapper'>";
2148-
$field .= "<div class='jetpack-input-image-option__label-code'>" . esc_html( $perceived_letters[ $option_index ] ) . '</div>';
2149-
2150-
$label_classes = 'jetpack-input-image-option__label';
2151-
$label_classes .= $show_labels ? '' : ' visually-hidden';
2152-
$field .= "<span class='{$label_classes}'>" . esc_html( $option_label ) . '</span>';
2153-
$field .= '</div></div></div>';
2154-
}
2122+
$context = array(
2123+
'inputId' => $input_id,
2124+
);
2125+
$interactivity_attrs = ' data-wp-interactive="jetpack/form" ' . wp_interactivity_data_wp_context( $context ) . ' ';
2126+
2127+
$field .= "<div class='jetpack-input-image-option__wrapper'>";
2128+
$field .= "<input
2129+
id='" . $input_id . "'
2130+
class='jetpack-input-image-option__input'
2131+
type='" . esc_attr( $input_type ) . "'
2132+
name='" . esc_attr( $input_name ) . "'
2133+
value='" . esc_attr( $option_value ) . "'
2134+
" . $interactivity_attrs . "
2135+
data-wp-init='callbacks.setImageOptionCheckColor'
2136+
data-wp-on--keydown='actions.onKeyDownImageOption'
2137+
data-wp-on--change='" . ( $is_multiple ? 'actions.onMultipleFieldChange' : 'actions.onFieldChange' ) . "' "
2138+
. $class
2139+
. ( $is_multiple ? checked( in_array( $option_value, (array) $value, true ), true, false ) : checked( $option_value, $value, false ) ) . ' '
2140+
. ( $required ? "required aria-required='true'" : '' )
2141+
. '/> ';
2142+
2143+
$field .= $rendered_image_block;
2144+
$field .= '</div>';
2145+
2146+
$field .= "<div class='jetpack-input-image-option__label-wrapper'>";
2147+
$field .= "<div class='jetpack-input-image-option__label-code'>" . esc_html( $perceived_letters[ $option_index ] ) . '</div>';
2148+
2149+
$label_classes = 'jetpack-input-image-option__label';
2150+
$label_classes .= $show_labels ? '' : ' visually-hidden';
2151+
$field .= "<span class='{$label_classes}'>" . esc_html( $option_label ) . '</span>';
2152+
$field .= '</div></div></div>';
21552153
}
21562154

21572155
$field .= '</div></div>';
@@ -2637,7 +2635,7 @@ private function maybe_override_type() {
26372635
public function is_field_renderable( $type ) {
26382636
// Check that radio, select, multiple choice, and image select
26392637
// fields have at least one valid option.
2640-
if ( $type === 'radio' || $type === 'checkbox-multiple' || $type === 'select' || $type === 'image-select' ) {
2638+
if ( $type === 'radio' || $type === 'checkbox-multiple' || $type === 'select' ) {
26412639
$options = (array) $this->get_attribute( 'options' );
26422640
$non_empty_options = array_filter(
26432641
$options,
@@ -2648,16 +2646,11 @@ function ( $option ) {
26482646
return count( $non_empty_options ) > 0;
26492647
}
26502648

2651-
// File field requires Jetpack to be active
2652-
if ( $type === 'file' ) {
2653-
/**
2654-
* Check if Jetpack is active for file uploads.
2655-
*
2656-
* @since 5.3.0
2657-
*
2658-
* @return bool
2659-
*/
2660-
return apply_filters( 'jetpack_forms_is_file_field_renderable', defined( 'JETPACK__PLUGIN_DIR' ) );
2649+
if ( $type === 'image-select' ) {
2650+
$options_data = (array) $this->get_attribute( 'optionsdata' );
2651+
$trimmed_options_data = $this->trim_image_select_options( $options_data );
2652+
2653+
return ! empty( $trimmed_options_data );
26612654
}
26622655

26632656
return true;

projects/packages/forms/tests/php/contact-form/Feedback_Test.php

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,19 +1189,58 @@ public function test_get_all_values_with_image_select() {
11891189
$_post_data = Utility::get_post_request(
11901190
array(
11911191
'images' => array(
1192-
'{"perceived":"B","selected":"B","label":"Test choice","showLabels":true,"image":{"id":null,"src":"https://www.example.com/test-choice.png"}}',
1193-
'{"perceived":"C","selected":"C","label":"Another test choice","showLabels":true,"image":{"id":12346,"src":"https://www.example.com/another-test-choice.png"}}',
1192+
'{"perceived":"B","selected":"B","label":"Choice B","showLabels":true,"image":{"id":null,"src":"https://www.example.com/choice-b.png"}}',
1193+
'{"perceived":"C","selected":"C","label":"Choice C","showLabels":true,"image":{"id":12346,"src":"https://www.example.com/choice-c.png"}}',
11941194
),
11951195
),
11961196
'g' . $form_id
11971197
);
11981198

1199+
// Helper function to create image block data for optionsdata
1200+
$create_image_block = function ( $url, $alt ) {
1201+
return array(
1202+
'blockName' => 'core/image',
1203+
'attrs' => array(
1204+
'url' => $url,
1205+
'alt' => $alt,
1206+
'scale' => 'cover',
1207+
'aspectRatio' => '1',
1208+
),
1209+
'innerHTML' => "<img src=\"{$url}\" alt=\"{$alt}\" />",
1210+
'innerContent' => array( "<img src=\"{$url}\" alt=\"{$alt}\" />" ),
1211+
);
1212+
};
1213+
1214+
$optionsdata = Contact_Form::esc_shortcode_val(
1215+
wp_json_encode(
1216+
array(
1217+
array(
1218+
'letter' => 'A',
1219+
'label' => 'Choice A',
1220+
'image' => $create_image_block( 'https://www.example.com/choice-a.png', 'Choice A' ),
1221+
),
1222+
array(
1223+
'letter' => 'B',
1224+
'label' => 'Choice B',
1225+
'image' => $create_image_block( 'https://www.example.com/choice-b.png', 'Choice B' ),
1226+
),
1227+
array(
1228+
'letter' => 'C',
1229+
'label' => 'Choice C',
1230+
'image' => $create_image_block( 'https://www.example.com/choice-c.png', 'Choice C' ),
1231+
),
1232+
)
1233+
)
1234+
);
1235+
1236+
$shortcode = "[contact-field type=\"image-select\" label=\"Images\" isMultiple=\"1\" options=\"A,B,C\" showLabels=\"1\" optionsdata=\"{$optionsdata}\" /]";
1237+
11991238
$form = new Contact_Form(
12001239
array(
12011240
'title' => 'Test Form',
12021241
'description' => 'This is a test form.',
12031242
),
1204-
"[contact-field type='image-select' label='Images' isMultiple='1' options='A,B,C' showLabels='1' /]"
1243+
$shortcode
12051244
);
12061245

12071246
$expected_images = array(
@@ -1210,21 +1249,21 @@ public function test_get_all_values_with_image_select() {
12101249
array(
12111250
'perceived' => 'B',
12121251
'selected' => 'B',
1213-
'label' => 'Test choice',
1252+
'label' => 'Choice B',
12141253
'showLabels' => true,
12151254
'image' => array(
12161255
'id' => null,
1217-
'src' => 'https://www.example.com/test-choice.png',
1256+
'src' => 'https://www.example.com/choice-b.png',
12181257
),
12191258
),
12201259
array(
12211260
'perceived' => 'C',
12221261
'selected' => 'C',
1223-
'label' => 'Another test choice',
1262+
'label' => 'Choice C',
12241263
'showLabels' => true,
12251264
'image' => array(
12261265
'id' => 12346,
1227-
'src' => 'https://www.example.com/another-test-choice.png',
1266+
'src' => 'https://www.example.com/choice-c.png',
12281267
),
12291268
),
12301269
),

0 commit comments

Comments
 (0)