-
Notifications
You must be signed in to change notification settings - Fork 70
feat(ui): Make ProductShelf a presentational component #2964
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
base: main
Are you sure you want to change the base?
feat(ui): Make ProductShelf a presentational component #2964
Conversation
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.
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. |
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; |
Hi @hellofanny and @renatomaurovtex! This is my first PR on the FastStore repo, so I’m still getting familiar with the contribution flow here. |
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? |
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 ofProductSummary_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 asuseProductsQuery
). 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 ofProductSummary_ProductFragment
objects. It no longer fetches data on its own. Instead, the data retrieval logic has been delegated to theuseProductsQuery
hook, which is responsible for fetching and then passing the data to the component.This approach:
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
feat
,fix
,chore
,docs
,style
,refactor
,ci
andtest
PR Description
breaking change
,bug
,contributing
,performance
,documentation
..Dependencies
pnpm-lock.yaml
file when there were changes to the packagesDocumentation
@Mariana-Caetano
to review and update (Or submit a doc request)