Skip to content

Commit

Permalink
Query Filter: Update to match new Interactivity API format (#563)
Browse files Browse the repository at this point in the history
* Query Filter: Update to match new Interactivity API format

* Pass translations from PHP

This is a workaround for i18n functions, since they can't be imported into a module

* Fix linter issue

* Switch back to PostCSS
  • Loading branch information
ryelle authored Feb 1, 2024
1 parent 9e39b9a commit fb48365
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 150 deletions.
37 changes: 21 additions & 16 deletions mu-plugins/blocks/query-filter/render.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
'hasHover' => false,
'hasMultiple' => $has_multiple,
];
$encoded_state = wp_json_encode( [ 'wporg' => [ 'queryFilter' => $init_state ] ] );
$encoded_state = wp_json_encode( $init_state );

// Set up a unique ID for this filter.
$html_id = wp_unique_id( "filter-{$settings['key']}-" );
Expand All @@ -66,43 +66,43 @@
?>
<div
<?php echo get_block_wrapper_attributes(); // phpcs:ignore ?>
data-wp-interactive
data-wp-interactive="<?php echo esc_attr( '{"namespace":"wporg/query-filter"}' ); ?>"
data-wp-context="<?php echo esc_attr( $encoded_state ); ?>"
data-wp-effect="effects.wporg.queryFilter.init"
data-wp-class--is-modal-open="context.wporg.queryFilter.isOpen"
data-wp-on--keydown="actions.wporg.queryFilter.handleKeydown"
data-wp-watch="effects.init"
data-wp-class--is-modal-open="context.isOpen"
data-wp-on--keydown="actions.handleKeydown"
>
<button
class="<?php echo esc_attr( implode( ' ', $button_classes ) ); ?>"
data-wp-class--is-active="context.wporg.queryFilter.isOpen"
data-wp-on--click="actions.wporg.queryFilter.toggle"
data-wp-bind--aria-expanded="context.wporg.queryFilter.isOpen"
data-wp-class--is-active="context.isOpen"
data-wp-on--click="actions.toggle"
data-wp-bind--aria-expanded="context.isOpen"
aria-controls="<?php echo esc_attr( $html_id ); ?>"
><?php echo wp_kses_post( $settings['label'] ); ?></button>

<div
class="wporg-query-filter__modal-backdrop"
data-wp-bind--hidden="!context.wporg.queryFilter.isOpen"
data-wp-on--click="actions.wporg.queryFilter.toggle"
data-wp-bind--hidden="!context.isOpen"
data-wp-on--click="actions.toggle"
></div>

<div
class="wporg-query-filter__modal"
id="<?php echo esc_attr( $html_id ); ?>"
data-wp-bind--hidden="!context.wporg.queryFilter.isOpen"
data-wp-effect--focus="effects.wporg.queryFilter.focusFirstElement"
data-wp-effect--position="effects.wporg.queryFilter.checkPosition"
data-wp-bind--hidden="!context.isOpen"
data-wp-effect--focus="effects.focusFirstElement"
data-wp-effect--position="effects.checkPosition"
>
<form
action="<?php echo esc_attr( $settings['action'] ); ?>"
data-wp-on--change="actions.wporg.queryFilter.handleFormChange"
data-wp-on--change="actions.handleFormChange"
>
<div class="wporg-query-filter__modal-header">
<h2><?php echo wp_kses_post( $settings['title'] ); ?></h2>
<input
type="button"
class="wporg-query-filter__modal-close"
data-wp-on--click="actions.wporg.queryFilter.toggle"
data-wp-on--click="actions.toggle"
aria-label="<?php esc_attr_e( 'Close', 'wporg' ); ?>"
/>
</div> <!-- /.wporg-query-filter__modal-header -->
Expand Down Expand Up @@ -140,18 +140,23 @@ class="wporg-query-filter__modal-close"
* @param WP_Block $block The current block being rendered.
*/
do_action( 'wporg_query_filter_in_form', $settings['key'], $block );

/* translators: %s is count of currently selected filters. */
$label_count = __( 'Apply (%s)', 'wporg' );
?>

<div class="wporg-query-filter__modal-actions">
<input
type="button"
class="wporg-query-filter__modal-action-clear"
value="<?php esc_attr_e( 'Clear', 'wporg' ); ?>"
data-wp-on--click="actions.wporg.queryFilter.clearSelection"
data-wp-on--click="actions.clearSelection"
aria-disabled="<?php echo $selected_count ? 'false' : 'true'; ?>"
/>
<input
type="submit"
data-label-with-count="<?php echo esc_attr( $label_count ); ?>"
data-label=<?php esc_attr_e( 'Apply', 'wporg' ); ?>
value="<?php echo esc_html( $apply_label ); ?>"
/>
</div> <!-- /.wporg-query-filter__modal-actions -->
Expand Down
4 changes: 2 additions & 2 deletions mu-plugins/blocks/query-filter/src/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
}
},
"editorScript": "file:./index.js",
"style": "file:./style.css",
"viewScript": "file:./view.js",
"style": "file:./style-index.css",
"viewModule": "file:./view.js",
"render": "file:../render.php"
}
1 change: 1 addition & 0 deletions mu-plugins/blocks/query-filter/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useBlockProps } from '@wordpress/block-editor';
* Internal dependencies
*/
import metadata from './block.json';
import './style.pcss';

