Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Product Collection: Add Taxonomy filters to sidebar settings #9634

Merged
merged 51 commits into from
Jun 2, 2023

Conversation

imanish003
Copy link
Contributor

@imanish003 imanish003 commented May 29, 2023

⚠️ This PR will be merged after PR #9600, as this branch is checked out from the branch of PR #9600.

This PR adds Taxonomy filters to sidebar settings.

image image

Fixes #9364

Testing

  1. Create a new post
  2. Add Product Collection block
  3. With Product Collection block selected, check the sidebar for the Taxonomy filter i.e., product categories, tags etc. These will be displayed as shown in images below:
image image
  1. Make sure that you have products with categories and tags. If not, add categories or tags to some products.
  2. Now select a product category or tags. After the selection, verify that changes reflect immediately in the Editor view.
  3. Save and publish the post. After publishing, you can just navigate to the Frontend and verify that the changes you made in the Editor are accurately reflected.
  4. Return to the Editor and validate the Reset all functionality of filters:
    • Clicking the Reset all button resets your selected filters.
    • The Reset all button can be found as shown in image below:
image
  • Do not include in the Testing Notes

WooCommerce Visibility

  • WooCommerce Core
  • Feature plugin
  • Experimental

Changelog

Product Collection: Add Taxonomy filters to sidebar settings

imanish003 and others added 30 commits May 15, 2023 16:57
- `InspectorControls` from './inspector-controls' is now imported in `edit.tsx` and used in the returned JSX of `Edit` function.
- A new file `columns-control.tsx` is added under 'product-collection' block's 'inspector-controls' directory which exports a `ColumnsControl` component. This component uses `RangeControl` from '@wordpress/components' to control the number of columns in the product collection display layout when the layout type is 'flex'.
- The types file (`types.ts`) for 'product-collection' block is updated. The `Attributes` interface is renamed to `ProductCollectionAttributes` and the `ProductCollectionContext` interface is removed. The `ProductCollectionAttributes` now includes 'queryContext', 'templateSlug', and 'displayLayout' properties.
This commit simplifies the fallback return value of the ColumnsControl component. Instead of returning an empty fragment (<> </>), it now returns null when the condition isn't met. This change improves readability and aligns with best practices for conditional rendering in React.
This commit adds a new 'Order By' control to the product collection inspector. The control allows users to specify the order of products in a collection by various attributes such as title and date. To support this, a new component 'OrderByControl' has been created and included in the product collection inspector. Additionally, the types for 'order' and 'orderBy' attributes have been updated and exported for reuse.
The main changes include:
1. Added a new property 'isProductCollectionBlock' in the block.json to denote if a block is a product collection block.
2. In the ProductCollection PHP class, a new initialization function has been defined to hook into the WordPress lifecycle, register the block, and update the query based on this block.
3. Added methods to manage query parameters for both frontend rendering and the Editor.
4. Expanded allowed 'collection_params' for the REST API to include custom 'orderby' values.
5. Defined a function to build the query based on block attributes, filters, and global WP_Query.
6. Created utility functions to handle complex query operations such as merging queries, handling custom sort values, and merging arrays recursively.

These improvements allow for more flexible and robust handling of product collections in both the front-end display and the WordPress editor. It also extends support for custom 'orderby' values in the REST API, which allows for more advanced sorting options in product collections.
…ction block

This commit introduces several changes to the product collection block.
- First, it adds a new 'on sale' filter that can be used to display only the products that are currently on sale.
- It also refactors the settings management in the product collection block to use the experimental ToolsPanel component from WordPress, which provides a more flexible and intuitive way to manage block settings.
- It moves the 'Columns' control into the ToolsPanel, along with the 'Order by' control.
- A new utility function `setQueryAttribute` is introduced to simplify setting nested query parameters.
- The structure of the `ProductCollectionAttributes` and `ProductCollectionQuery` types have been adjusted to accommodate the changes.
- Finally, it makes corresponding changes in the PHP part to handle the new 'on sale' query parameter.

This should enhance the flexibility and user-friendliness of the product collection block.
This commit introduces a stock status filter to the WooCommerce product collection block.

The changes include:
1. Added the ability to filter products based on their stock status within the 'product-collection' block. A new stock status control is created within the inspector-controls of the block.
2. A new 'get_stock_status_query' function is introduced in 'ProductCollection.php' which returns a query for products depending on their stock status.

Please note that the stock status filter will only appear in the experimental build for now.
This commit refactors the Stock Status control. The changes aim to improve the code organization and make the behavior of the component more explicit.

