Skip to content

Commit 2602bc3

Browse files
authored
Add column settings to search widget (#2409)
This PR introduces column settings functionality for the Search Widget, allowing users to configure individual columns and areas within the search interface. It also addresses #2374, by adding (back) a horizontal option for columns. ## Todo: - [x] Update readme - [x] Set default to horizontal for backwards compatibility - [x] Fix keyboard compatibility (make icons visible while tabbing through - [x] Add E2E tests ## Changes ✨ **New Features** - Column Settings: Added ability to configure individual columns in the search widget with a settings icon - Column Layout: Added a setting to set how to arrange the fields (horizontally, vertically) per column 🔧 **Improvements** - Refactored area actions to use an action hook for better extensibility - Added horizontal layout support for areas inside columns - Removed text alignment classes (left, right) from search widget columns for cleaner styling 🏗️ **Technical Changes** - Updated **class-search-field-collection.php** with position configuration support - Refactored search widget template rendering to respect area and column settings - Fixed **gk/gravityview/admin-views/row/after** hook to trigger once (was running for every column in a row, instead of after all rows were generated) - Updated admin JavaScript to handle column settings display interactions **Testing** - Verify column settings icon appears and displays settings on click - Test area configuration within columns - Confirm horizontal layout works for areas inside columns - Ensure search functionality remains intact with new settings 💾 [Build file](https://www.dropbox.com/scl/fi/v8jxxd51duj40uufk0bcr/gravityview-2.43.3-13e5f74f1.zip?rlkey=db2y4woawz19bv1ej8txhkc8y&dl=1) (13e5f74). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Per-area layout settings for the Search Bar widget with a Configure Area Settings button. * **Improvements** * Cleaner droppable area headers and actions with smoother show/hide and RTL support. * Flexbox-based search widget layouts for reliable column/row behavior and icon hover animation. * "Clear all fields" now rendered via an extensible action; visibility uses CSS classes. * **Bug Fixes** * Fixed admin hook parameter ordering and minor JS/form handling issues. * **Tests** * Added E2E and unit tests for search area/column settings. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
2 parents 938ea3c + 13e5f74 commit 2602bc3

19 files changed

+534
-84
lines changed

assets/css/admin-views.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/css/scss/admin-views.scss

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,27 @@ $gv-overlay-index: 10000;
100100
}
101101

102102
&.is-sortable {
103-
.gv-droppable-area-title {
103+
.gv-droppable-area-header-title {
104104
display: none;
105105
}
106+
.gv-droppable-area-header {
107+
opacity: 0;
108+
transition: opacity 200ms ease-out;
109+
}
110+
111+
.gv-droppable-area:hover,
112+
.gv-droppable-area:focus-within
113+
{
114+
> .gv-droppable-area-header {
115+
opacity: 1;
116+
}
117+
}
118+
119+
.gv-droppable-area:focus-within {
120+
> .gv-droppable-area-header {
121+
transition: none; // Instant visibility on tab-focus.
122+
}
123+
}
106124
}
107125
}
108126

@@ -148,7 +166,7 @@ $gv-overlay-index: 10000;
148166
}
149167

150168
&:hover {
151-
color: #2271B1;
169+
color: $color-blue-50;
152170
}
153171
}
154172

@@ -764,6 +782,11 @@ $gv-overlay-index: 10000;
764782
border-radius: 3px;
765783
text-decoration: underline;
766784
}
785+
786+
&.gv-hide {
787+
opacity: 0;
788+
pointer-events: none;
789+
}
767790
}
768791

769792
/** Show spinner cursor while the View is being AJAX updated. **/
@@ -1219,11 +1242,25 @@ hr {
12191242
clear: both;
12201243
}
12211244

