From f6f3a997f73ff1bba7c864b0fab87f93c01355df Mon Sep 17 00:00:00 2001 From: ncoronel Date: Tue, 11 Aug 2026 08:28:42 -0300 Subject: [PATCH] feat: refactor variant selection to use app-variant-selector for improved maintainability --- ...uct-vertical-with-cart-card.component.html | 16 +--- ...uct-vertical-with-cart-card.component.scss | 26 +++---- ...-vertical-with-cart-card.component.spec.ts | 8 +- ...oduct-vertical-with-cart-card.component.ts | 75 +++---------------- 4 files changed, 29 insertions(+), 96 deletions(-) diff --git a/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.html b/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.html index 267dd4b..b6e3baa 100644 --- a/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.html +++ b/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.html @@ -21,18 +21,10 @@
- @for (selector of variantSelectors(); track selector.key) { - - } +
} diff --git a/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.scss b/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.scss index 00ad1f3..1f33e56 100644 --- a/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.scss +++ b/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.scss @@ -66,24 +66,22 @@ } &__variant-selectors { - display: flex; - align-items: center; width: 100%; - gap: 8px; } - &__variant-select { - flex: 1 1 0; - min-width: 0; - height: 40px; - border-color: #d8d8d8; - color: #6f6f6f; - font-size: 12px; + &__variant-selectors app-variant-selector { + width: 100%; + } - &:focus { - border-color: var(--tenant-primary, #009933); - box-shadow: 0 0 0 0.2rem color-mix(in srgb, transparent 75%, var(--tenant-primary, #009933)); - } + &__variant-selectors ::ng-deep .variant-selector { + flex-wrap: nowrap; + width: 100%; + } + + &__variant-selectors ::ng-deep .variant-selector__select { + flex: 1 1 0; + width: 0; + min-width: 0; } &__price { diff --git a/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.spec.ts b/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.spec.ts index ef6fdca..ea0b626 100644 --- a/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.spec.ts +++ b/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.spec.ts @@ -131,7 +131,7 @@ describe('ProductVerticalWithCartCardComponent', () => { expect(summary?.querySelector('.product-vertical-with-cart-card__price')).not.toBeNull(); expect(summary?.querySelector('app-quantity-selector')).not.toBeNull(); expect( - selectors?.querySelectorAll('.product-vertical-with-cart-card__variant-select'), + selectors?.querySelectorAll('.variant-selector__select'), ).toHaveLength(1); expect(selectors?.querySelector('app-quantity-selector')).toBeNull(); }); @@ -150,7 +150,7 @@ describe('ProductVerticalWithCartCardComponent', () => { expect(layout?.querySelector('.product-vertical-with-cart-card__summary')).not.toBeNull(); expect( layout?.querySelectorAll( - '.product-vertical-with-cart-card__variant-selectors .product-vertical-with-cart-card__variant-select', + '.product-vertical-with-cart-card__variant-selectors .variant-selector__select', ), ).toHaveLength(2); }); @@ -166,7 +166,7 @@ describe('ProductVerticalWithCartCardComponent', () => { fixture.componentInstance.buy.subscribe(buySpy); const select = fixture.nativeElement.querySelector( - '.product-vertical-with-cart-card__variant-select', + '.variant-selector__select', ) as HTMLSelectElement; select.value = select.options[1].value; select.dispatchEvent(new Event('change')); @@ -207,7 +207,7 @@ describe('ProductVerticalWithCartCardComponent', () => { ).toBe('$ 10.000'); const select = element.querySelector( - '.product-vertical-with-cart-card__variant-select', + '.variant-selector__select', ) as HTMLSelectElement; select.value = select.options[1].value; select.dispatchEvent(new Event('change')); diff --git a/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.ts b/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.ts index c4a06e7..0ceed82 100644 --- a/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.ts +++ b/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.ts @@ -2,17 +2,17 @@ import { ChangeDetectionStrategy, Component, computed, - effect, input, model, output, - signal, - untracked, } from '@angular/core'; -import { FormsModule } from '@angular/forms'; import { ButtonComponent } from '../button/button.component'; import { QuantitySelectorComponent } from '../quantity-selector/quantity-selector.component'; +import { + VariantSelectorComponent, + VariantSelectorVariant, +} from '../variant-selector/variant-selector.component'; export interface VerticalCartVariant { id: number; @@ -21,15 +21,9 @@ export interface VerticalCartVariant { values: Record; } -interface VariantSelector { - key: string; - label: string; - options: string[]; -} - @Component({ selector: 'app-product-vertical-with-cart-card', - imports: [ButtonComponent, FormsModule, QuantitySelectorComponent], + imports: [ButtonComponent, QuantitySelectorComponent, VariantSelectorComponent], templateUrl: './product-vertical-with-cart-card.component.html', styleUrl: './product-vertical-with-cart-card.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, @@ -46,8 +40,6 @@ export class ProductVerticalWithCartCardComponent { readonly buy = output<{ quantity: number; variant: number | null }>(); readonly addToCart = output<{ quantity: number; variant: number | null }>(); - protected readonly selectedValues = signal>({}); - protected readonly selectedVariantData = computed(() => this.variants().find((variant) => variant.id === this.selectedVariant()), ); @@ -60,54 +52,10 @@ export class ProductVerticalWithCartCardComponent { return Number.isFinite(variantPrice) ? variantPrice : this.price(); }); protected readonly formattedPrice = computed(() => this.formatCurrency(this.effectivePrice())); - protected readonly variantSelectors = computed(() => { - const variants = this.variants(); - const keys = Array.from(new Set(variants.flatMap((variant) => Object.keys(variant.values)))); - - return keys.map((key) => ({ - key, - label: this.formatVariantLabel(key), - options: Array.from( - new Set( - variants - .map((variant) => variant.values[key]) - .filter((value): value is string => Boolean(value)), - ), - ), - })); - }); - - protected readonly hasVariants = computed(() => this.variantSelectors().length > 0); - - constructor() { - effect(() => { - const variants = this.variants(); - - untracked(() => { - if (variants.length === 0) { - this.selectedValues.set({}); - this.selectedVariant.set(null); - return; - } - - const selected = - variants.find((variant) => variant.id === this.selectedVariant()) ?? variants[0]; - this.selectedValues.set({ ...selected.values }); - this.selectedVariant.set(selected.id); - }); - }); - } - - protected onVariantValueChange(key: string, value: string): void { - const values = { ...this.selectedValues(), [key]: value }; - const selectors = this.variantSelectors(); - const matchingVariant = this.variants().find((variant) => - selectors.every((selector) => variant.values[selector.key] === values[selector.key]), - ); - - this.selectedValues.set(values); - this.selectedVariant.set(matchingVariant?.id ?? null); - } + protected readonly selectorVariants = computed(() => + this.variants().map((variant) => ({ value: variant.id, values: variant.values })), + ); + protected readonly hasVariants = computed(() => this.selectorVariants().length > 0); protected onAddToCart(): void { this.addToCart.emit({ quantity: this.quantity(), variant: this.selectedVariant() }); @@ -117,11 +65,6 @@ export class ProductVerticalWithCartCardComponent { this.buy.emit({ quantity: this.quantity(), variant: this.selectedVariant() }); } - private formatVariantLabel(key: string): string { - const label = key.replace(/[_-]+/g, ' '); - return label.charAt(0).toUpperCase() + label.slice(1); - } - private formatCurrency(value: number): string { const rounded = Math.round(value); const parts = rounded.toString().split('.');