function Edit( { attributes, name } ) {
return (
Expand Down
252 changes: 120 additions & 132 deletions mu-plugins/blocks/query-filter/src/view.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/**
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { store as wpStore } from '@wordpress/interactivity';
import { getContext, getElement, store } from '@wordpress/interactivity';

// See https://github.com/WordPress/gutenberg/blob/37f52ae884a40f7cb77ac2484648b4e4ad973b59/packages/block-library/src/navigation/view-interactivity.js
const focusableSelectors = [
Expand All @@ -15,151 +14,140 @@ const focusableSelectors = [
'[tabindex]:not([tabindex^="-"])',
];

/**
* Toggles the overflow-x style of the query filter between 'hidden' and 'scroll'.
*
* In certain themes (e.g., showcase), an 'overflow-x: scroll' is added on mobile screens to always display
* the horizontal scrollbar, indicating to users that there's more content to the right.
* However, this persistent display feature causes the dropdown menu to be overlaid by the scrollbar
* when opened (See issue https://github.com/WordPress/wporg-mu-plugins/issues/467#issuecomment-1754349676).
* This function serves to address that issue.
*
*/
function toggleOverflowX() {
const filtersElement = document.querySelector( '.wporg-query-filters' );
const { actions } = store( 'wporg/query-filter', {
actions: {
/**
* Toggles the overflow-x style of the query filter between 'hidden' and 'scroll'.
*
* In certain themes (e.g., showcase), an 'overflow-x: scroll' is added on mobile screens to always display
* the horizontal scrollbar, indicating to users that there's more content to the right.
* However, this persistent display feature causes the dropdown menu to be overlaid by the scrollbar
* when opened (See issue https://github.com/WordPress/wporg-mu-plugins/issues/467#issuecomment-1754349676).
* This function serves to address that issue.
*
*/
toggleOverflowX: () => {
const filtersElement = document.querySelector( '.wporg-query-filters' );

if ( filtersElement ) {
const currentOverflowX = window.getComputedStyle( filtersElement ).overflowX;
if ( filtersElement ) {
const currentOverflowX = window.getComputedStyle( filtersElement ).overflowX;

if ( 'hidden' === currentOverflowX ) {
filtersElement.style.overflowX = 'scroll';
} else if ( 'scroll' === currentOverflowX || 'auto' === currentOverflowX ) {
filtersElement.style.overflowX = 'hidden';
}
}
}
if ( 'hidden' === currentOverflowX ) {
filtersElement.style.overflowX = 'scroll';
} else if ( 'scroll' === currentOverflowX || 'auto' === currentOverflowX ) {
filtersElement.style.overflowX = 'hidden';
}
}
},

function closeDropdown( store ) {
const { context } = store;
context.wporg.queryFilter.isOpen = false;
context.wporg.queryFilter.form?.reset();
closeDropdown: () => {
const context = getContext();
context.isOpen = false;
context.form?.reset();

const count = context.wporg.queryFilter.form?.querySelectorAll( 'input:checked' ).length;
updateButtons( store, count );
document.documentElement.classList.remove( 'is-query-filter-open' );
const count = context.form?.querySelectorAll( 'input:checked' ).length;
actions.updateButtons( count );
document.documentElement.classList.remove( 'is-query-filter-open' );

toggleOverflowX();
}
actions.toggleOverflowX();
},

function updateButtons( store, count ) {
const { context } = store;
if ( ! context.wporg.queryFilter.form ) {
return;
}
updateButtons: ( count ) => {
const context = getContext();
if ( ! context.form ) {
return;
}

const applyButton = context.wporg.queryFilter.form.querySelector( 'input[type="submit"]' );
const clearButton = context.wporg.queryFilter.form.querySelector( '.wporg-query-filter__modal-action-clear' );
const applyButton = context.form.querySelector( 'input[type="submit"]' );
const clearButton = context.form.querySelector( '.wporg-query-filter__modal-action-clear' );

// Only update the apply button if multiple selections are allowed.
if ( context.wporg.queryFilter.hasMultiple ) {
if ( count ) {
/* translators: %s is count of currently selected filters. */
applyButton.value = sprintf( __( 'Apply (%s)', 'wporg' ), count );
} else {
applyButton.value = __( 'Apply', 'wporg' );
}
}
// Only update the apply button if multiple selections are allowed.
if ( context.hasMultiple ) {
if ( count ) {
applyButton.value = applyButton.dataset.labelWithCount.replace( '%s', count );
} else {
applyButton.value = applyButton.dataset.label;
}
}

clearButton.setAttribute( 'aria-disabled', count ? 'false' : 'true' );
}
clearButton.setAttribute( 'aria-disabled', count ? 'false' : 'true' );
},

wpStore( {
actions: {
wporg: {
queryFilter: {
toggle: ( store ) => {
const { context } = store;
if ( context.wporg.queryFilter.isOpen ) {
closeDropdown( store );
} else {
context.wporg.queryFilter.isOpen = true;
document.documentElement.classList.add( 'is-query-filter-open' );
toggleOverflowX();
}
},
handleKeydown: ( store ) => {
const { context, event } = store;
// If Escape close the dropdown.
if ( event.key === 'Escape' ) {
closeDropdown( store );
context.wporg.queryFilter.toggleButton.focus();
return;
}
toggle: () => {
const context = getContext();
if ( context.isOpen ) {
actions.closeDropdown();
} else {
context.isOpen = true;
document.documentElement.classList.add( 'is-query-filter-open' );
actions.toggleOverflowX();
}
},
handleKeydown: ( event ) => {
const context = getContext();
// If Escape close the dropdown.
if ( event.key === 'Escape' ) {
actions.closeDropdown();
context.toggleButton.focus();
return;
}

// Trap focus.
if ( event.key === 'Tab' ) {
// If shift + tab it change the direction.
if (
event.shiftKey &&
window.document.activeElement === context.wporg.queryFilter.firstFocusableElement
) {
event.preventDefault();
context.wporg.queryFilter.lastFocusableElement.focus();
} else if (
! event.shiftKey &&
window.document.activeElement === context.wporg.queryFilter.lastFocusableElement
) {
event.preventDefault();
context.wporg.queryFilter.firstFocusableElement.focus();
}
}
},
handleFormChange: ( store ) => {
const { context } = store;
const count = context.wporg.queryFilter.form.querySelectorAll( 'input:checked' ).length;
updateButtons( store, count );
},
clearSelection: ( store ) => {
const { context, ref } = store;
if ( 'true' === ref.getAttribute( 'aria-disabled' ) ) {
return;
}
context.wporg.queryFilter.form
.querySelectorAll( 'input' )
.forEach( ( input ) => ( input.checked = false ) );
updateButtons( store, 0 );
},
},
// Trap focus.
if ( event.key === 'Tab' ) {
// If shift + tab it change the direction.
if ( event.shiftKey && window.document.activeElement === context.firstFocusableElement ) {
event.preventDefault();
context.lastFocusableElement.focus();
} else if ( ! event.shiftKey && window.document.activeElement === context.lastFocusableElement ) {
event.preventDefault();
context.firstFocusableElement.focus();
}
}
},
handleFormChange: () => {
const context = getContext();
const count = context.form.querySelectorAll( 'input:checked' ).length;
actions.updateButtons( count );
},
clearSelection: () => {
const context = getContext();
const { ref } = getElement();
if ( 'true' === ref.getAttribute( 'aria-disabled' ) ) {
return;
}
context.form.querySelectorAll( 'input' ).forEach( ( input ) => ( input.checked = false ) );
actions.updateButtons( 0 );
},
},
effects: {
wporg: {
queryFilter: {
init: ( { context, ref } ) => {
context.wporg.queryFilter.toggleButton = ref.querySelector( '.wporg-query-filter__toggle' );
context.wporg.queryFilter.form = ref.querySelector( 'form' );
init: () => {
const context = getContext();
const { ref } = getElement();
context.toggleButton = ref.querySelector( '.wporg-query-filter__toggle' );
context.form = ref.querySelector( 'form' );

if ( context.wporg.queryFilter.isOpen ) {
const focusableElements = ref.querySelectorAll( focusableSelectors );
context.wporg.queryFilter.firstFocusableElement = focusableElements[ 0 ];
context.wporg.queryFilter.lastFocusableElement =
focusableElements[ focusableElements.length - 1 ];
}
},
checkPosition: ( { context, ref } ) => {
if ( context.wporg.queryFilter.isOpen ) {
const position = ref.getBoundingClientRect();
if ( position.left < 0 ) {
ref.style.left = 0;
}
}
},
focusFirstElement: ( { context, ref } ) => {
if ( context.wporg.queryFilter.isOpen ) {
ref.querySelector( 'form input:first-child' ).focus();
}
},
},
if ( context.isOpen ) {
const focusableElements = ref.querySelectorAll( focusableSelectors );
context.firstFocusableElement = focusableElements[ 0 ];
context.lastFocusableElement = focusableElements[ focusableElements.length - 1 ];
}
},
checkPosition: () => {
const context = getContext();
const { ref } = getElement();
if ( context.isOpen ) {
const position = ref.getBoundingClientRect();
if ( position.left < 0 ) {
ref.style.left = 0;
}
}
},
focusFirstElement: () => {
const context = getContext();
const { ref } = getElement();
if ( context.isOpen ) {
ref.querySelector( 'form input:first-child' ).focus();
}
},
},
} );

0 comments on commit fb48365

Please sign in to comment.