1222-
.gv-droppable-area-title {
1223-
padding: .5em 0;
1224-
text-align: left;
1245+
.gv-droppable-area-header-title {
1246+
display: flex;
1247+
align-items: center;
1248+
gap: 4px;
1249+
}
1250+
1251+
.gv-droppable-area-header-actions {
1252+
display: flex;
1253+
align-items: center;
1254+
gap: 4px;
1255+
margin-left: auto;
1256+
}
12251257

1226-
margin: 6px 8px 0 8px;
1258+
.gv-droppable-area-header {
1259+
text-align: left;
1260+
display: flex;
1261+
justify-content: space-between;
1262+
align-items: center;
1263+
margin: 0 8px;
12271264

12281265
html[dir=rtl] & {
12291266
margin: 0 0 6px 12px;
@@ -1239,10 +1276,18 @@ hr {
12391276
text-transform: none;
12401277
font-weight: normal;
12411278
}
1279+
1280+
> .clear-all-fields {
1281+
margin-left: auto; // align the first item to the right.
1282+
}
1283+
1284+
> * + .clear-all-fields {
1285+
margin-left: 0;
1286+
}
12421287
}
12431288

12441289
.gv-droppable-area-subtitle {
1245-
margin: 0.25em 0 0 0;
1290+
margin: 0;
12461291
padding: 0;
12471292
color: #636D77;
12481293
}
@@ -2528,6 +2573,24 @@ hr {
25282573
/**
25292574
* Search Bar
25302575
*/
2576+
a.gv-search-area-settings {
2577+
display: flex;
2578+
text-decoration: none;
2579+
color: inherit;
2580+
padding: 4px;
2581+
2582+
i.dashicons {
2583+
transition: transform 200ms;
2584+
}
2585+
2586+
&:hover {
2587+
color: #2271b1;
2588+
i.dashicons {
2589+
transform: rotate(30deg);
2590+
}
2591+
}
2592+
}
2593+
25312594
#search-view {
25322595
display: none;
25332596

assets/js/admin-views.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3051,14 +3051,13 @@
30513051
viewConfiguration.toggleDropMessage();
30523052
},
30533053

3054-
toggleRemoveAllFields: function ( e, item ) {
3054+
toggleRemoveAllFields: function ( ) {
3055+
let has_fields = false;
30553056

3056-
has_fields = false;
3057-
3058-
$( ".active-drop:visible" ).each( function ( index, item ) {
3057+
$( ".active-drop:visible" ).each( function ( _index, item ) {
30593058
has_fields = ( $( this ).find( '.gv-fields' ).length > 1 );
3060-
$( '.clear-all-fields', $( item ).parents('.gv-droppable-area') ).toggle( has_fields );
3061-
});
3059+
$( '.clear-all-fields', $( item ).parents( '.gv-droppable-area' ) ).toggleClass( 'gv-hide', ! has_fields );
3060+
} );
30623061
},
30633062

30643063
/**

assets/js/admin-views.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

includes/admin/class-gravityview-admin-view-item.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ function getOutput() {
178178

179179
// $settings_html will just be hidden inputs if empty. Otherwise, it'll have an <ul>. Ugly hack, I know.
180180
// TODO: Un-hack this
181-
$hide_settings_link_class = ( empty( $this->item['settings_html'] ) || strpos( $this->item['settings_html'], '<!-- No Options -->' ) > 0 ) ? 'hide-if-js' : '';
181+
$hide_settings_link_class = ( empty( $this->item['settings_html'] ) || strpos( $this->item['settings_html'], GravityView_Render_Settings::NO_OPTIONS ) > 0 ) ? 'hide-if-js' : '';
182182
$settings_link = sprintf( '<button class="gv-field-settings %2$s" title="%1$s" aria-label="%1$s"><span class="dashicons-admin-generic dashicons"></span></button>', esc_attr( $settings_title ), $hide_settings_link_class );
183183

184184
// When a field label is empty, use the Field ID

includes/admin/class.render.settings.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@
1616
*/
1717

1818
class GravityView_Render_Settings {
19+
/**
20+
* A placeholder when no options are available.
21+
*
22+
* @since $ver$
23+
*/
24+
public const NO_OPTIONS = '<!-- No Options -->';
25+
1926
/**
2027
* Registers required filter hooks.
2128
*
@@ -444,7 +451,7 @@ public static function render_field_options( $form_id, $field_type, $template_id
444451

445452
// If there are no options, return what we got.
446453
if ( empty( $option_groups ) ) {
447-
return $hidden_fields . '<!-- No Options -->'; // The HTML comment is here for checking if the output is empty in render_label()
454+
return $hidden_fields . self::NO_OPTIONS; // The NO OPTIONS marker is here for checking if the output is empty in render_label().
448455
}
449456

450457
$settings_title = esc_attr( sprintf( __( '%s Settings', 'gk-gravityview' ), wp_strip_all_tags( html_entity_decode( $field_label ) ) ) );
@@ -580,6 +587,12 @@ public static function render_field_options( $form_id, $field_type, $template_id
580587
);
581588

582589
$field_settings = sprintf( '<div class="gv-dialog-options--content">%s</div>', $field_settings );
590+
} elseif ( 'area' === $field_type ) {
591+
$field_settings = sprintf( '<div class="gv-dialog-options--content">%s</div>', $field_settings );
592+
$item_details = sprintf(
593+
'<div class="gv-field-details--container"><h3 class="search-field-title"><span>%s</span></h3></div>',
594+
$item['label'] ?? ''
595+
);
583596
} else {
584597
$subtitle = ! empty( $item['subtitle'] ) ? '<div class="subtitle">' . $item['subtitle'] . '</div>' : '';
585598
$widget_details_content = Utils::get( $item, 'description', '' );

includes/class-admin-views.php

Lines changed: 73 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,9 @@
1414
/** If this file is called directly, abort. */
1515

1616
use GV\Field_Collection;
17-
use GV\GF_Form;
1817
use GV\Grid;
1918
use GV\Plugin;
2019
use GV\Search\Fields\Search_Field;
21-
use GV\Search\Fields\Search_Field_All;
22-
use GV\Search\Fields\Search_Field_Search_Mode;
23-
use GV\Search\Fields\Search_Field_Submit;
24-
use GV\Search\Search_Field_Collection;
2520
use GV\View;
2621
use GV\Widget_Collection;
2722

@@ -101,6 +96,8 @@ function __construct() {
10196
add_action( 'gk/gravityview/admin-views/row/before', [ $this, 'render_actions' ], 5, 4 );
10297
add_action( 'gk/gravityview/admin-views/view/after-zone', [ $this, 'render_add_row' ], 5, 4 );
10398
add_filter( 'gk/gravityview/admin-views/view/is-dynamic', [ $this, 'set_dynamic_areas' ], 0, 4 );
99+
100+
add_action( 'gk/gravityview/admin-views/area/actions', [ $this, 'add_clear_all_fields_action' ], 0, 6 );
104101
}
105102

106103
/**
@@ -1248,32 +1245,40 @@ function render_active_areas( $template_id, $type, $zone, $rows, $values ) {
12481245
data-areaid="<?php echo esc_attr( $zone . '_' . $area['areaid'] ); ?>"
12491246
data-context="<?php echo esc_attr( $zone ); ?>"
12501247
data-templateid="<?php echo esc_attr( $template_id ); ?>">
1251-
<p class="gv-droppable-area-title"
1252-
<?php
1253-
if ( 'widget' === $type && empty( $area['subtitle'] ) ) {
1254-
echo ' style="margin: 0; padding: 0;"';
1255-
}
1256-
?>
1257-
>
1258-
<strong
1248+
<div class="gv-droppable-area-header">
1249+
<div class="gv-droppable-area-header-title">
12591250
<?php
1260-
if ( 'widget' === $type ) {
1261-
echo 'class="screen-reader-text"';
1262-
}
1251+
printf(
1252+
'<strong%s>%s</strong>',
1253+
'widget' === $type ? ' class="screen-reader-text"' : '',
1254+
esc_html( $area['title'] )
1255+
);
12631256
?>
1264-
><?php echo esc_html( $area['title'] ); ?></strong>
1265-
1266-
<?php if ( 'widget' !== $type ) { ?>
1267-
<a class="clear-all-fields alignright" role="button" href="#"
1268-
data-areaid="<?php echo esc_attr( $zone . '_' . $area['areaid'] ); ?>"><?php esc_html_e( 'Clear all fields',
1269-
'gk-gravityview' ); ?></a>
1270-
<?php } ?>
1271-
1272-
<?php if ( ! empty( $area['subtitle'] ) ) { ?>
1273-
<span class="gv-droppable-area-subtitle"><span class="gf_tooltip gv_tooltip tooltip"
1274-
title="<?php echo esc_attr( $area['subtitle'] ); ?>"></span></span>
1275-
<?php } ?>
1276-
</p>
1257+
1258+
<?php if ( ! empty( $area['subtitle'] ) ) { ?>
1259+
<span class="gv-droppable-area-subtitle">
1260+
<span class="gf_tooltip gv_tooltip tooltip" title="<?php echo esc_attr( $area['subtitle'] ); ?>"></span>
1261+
</span>
1262+
<?php } ?>
1263+
</div>
1264+
<div class="gv-droppable-area-header-actions">
1265+
<?php
1266+
/**
1267+
* @action Modifies the area actions in the admin View editor.
1268+
*
1269+
* @since $ver$
1270+
*
1271+
* @param array $area The current area configuration.
1272+
* @param string $type The type of area (e.g., 'widget', 'field').
1273+
* @param array $values The values in the area.
1274+
* @param bool $is_dynamic Whether the area is dynamic.
1275+
* @param string $template_id The ID of the template being used.
1276+
* @param string $zone The zone where the area is located.
1277+
*/
1278+
do_action( 'gk/gravityview/admin-views/area/actions', $area, $type, $values, $is_dynamic, $template_id, $zone );
1279+
?>
1280+
</div>
1281+
</div>
12771282
<div class="active-drop-container active-drop-container-<?php echo esc_attr( $type ); ?>">
12781283
<div class="active-drop active-drop-<?php echo esc_attr( $type ); ?>"
12791284
data-areaid="<?php echo esc_attr( $zone . '_' . $area['areaid'] ); ?>">
@@ -1381,22 +1386,22 @@ function render_active_areas( $template_id, $type, $zone, $rows, $values ) {
13811386
<?php endforeach; ?>
13821387
</div>
13831388
<?php
1384-
/**
1385-
* Triggers after a row is rendered in the View editor.
1386-
*
1387-
* @since 2.31.0
1388-
*
1389-
* @action `gk/gravityview/admin-views/row/before`
1390-
*
1391-
* @param bool $is_dynamic Whether the area is dynamic.
1392-
* @param View $view The View.
1393-
* @param string $template_id The template ID.
1394-
* @param string $type The object type (widget or field).
1395-
* @param string $zone The render zone.
1396-
*/
1397-
do_action( 'gk/gravityview/admin-views/row/after', $is_dynamic, $view, $template_id, $type, $zone );
1398-
13991389
endforeach;
1390+
/**
1391+
* Triggers after a row is rendered in the View editor.
1392+
*
1393+
* @since 2.31.0
1394+
*
1395+
* @action `gk/gravityview/admin-views/row/before`
1396+
*
1397+
* @param bool $is_dynamic Whether the area is dynamic.
1398+
* @param View $view The View.
1399+
* @param string $template_id The template ID.
1400+
* @param string $type The object type (widget or field).
1401+
* @param string $zone The render zone.
1402+
*/
1403+
do_action( 'gk/gravityview/admin-views/row/after', $is_dynamic, $view, $template_id, $type, $zone );
1404+
14001405
echo '</div>';
14011406
endforeach;
14021407
}
@@ -2160,6 +2165,30 @@ private function is_dynamic( string $template_id, string $type, string $zone ):
21602165
*/
21612166
return (bool) apply_filters( 'gk/gravityview/admin-views/view/is-dynamic', false, $template_id, $type, $zone );
21622167
}
2168+
2169+
/**
2170+
* Renders a "Clear all fields" button in the View configuration.
2171+
*
2172+
* @since $ver$
2173+
*
2174+
* @param array $area The area.
2175+
* @param string $type The type.
2176+
*
2177+
* @param bool $is_dynamic Whether the zone is dynamic.
2178+
* @param string $template_id The template ID.
2179+
* @param string $zone The zone.
2180+
*/
2181+
public function add_clear_all_fields_action( $area, $type, $values, $is_dynamic, $template_id, $zone ): void {
2182+
if ( 'widget' === $type ) {
2183+
return;
2184+
}
2185+
2186+
printf(
2187+
'<a class="clear-all-fields" role="button" href="#" data-areaid="%s">%s</a>',
2188+
esc_attr( $zone . '_' . $area['areaid'] ),
2189+
esc_html__( 'Clear all fields', 'gk-gravityview' )
2190+
);
2191+
}
21632192
}
21642193

21652194
GravityView_Admin_Views::get_instance();

0 commit comments

Comments
 (0)