Skip to content

Conversation

fabiooshiro
Copy link

@fabiooshiro fabiooshiro commented Jul 27, 2025

The refactor delegates the products to the overridden ProductShelf component.
This change makes the overridden ProductShelf more presentational by separating data fetching from rendering logic.

The overridden component now receives a products prop, which is an array of ProductSummary_ProductFragment objects. This makes the component more reusable and easier to test.

This change promotes a clearer separation of concerns and maintains VTEX’s governance over shelf rendering by requiring developers to use the component rather than interacting with the hooks directly.

What's the purpose of this pull request?

Refactor the override ProductShelf component into a purely presentational component, enabling AI tools and other systems to use it without needing to understand VTEX’s internal hooks (such as useProductsQuery). This makes the component easier to reuse, test, and extend in the future.

How it works?

The ProductShelf now receives a products prop, which is an array of ProductSummary_ProductFragment objects. It no longer fetches data on its own. Instead, the data retrieval logic has been delegated to the useProductsQuery hook, which is responsible for fetching and then passing the data to the component.

This approach:

  • Reduces coupling between the component and data logic
  • Makes testing more straightforward
  • Allows external tools (like AIs) to use the component without relying on data hooks

How to test it?

You can verify its behavior in two ways:

  • Using the useProductsQuery hook Fetch product data using the hook and pass the resulting array as a prop to ProductShelf.

  • Using mocked data (for unit/integration tests) Pass a simulated array of ProductSummary_ProductFragment to the component and ensure it renders correctly.

Starters Deploy Preview

References

Checklist

You may erase this after checking them all 😉

PR Title and Commit Messages

  • PR title and commit messages follow the Conventional Commits specification
    • Available prefixes: feat, fix, chore, docs, style, refactor, ci and test

PR Description

  • Added a label according to the PR goal - breaking change, bug, contributing, performance, documentation..

Dependencies

  • Committed the pnpm-lock.yaml file when there were changes to the packages

Documentation

  • PR description
  • For documentation changes, ping @Mariana-Caetano to review and update (Or submit a doc request)

This commit refactors the `ProductShelf` component to be more presentational.

The component now receives a `products` prop, which is an array of `ProductSummary_ProductFragment` objects. This makes the component more reusable and easier to test, as it no longer fetches its own data.

The data fetching logic has been moved to the `useProductsQuery` hook, which is now responsible for fetching the products and passing them to the `ProductShelf` component.
Copy link

codesandbox-ci bot commented Jul 27, 2025

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

@fabiooshiro fabiooshiro marked this pull request as ready for review July 30, 2025 20:58
@fabiooshiro fabiooshiro requested a review from a team as a code owner July 30, 2025 20:58
@fabiooshiro fabiooshiro requested review from hellofanny and renatomaurovtex and removed request for a team July 30, 2025 20:58
@fabiooshiro fabiooshiro marked this pull request as draft July 30, 2025 21:47
@fabiooshiro
Copy link
Author

fabiooshiro commented Jul 31, 2025

ProductShelf overridden example:

const ProductShelf: React.FC<ProductShelfProps> = ({ title, products }) => {
  const [currentSlide, setCurrentSlide] = useState(0);
  const itemsPerSlide = 4;
  const totalSlides = products ? Math.ceil(products.length / itemsPerSlide) : 0;

  const nextSlide = useCallback(() => {
    setCurrentSlide((prev) => (prev + 1) % totalSlides);
  }, [totalSlides]);

  const prevSlide = useCallback(() => {
    setCurrentSlide((prev) => (prev - 1 + totalSlides) % totalSlides);
  }, [totalSlides]);

  if (!products || products.length === 0) {
    return null;
  }

  const startIndex = currentSlide * itemsPerSlide;
  const endIndex = startIndex + itemsPerSlide;
  const visibleProducts = products.slice(startIndex, endIndex);

  return (
    <div className="bg-gray-50 w-full flex items-center justify-center py-12">
      <div className="w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        <h2 className="text-4xl font-extrabold text-center text-gray-900 mb-10">
          {title}
        </h2>
        <div className="relative">
          <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
            {visibleProducts.map((product) => (
              <ProductCard key={product.id} product={product} />
            ))}
          </div>

          {totalSlides > 1 && (
            <>
              <button
                onClick={prevSlide}
                className="absolute top-1/2 -left-4 md:-left-12 transform -translate-y-1/2 bg-white rounded-full p-2 shadow-lg hover:bg-gray-100 transition-all duration-300 z-10"
                aria-label="Previous slide"
              >
                <ChevronLeft className="h-6 w-6 text-gray-800" />
              </button>
              <button
                onClick={nextSlide}
                className="absolute top-1/2 -right-4 md:-right-12 transform -translate-y-1/2 bg-white rounded-full p-2 shadow-lg hover:bg-gray-100 transition-all duration-300 z-10"
                aria-label="Next slide"
              >
                <ChevronRight className="h-6 w-6 text-gray-800" />
              </button>
            </>
          )}
        </div>
        
        {totalSlides > 1 && (
          <div className="flex justify-center mt-8 space-x-2">
            {Array.from({ length: totalSlides }).map((_, index) => (
              <button
                key={index}
                onClick={() => setCurrentSlide(index)}
                className={`w-2.5 h-2.5 rounded-full transition-colors ${
                  currentSlide === index ? 'bg-gray-800' : 'bg-gray-300 hover:bg-gray-400'
                }`}
                aria-label={`Go to slide ${index + 1}`}
              />
            ))}
          </div>
        )}
      </div>
    </div>
  );
};

export default ProductShelf;

@fabiooshiro fabiooshiro marked this pull request as ready for review July 31, 2025 14:45
@fabiooshiro
Copy link
Author

Hi @hellofanny and @renatomaurovtex!

This is my first PR on the FastStore repo, so I’m still getting familiar with the contribution flow here.
I’d love your feedback and guidance to make sure everything aligns with the repo’s standards.
Thanks in advance for taking a look!

@hellofanny
Copy link
Contributor

Hello @fabiooshiro! Sorry for the late response! Thanks for this PR 🤩 I'll take a look and get back to you as soon as possible, okay?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants