feat(product-list): implement pagination and update items to use paginated response

This commit is contained in:
2026-07-20 14:39:12 -03:00
parent c0eb3369c5
commit 5165ea0716
4 changed files with 60 additions and 4 deletions

View File

@@ -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) {
<div class="product-list__item">
@switch (layout()) {
@case ('row') {
@@ -38,3 +38,14 @@
</div>
}
</div>
@if (items().meta.last_page > 1) {
<div class="product-list__paginator d-flex justify-content-center">
<app-paginator
[page]="items().meta.current_page"
[totalPages]="items().meta.last_page"
[disabled]="loading()"
(pageChange)="pageChange.emit($event)"
/>
</div>
}

View File

@@ -25,3 +25,7 @@
min-width: 0;
}
}
.product-list__paginator {
margin-top: 1.5rem;
}

View File

@@ -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<ProductListItem[]> {
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);
});
});

View File

@@ -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<ProductListLayout>();
readonly items = input.required<readonly ProductListItem[]>();
readonly items = input.required<ApiPaginatedResponse<ProductListItem[]>>();
readonly loading = input(false);
readonly buy = output<ProductListItem>();
readonly addToCart = output<ProductListCartEvent>();
readonly pageChange = output<number>();
protected price(item: ProductListItem): number {
const price = Number(item.precio);