[Experimental] Product Filters Redesign: Attribute Filters: List style (#50340)

* update: new simplified markup and style

* add: editor preview

* add: show 15 items initially

* fix: render li class for checked items

* chore: naming

* chore: changelog

* update: set some preview items checked for styling purpose

* fix: only show show more button when the list is long

* chore: lint

* fix: e2e tests
This commit is contained in:
Tung Du 2024-08-09 16:17:14 +07:00 committed by GitHub
parent 7e691560d4
commit aaf3046eb6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 236 additions and 112 deletions

View File

@ -1,29 +0,0 @@
/**
* External dependencies
*/
import FilterElementLabel from '@woocommerce/base-components/filter-element-label';
import { CheckboxList } from '@woocommerce/blocks-components';
import { AttributeTerm } from '@woocommerce/types';
type Props = {
attributeTerms: AttributeTerm[];
showCounts?: boolean;
};
export const AttributeCheckboxList = ( {
attributeTerms,
showCounts,
}: Props ) => (
<CheckboxList
className="wc-block-attribute-filter style-list"
onChange={ () => null }
options={ attributeTerms.map( ( term ) => ( {
label: (
<FilterElementLabel
name={ term.name }
count={ showCounts ? term.count : null }
/>
),
value: term.slug,
} ) ) }
/>
);

View File

@ -0,0 +1,68 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
/**
* Ideally, this component should belong to packages/interactivity-components.
* But we haven't export it as a packages so we place it here temporary.
*/
export const Preview = ( { items }: { items: string[] } ) => {
const threshold = 15;
const isLongList = items.length > threshold;
return (
<div className="wc-block-interactivity-components-checkbox-list">
<ul className="wc-block-interactivity-components-checkbox-list__list">
{ ( isLongList ? items.slice( 0, threshold ) : items ).map(
( item, index ) => (
<li
key={ index }
className="wc-block-interactivity-components-checkbox-list__item"
>
<label
htmlFor={ `interactive-checkbox-${ index }` }
className=" wc-block-interactivity-components-checkbox-list__label"
>
<span className="wc-block-interactive-components-checkbox-list__input-wrapper">
<span className="wc-block-interactivity-components-checkbox-list__input-wrapper">
<input
name={ `interactive-checkbox-${ index }` }
type="checkbox"
className="wc-block-interactivity-components-checkbox-list__input"
// Harded coded some checked items for styling purpose.
defaultChecked={ [
1, 3, 4,
].includes( index ) }
/>
<svg
className="wc-block-interactivity-components-checkbox-list__mark"
viewBox="0 0 10 8"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.25 1.19922L3.75 6.69922L1 3.94922"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</span>
</span>
<span className="wc-block-interactivity-components-checkbox-list__text">
{ item }
</span>
</label>
</li>
)
) }
</ul>
{ isLongList && (
<span className="wc-block-interactivity-components-checkbox-list__show-more">
<small>{ __( 'Show more…', 'woocommerce' ) }</small>
</span>
) }
</div>
);
};

View File

@ -24,10 +24,10 @@ import { EditProps, isAttributeCounts } from './types';
import { NoAttributesPlaceholder } from './components/placeholder';
import { getAttributeFromId } from './utils';
import { Inspector } from './components/inspector';
import { AttributeCheckboxList } from './components/attribute-checkbox-list';
import { AttributeDropdown } from './components/attribute-dropdown';
import { attributeOptionsPreview } from './constants';
import './style.scss';
import { Preview as CheckboxListPreview } from './components/checkbox-list-editor';
const ATTRIBUTES = getSetting< AttributeSetting[] >( 'attributes', [] );
@ -184,9 +184,12 @@ const Edit = ( props: EditProps ) => {
return (
<Wrapper>
<Disabled>
<AttributeCheckboxList
showCounts={ showCounts }
attributeTerms={ attributeOptionsPreview }
<CheckboxListPreview
items={ attributeOptionsPreview.map( ( term ) => {
if ( showCounts )
return `${ term.name } (${ term.count })`;
return term.name;
} ) }
/>
</Disabled>
</Wrapper>
@ -240,11 +243,14 @@ const Edit = ( props: EditProps ) => {
}
/>
) : (
<AttributeCheckboxList
showCounts={ showCounts }
attributeTerms={ attributeOptions }
<CheckboxListPreview
items={ attributeOptions.map( ( term ) => {
if ( showCounts )
return `${ term.name } (${ term.count })`;
return term.name;
} ) }
/>
) }{ ' ' }
) }
</Disabled>
</Wrapper>
);

