Product Collection - Add `Created` filter in inspector controls (https://github.com/woocommerce/woocommerce-blocks/pull/11562)

* Add time frame filter to Product Collection block

This commit introduces the ability to filter products within the Product Collection block by a specified time frame. The changes include:

- A new 'timeFrame' property added to the DEFAULT_QUERY constant in constants.ts, initialized as null, allowing for the storage of time frame data.
- Creation of a new component `CreatedControl` in created-control.tsx that provides UI elements for selecting a time frame filter.
- Inclusion of `CreatedControl` in the Product Collection Inspector Controls.
- Expansion of the ProductCollectionQuery interface in types.ts to include a 'timeFrame' attribute.
- Addition of the 'timeFrame' parameter handling within the ProductCollection PHP class to construct and execute the date query based on the provided time frame.

The addition of the time frame filter offers enhanced flexibility in presenting products and allows users to dynamically segment their product lists based on product creation dates.

* Refactor: Standardize 'timeFrame' to be 'undefined' instead of 'null'

This commit includes a refactoring that changes the initialization and reset values for the `timeFrame` property from `null` to `undefined`. This standardization affects the constants, type definitions, and the handling of the `timeFrame` property in both the inspector controls and the PHP backend.

* Switch date query to use post_date_gmt for DST consistency

This commit changes the column reference in the date query from 'post_date' to 'post_date_gmt'. This update ensures that the product collection filtering is based on Coordinated Universal Time (UTC) rather than local time, which can be affected by Daylight Saving Time (DST) shifts. The modification will lead to more consistent and reliable behavior across different time zones and during DST changes.

* Capitalize toggle group labels

The following adjustments have been made:
- Introduced a constant `uppercaseStyle` to store the `{ textTransform: 'uppercase' }` style.
- Applied `uppercaseStyle` to both the 'IN' and 'NOT IN' toggle options to ensure label text is consistently uppercase.
- Updated the 'Not in' label text to uppercase ('NOT IN') to match the newly applied style.

These changes ensure that the toggle labels align with the design guidelines that call for uppercase styling in control elements.

* Make first letter of first work capital

* Rename to Within & Before

* Update i18n for Product Collection query operators

This commit updates the internationalization (i18n) for the Product Collection query operators in the 'Created' control component of the WooCommerce Blocks plugin. It replaces the '__' function with '_x' for translation and provides context comments for better translation handling. This improvement enhances the localization of the query operators for better multilingual support.
This commit is contained in:
Manish Menaria 2023-11-08 14:32:49 +05:30 committed by GitHub
parent 3f98449f90
commit b177aa9048
5 changed files with 169 additions and 1 deletions

View File

@ -49,6 +49,7 @@ export const DEFAULT_QUERY: ProductCollectionQuery = {
woocommerceStockStatus: getDefaultStockStatuses(), woocommerceStockStatus: getDefaultStockStatuses(),
woocommerceAttributes: [], woocommerceAttributes: [],
woocommerceHandPickedProducts: [], woocommerceHandPickedProducts: [],
timeFrame: undefined,
}; };
export const DEFAULT_ATTRIBUTES: Partial< ProductCollectionAttributes > = { export const DEFAULT_ATTRIBUTES: Partial< ProductCollectionAttributes > = {
@ -86,4 +87,5 @@ export const DEFAULT_FILTERS: Partial< ProductCollectionQuery > = {
woocommerceAttributes: [], woocommerceAttributes: [],
taxQuery: DEFAULT_QUERY.taxQuery, taxQuery: DEFAULT_QUERY.taxQuery,
woocommerceHandPickedProducts: [], woocommerceHandPickedProducts: [],
timeFrame: undefined,
}; };

View File

@ -0,0 +1,114 @@
/**
* External dependencies
*/
import { __, _x } from '@wordpress/i18n';
import {
Flex,
FlexItem,
RadioControl,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - Ignoring because `__experimentalToggleGroupControl` is not yet in the type definitions.
// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
__experimentalToggleGroupControl as ToggleGroupControl,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - Ignoring because `__experimentalToggleGroupControlOption` is not yet in the type definitions.
// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
__experimentalToggleGroupControlOption as ToggleGroupControlOption,
// @ts-expect-error Using experimental features
// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
__experimentalToolsPanelItem as ToolsPanelItem,
} from '@wordpress/components';
/**
* Internal dependencies
*/
import { ETimeFrameOperator, QueryControlProps } from '../types';
const CreatedControl = ( props: QueryControlProps ) => {
const { query, setQueryAttribute } = props;
const { timeFrame } = query;
return (
<ToolsPanelItem
label={ __( 'Created', 'woo-gutenberg-products-block' ) }
hasValue={ () => timeFrame?.operator && timeFrame?.value }
onDeselect={ () => {
setQueryAttribute( {
timeFrame: undefined,
} );
} }
>
<Flex direction="column" gap={ 3 }>
<FlexItem>
<ToggleGroupControl
label={ __(
'Created',
'woo-gutenberg-products-block'
) }
isBlock
onChange={ ( value: ETimeFrameOperator ) => {
setQueryAttribute( {
timeFrame: {
...timeFrame,
operator: value,
},
} );
} }
value={ timeFrame?.operator || ETimeFrameOperator.IN }
>
<ToggleGroupControlOption
value={ ETimeFrameOperator.IN }
label={ _x(
'Within',
'Product Collection query operator',
'woo-gutenberg-products-block'
) }
/>
<ToggleGroupControlOption
value={ ETimeFrameOperator.NOT_IN }
label={ _x(
'Before',
'Product Collection query operator',
'woo-gutenberg-products-block'
) }
/>
</ToggleGroupControl>
</FlexItem>
<FlexItem>
<RadioControl
onChange={ ( value: string ) => {
setQueryAttribute( {
timeFrame: {
operator: ETimeFrameOperator.IN,
...timeFrame,
value,
},
} );
} }
options={ [
{
label: 'last 24 hours',
value: '-1 day',
},
{
label: 'last 7 days',
value: '-7 days',
},
{
label: 'last 30 days',
value: '-30 days',
},
{
label: 'last 3 months',
value: '-3 months',
},
] }
selected={ timeFrame?.value }
/>
</FlexItem>
</Flex>
</ToolsPanelItem>
);
};
export default CreatedControl;

View File

@ -39,6 +39,7 @@ import AttributesControl from './attributes-control';
import TaxonomyControls from './taxonomy-controls'; import TaxonomyControls from './taxonomy-controls';
import HandPickedProductsControl from './hand-picked-products-control'; import HandPickedProductsControl from './hand-picked-products-control';
import LayoutOptionsControl from './layout-options-control'; import LayoutOptionsControl from './layout-options-control';
import CreatedControl from './created-control';
const ProductCollectionInspectorControls = ( const ProductCollectionInspectorControls = (
props: BlockEditProps< ProductCollectionAttributes > props: BlockEditProps< ProductCollectionAttributes >
@ -98,6 +99,7 @@ const ProductCollectionInspectorControls = (
<KeywordControl { ...queryControlProps } /> <KeywordControl { ...queryControlProps } />
<AttributesControl { ...queryControlProps } /> <AttributesControl { ...queryControlProps } />
<TaxonomyControls { ...queryControlProps } /> <TaxonomyControls { ...queryControlProps } />
<CreatedControl { ...queryControlProps } />
</ToolsPanel> </ToolsPanel>
) : null } ) : null }
<ProductCollectionFeedbackPrompt /> <ProductCollectionFeedbackPrompt />

View File

@ -28,6 +28,16 @@ export interface ProductCollectionDisplayLayout {
shrinkColumns?: boolean; shrinkColumns?: boolean;
} }
export enum ETimeFrameOperator {
IN = 'in',
NOT_IN = 'not-in',
}
export interface TimeFrame {
operator?: ETimeFrameOperator;
value?: string;
}
export interface ProductCollectionQuery { export interface ProductCollectionQuery {
exclude: string[]; exclude: string[];
inherit: boolean | null; inherit: boolean | null;
@ -39,6 +49,7 @@ export interface ProductCollectionQuery {
postType: string; postType: string;
search: string; search: string;
taxQuery: Record< string, number[] >; taxQuery: Record< string, number[] >;
timeFrame: TimeFrame | undefined;
woocommerceOnSale: boolean; woocommerceOnSale: boolean;
/** /**
* Filter products by their stock status. * Filter products by their stock status.

View File

@ -207,6 +207,7 @@ class ProductCollection extends AbstractBlock {
$stock_status = $request->get_param( 'woocommerceStockStatus' ); $stock_status = $request->get_param( 'woocommerceStockStatus' );
$product_attributes = $request->get_param( 'woocommerceAttributes' ); $product_attributes = $request->get_param( 'woocommerceAttributes' );
$handpicked_products = $request->get_param( 'woocommerceHandPickedProducts' ); $handpicked_products = $request->get_param( 'woocommerceHandPickedProducts' );
$time_frame = $request->get_param( 'timeFrame' );
// This argument is required for the tests to PHP Unit Tests to run correctly. // This argument is required for the tests to PHP Unit Tests to run correctly.
// Most likely this argument is being accessed in the test environment image. // Most likely this argument is being accessed in the test environment image.
$args['author'] = ''; $args['author'] = '';
@ -219,6 +220,7 @@ class ProductCollection extends AbstractBlock {
'stock_status' => $stock_status, 'stock_status' => $stock_status,
'product_attributes' => $product_attributes, 'product_attributes' => $product_attributes,
'handpicked_products' => $handpicked_products, 'handpicked_products' => $handpicked_products,
'timeFrame' => $time_frame,
) )
); );
} }
@ -305,6 +307,7 @@ class ProductCollection extends AbstractBlock {
$product_attributes = $query['woocommerceAttributes'] ?? []; $product_attributes = $query['woocommerceAttributes'] ?? [];
$taxonomies_query = $this->get_filter_by_taxonomies_query( $query['tax_query'] ?? [] ); $taxonomies_query = $this->get_filter_by_taxonomies_query( $query['tax_query'] ?? [] );
$handpicked_products = $query['woocommerceHandPickedProducts'] ?? []; $handpicked_products = $query['woocommerceHandPickedProducts'] ?? [];
$time_frame = $query['timeFrame'] ?? null;
$final_query = $this->get_final_query_args( $final_query = $this->get_final_query_args(
$common_query_values, $common_query_values,
@ -315,6 +318,7 @@ class ProductCollection extends AbstractBlock {
'product_attributes' => $product_attributes, 'product_attributes' => $product_attributes,
'taxonomies_query' => $taxonomies_query, 'taxonomies_query' => $taxonomies_query,
'handpicked_products' => $handpicked_products, 'handpicked_products' => $handpicked_products,
'timeFrame' => $time_frame,
), ),
$is_exclude_applied_filters $is_exclude_applied_filters
); );
@ -338,11 +342,12 @@ class ProductCollection extends AbstractBlock {
$attributes_query = $this->get_product_attributes_query( $query['product_attributes'] ); $attributes_query = $this->get_product_attributes_query( $query['product_attributes'] );
$taxonomies_query = $query['taxonomies_query'] ?? []; $taxonomies_query = $query['taxonomies_query'] ?? [];
$tax_query = $this->merge_tax_queries( $visibility_query, $attributes_query, $taxonomies_query ); $tax_query = $this->merge_tax_queries( $visibility_query, $attributes_query, $taxonomies_query );
$date_query = $this->get_date_query( $query['timeFrame'] ?? [] );
// We exclude applied filters to generate product ids for the filter blocks. // We exclude applied filters to generate product ids for the filter blocks.
$applied_filters_query = $is_exclude_applied_filters ? [] : $this->get_queries_by_applied_filters(); $applied_filters_query = $is_exclude_applied_filters ? [] : $this->get_queries_by_applied_filters();
$merged_query = $this->merge_queries( $common_query_values, $orderby_query, $on_sale_query, $stock_query, $tax_query, $applied_filters_query ); $merged_query = $this->merge_queries( $common_query_values, $orderby_query, $on_sale_query, $stock_query, $tax_query, $applied_filters_query, $date_query );
return $this->filter_query_to_only_include_ids( $merged_query, $handpicked_products ); return $this->filter_query_to_only_include_ids( $merged_query, $handpicked_products );
} }
@ -959,4 +964,38 @@ class ProductCollection extends AbstractBlock {
), ),
); );
} }
/**
* Constructs a date query for product filtering based on a specified time frame.
*
* @param array $time_frame {
* Associative array with 'operator' (in or not-in) and 'value' (date string).
*
* @type string $operator Determines the inclusion or exclusion of the date range.
* @type string $value The date around which the range is applied.
* }
* @return array Date query array; empty if parameters are invalid.
*/
private function get_date_query( array $time_frame ) : array {
// Validate time_frame elements.
if ( empty( $time_frame['operator'] ) || empty( $time_frame['value'] ) ) {
return array();
}
// Determine the query operator based on the 'operator' value.
$query_operator = 'in' === $time_frame['operator'] ? 'after' : 'before';
// Construct and return the date query.
return array(
'date_query' => array(
array(
'column' => 'post_date_gmt',
$query_operator => $time_frame['value'],
'inclusive' => true,
),
),
);
}
} }