feat: implement quantity selector component for improved cart functionality

This commit is contained in:
2026-07-02 08:47:52 -03:00
parent 8471505e6f
commit 394a1dde0d
7 changed files with 123 additions and 63 deletions

View File

@@ -53,28 +53,13 @@
<section class="product-detail__section">
<div class="product-detail__purchase">
<div class="product-detail__quantity" aria-label="Selector de cantidad">
<button
type="button"
class="product-detail__quantity-button"
aria-label="Disminuir cantidad"
(click)="decreaseQuantity()"
>
-
</button>
<span class="product-detail__quantity-value">{{ quantity() }}</span>
<button
type="button"
class="product-detail__quantity-button"
aria-label="Aumentar cantidad"
[disabled]="quantity() >= (selectedVariant()?.stock ?? 1)"
(click)="increaseQuantity()"
>
+
</button>
</div>
<app-quantity-selector
[quantity]="quantity()"
[max]="selectedVariant()?.stock ?? 1"
(increase)="increaseQuantity()"
(decrease)="decreaseQuantity()"
(quantityChange)="quantity.set($event)"
/>
<div class="product-detail__actions">
<app-button

View File

@@ -62,12 +62,6 @@
flex-wrap: wrap;
}
&__quantity-button {
border: 1px solid #dcdcdc;
background: #ffffff;
color: #666666;
transition: border-color 0.2s ease, color 0.2s ease, background-color 0.2s ease;
}
&__purchase {
display: flex;
@@ -76,38 +70,6 @@
flex-wrap: wrap;
}
&__quantity {
display: inline-flex;
align-items: center;
border: 1px solid #d8d8d8;
border-radius: 6px;
overflow: hidden;
min-height: 44px;
background: #f6f6f6;
}
&__quantity-button {
width: 25px;
height: 44px;
padding: 0;
font-size: 20px;
font-weight: 700;
line-height: 1;
color: #6f6f6f;
background: #f6f6f6;
border: 0;
}
&__quantity-value {
min-width: 35px;
text-align: center;
font-size: 16px;
font-weight: 700;
line-height: 1;
color: #6f6f6f;
}
&__actions {
flex: 1 1 0;

View File

@@ -295,13 +295,13 @@ describe('ProductDetailPageComponent', () => {
decreaseButton.click();
fixture.detectChanges();
expect(element.querySelector('.product-detail__quantity-value')?.textContent?.trim()).toBe('1');
expect(element.querySelector('.quantity-selector__value')?.textContent?.trim()).toBe('1');
increaseButton.click();
increaseButton.click();
fixture.detectChanges();
expect(element.querySelector('.product-detail__quantity-value')?.textContent?.trim()).toBe('3');
expect(element.querySelector('.quantity-selector__value')?.textContent?.trim()).toBe('3');
});
it('toggles the full description state when overflow is available', async () => {

View File

@@ -29,11 +29,19 @@ import {
import { ProductCarouselComponent } from '../../components/product-carousel/product-carousel.component';
import { ButtonComponent } from '../../../../shared/components/button/button.component';
import { ProductAttributeSelectorComponent } from '../../components/product-attribute-selector/product-attribute-selector.component';
import { QuantitySelectorComponent } from '../../../../shared/components/quantity-selector/quantity-selector.component';
@Component({
selector: 'app-product-detail-page',
standalone: true,
imports: [CommonModule, RouterModule, ProductCarouselComponent, ButtonComponent, ProductAttributeSelectorComponent],
imports: [
CommonModule,
RouterModule,
ProductCarouselComponent,
ButtonComponent,
ProductAttributeSelectorComponent,
QuantitySelectorComponent
],
templateUrl: './product-detail-page.component.html',
styleUrl: './product-detail-page.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush

View File

@@ -0,0 +1,23 @@
<div class="quantity-selector" aria-label="Selector de cantidad">
<button
type="button"
class="quantity-selector__button"
aria-label="Disminuir cantidad"
[disabled]="quantity() <= min()"
(click)="onDecrease()"
>
-
</button>
<span class="quantity-selector__value">{{ quantity() }}</span>
<button
type="button"
class="quantity-selector__button"
aria-label="Aumentar cantidad"
[disabled]="quantity() >= max()"
(click)="onIncrease()"
>
+
</button>
</div>

View File

@@ -0,0 +1,47 @@
:host {
display: inline-block;
}
.quantity-selector {
display: inline-flex;
align-items: center;
border: 1px solid #d8d8d8;
border-radius: 6px;
overflow: hidden;
min-height: 44px;
background: #f6f6f6;
&__button {
width: 25px;
height: 44px;
padding: 0;
font-size: 20px;
font-weight: 700;
line-height: 1;
color: #6f6f6f;
background: #f6f6f6;
border: 0;
transition: background-color 0.2s ease, color 0.2s ease;
cursor: pointer;
&:not(:disabled):hover {
background-color: #e9e9e9;
color: #333333;
}
&:disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
&__value {
min-width: 35px;
text-align: center;
font-size: 16px;
font-weight: 700;
line-height: 1;
color: #6f6f6f;
user-select: none;
}
}

View File

@@ -0,0 +1,35 @@
import { ChangeDetectionStrategy, Component, input, output } from '@angular/core';
@Component({
selector: 'app-quantity-selector',
standalone: true,
imports: [],
templateUrl: './quantity-selector.component.html',
styleUrl: './quantity-selector.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class QuantitySelectorComponent {
readonly quantity = input<number>(1);
readonly min = input<number>(1);
readonly max = input<number>(100);
readonly increase = output<void>();
readonly decrease = output<void>();
readonly quantityChange = output<number>();
protected onDecrease(): void {
if (this.quantity() > this.min()) {
const newValue = this.quantity() - 1;
this.decrease.emit();
this.quantityChange.emit(newValue);
}
}
protected onIncrease(): void {
if (this.quantity() < this.max()) {
const newValue = this.quantity() + 1;
this.increase.emit();
this.quantityChange.emit(newValue);
}
}
}