diff --git a/assets/js/base/components/filter-placeholder/index.tsx b/assets/js/base/components/filter-placeholder/index.tsx index 06a06f92101..679e716ba60 100644 --- a/assets/js/base/components/filter-placeholder/index.tsx +++ b/assets/js/base/components/filter-placeholder/index.tsx @@ -1,5 +1,10 @@ +/** + * Internal dependencies + */ +import './style.scss'; + interface FilterTitlePlaceholderProps { - children?: React.ReactChildren; + children?: React.ReactNode; } const FilterTitlePlaceholder = ( { diff --git a/assets/js/base/components/filter-placeholder/style.scss b/assets/js/base/components/filter-placeholder/style.scss index a1a8751ec6a..43ef138eb67 100644 --- a/assets/js/base/components/filter-placeholder/style.scss +++ b/assets/js/base/components/filter-placeholder/style.scss @@ -7,7 +7,12 @@ min-width: 80px; max-width: max-content !important; - .wc-block-stock-filter__title { + &::after { + background-image: linear-gradient(90deg, $gray-400, $gray-200, $gray-400); + } + + .wc-block-stock-filter__title, + .wc-block-price-filter__title { margin: 0; height: 1em; } diff --git a/assets/js/base/components/price-slider/index.tsx b/assets/js/base/components/price-slider/index.tsx index d9f2faa0704..e359a2817c0 100644 --- a/assets/js/base/components/price-slider/index.tsx +++ b/assets/js/base/components/price-slider/index.tsx @@ -33,6 +33,10 @@ export interface PriceSliderProps { * Whether values are loading or not. */ isLoading?: boolean; + /** + * Whether values are updating or not. The update starts when the price slider is changed. + */ + isUpdating?: boolean; /** * Maximum constraint. */ @@ -87,6 +91,7 @@ const PriceSlider = ( { showFilterButton = false, inlineInput = true, isLoading = false, + isUpdating = false, onSubmit = () => void 0, }: PriceSliderProps ): JSX.Element => { const minRange = useRef< HTMLInputElement >( null ); @@ -301,7 +306,6 @@ const PriceSlider = ( { showFilterButton && 'wc-block-price-filter--has-filter-button', showFilterButton && 'wc-block-components-price-slider--has-filter-button', - isLoading && 'is-loading', ! hasValidConstraints && 'is-disabled', ( inlineInput || wrapperWidth <= 300 ) && 'wc-block-components-price-slider--is-input-inline' @@ -326,7 +330,11 @@ const PriceSlider = ( { const slider = (
@@ -388,58 +396,68 @@ const PriceSlider = ( { { ( ! inlineInputAvailable || ! showInputFields ) && slider } { showInputFields && (
- { - if ( value === minPriceInput ) { - return; - } - setMinPriceInput( value ); - } } - onBlur={ priceInputOnBlur } - disabled={ isLoading || ! hasValidConstraints } - value={ minPriceInput } - /> + { ! isUpdating ? ( + { + if ( value === minPriceInput ) { + return; + } + setMinPriceInput( value ); + } } + onBlur={ priceInputOnBlur } + disabled={ isLoading || ! hasValidConstraints } + value={ minPriceInput } + /> + ) : ( +
+ ) } { inlineInputAvailable && slider } - { - if ( value === maxPriceInput ) { - return; - } - setMaxPriceInput( value ); - } } - onBlur={ priceInputOnBlur } - disabled={ isLoading || ! hasValidConstraints } - value={ maxPriceInput } - /> + { ! isUpdating ? ( + { + if ( value === maxPriceInput ) { + return; + } + setMaxPriceInput( value ); + } } + onBlur={ priceInputOnBlur } + disabled={ isLoading || ! hasValidConstraints } + value={ maxPriceInput } + /> + ) : ( +
+ ) }
) } { ! showInputFields && - ! isLoading && + ! isUpdating && Number.isFinite( minPrice ) && Number.isFinite( maxPrice ) && (
@@ -453,32 +471,34 @@ const PriceSlider = ( { />
) } -
- { ( minPrice !== minConstraint || - maxPrice !== maxConstraint ) && ( - { - onChange( [ minConstraint, maxConstraint ] ); - debouncedUpdateQuery(); - } } - screenReaderLabel={ __( - 'Reset price filter', - 'woo-gutenberg-products-block' - ) } - /> - ) } - { showFilterButton && ( - - ) } -
+ { ! isUpdating && ( +
+ { ( minPrice !== minConstraint || + maxPrice !== maxConstraint ) && ( + { + onChange( [ minConstraint, maxConstraint ] ); + debouncedUpdateQuery(); + } } + screenReaderLabel={ __( + 'Reset price filter', + 'woo-gutenberg-products-block' + ) } + /> + ) } + { showFilterButton && ( + + ) } +
+ ) }
); }; diff --git a/assets/js/base/components/price-slider/style.scss b/assets/js/base/components/price-slider/style.scss index b894c810a9d..a445c12f902 100644 --- a/assets/js/base/components/price-slider/style.scss +++ b/assets/js/base/components/price-slider/style.scss @@ -83,6 +83,12 @@ height: 4px; margin: 15px 0; position: relative; + + &.is-loading { + @include placeholder(); + height: em(9px); + border-radius: 0; + } } .wc-block-components-price-slider__range-input-progress { @@ -116,6 +122,12 @@ .wc-block-components-price-slider--is-input-inline & { max-width: 60px; } + + &.is-loading { + @include placeholder(); + border-radius: 0 !important; + width: max-content; + } } } diff --git a/assets/js/blocks/price-filter/block.tsx b/assets/js/blocks/price-filter/block.tsx index 87c5df5cb2d..4e191b55658 100644 --- a/assets/js/blocks/price-filter/block.tsx +++ b/assets/js/blocks/price-filter/block.tsx @@ -9,6 +9,7 @@ import { } from '@woocommerce/base-context/hooks'; import { useCallback, useState, useEffect } from '@wordpress/element'; import PriceSlider from '@woocommerce/base-components/price-slider'; +import FilterTitlePlaceholder from '@woocommerce/base-components/filter-placeholder'; import { useDebouncedCallback } from 'use-debounce'; import PropTypes from 'prop-types'; import { getCurrencyFromPriceResponse } from '@woocommerce/price-format'; @@ -164,6 +165,8 @@ const PriceFilterBlock = ( { setMinPriceQuery, ] ); + const [ isUpdating, setIsUpdating ] = useState( isLoading ); + // Updates the query based on slider values. const onSubmit = useCallback( ( newMinPrice, newMaxPrice ) => { @@ -206,6 +209,7 @@ const PriceFilterBlock = ( { // Callback when slider or input fields are changed. const onChange = useCallback( ( prices ) => { + setIsUpdating( true ); if ( prices[ 0 ] !== minPrice ) { setMinPrice( prices[ 0 ] ); } @@ -303,13 +307,26 @@ const PriceFilterBlock = ( { const TagName = `h${ attributes.headingLevel }` as keyof JSX.IntrinsicElements; + if ( ! isLoading && isUpdating ) { + setIsUpdating( false ); + } + + const heading = ( + + { attributes.heading } + + ); + + const filterHeading = + isLoading && isUpdating ? ( + { heading } + ) : ( + heading + ); + return ( <> - { ! isEditor && attributes.heading && ( - - { attributes.heading } - - ) } + { ! isEditor && attributes.heading && filterHeading }
onSubmit( minPrice, maxPrice ) } isLoading={ isLoading } + isUpdating={ isUpdating } />
diff --git a/assets/js/blocks/price-filter/style.scss b/assets/js/blocks/price-filter/style.scss index c67de60650f..1aa0bc92f67 100644 --- a/assets/js/blocks/price-filter/style.scss +++ b/assets/js/blocks/price-filter/style.scss @@ -23,17 +23,23 @@ border-color: inherit; } - .wc-block-price-filter__controls { border-radius: inherit; border-color: inherit; - // Force inherting style is required for global style. + // Force inheriting style is required for global style. input { border-radius: inherit !important; border-color: inherit !important; border-style: solid; } + + .input-loading { + @include placeholder(); + border-radius: 0; + height: em(32px); + width: em(90px); + } } .editor-styles-wrapper .wc-block-price-filter__button.wc-block-components-price-slider__button,