Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Product Collection: Implement Preview Mode #46369

Merged

Conversation

imanish003
Copy link
Contributor

@imanish003 imanish003 commented Apr 9, 2024

Changes proposed in this Pull Request:

Introduction

We are working on Preview mode for Product Collection block. Here's the scoop: some collections might look a bit different in the Editor than they do on the Frontend. Take the Recently Viewed collection as an example; its appearance in the Editor might not exactly mirror its Frontend display. To bridge this gap, we're introducing a label, which you can peek at in the screenshot below.

Tooltip
A message will be shown when the merchant hovers over the "preview" label in Editor as shown in screenshot below:
image

What is implemented in this PR?

For all Product Collection blocks that inherit query from the template, we are showing a preview message in the editor if the block is in a generic archive template, i.e.

  • Products by category
  • Products by tag
  • Products by attribute

Here is what it should look like:
image

Technical details

I have abstracted logic to register a new collection into registerProductCollection. This function will allow us to register a new collection. For now, it's not public, but in follow-up PR, we plan to make it public (i.e., usable by 3PDs). For now, this function accepts all the arguments which are accepted by Block Variation. Also, registerProductCollection accepts an preview argument. Here are some examples of how preview can be used:

Example 1:
As you can see in the example below, you can pass preview object to registerProductCollection to set the initial state of the preview mode.

  • isPreview is a boolean that will determine if the collection is in preview mode.
  • previewMessage is a string that will be shown as a tooltip when the merchant hovers over the preview label in the Editor.
registerProductCollection(
	...,
	preview: {
		initialPreviewState: {
			isPreview: true,
			previewMessage: 'On sale Collection Preview',
		},
	},
)

Example 2:

In the example below, you can see how you can use setPreviewState to set the preview state. In setPreviewState, you can access attributes and location in the preview state, utilize async operations, and implement a cleanup function as a return value.

/**
 * Example:
 * - How to access attributes and location in the preview state?
 * - How to use async operations?
 * - How to use cleanup function as a return value?
 */
registerProductCollection(
	...,
	preview: {
		setPreviewState: ( {
			setState,
			attributes: currentAttributes,
			location,
		}: SetPreviewStateArgs ) => {
			// setPreviewState has access to the current attributes and location.
			console.log( 'setPreviewState' );
			console.log( currentAttributes, location );

			const timeoutID = setTimeout( () => {
				setState( {
					isPreview: false,
					previewMessage: '',
				} );
			}, 5000 );

			return () => clearTimeout( timeoutID );
		},
		initialPreviewState: {
			isPreview: true,
			previewMessage: 'This is in preview mode',
		},
	}
)
When setPreviewState will be triggered?
  1. On initial load: When either Collection is added or editor page is refreshed, then setPreviewState will be triggered.
  2. When block is moved: IMO it's needed because location of block might change when block is moved. For example, if it's moved to Single Products block then location type will become:
{
  type: "product",
  sourceData: {
    productId: 2777
  }
};
What isn't part of this PR?
Preview argument validation

In followup PR, we will add validation for preview argument passed in registerProductCollection function. For now, this is still a private API, but before making it public, we will need to add some validation, as incorrect data is possible.

Required context

As mentioned by Karol in #45703 (review), collection should be able to specify requiredContext or requiredLocation. In case it's not satisfied we should not even call setPreviewState and set the preview ourselves.
For now, there is no way for collection to specify require context but when it will be implemented, we will need to handle this case, i.e. we will need to show preview label when required context isn't satisfied.

Expose registerProductCollection for 3PDs

This will require a bit more discussion about what is the best way to expose it. Therefore, I will be implementing it in a follow-up PR.

Closes #46368

How to test the changes in this Pull Request:

Using the WooCommerce Testing Instructions Guide, include your detailed testing instructions:

  1. Open Editor & Go to any of the following templates
    1. Products by category
    2. Products by tag
    3. Products by attribute
  2. Add Product Collection block & choose "Product Catalog" collection
  3. Verify the "Preview" label is showing at top-right corner of Product Collection block as shown in screenshot below:
image
  1. Also, verify that hovering over "preview" label shows following tooltip: "Actual products will vary depending on the page being viewed."
    Pasted image 20240424145336

  2. Save and go to frontend. Verify that "preview" label isn't visible on frontend. It should be only visible on Editor side. Also, verify that everything is working as expected in Product Collection block and there are no errors.

    • For example, if you modify the "Product by Category" template, then you can check the frontend URLs like "/product-category/clothing/accessories/"
  3. Now, Toggle "Sync with current query" to off using inspector control. Verify that:

    • If toggle is off, then preview label should not be visible.
    • If toggle is on, then preview label should be visible.

Changelog entry

  • Automatically create a changelog entry from the details below.