The key modifications are:
1. Moved stock status related constants and functions from `inspector-controls/utils.tsx` to `inspector-controls/constants.ts`. This is done to ensure that all constants and similar utility functions are organized in one place.
2. Updated `product-collection/index.tsx` to import `getDefaultStockStatuses` from `inspector-controls/constants` instead of `inspector-controls/utils`.
3. Updated `stock-status-control.tsx` to determine whether the stock status has value or not by comparing with the default stock statuses using `fastDeepEqual`. If the stock status control is deselected, it resets the stock status to the default statuses.

These changes do not introduce any new functionalities, but improve the readability and maintainability of the code.
This commit introduces a keyword search functionality to the Product Collection block. The update is aimed to provide users with more flexibility and precision in product collection queries.

Key changes:
1. Introduced a new file `keyword-control.tsx` that creates a Keyword Control component. This component includes a TextControl field that allows inputting a search keyword. The keyword search is debounced to prevent unnecessary queries during input and updates the block's attributes accordingly.
2. Modified `inspector-controls/index.tsx` to include the KeywordControl in the ToolsPanel for the block's filters.
3. Adjusted `ProductCollection.php` to include the keyword search in the product query array.

With these changes, users can now search for products by keyword in the Product Collection block.
- This commit introduces the ability to filter products by attributes in ProductCollection block.
- A new `woocommerceAttributes` key was added to the `block.json` file and the `ProductCollectionQuery` type. Also, a new file `attributes-control.tsx` was created, providing the UI component for the attribute filter control in the editor.
- In addition, updates were made to the `ProductCollection.php` file in the backend to support filtering products by attributes, and the tax query was updated to include attribute queries.
- Lastly, the `ProductCollectionInspectorControls` was updated to include the `AttributesControl` component, thus enabling users to filter products by attributes in the block editor."`
The primary changes include:
1. `taxQuery` field in the `ProductCollectionAttributes` was changed from a string to an object in `assets/js/blocks/product-collection/types.ts` and `assets/js/blocks/product-collection/constants.ts`, accommodating the ability to query products by taxonomy terms.

2. `assets/js/blocks/product-collection/inspector-controls/utils.tsx` was moved to `assets/js/blocks/product-collection/utils.tsx` to make it available for broader use.

3. New component `TaxonomyControls` was created in `assets/js/blocks/product-collection/inspector-controls/taxonomy-controls.tsx`, which is included in `assets/js/blocks/product-collection/inspector-controls/index.tsx`. This new control allows users to filter products in the block by their taxonomy terms.

4. Updated the block's inspector controls in `assets/js/blocks/product-collection/inspector-controls/index.tsx` to use the new `TaxonomyControls` component.

Please note that the TaxonomyControls component uses experimental features of WordPress's FormTokenField. As a result, a comment has been added to disable eslint warnings regarding the use of experimental APIs.
Copy link
Contributor

@Aljullu Aljullu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating and rebasing this PR, @imanish003! This is looking good, but I found one issue. There is the possibility that two subcategories might have the same name:

imatge

When that happens, there is a JS warning in the console: Warning: Encountered two children with the same key, Accessories. And it's impossible for the user to know which category is which.

One idea to fix this could be to append the slug when there are two or more terms with the same name, as we know slugs are unique. What do you think? Another idea would be to append the parent category name, but I guess that complicates the code a lot and there is also the possibility that parent categories might have the same name, so it doesn't really solve the problem.

src/BlockTypes/ProductCollection.php Outdated Show resolved Hide resolved
<div className="wc-block-editor-product-collection-inspector__taxonomy-control">
<FormTokenField
label={ taxonomy.name }
value={ value }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused by this, but if I'm not wrong, value can be directly inferred from existingTerms, right? In that case, we shouldn't need to store value in the state. Ie, we could do something like this:

Suggested change
value={ value }
value={ ( existingTerms || [] ).map( ( { id, name } ) => ( {
id,
value: name,
} ) ) }

and then get rid of the state value and the useEffect() associated with it. (I might have missed something, though 😅)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for using useState is that when a user adds a new term, existingTerms becomes empty. Therefore, if we were to use existingTerms as you suggested, the input would become empty for a few seconds while the existingTerms are being fetched. However, I will take a look at this and see if any improvements can be made. Thanks for pointing this out 🙌🏻

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oooh, you are right, I see what you mean. I wonder if we could use usePrevious(), however. IMO it seems like a better hook for this use case. So we would use existingTerms by default, but if it's empty, we would use previousExistingTerms to make sure the input field doesn't become empty. What do you think? 🤔

