[Experimental] Allow overriding placeholder in interactivity Dropdown::render (#43211)

* add: multiple select support to attribute dropdown
* fix: remove active filter from dropdown
* chore: remove unused extractBuiltinColor

---------
Co-authored-by: Tung Du <dinhtungdu@gmail.com>
This commit is contained in:
Sam Seay 2024-01-03 15:52:45 +08:00 committed by GitHub
parent 97e4c55aeb
commit 161bf51bd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 63 additions and 67 deletions

View File

@ -11,7 +11,6 @@ import { PreviewDropdown } from '../../components/preview-dropdown';
type Props = { type Props = {
label: string; label: string;
textColor: string;
}; };
export const AttributeDropdown = ( { label }: Props ) => { export const AttributeDropdown = ( { label }: Props ) => {

View File

@ -41,8 +41,6 @@ import { Inspector } from './components/inspector-controls';
import { AttributeCheckboxList } from './components/attribute-checkbox-list'; import { AttributeCheckboxList } from './components/attribute-checkbox-list';
import { AttributeDropdown } from './components/attribute-dropdown'; import { AttributeDropdown } from './components/attribute-dropdown';
import './style.scss'; import './style.scss';
import { extractBuiltInColor } from '../../utils';
import { useStyleProps } from '../../../../base/hooks';
const ATTRIBUTES = getSetting< AttributeSetting[] >( 'attributes', [] ); const ATTRIBUTES = getSetting< AttributeSetting[] >( 'attributes', [] );
@ -62,13 +60,6 @@ const Edit = ( props: EditProps ) => {
showCounts, showCounts,
} = blockAttributes; } = blockAttributes;
const { className, style } = useStyleProps( props.attributes );
const builtInColor = extractBuiltInColor( className );
const textColor = builtInColor
? `var(--wp--preset--color--${ builtInColor })`
: style.color;
const attributeObject = getAttributeFromId( attributeId ); const attributeObject = getAttributeFromId( attributeId );
const [ isEditing, setIsEditing ] = useState( const [ isEditing, setIsEditing ] = useState(
@ -267,7 +258,6 @@ const Edit = ( props: EditProps ) => {
attributeObject.label || attributeObject.label ||
__( 'attribute', 'woocommerce' ) __( 'attribute', 'woocommerce' )
} }
textColor={ textColor || '' }
/> />
) : ( ) : (
<AttributeCheckboxList <AttributeCheckboxList

View File

@ -15,6 +15,10 @@ interface ActiveAttributeFilterContext extends AttributeFilterContext {
value: string; value: string;
} }
function nonNullable< T >( value: T ): value is NonNullable< T > {
return value !== null && value !== undefined;
}
function getUrl( function getUrl(
selectedTerms: string[], selectedTerms: string[],
slug: string, slug: string,
@ -47,18 +51,14 @@ store( 'woocommerce/collection-attribute-filter', {
const dropdownContext = getContext< DropdownContext >( const dropdownContext = getContext< DropdownContext >(
'woocommerce/interactivity-dropdown' 'woocommerce/interactivity-dropdown'
); );
const context = getContext< AttributeFilterContext >(); const context = getContext< AttributeFilterContext >();
const filters = dropdownContext.selectedItems
.map( ( item ) => item.value )
.filter( nonNullable );
if ( dropdownContext.selectedItem.value ) { navigate(
navigate( getUrl( filters, context.attributeSlug, context.queryType )
getUrl( );
[ dropdownContext.selectedItem.value ],
context.attributeSlug,
context.queryType
)
);
}
}, },
updateProducts: ( event: HTMLElementEvent< HTMLInputElement > ) => { updateProducts: ( event: HTMLElementEvent< HTMLInputElement > ) => {
if ( ! event.target.value ) return; if ( ! event.target.value ) return;

View File

@ -1,12 +0,0 @@
/**
* Extracts the built-in color from a block class name string if it exists.
* Returns null if no built-in color is found.
*
* @param blockClassString The block class name string.
* @return {string|null} The color name or null if no built-in color is found.
*/
export const extractBuiltInColor = ( blockClassString: string ) => {
const regex = /has-(?!link|text|background)([a-z-]+)-color/;
const match = blockClassString.match( regex );
return match ? match[ 1 ] : null;
};

View File

@ -2,7 +2,6 @@
* External dependencies * External dependencies
*/ */
import { getContext, store } from '@woocommerce/interactivity'; import { getContext, store } from '@woocommerce/interactivity';
import { __ } from '@wordpress/i18n';
/** /**
* Internal dependencies * Internal dependencies
@ -11,6 +10,7 @@ import './style.scss';
export type DropdownContext = { export type DropdownContext = {
selectType: 'multiple' | 'single'; selectType: 'multiple' | 'single';
defaultPlaceholder: string;
item: { item: {
label: string; label: string;
value: string; value: string;
@ -42,18 +42,18 @@ type DropdownStore = {
store< DropdownStore >( 'woocommerce/interactivity-dropdown', { store< DropdownStore >( 'woocommerce/interactivity-dropdown', {
state: { state: {
get placeholderText(): string { get placeholderText(): string {
const { selectType, selectedItems } = const { selectType, selectedItems, defaultPlaceholder } =
getContext< DropdownContext >(); getContext< DropdownContext >();
if ( selectType === 'single' ) { if ( selectType === 'single' ) {
return selectedItems?.length && selectedItems[ 0 ].label return selectedItems?.length && selectedItems[ 0 ].label
? selectedItems[ 0 ]?.label ? selectedItems[ 0 ]?.label
: __( 'Select an option', 'woocommerce' ); : defaultPlaceholder;
} else if ( } else if (
selectType === 'multiple' && selectType === 'multiple' &&
selectedItems.length === 0 selectedItems.length === 0
) { ) {
return __( 'Select options', 'woocommerce' ); return defaultPlaceholder;
} }
return ''; return '';

View File

@ -0,0 +1,4 @@
Significance: patch
Type: add
[Experimental] support passing a placeholder text to the interactivity Dropdown component.

View File

@ -211,8 +211,10 @@ final class CollectionAttributeFilter extends AbstractBlock {
return ''; return '';
} }
$list_items = array(); $list_items = array();
$selected_item = array(); $selected_items = array();
$product_attribute = wc_get_attribute( $attributes['attributeId'] );
foreach ( $options as $option ) { foreach ( $options as $option ) {
$item = array( $item = array(
@ -223,7 +225,7 @@ final class CollectionAttributeFilter extends AbstractBlock {
$list_items[] = $item; $list_items[] = $item;
if ( $option['selected'] ) { if ( $option['selected'] ) {
$selected_item = $item; $selected_items[] = $item;
} }
} }
@ -231,7 +233,10 @@ final class CollectionAttributeFilter extends AbstractBlock {
array( array(
'items' => $list_items, 'items' => $list_items,
'action' => 'woocommerce/collection-attribute-filter::actions.navigate', 'action' => 'woocommerce/collection-attribute-filter::actions.navigate',
'selected_items' => array( $selected_item ), 'selected_items' => $selected_items,
'select_type' => $attributes['selectType'] ?? 'multiple',
// translators: %s is a product attribute name.
'placeholder' => sprintf( __( 'Select %s', 'woocommerce' ), $product_attribute->name ),
) )
); );
} }

View File

@ -222,7 +222,8 @@ final class CollectionRatingFilter extends AbstractBlock {
* @return array<array-key, array> * @return array<array-key, array>
*/ */
private function get_dropdown_props( $rating_counts, $selected_ratings_query, $show_counts, $select_type ) { private function get_dropdown_props( $rating_counts, $selected_ratings_query, $show_counts, $select_type ) {
$ratings_array = explode( ',', $selected_ratings_query ); $ratings_array = explode( ',', $selected_ratings_query );
$placeholder_text = 'single' === $select_type ? __( 'Select a rating', 'woocommerce' ) : __( 'Select ratings', 'woocommerce' );
$selected_items = array_reduce( $selected_items = array_reduce(
$rating_counts, $rating_counts,
@ -259,6 +260,7 @@ final class CollectionRatingFilter extends AbstractBlock {
'select_type' => $select_type, 'select_type' => $select_type,
'selected_items' => $selected_items, 'selected_items' => $selected_items,
'action' => 'woocommerce/collection-rating-filter::actions.onDropdownChange', 'action' => 'woocommerce/collection-rating-filter::actions.onDropdownChange',
'placeholder' => $placeholder_text,
); );
} }
} }

View File

@ -151,6 +151,8 @@ final class CollectionStockFilter extends AbstractBlock {
$select_type = $attributes['selectType'] ?? 'single'; $select_type = $attributes['selectType'] ?? 'single';
$stock_statuses = wc_get_product_stock_status_options(); $stock_statuses = wc_get_product_stock_status_options();
$placeholder_text = 'single' === $select_type ? __( 'Select stock status', 'woocommerce' ) : __( 'Select stock statuses', 'woocommerce' );
// check the url params to select initial item on page load. // check the url params to select initial item on page load.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here. // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
$query = isset( $_GET[ self::STOCK_STATUS_QUERY_VAR ] ) ? sanitize_text_field( wp_unslash( $_GET[ self::STOCK_STATUS_QUERY_VAR ] ) ) : ''; $query = isset( $_GET[ self::STOCK_STATUS_QUERY_VAR ] ) ? sanitize_text_field( wp_unslash( $_GET[ self::STOCK_STATUS_QUERY_VAR ] ) ) : '';
@ -236,6 +238,7 @@ final class CollectionStockFilter extends AbstractBlock {
'action' => 'woocommerce/collection-stock-filter::actions.navigate', 'action' => 'woocommerce/collection-stock-filter::actions.navigate',
'selected_items' => $selected_items, 'selected_items' => $selected_items,
'select_type' => $select_type, 'select_type' => $select_type,
'placeholder' => $placeholder_text,
) )
); );
?> ?>

View File

@ -24,15 +24,18 @@ class Dropdown {
// Items should be an array of objects with a label and value property. // Items should be an array of objects with a label and value property.
$items = $props['items'] ?? array(); $items = $props['items'] ?? array();
$default_placeholder = 'single' === $select_type ? __( 'Select an option', 'woocommerce' ) : __( 'Select options', 'woocommerce' );
$placeholder = $props['placeholder'] ?? $default_placeholder;
$dropdown_context = array( $dropdown_context = array(
'selectedItems' => $selected_items, 'selectedItems' => $selected_items,
'isOpen' => false, 'isOpen' => false,
'selectType' => $select_type, 'selectType' => $select_type,
'defaultPlaceholder' => $placeholder,
); );
$action = $props['action'] ?? ''; $action = $props['action'] ?? '';
$namespace = wp_json_encode( array( 'namespace' => 'woocommerce/interactivity-dropdown' ) ); $namespace = wp_json_encode( array( 'namespace' => 'woocommerce/interactivity-dropdown' ) );
$default_placeholder = 'single' === $select_type ? __( 'Select an option', 'woocommerce' ) : __( 'Select options', 'woocommerce' );
ob_start(); ob_start();
?> ?>
@ -41,7 +44,7 @@ class Dropdown {
<div class="wc-interactivity-dropdown__dropdown" tabindex="-1" > <div class="wc-interactivity-dropdown__dropdown" tabindex="-1" >
<div class="wc-interactivity-dropdown__dropdown-selection" id="options-dropdown" tabindex="0" aria-haspopup="listbox"> <div class="wc-interactivity-dropdown__dropdown-selection" id="options-dropdown" tabindex="0" aria-haspopup="listbox">
<span class="wc-interactivity-dropdown__placeholder" data-wc-text="state.placeholderText"> <span class="wc-interactivity-dropdown__placeholder" data-wc-text="state.placeholderText">
<?php echo esc_html( $default_placeholder ); ?> <?php echo esc_html( $placeholder ); ?>
</span> </span>
<?php if ( 'multiple' === $select_type ) { ?> <?php if ( 'multiple' === $select_type ) { ?>
<div class="selected-options"> <div class="selected-options">
@ -51,14 +54,15 @@ class Dropdown {
> >
<div class="wc-interactivity-dropdown__selected-badge"> <div class="wc-interactivity-dropdown__selected-badge">
<span class="wc-interactivity-dropdown__badge-text" data-wc-text="context.item.label"></span> <span class="wc-interactivity-dropdown__badge-text" data-wc-text="context.item.label"></span>
<svg <svg
data-wc-on--click="actions.unselectDropdownItem" data-wc-on--click="actions.unselectDropdownItem"
class="wc-interactivity-dropdown__badge-remove" data-wc-on--click--parent-action="<?php echo esc_attr( $action ); ?>"
width="24" class="wc-interactivity-dropdown__badge-remove"
height="24" width="24"
height="24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
aria-hidden="true" aria-hidden="true"
> >
<path d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"></path> <path d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"></path>
</svg> </svg>
@ -66,27 +70,28 @@ class Dropdown {
</template> </template>
<?php foreach ( $selected_items as $selected ) { ?> <?php foreach ( $selected_items as $selected ) { ?>
<div <div
class="wc-interactivity-dropdown__selected-badge" class="wc-interactivity-dropdown__selected-badge"
data-wc-key="<?php echo esc_attr( $selected['label'] ); ?>" data-wc-key="<?php echo esc_attr( $selected['value'] ); ?>"
data-wc-each-child data-wc-each-child
> >
<span class="wc-interactivity-dropdown__badge-text"><?php echo esc_html( $selected['label'] ); ?></span> <span class="wc-interactivity-dropdown__badge-text"><?php echo esc_html( $selected['label'] ); ?></span>
<svg <svg
data-wc-on--click="actions.unselectDropdownItem" data-wc-on--click="actions.unselectDropdownItem"
class="wc-interactivity-dropdown__badge-remove" data-wc-on--click--parent-action="<?php echo esc_attr( $action ); ?>"
width="24" class="wc-interactivity-dropdown__badge-remove"
height="24" width="24"
height="24"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
aria-hidden="true" aria-hidden="true"
> >
<path d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"></path> <path d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"></path>
</svg> </svg>
</div> </div>
<?php } ?> <?php } ?>
</div> </div>
<?php } ?> <?php } ?>
<span class="wc-interactivity-dropdown__svg-container"> <span class="wc-interactivity-dropdown__svg-container">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="30" height="30" > <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="30" height="30" >
<path d="M17.5 11.6L12 16l-5.5-4.4.9-1.2L12 14l4.5-3.6 1 1.2z" ></path> <path d="M17.5 11.6L12 16l-5.5-4.4.9-1.2L12 14l4.5-3.6 1 1.2z" ></path>
@ -98,9 +103,9 @@ class Dropdown {
foreach ( $items as $item ) : foreach ( $items as $item ) :
$context = array( 'item' => $item ); $context = array( 'item' => $item );
?> ?>
<div <div
class="wc-interactivity-dropdown__dropdown-option" class="wc-interactivity-dropdown__dropdown-option"
role="option" role="option"
tabindex="0" tabindex="0"
data-wc-on--click--select-item="actions.selectDropdownItem" data-wc-on--click--select-item="actions.selectDropdownItem"
data-wc-on--click--parent-action="<?php echo esc_attr( $action ); ?>" data-wc-on--click--parent-action="<?php echo esc_attr( $action ); ?>"