Migrate interactivity stock filter to new store API, add improvements and bugfixes (https://github.com/woocommerce/woocommerce-blocks/pull/11827)

This commit is contained in:
Sam Seay 2023-11-27 12:26:01 +08:00 committed by GitHub
parent 590263543f
commit 1cd4df5b19
5 changed files with 217 additions and 235 deletions

View File

@ -1,10 +1,7 @@
/**
* External dependencies
*/
import {
store as interactivityStore,
navigate,
} from '@woocommerce/interactivity';
import { store, navigate, getContext } from '@woocommerce/interactivity';
import { DropdownContext } from '@woocommerce/interactivity-components/dropdown';
import { HTMLElementEvent } from '@woocommerce/types';
@ -21,35 +18,17 @@ const getUrl = ( activeFilters: string ) => {
return url.href;
};
type StockFilterState = {
filters: {
stockStatus: string;
activeFilters: string;
showDropdown: boolean;
};
};
type ActionProps = {
state: StockFilterState;
event: HTMLElementEvent< HTMLInputElement >;
};
interactivityStore( {
state: {
filters: {
stockStatus: '',
},
},
store( 'woocommerce/collection-stock-filter', {
actions: {
filters: {
navigate: ( { context }: { context: DropdownContext } ) => {
if ( context.woocommerceDropdown.selectedItem.value ) {
navigate(
getUrl( context.woocommerceDropdown.selectedItem.value )
// "on select" handler passed to the dropdown component.
navigate: () => {
const context = getContext< DropdownContext >(
'woocommerce/interactivity-dropdown'
);
}
navigate( getUrl( context.selectedItem.value || '' ) );
},
updateProducts: ( { event }: ActionProps ) => {
updateProducts: ( event: HTMLElementEvent< HTMLInputElement > ) => {
// get the active filters from the url:
const url = new URL( window.location.href );
const currentFilters =
@ -75,5 +54,4 @@ interactivityStore( {
navigate( getUrl( filtersArr.join( ',' ) ) );
},
},
},
} );

View File

@ -100,9 +100,18 @@ export default () => {
// data-wc-on--[event]
directive( 'on', ( { directives: { on }, element, evaluate } ) => {
const events = new Map();
on.forEach( ( entry ) => {
element.props[ `on${ entry.suffix }` ] = ( event ) => {
const event = entry.suffix.split( '--' )[ 0 ];
if ( ! events.has( event ) ) events.set( event, new Set() );
events.get( event ).add( entry );
} );
events.forEach( ( entries, event ) => {
element.props[ `on${ event }` ] = ( event ) => {
entries.forEach( ( entry ) => {
evaluate( entry, event );
} );
};
} );
} );

View File

@ -1,10 +1,9 @@
/**
* External dependencies
*/
import { store as interactivityStore } from '@woocommerce/interactivity';
import { getContext, store } from '@woocommerce/interactivity';
export type DropdownContext = {
woocommerceDropdown: {
currentItem: {
label: string;
value: string;
@ -18,78 +17,77 @@ export type DropdownContext = {
value: string | null;
};
isOpen: boolean;
};
};
type Store = {
context: DropdownContext;
selectors: unknown;
ref: HTMLElement;
};
interactivityStore( {
store( 'woocommerce/interactivity-dropdown', {
state: {},
selectors: {
woocommerceDropdown: {
placeholderText: ( { context }: { context: DropdownContext } ) => {
const {
woocommerceDropdown: { selectedItem },
} = context;
placeholderText: () => {
const context = getContext< DropdownContext >();
const { selectedItem } = context;
return selectedItem.label || 'Select an option';
},
isSelected: ( { context }: { context: DropdownContext } ) => {
isSelected: () => {
const context = getContext< DropdownContext >();
const {
woocommerceDropdown: {
currentItem: { value },
},
} = context;
return (
context.woocommerceDropdown.selectedItem.value === value ||
context.woocommerceDropdown.hoveredItem.value === value
context.selectedItem.value === value ||
context.hoveredItem.value === value
);
},
},
},
actions: {
woocommerceDropdown: {
toggleIsOpen: ( store: Store ) => {
const {
context: { woocommerceDropdown },
} = store;
toggleIsOpen: () => {
const context = getContext< DropdownContext >();
woocommerceDropdown.isOpen = ! woocommerceDropdown.isOpen;
context.isOpen = ! context.isOpen;
},
selectDropdownItem: ( {
context,
}: {
context: DropdownContext;
} ) => {
selectDropdownItem: ( event: MouseEvent ) => {
const context = getContext< DropdownContext >();
const {
woocommerceDropdown: {
currentItem: { label, value },
},
} = context;
context.woocommerceDropdown.selectedItem = { label, value };
context.woocommerceDropdown.isOpen = false;
const { selectedItem } = context;
if (
selectedItem.value === value &&
selectedItem.label === label
) {
context.selectedItem = {
label: null,
value: null,
};
} else {
context.selectedItem = { label, value };
}
context.isOpen = false;
event.stopPropagation();
},
addHoverClass: ( { context }: { context: DropdownContext } ) => {
addHoverClass: () => {
const context = getContext< DropdownContext >();
const {
woocommerceDropdown: {
currentItem: { label, value },
},
} = context;
context.woocommerceDropdown.hoveredItem = { label, value };
context.hoveredItem = { label, value };
},
removeHoverClass: ( { context }: { context: DropdownContext } ) => {
context.woocommerceDropdown.hoveredItem = {
removeHoverClass: () => {
const context = getContext< DropdownContext >();
context.hoveredItem = {
label: null,
value: null,
};
},
},
},
} );

View File

@ -47,17 +47,6 @@ final class CollectionStockFilter extends AbstractBlock {
$stock_status_counts = $block->context['collectionData']['stock_status_counts'] ?? [];
$wrapper_attributes = get_block_wrapper_attributes();
wc_store(
array(
'state' => array(
'filters' => array(
'stockStatus' => $stock_status_counts,
'activeFilters' => '',
),
),
)
);
return sprintf(
'<div %1$s>
<div class="wc-block-stock-filter__controls">%2$s</div>
@ -77,6 +66,7 @@ final class CollectionStockFilter extends AbstractBlock {
*/
private function get_stock_filter_html( $stock_counts, $attributes ) {
$display_style = $attributes['displayStyle'] ?? 'list';
$show_counts = $attributes['showCounts'] ?? false;
$stock_statuses = wc_get_product_stock_status_options();
// check the url params to select initial item on page load.
@ -84,9 +74,10 @@ final class CollectionStockFilter extends AbstractBlock {
$selected_stock_status = isset( $_GET[ self::STOCK_STATUS_QUERY_VAR ] ) ? sanitize_text_field( wp_unslash( $_GET[ self::STOCK_STATUS_QUERY_VAR ] ) ) : '';
$list_items = array_map(
function( $item ) use ( $stock_statuses ) {
function( $item ) use ( $stock_statuses, $show_counts ) {
$label = $show_counts ? $stock_statuses[ $item['status'] ] . ' (' . $item['count'] . ')' : $stock_statuses[ $item['status'] ];
return array(
'label' => $stock_statuses[ $item['status'] ],
'label' => $label,
'value' => $item['status'],
);
},
@ -108,9 +99,12 @@ final class CollectionStockFilter extends AbstractBlock {
'value' => null,
);
$data_directive = wp_json_encode( array( 'namespace' => 'woocommerce/collection-stock-filter' ) );
ob_start();
?>
<div data-wc-interactive='<?php echo esc_attr( $data_directive ); ?>'>
<?php if ( 'list' === $display_style ) : ?>
<div class="wc-block-stock-filter style-list">
<ul class="wc-block-checkbox-list wc-block-components-checkbox-list wc-block-stock-filter-list">
@ -123,7 +117,7 @@ final class CollectionStockFilter extends AbstractBlock {
class="wc-block-components-checkbox__input"
type="checkbox"
aria-invalid="false"
data-wc-on--change="actions.filters.updateProducts"
data-wc-on--change="actions.updateProducts"
value="<?php echo esc_attr( $stock_count['status'] ); ?>"
<?php checked( strpos( $selected_stock_status, $stock_count['status'] ) !== false, 1 ); ?>
>
@ -132,18 +126,21 @@ final class CollectionStockFilter extends AbstractBlock {
</svg>
<span class="wc-block-components-checkbox__label">
<?php echo esc_html( $stock_statuses[ $stock_count['status'] ] ); ?>
<?php if ( $show_counts ) : ?>
<?php
// translators: %s: number of products.
$screen_reader_text = sprintf( _n( '%s product', '%s products', $stock_count['count'], 'woo-gutenberg-products-block' ), number_format_i18n( $stock_count['count'] ) );
?>
<span class="wc-filter-element-label-list-count">
<span>
<span aria-hidden="true">
<?php echo esc_html( $stock_count['count'] ); ?>
<?php $show_counts ? print( esc_html( '(' . $stock_count['count'] . ')' ) ) : null; ?>
</span>
<span class="screen-reader-text">
<?php esc_html( $screen_reader_text ); ?>
</span>
</span>
<?php endif; ?>
</span>
</label>
</div>
@ -159,12 +156,13 @@ final class CollectionStockFilter extends AbstractBlock {
echo Dropdown::render(
array(
'items' => $list_items,
'action' => 'actions.filters.navigate',
'action' => 'woocommerce/collection-stock-filter::actions.navigate',
'selected_item' => $selected_item,
)
);
?>
<?php endif; ?>
</div>
<?php
return ob_get_clean();

View File

@ -23,14 +23,12 @@ class Dropdown {
);
$dropdown_context = array(
'woocommerceDropdown' => array(
'selectedItem' => $selected_item,
'hoveredItem' => array(
'label' => null,
'value' => null,
),
'isOpen' => false,
),
);
$action = $props['action'] ?? '';
@ -40,33 +38,33 @@ class Dropdown {
ob_start();
?>
<div data-wc-interactive='<?php echo wp_json_encode( array( 'namespace' => 'woocommerce/interactivity-dropdown' ) ); ?>'>
<div class="wc-block-stock-filter style-dropdown" data-wc-context='<?php echo wp_json_encode( $dropdown_context ); ?>' >
<div class="wc-blocks-components-form-token-field-wrapper single-selection" >
<div class="components-form-token-field" tabindex="-1">
<div class="components-form-token-field__input-container"
data-wc-class--is-active="context.woocommerceDropdown.isOpen"
data-wc-class--is-active="context.isOpen"
tabindex="-1"
data-wc-on--click="actions.woocommerceDropdown.toggleIsOpen"
data-wc-on--click="actions.toggleIsOpen"
>
<input id="components-form-token-input-1" type="text" autocomplete="off" data-wc-bind--placeholder="selectors.woocommerceDropdown.placeholderText" class="components-form-token-field__input" role="combobox" aria-expanded="false" aria-autocomplete="list" aria-describedby="components-form-token-suggestions-howto-1" value="">
<ul hidden data-wc-bind--hidden="!context.woocommerceDropdown.isOpen" class="components-form-token-field__suggestions-list" id="components-form-token-suggestions-1" role="listbox">
<input id="components-form-token-input-1" type="text" autocomplete="off" data-wc-bind--placeholder="selectors.placeholderText" class="components-form-token-field__input" role="combobox" aria-expanded="false" aria-autocomplete="list" aria-describedby="components-form-token-suggestions-howto-1" value="">
<ul hidden data-wc-bind--hidden="!context.isOpen" class="components-form-token-field__suggestions-list" id="components-form-token-suggestions-1" role="listbox">
<?php
foreach ( $items as $item ) :
$context = array(
'woocommerceDropdown' => array( 'currentItem' => $item ),
JSON_NUMERIC_CHECK,
'currentItem' => $item,
);
?>
<li
role="option"
data-wc-on--click--select-item="actions.woocommerceDropdown.selectDropdownItem"
data-wc-on--click--select-item="actions.selectDropdownItem"
data-wc-on--click--parent-action="<?php echo esc_attr( $action ); ?>"
data-wc-class--is-selected="selectors.woocommerceDropdown.isSelected"
data-wc-on--mouseover="actions.woocommerceDropdown.addHoverClass"
data-wc-on--mouseout="actions.woocommerceDropdown.removeHoverClass"
data-wc-class--is-selected="selectors.isSelected"
data-wc-on--mouseover="actions.addHoverClass"
data-wc-on--mouseout="actions.removeHoverClass"
data-wc-context='<?php echo wp_json_encode( $context ); ?>'
class="components-form-token-field__suggestion"
data-wc-bind--aria-selected="selectors.woocommerceDropdown.isSelected"
data-wc-bind--aria-selected="selectors.isSelected"
>
<?php echo esc_html( $item['label'] ); ?>
</li>
@ -79,6 +77,7 @@ class Dropdown {
<path d="M17.5 11.6L12 16l-5.5-4.4.9-1.2L12 14l4.5-3.6 1 1.2z" ></path>
</svg>
</div>
</div>
<?php
return ob_get_clean();
}