Copy link
Contributor Author

@imanish003 imanish003 Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I did consider using usePrevious(), but the implementation would be somewhat complex. For instance, if there is one term selected and the user select another term, the value of existingTerms would look like this:

image

As you can see in the console.log output above. There are multiple null and empty values before the second term is added to existing terms. This complicates determining when we should use the usePrevious value. Do you see the point? However, I will experiment with it today to see if I can figure out how to use usePrevious, which may require some code refactoring.

The first thing I will try is useEntityRecords hook.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for future reference: heads-up that usePrevious() accepts a validation function so you can determine whether it updates or not. So in this case, it should be possible to make it so it doesn't update in certain circumstances.

the taxonomy controls have been enhanced in the following ways:
1. Modified the BASE_QUERY object to include 'slug' in the '_fields' property. This will ensure that the 'slug' of the taxonomy term is fetched along with its 'id' and 'name'.

2. Added a 'slug' property to the Term type to store the 'slug' of each term.

3. Updated the useEffect hook inside the TaxonomyItem function to generate suggestions based on search results. The suggestions now include the 'slug' of a term if the term's name is not unique. This change will help users distinguish between terms with the same name but different slugs.
Following changes were made:

1. The useSelect hooks which were being used to fetch existing terms and search results have been moved into their own custom hooks named 'useExistingTerms' and 'useSearchResults' respectively. This simplifies the TaxonomyItem function's body and makes the hooks' purposes clearer.

2. The comments and props destructuring for the TaxonomyItem function have been moved up to make it easier to understand the function's purpose and the props it receives.

3. This refactor does not introduce any changes in functionality. It only changes how the code is organized and presented, which will make future development easier.
This commit enhances the `TaxonomyControls` component within `product-collection` block by adding memoization and improving term uniqueness handling.

Changes:

1. Imported `useMemo` from `@wordpress/element` for memoizing certain results.

2. `getTermIdByTermValue` function has been modified to use a `termIdToNameMap` (term ids as keys and term names as values). This provides a more efficient and direct mapping for term search.

3. Introduced `useTermIdToNameMap` function, which returns a `Map` where term ids are keys and term names are values. It handles duplicate term names by appending the term slug to the name, ensuring unique term names.

4. Updated the `useExistingTerms` and `useSearchResults` to include `taxonomy` in their dependency arrays for `useSelect` hook. This will force re-computation when `taxonomy` changes.

5. Changed `TaxonomyItem` from a function declaration to a const arrow function, consistent with the rest of the codebase.

6. Updated `onTermsChange` function in `TaxonomyItem` to accommodate the changes in `getTermIdByTermValue` and the introduction of `termIdToNameMap`.

7. Replaced `Set` with a standard array for storing new term IDs in `onTermsChange`. The `Set` was unnecessary as term IDs are unique by default.

8. Updated `TaxonomyItem`'s effects and rendering to work with `termIdToNameMap`, ensuring the displayed term names are unique.

This update will result in more efficient term search and handling, and it will solve issues related to duplicate term names.
@imanish003 imanish003 changed the title [Product Collection] Add Taxonomy filters to sidebar settings Product Collection: Add Taxonomy filters to sidebar settings Jun 1, 2023
This commit restructures the taxonomy controls in the product collection block for improved clarity and maintainability.
- The file `taxonomy-controls.tsx` has been deleted, and its functionality has been divided into two new files: `index.tsx` and `taxonomy-item.tsx`.

- The `index.tsx` file contains the main TaxonomyControls component, which is responsible for displaying taxonomy-related options in the block's inspector controls. It includes a custom hook `useTaxonomies` that fetches and returns taxonomies associated with product post type.

- The `taxonomy-item.tsx` file, on the other hand, contains a TaxonomyItem component that handles the rendering of individual taxonomy items. It also contains some utility functions for mapping term names and ids and fetching terms based on the search query.

This refactor aims to improve code readability and separation of concerns, thus making future changes and maintenance easier.
@imanish003
Copy link
Contributor Author

Hey @Aljullu, I have made a good amount of changes:

  • Added handling for duplicate term names
  • Refactored a lot of code to improve code readability and separation of concerns, thus making future changes and maintenance easier.

Can you please take a look again? 🙂

@imanish003 imanish003 requested a review from Aljullu June 1, 2023 11:04
Copy link
Contributor