Significance

  • Patch
  • Minor
  • Major

Type

  • Fix - Fixes an existing bug
  • Add - Adds functionality
  • Update - Update existing functionality
  • Dev - Development related task
  • Tweak - A minor adjustment to the codebase
  • Performance - Address performance issues
  • Enhancement - Improvement to existing functionality

Message

Product Collection: Add preview mode on Editor side

Comment

POC: Implement preview mode for Product Collection block in editor
- Added extensive commentary to clarify the mechanism and usage of the `handlePreviewState` function
- Implemented an internal state within `ProductCollectionContent` to manage preview status and messages, serving as a foundational example of how preview mode can enrich block functionality.
- Showcased the application of `handlePreviewState` by incorporating it as a prop in `BlockEdit`, illustrating the potential for extending the block's capabilities for dynamic and interactive previews.

This POC demonstrates a flexible approach to managing preview states within the editor, paving the way for further development and integration based on feedback and use-case analysis.
This commit introduces a centralized approach for registering product collection variations and managing their preview states. It abstracts the registration logic into a dedicated function and enhances the flexibility of preview state handling across different collection types.
I don't see any good use of it in handlePreviewState. Also, We will be going to call handlePreviewState only once
therefore, it will always have the same value as the initial value of the previewState. If in future, we decide to run it
multiple times then we can pass the previewState as an argument to handlePreviewState.
This commit introduces a refined approach for injecting the `handlePreviewState` function into product collection blocks, utilizing JavaScript closures to streamline the process. This method replaces the previous global registry mechanism, offering a more direct and efficient way to manage preview states.

Advantages of This Approach:
- Utilizing JavaScript closures for injecting `handlePreviewState` simplifies the overall architecture by directly modifying block edit components without relying on an external registry. This method enhances code clarity and reduces the cognitive load for developers.
- The conditional application of `withHandlePreviewState` ensures that the preview state handling logic is only added to blocks that require it, optimizing performance and maintainability.
This commit enhances the organization and readability of the product collection content component by abstracting the preview state management into a custom hook named `usePreviewState`. This change streamlines the component's structure and aligns with React best practices for managing state and side effects.

Key Changes:
- Introduced `usePreviewState`, a custom hook responsible for initializing and managing the preview state (`isPreview` and `previewMessage`) of the product collection block. This hook encapsulates the state logic and its side effects, including the conditional invocation of `handlePreviewState`.
- Modified `ProductCollectionContent` to utilize `usePreviewState` for handling its preview state. This adjustment makes the component cleaner and focuses it more on presentation and behavior rather than state management details.
Based on [this discussion](#45703 (comment)), I added a cleanup function support for handlePreviewState. `handlePreviewState` can return a function which will be called on cleanup in `useLayoutEffect` hook.
- Consolidated `handlePreviewState` and `initialPreviewState` into a single `preview` prop in `register-product-collection.tsx` and `product-collection-content.tsx` to streamline prop passing and improve the component interface.
- Updated the `queryContextIncludes` in `constants.ts` to include 'previewState'
- Enhanced the `ProductCollection` PHP class to handle preview-specific queries more effectively, introducing a new method `get_preview_query_args` that adjusts query parameters based on the collection being previewed, thereby improving the relevance and accuracy of products displayed in preview mode.
- Renamed `HandlePreviewStateArgs` to `SetPreviewStateArgs` in `featured.tsx` to better reflect its purpose, which is now more focused on setting rather than handling states. The implementation details within `featured.tsx` have also been refined to include async operations and cleanup functions, demonstrating a more sophisticated approach to managing state.

Overall, these updates make the preview state logic more understandable and maintainable.
This commit addresses an issue in the product-collection-content.tsx where the newPreviewState was not properly merged into the existing previewState attribute. Previously, the spread operator was incorrectly applied, leading to potential loss of existing state attributes. By changing the order of operations and correctly spreading the existing attributes before merging the newPreviewState, we ensure that all state attributes are preserved and updated correctly.
…b.com/woocommerce/woocommerce into add/46368-product-collection-productionize-the-preview-mode-poc
@imanish003 imanish003 linked an issue Apr 9, 2024 that may be closed by this pull request
@github-actions github-actions bot added the plugin: woocommerce Issues related to the WooCommerce Core plugin. label Apr 9, 2024
…to add/46368-product-collection-productionize-the-preview-mode-poc
Implemented a new useLayoutEffect in `utils.tsx` to dynamically set a preview message in the editor for product collection blocks located in generic archive templates (like Products by Category, Products by Tag, or Products by Attribute).
1. **Template-Specific Tests:** Each template (tag, category, attribute) undergoes a test to ensure the preview button behaves as expected when replacing products with product collections in these contexts.
2. **Visibility Checks:** The tests verify that the preview button is visible when the block or its inner blocks are selected and hidden when the block is not selected. This helps confirm the correct implementation of the preview button visibility logic across different use cases.
3. **Interaction with Inner Blocks:** Additional checks are included to ensure the preview button's visibility toggles appropriately when interacting with inner blocks, reinforcing the dynamic nature of block selection and its effect on UI elements within the editor.
@imanish003
Copy link
Contributor Author

Hi @samueljseay @xristos3490 👋

I have made following requested changes:

  • Added E2E tests
  • I changed the preview label position as suggested by @jarekmorawski in p1714028491661149/1714028083.983709-slack-C05UMTA7W0P.
  • Now, the Preview label will only be visible if either the Product Collection block is selected or any of its inner blocks are selected to avoid cluttering the UI.
  • Added __private prefix with Attribute name to make it clear that this attribute is for internal use only. As I mentioned in this comment above, I will come back to this.
  • Moved styles to a class
  • Some Typescript types improvements
  • Other minor improvements as requests in feedback.

PR is now ready for review again. Can you please review it again? Please let me know in case I missed something. Thank you for the helpful feedback so far 🙂

Copy link
Contributor

@samueljseay samueljseay left a comment

Choose a reason for hiding this comment

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

I don't have much else to add, it's looking great. A few suggestions but they're mostly minor and won't block approval from me.

*/
if ( setPreviewState || initialPreviewState ) {
const withSetPreviewState =
< T extends EditorBlock< T > >( BlockEdit: ElementType ) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

If it helps, I tried to use ComponentType from `'@wordpress/element' as we aren't supposed to import directly from "react", right?

as i mentioned in another comment, you absolutely can import types from react. types are erased at compilation. For some reason wp element is erasing the generics of the ComponentType so the fix here would indeed be to import from react (I tested and it works well).

Modifications:
- Added `data-test-id="product-collection-preview-button"` to the Preview button in `product-collection-content.tsx`.
- Updated the corresponding e2e test locator in `product-collection.block_theme.side_effects.spec.ts` to use the new `data-test-id` instead of the class name.

By using `data-test-id`, we ensure that the e2e tests are not affected by changes in the styling or restructuring of the DOM that might alter CSS classes but do not affect functionality.
…to add/46368-product-collection-productionize-the-preview-mode-poc
Copy link
Contributor

github-actions bot commented May 10, 2024

Test using WordPress Playground

The changes in this pull request can be previewed and tested using a WordPress Playground instance.
WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Test this pull request with WordPress Playground.

Note that this URL is valid for 30 days from when this comment was last updated. You can update it by closing/reopening the PR or pushing a new commit.

Copy link
Member

@xristos3490 xristos3490 left a comment

Choose a reason for hiding this comment

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

Overall looking good, @imanish003! I couldn't test this in a 3PD environment of course but it seems to be checking all the boxes! Fingers crossed, things should work as expected!

Left some comments before approving!

Copy link
Member

@xristos3490 xristos3490 left a comment

Choose a reason for hiding this comment

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

Left some comments before giving the final approval.

@xristos3490 xristos3490 self-requested a review May 14, 2024 08:46
xristos3490

This comment was marked as duplicate.

@xristos3490 xristos3490 self-requested a review May 14, 2024 08:47
Copy link
Member

@xristos3490 xristos3490 left a comment

Choose a reason for hiding this comment

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

Works as expected! Nice work, @imanish003!

…to add/46368-product-collection-productionize-the-preview-mode-poc
@imanish003 imanish003 merged commit 9f5f93c into trunk May 15, 2024
36 checks passed
@imanish003 imanish003 deleted the add/46368-product-collection-productionize-the-preview-mode-poc branch May 15, 2024 07:48
@github-actions github-actions bot added this to the 9.0.0 milestone May 15, 2024
@github-actions github-actions bot added the needs: analysis Indicates if the PR requires a PR testing scrub session. label May 15, 2024
@nigeljamesstevenson nigeljamesstevenson added the release: highlight Issues that have a high user impact and need to be discussed/paid attention to. label May 15, 2024
@rodelgc rodelgc added needs: external testing Indicates if the PR requires further testing conducted by testers external to the development team. status: analysis complete Indicates if a PR has been analysed by Solaris and removed needs: analysis Indicates if the PR requires a PR testing scrub session. labels May 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
block: product collection Issues related to the Product Collection block focus: FSE Issues related to Full Site Editing needs: external testing Indicates if the PR requires further testing conducted by testers external to the development team. plugin: woocommerce Issues related to the WooCommerce Core plugin. release: highlight Issues that have a high user impact and need to be discussed/paid attention to. status: analysis complete Indicates if a PR has been analysed by Solaris team: Kirigami & Origami
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Product Collection: Productionize the Preview Mode POC
6 participants