From 5165ea071660a6c5d0f2b582b792dda34e5e0e8a Mon Sep 17 00:00:00 2001 From: ncoronel Date: Mon, 20 Jul 2026 14:39:12 -0300 Subject: [PATCH] feat(product-list): implement pagination and update items to use paginated response --- .../product-list/product-list.component.html | 13 +++++- .../product-list/product-list.component.scss | 4 ++ .../product-list.component.spec.ts | 40 ++++++++++++++++++- .../product-list/product-list.component.ts | 7 +++- 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/app/shared/components/product-list/product-list.component.html b/src/app/shared/components/product-list/product-list.component.html index 77ccb6d..c3e9ff2 100644 --- a/src/app/shared/components/product-list/product-list.component.html +++ b/src/app/shared/components/product-list/product-list.component.html @@ -3,7 +3,7 @@ [class.product-list--row]="layout() === 'row'" [class.product-list--column]="layout() !== 'row'" > - @for (item of items(); track item.id; let index = $index) { + @for (item of items().data; track item.id; let index = $index) {
@switch (layout()) { @case ('row') { @@ -38,3 +38,14 @@
} + +@if (items().meta.last_page > 1) { +
+ +
+} diff --git a/src/app/shared/components/product-list/product-list.component.scss b/src/app/shared/components/product-list/product-list.component.scss index 26febf5..4752a6a 100644 --- a/src/app/shared/components/product-list/product-list.component.scss +++ b/src/app/shared/components/product-list/product-list.component.scss @@ -25,3 +25,7 @@ min-width: 0; } } + +.product-list__paginator { + margin-top: 1.5rem; +} diff --git a/src/app/shared/components/product-list/product-list.component.spec.ts b/src/app/shared/components/product-list/product-list.component.spec.ts index f1a5031..bd969c5 100644 --- a/src/app/shared/components/product-list/product-list.component.spec.ts +++ b/src/app/shared/components/product-list/product-list.component.spec.ts @@ -1,7 +1,8 @@ import { TestBed, getTestBed } from '@angular/core/testing'; import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing'; -import { afterEach, beforeAll, describe, expect, it } from 'vitest'; +import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'; +import { ApiPaginatedResponse } from '../../../core/services/api-paginated-response.interface'; import { ProductListComponent, ProductListItem, ProductListLayout } from './product-list.component'; describe('ProductListComponent', () => { @@ -38,12 +39,34 @@ describe('ProductListComponent', () => { await TestBed.configureTestingModule({ imports: [ProductListComponent] }).compileComponents(); const fixture = TestBed.createComponent(ProductListComponent); fixture.componentRef.setInput('layout', layout); - fixture.componentRef.setInput('items', items); + fixture.componentRef.setInput('items', paginatedItems()); fixture.detectChanges(); return fixture; } + function paginatedItems(currentPage = 1, lastPage = 2): ApiPaginatedResponse { + return { + data: items, + meta: { + current_page: currentPage, + from: 1, + last_page: lastPage, + links: [], + path: '/catalog/featured-groups/1/items', + per_page: 12, + to: 2, + total: 13, + }, + links: { + first: '/catalog/featured-groups/1/items?page=1', + last: '/catalog/featured-groups/1/items?page=2', + prev: null, + next: '/catalog/featured-groups/1/items?page=2', + }, + }; + } + it('renders every row product at full available width', async () => { const fixture = await render('row'); const element = fixture.nativeElement as HTMLElement; @@ -70,4 +93,17 @@ describe('ProductListComponent', () => { ), ).toHaveLength(2); }); + + it('renders pagination and emits the requested page', async () => { + const fixture = await render('row'); + const pageChangeSpy = vi.fn(); + fixture.componentInstance.pageChange.subscribe(pageChangeSpy); + + const nextButton = (fixture.nativeElement as HTMLElement).querySelector( + '[data-testid="paginator-next"]', + ) as HTMLButtonElement; + nextButton.click(); + + expect(pageChangeSpy).toHaveBeenCalledWith(2); + }); }); diff --git a/src/app/shared/components/product-list/product-list.component.ts b/src/app/shared/components/product-list/product-list.component.ts index 8e0b399..af3ed5d 100644 --- a/src/app/shared/components/product-list/product-list.component.ts +++ b/src/app/shared/components/product-list/product-list.component.ts @@ -1,5 +1,7 @@ import { ChangeDetectionStrategy, Component, input, output } from '@angular/core'; +import { ApiPaginatedResponse } from '../../../core/services/api-paginated-response.interface'; +import { PaginatorComponent } from '../paginator/paginator.component'; import { ProductColumnWithImageComponent } from '../product-column-with-image/product-column-with-image.component'; import { ProductRowCardComponent, @@ -35,6 +37,7 @@ export interface ProductListCartEvent { selector: 'app-product-list', imports: [ ProductColumnWithImageComponent, + PaginatorComponent, ProductRowCardComponent, ProductVerticalWithCartCardComponent, ], @@ -44,10 +47,12 @@ export interface ProductListCartEvent { }) export class ProductListComponent { readonly layout = input.required(); - readonly items = input.required(); + readonly items = input.required>(); + readonly loading = input(false); readonly buy = output(); readonly addToCart = output(); + readonly pageChange = output(); protected price(item: ProductListItem): number { const price = Number(item.precio);