Skip to content

Commit 4ac0d80

Browse files
authored
Forms: Trim empty options and display custom placeholder for image select field (#45528)
* trims empty image options * display empty images and override image placeholder with same icon * changelog
1 parent d49e908 commit 4ac0d80

File tree

10 files changed

+176
-19
lines changed

10 files changed

+176
-19
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: changed
3+
4+
Forms: Trim empty options and display custom placeholder

projects/packages/forms/src/blocks/field-image-select/editor.scss

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@use "style" as *;
2+
@use "../shared/styles/empty_image_option" as *;
23

34
// Editor-only styles.
45

@@ -15,6 +16,16 @@
1516

1617
.jetpack-contact-form .jetpack-input-image-option .components-placeholder.has-illustration {
1718
overflow: hidden;
19+
position: relative;
20+
21+
&::after {
22+
23+
@extend %empty-image-option-icon;
24+
}
25+
26+
.components-placeholder__illustration {
27+
display: none;
28+
}
1829
}
1930

2031
// There is no outer block in the editor, so we apply

projects/packages/forms/src/blocks/field-image-select/style.scss

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@use "@automattic/jetpack-base-styles/gutenberg-base-styles" as gb;
22
@use "../shared/styles/visually-hidden" as *;
3+
@use "../shared/styles/empty_image_option" as *;
34

45
@mixin image-select-responsive-width {
56
width: calc(( 1 / var(--row-options-count) ) * 100% - var(--jetpack-field-image-options-gap) * ( (var(--row-options-count) - 1) / var(--row-options-count) ));
@@ -161,3 +162,17 @@ div:where(.jetpack-input-image-option) {
161162
padding: 8px;
162163
margin: 0;
163164
}
165+
166+
.jetpack-input-image-option__empty-image {
167+
position: relative;
168+
169+
&::before {
170+
171+
@extend %empty-image-option-background;
172+
}
173+
174+
&::after {
175+
176+
@extend %empty-image-option-icon;
177+
}
178+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
%empty-image-option-icon {
2+
content: "";
3+
$mask-url: "data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 19 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M16.5 0H2.5C1.4 0 0.5 0.9 0.5 2V16C0.5 17.1 1.4 18 2.5 18H16.5C17.6 18 18.5 17.1 18.5 16V2C18.5 0.9 17.6 0 16.5 0ZM2.5 1.5H16.5C16.8 1.5 17 1.7 17 2V10.4L14 7.5C13.7 7.2 13.2 7.2 13 7.5L9.4 11L6.5 9C6.2 8.8 5.9 8.8 5.7 9L2.1 11.6V2C2 1.7 2.2 1.5 2.5 1.5ZM16.5 16.5H2.5C2.2 16.5 2 16.3 2 16V13.6L6.1 10.6L9.1 12.5C9.4 12.7 9.8 12.7 10 12.4L13.5 9L17 12.4V16C17 16.3 16.8 16.5 16.5 16.5Z' fill='%233858E9'/%3E%3C/svg%3E";
4+
mask-image: url(#{$mask-url});
5+
mask-size: contain;
6+
mask-repeat: no-repeat;
7+
position: absolute;
8+
top: calc(50% - 10px);
9+
left: calc(50% - 10px);
10+
width: 20px;
11+
height: 20px;
12+
background-color: var(--jetpack--contact-form--primary-color);
13+
}
14+
15+
%empty-image-option-background {
16+
content: "";
17+
position: absolute;
18+
top: 0;
19+
bottom: 0;
20+
left: 0;
21+
right: 0;
22+
opacity: 0.1;
23+
background: currentColor;
24+
}

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

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2013,7 +2013,11 @@ public function render_image_select_field( $id, $label, $value, $class, $require
20132013
$field .= "<div class='" . esc_attr( $options_classes ) . " jetpack-field jetpack-fieldset-image-options' style='" . esc_attr( $options_styles ) . "'>";
20142014
$field .= "<div class='jetpack-fieldset-image-options__wrapper'>";
20152015

2016-
$options_data = $this->get_attribute( 'optionsdata' );
2016+
$options_data = $this->get_attribute( 'optionsdata' );
2017+
2018+
// Filter out empty options from the end
2019+
$options_data = $this->trim_image_select_options( $options_data );
2020+
20172021
$used_html_ids = array();
20182022

20192023
if ( ! empty( $options_data ) ) {
@@ -2030,6 +2034,9 @@ public function render_image_select_field( $id, $label, $value, $class, $require
20302034
// Randomize options if requested, but preserve original letter values
20312035
if ( $randomize_options ) {
20322036
shuffle( $working_options );
2037+
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 );
20332040
}
20342041

20352042
// Calculate row options count for CSS variable
@@ -2056,7 +2063,7 @@ public function render_image_select_field( $id, $label, $value, $class, $require
20562063
}
20572064
}
20582065
} else {
2059-
$rendered_image_block = '<figure class="wp-block-image"><img src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" alt="" style="aspect-ratio:1;object-fit:cover"/></figure>';
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>';
20602067
}
20612068

20622069
$option_value = wp_json_encode(
@@ -3219,4 +3226,44 @@ private function enqueue_phone_field_assets() {
32193226
$version
32203227
);
32213228
}
3229+
3230+
/**
3231+
* Trims the image select options from the end of the array if they are empty.
3232+
*
3233+
* @param array $options The options to trim.
3234+
*
3235+
* @return array The trimmed options array.
3236+
*/
3237+
private function trim_image_select_options( $options ) {
3238+
if ( empty( $options ) ) {
3239+
return $options;
3240+
}
3241+
3242+
// Work backwards through the array to find the last valid option
3243+
$last_valid_index = -1;
3244+
3245+
for ( $i = count( $options ) - 1; $i >= 0; $i-- ) {
3246+
$option = $options[ $i ];
3247+
3248+
// Check if option has a label
3249+
$has_label = ! empty( $option['label'] );
3250+
3251+
// Check if option has an image with src
3252+
$has_image = false;
3253+
3254+
if ( isset( $option['image']['innerHTML'] ) ) {
3255+
// Extract src from innerHTML using regex
3256+
preg_match( '/src="([^"]*)"/', $option['image']['innerHTML'], $matches );
3257+
$has_image = ! empty( $matches[1] );
3258+
}
3259+
3260+
// If this option has either a label or an image, it's valid
3261+
if ( $has_label || $has_image ) {
3262+
$last_valid_index = $i;
3263+
break;
3264+
}
3265+
}
3266+
3267+
return array_slice( $options, 0, $last_valid_index + 1 );
3268+
}
32223269
}

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,7 +1094,10 @@ private static function render_ajax_success_wrapper( $form, $submission_success
10941094
<div class="field-value" data-wp-text="context.submission.value"></div>
10951095
<div class="field-images" data-wp-bind--hidden="!context.submission.images">
10961096
<template data-wp-each--image="context.submission.images">
1097-
<img class="field-image" data-wp-bind--src="context.image" data-wp-bind--hidden="!context.image"/>
1097+
<figure class="field-image" data-wp-class--is-empty="!context.image">
1098+
<img data-wp-bind--src="context.image" data-wp-bind--hidden="!context.image" />
1099+
<img src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" data-wp-bind--hidden="context.image" />
1100+
</figure>
10981101
</template>
10991102
</div>
11001103
</div>
@@ -1109,7 +1112,10 @@ private static function render_ajax_success_wrapper( $form, $submission_success
11091112

11101113
if ( ! empty( $submission['images'] ) ) {
11111114
foreach ( $submission['images'] as $image ) {
1112-
$html .= '<img data-wp-each-child class="field-image" data-wp-bind--src="context.image" src="' . $image . '" data-wp-bind--hidden="!context.image" ' . ( empty( $image ) ? 'hidden' : '' ) . ' />';
1115+
$html .= '<figure data-wp-each-child class="field-image ' . ( empty( $image ) ? 'is-empty' : '' ) . '" data-wp-class--is-empty="!context.image">';
1116+
$html .= '<img data-wp-bind--src="context.image" src="' . $image . '" data-wp-bind--hidden="!context.image"' . ( empty( $image ) ? 'hidden' : '' ) . '/>';
1117+
$html .= '<img src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" data-wp-bind--hidden="context.image"' . ( empty( $image ) ? '' : 'hidden' ) . '/>';
1118+
$html .= '</figure>';
11131119
}
11141120
} else {
11151121
$html .= '<template data-wp-each--image="context.submission.images"></template>';

projects/packages/forms/src/contact-form/css/grunion.scss

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* Import shared visually-hidden utility so editor and frontend match */
22
@use "../../blocks/shared/styles/visually-hidden" as *;
3+
@use "../../blocks/shared/styles/empty_image_option" as *;
34

45
:root {
56
--jetpack--contact-form--border: 1px solid #8c8f94;
@@ -271,9 +272,28 @@ that needs to mimic the input element styles */
271272
}
272273

273274
.field-image {
274-
width: 200px;
275-
aspect-ratio: 1/1;
276-
object-fit: cover;
275+
width: 196px;
276+
height: 196px;
277+
278+
&.is-empty {
279+
position: relative;
280+
281+
&::before {
282+
283+
@extend %empty-image-option-background;
284+
}
285+
286+
&::after {
287+
288+
@extend %empty-image-option-icon;
289+
}
290+
}
291+
292+
img {
293+
width: 100%;
294+
aspect-ratio: 1/1;
295+
object-fit: cover;
296+
}
277297
}
278298
}
279299

projects/packages/forms/src/dashboard/components/response-view/body.tsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -265,16 +265,25 @@ const ResponseViewBody = ( {
265265
</div>
266266
<div className="image-select-field-images">
267267
{ value.choices.map( choice => {
268+
const imageSrc =
269+
choice.image?.src ||
270+
'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
271+
268272
return (
269-
<img
273+
<figure
270274
key={ choice.selected }
271-
className="image-select-field-image"
272-
src={
273-
choice.image.src ||
274-
'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
275-
}
276-
alt={ choice.selected }
277-
/>
275+
className={ clsx( 'image-select-field-image', {
276+
'is-empty': ! choice.image?.src,
277+
} ) }
278+
>
279+
<img
280+
className={ clsx( 'image-select-field-image', {
281+
'is-empty': ! choice.image?.src,
282+
} ) }
283+
src={ imageSrc }
284+
alt={ choice.selected }
285+
/>
286+
</figure>
278287
);
279288
} ) }
280289
</div>
@@ -364,7 +373,7 @@ const ResponseViewBody = ( {
364373
method: 'POST',
365374
data: { is_unread: false },
366375
} )
367-
.then( ( { count } ) => {
376+
.then( ( { count }: { count: number } ) => {
368377
// Update menu counter with accurate count from server
369378
updateMenuCounter( count );
370379
// Mark record as invalid instead of removing from view

projects/packages/forms/src/dashboard/inbox/style.scss

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@use "@wordpress/base-styles/breakpoints";
22
@use "@wordpress/base-styles/variables";
3+
@use "../../blocks/shared/styles/empty_image_option" as *;
34
@import "@wordpress/dataviews/build-style/style.css";
45

56
.jp-forms__inbox {
@@ -280,9 +281,29 @@
280281
}
281282

282283
.image-select-field-image {
284+
margin: 0;
283285
width: 200px;
284-
aspect-ratio: 1/1;
285-
object-fit: cover;
286+
height: 200px;
287+
288+
&.is-empty {
289+
position: relative;
290+
291+
&::before {
292+
293+
@extend %empty-image-option-background;
294+
}
295+
296+
&::after {
297+
298+
@extend %empty-image-option-icon;
299+
background-color: #000;
300+
}
301+
}
302+
303+
img {
304+
aspect-ratio: 1/1;
305+
object-fit: cover;
306+
}
286307
}
287308
}
288309

projects/packages/forms/src/modules/form/view.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ const maybeTransformValue = value => {
135135

136136
const getImages = value => {
137137
if ( value?.type === 'image-select' ) {
138-
return value.choices.filter( choice => choice.image?.src ).map( choice => choice.image?.src );
138+
return value.choices.map( choice => choice.image?.src );
139139
}
140140

141141
return null;

0 commit comments

Comments
 (0)