@Aljullu Aljullu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating this PR, @imanish003! I really appreciate how you split the code in two files and now it's much easier to read. I found one issue: the Uncategorized category appears for me in the UI, but I'm not able to select it. Clicking on it doesn't add it to the field. Can you reproduce?

Enregistrament.de.pantalla.des.de.2023-06-01.13-32-55.webm

Another thing, which is probably not a blocker, is that I feel this component makes too many calls to the API. Ie: just enabling the "Taxonomies" filter, causes three API calls. It might be nice to investigate if they can be reduced.

Enregistrament.de.pantalla.des.de.2023-06-01.13-47-53.webm

Besides that, I left some comments inline. Some of them are code suggestions, I did test them but might have missed some edge cases, so feel free to disregard if they don't work well.

This change enhances the search functionality of the FormTokenField by introducing support for case insensitive search. This has been achieved by adding a lower-case version of the term name to the 'termNameToIdMap'.

This is an important enhancement as it will make the search process more user-friendly and resilient to different casing inputs. Users will now be able to find the desired taxonomy term regardless of their input's case.
@imanish003
Copy link
Contributor Author

imanish003 commented Jun 2, 2023

Hey @Aljullu 👋

I found one issue: the Uncategorized category appears for me in the UI, but I'm not able to select it. Clicking on it doesn't add it to the field. Can you reproduce?

While I was able to add Uncategorized, I encountered an issue when trying to add its lowercase version, uncategorized. As a result, I pushed a fix in 6a10f3f. Perhaps that was the same problem you faced as well? Could you please check if this fix works for you too?

This commit does a couple of important things:
1. Reorders the definition of constants in `TaxonomyItemProps` for clarity.
2. Refactors the `getTermIdByTermValue` function. Instead of checking for an exact term name match in a convoluted manner, it now directly tries to fetch the `id` from the `searchTerm` if it is an object. If the `searchTerm` is not an object, the function tries to match it against the `termNameToIdMap` in both normal and lowercase forms. This simplification makes the function more readable and concise.
3. Updates the `newSuggestions` mapping in the `TaxonomyItem` component. It now has a fallback to `searchResult.name` if a term's name is not found in `termIdToNameMap`. This change ensures that even if the term's name is not in the map for some reason, we can still display a suggestion using the original name of the term.
This commit introduces a couple of improvements to the TaxonomyItem component.

1. The initial state of the 'search' state variable has been updated to 'undefined'. This change helps prevent unnecessary initial fetching of terms when the search input is empty.

2. Term fetching logic has been optimized to only enable term fetching when necessary:
   a) Fetching based on the search query is only enabled when 'search' is not 'undefined'.
   b) Fetching existing terms is only enabled when there are term IDs.

3. The block of code responsible for fetching existing terms and setting the current value has been moved upwards. This reordering of code does not change the functionality, but it groups together similar pieces of code, enhancing readability and maintainability.

These optimizations make the component more efficient by reducing unnecessary requests and computations, and they improve the code organization.
@imanish003
Copy link
Contributor Author

@Aljullu

Another thing, which is probably not a blocker, is that I feel this component makes too many calls to the API. Ie: just enabling the "Taxonomies" filter, causes three API calls. It might be nice to investigate if they can be reduced.

I have implemented the changes in 7b56958. I have optimized the data fetching process, resulting in a single call. Can you please take a look?

@imanish003 imanish003 requested a review from Aljullu June 2, 2023 06:59
Copy link
Contributor

@Aljullu Aljullu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for working on this, @imanish003!

I left one minor suggestion and answered all open threads, but approving. 🚢

<div className="wc-block-editor-product-collection-inspector__taxonomy-control">
<FormTokenField
label={ taxonomy.name }
value={ value }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for future reference: heads-up that usePrevious() accepts a validation function so you can determine whether it updates or not. So in this case, it should be possible to make it so it doesn't update in certain circumstances.

@github-actions github-actions bot added this to the 10.4.0 milestone Jun 2, 2023
@imanish003 imanish003 merged commit 515c674 into trunk Jun 2, 2023
@imanish003 imanish003 deleted the 9364-product-collection-filters-taxonomies branch June 2, 2023 10:01
@tarunvijwani tarunvijwani added the type: enhancement The issue is a request for an enhancement. label Jun 5, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
block: product collection Issues related to the Product Collection block focus: blocks Specific work involving or impacting how blocks behave. type: enhancement The issue is a request for an enhancement. type: task The issue is an internally driven task (e.g. from another A8c team).
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Product Collection - Filters: Taxonomies
4 participants