View File

@ -20,11 +20,17 @@ export type CheckboxListContext = {
value: string;
checked: boolean;
}[];
showAll: boolean;
};
store( 'woocommerce/interactivity-checkbox-list', {
state: {},
actions: {
showAllItems: () => {
const context = getContext< CheckboxListContext >();
context.showAll = true;
},
selectCheckboxItem: ( event: HTMLElementEvent< HTMLInputElement > ) => {
const context = getContext< CheckboxListContext >();
const value = event.target.value;

View File

@ -1,33 +1,69 @@
// Import styles we need to render the checkbox list and checkbox control.
@import "../../../packages/components/checkbox-list/style";
@import "../../../packages/components/checkbox-control/style";
.wc-block-stock-filter-list {
:where(.wc-block-interactivity-components-checkbox-list__list) {
list-style: none outside;
margin: 0;
li {
label {
cursor: pointer;
}
input {
cursor: pointer;
display: inline-block;
}
}
.wc-block-components-checkbox-list {
margin: 0;
li {
label {
cursor: pointer;
}
input {
cursor: pointer;
display: inline-block;
}
}
}
padding: 0;
}
.wc-block-interactivity-components-checkbox-list__item.hidden {
display: none;
}
:where(.wc-block-interactivity-components-checkbox-list__label) {
align-items: center;
display: flex;
gap: 0.625em;
}
.wc-block-interactivity-components-checkbox-list__item .wc-block-interactivity-components-checkbox-list__label {
margin-bottom: 0;
}
:where(.wc-block-interactivity-components-checkbox-list__input-wrapper) {
display: block;
position: relative;
}
:where(.wc-block-interactivity-components-checkbox-list__input) {
appearance: none;
background: currentColor;
border-radius: 2px;
border: none;
color: inherit;
display: block;
font-size: inherit;
height: 1em;
margin: 0;
opacity: 0.1;
width: 1em;
}
.wc-block-interactivity-components-checkbox-list__input:checked + .wc-block-interactivity-components-checkbox-list__mark {
display: block;
pointer-events: none;
}
.wc-block-interactivity-components-checkbox-list__input:focus {
outline-width: 1px;
}
:where(.wc-block-interactivity-components-checkbox-list__mark) {
box-sizing: border-box;
display: none;
height: 1em;
left: 0;
padding: 0.2em;
position: absolute;
top: 0;
width: 1em;
}
:where(.wc-block-interactivity-components-checkbox-list__show-more) {
cursor: pointer;
text-decoration: underline;
}
.wc-block-interactivity-components-checkbox-list__show-more.hidden {
display: none;
}

View File

@ -50,7 +50,7 @@ test.describe( 'Product Filter: Attribute Block', () => {
await page.goto( '/shop' );
const attributes = page.locator(
'.wc-block-components-checkbox__label'
'.wc-block-interactivity-components-checkbox-list__label'
);
await expect( attributes ).toHaveCount( 5 );
@ -156,7 +156,7 @@ test.describe( 'Product Filter: Attribute Block', () => {
await page.goto( '/shop' );
const attributes = page.locator(
'.wc-block-components-checkbox__label'
'.wc-block-interactivity-components-checkbox-list__label'
);
await expect( attributes ).toHaveCount( 5 );

View File

@ -37,7 +37,7 @@ test.describe( 'Product Filter: Stock Status Block', () => {
await page.goto( '/shop' );
const stockStatuses = page.locator(
'.wc-block-components-checkbox__label'
'.wc-block-interactivity-components-checkbox-list__label'
);
await expect( stockStatuses ).toHaveCount( 2 );

View File

@ -0,0 +1,5 @@
Significance: patch
Type: update
Comment: Experimental: Product Filters: Attribute Filter: Update List style

View File

@ -28,50 +28,82 @@ class CheckboxList {
$items = $props['items'] ?? array();
$checkbox_list_context = array( 'items' => $items );
$on_change = $props['on_change'] ?? '';
$namespace = wp_json_encode( array( 'namespace' => 'woocommerce/interactivity-checkbox-list' ), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP );
$namespace = wp_json_encode( array( 'namespace' => 'woocommerce/interactivity-checkbox-list' ), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP );
$checked_items = array_filter(
$items,
function ( $item ) {
return $item['checked'];
}
);
$show_initially = $props['show_initially'] ?? 15;
$remaining_initial_unchecked = count( $checked_items ) > $show_initially ? count( $checked_items ) : $show_initially - count( $checked_items );
$count = 0;
ob_start();
?>
<div data-wc-interactive='<?php echo esc_attr( $namespace ); ?>'>
<div data-wc-context='<?php echo wp_json_encode( $checkbox_list_context, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ); ?>' >
<div class="wc-block-stock-filter style-list">
<ul class="wc-block-components-checkbox-list">
<?php foreach ( $items as $item ) { ?>
<?php
$item['id'] = $item['id'] ?? uniqid( 'checkbox-' );
// translators: %s: checkbox label.
$i18n_label = sprintf( __( 'Checkbox: %s', 'woocommerce' ), $item['aria_label'] ?? '' );
?>
<li data-wc-key="<?php echo esc_attr( $item['id'] ); ?>">
<div class="wc-block-components-checkbox">
<label for="<?php echo esc_attr( $item['id'] ); ?>">
<input
id="<?php echo esc_attr( $item['id'] ); ?>"
class="wc-block-components-checkbox__input"
type="checkbox"
aria-invalid="false"
aria-label="<?php echo esc_attr( $i18n_label ); ?>"
data-wc-on--change--select-item="actions.selectCheckboxItem"
data-wc-on--change--parent-action="<?php echo esc_attr( $on_change ); ?>"
value="<?php echo esc_attr( $item['value'] ); ?>"
<?php checked( $item['checked'], 1 ); ?>
>
<svg class="wc-block-components-checkbox__mark" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 20">
<path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"></path>
</svg>
<span class="wc-block-components-checkbox__label">
<?php // The label can be HTML, so we don't want to escape it. ?>
<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php echo $item['label']; ?>
</span>
</label>
</div>
</li>
<?php } ?>
</ul>
</div>
</div>
<div
class="wc-block-interactivity-components-checkbox-list"
data-wc-interactive='<?php echo esc_attr( $namespace ); ?>'
data-wc-context='<?php echo wp_json_encode( $checkbox_list_context, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ); ?>'
>
<ul class="wc-block-interactivity-components-checkbox-list__list">
<?php foreach ( $items as $item ) { ?>
<?php
$item['id'] = $item['id'] ?? uniqid( 'checkbox-' );
// translators: %s: checkbox label.
$i18n_label = sprintf( __( 'Checkbox: %s', 'woocommerce' ), $item['aria_label'] ?? '' );
?>
<li
data-wc-key="<?php echo esc_attr( $item['id'] ); ?>"
<?php
if ( ! $item['checked'] ) :
if ( $count >= $remaining_initial_unchecked ) :
?>
class="wc-block-interactivity-components-checkbox-list__item hidden"
data-wc-class--hidden="!context.showAll"
<?php else : ?>
<?php ++$count; ?>
<?php endif; ?>
<?php endif; ?>
class="wc-block-interactivity-components-checkbox-list__item"
>
<label
class="wc-block-interactivity-components-checkbox-list__label"
for="<?php echo esc_attr( $item['id'] ); ?>"
>
<span class="wc-block-interactivity-components-checkbox-list__input-wrapper">
<input
id="<?php echo esc_attr( $item['id'] ); ?>"
class="wc-block-interactivity-components-checkbox-list__input"
type="checkbox"
aria-invalid="false"
aria-label="<?php echo esc_attr( $i18n_label ); ?>"
data-wc-on--change--select-item="actions.selectCheckboxItem"
data-wc-on--change--parent-action="<?php echo esc_attr( $on_change ); ?>"
value="<?php echo esc_attr( $item['value'] ); ?>"
<?php checked( $item['checked'], 1 ); ?>
>
<svg class="wc-block-interactivity-components-checkbox-list__mark" viewBox="0 0 10 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.25 1.19922L3.75 6.69922L1 3.94922" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</span>
<span class="wc-block-interactivity-components-checkbox-list__text">
<?php echo wp_kses_post( $item['label'] ); ?>
</span>
</label>
</li>
<?php } ?>
</ul>
<?php if ( count( $items ) > $show_initially ) : ?>
<span
role="button"
class="wc-block-interactivity-components-checkbox-list__show-more"
data-wc-class--hidden="context.showAll"
data-wc-on--click="actions.showAllItems"
>
<small role="presentation"><?php echo esc_html__( 'Show more...', 'woocommerce' ); ?></small>
</span>
<?php endif; ?>
</div>
<?php
return ob_get_clean();