From 985316d3b4da468a9aee5762471b38455601b755 Mon Sep 17 00:00:00 2001 From: ncoronel Date: Fri, 4 Sep 2026 11:36:21 -0300 Subject: [PATCH] fix(product-attribute-selector): improve variant availability logic and add tests for maximum quantity handling --- ...oduct-attribute-selector.component.spec.ts | 33 +++++++++++++++++++ .../product-attribute-selector.component.ts | 17 +++------- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/app/features/store/components/product-attribute-selector/product-attribute-selector.component.spec.ts b/src/app/features/store/components/product-attribute-selector/product-attribute-selector.component.spec.ts index e60db33..c1335b5 100644 --- a/src/app/features/store/components/product-attribute-selector/product-attribute-selector.component.spec.ts +++ b/src/app/features/store/components/product-attribute-selector/product-attribute-selector.component.spec.ts @@ -51,6 +51,39 @@ describe('ProductAttributeSelectorComponent', () => { }, ); + it.each([0, 2, null])( + 'uses maximum quantity %s for alternatives to a preselected option', + (maximum) => { + const fixture = TestBed.createComponent(ProductAttributeSelectorComponent); + const variants: CatalogItemVariant[] = [ + { id: 1, maximum_addable_quantity: 3, values: { size: { value: 'S', label: 'Small' } } }, + { + id: 2, + maximum_addable_quantity: maximum, + values: { size: { value: 'M', label: 'Medium' } }, + }, + ]; + const emittedIds: Array = []; + fixture.componentInstance.variantChange.subscribe((variant) => + emittedIds.push(variant?.id ?? null), + ); + fixture.componentRef.setInput('attributes', [sizeAttribute]); + fixture.componentRef.setInput('inventoryPolicy', maximum === null ? 'unlimited' : 'tracked'); + fixture.componentRef.setInput('variants', variants); + fixture.componentRef.setInput('selectedVariant', variants[0]); + fixture.detectChanges(); + + const buttons = fixture.nativeElement.querySelectorAll( + '.attribute-selector__text-option', + ) as NodeListOf; + expect(buttons[1].disabled).toBe(maximum === 0); + buttons[1].click(); + fixture.detectChanges(); + expect(emittedIds.at(-1)).toBe(maximum === 0 ? 1 : 2); + expect(buttons[0].disabled).toBe(false); + }, + ); + it('keeps an unlimited option available when maximum quantity is null', () => { const fixture = TestBed.createComponent(ProductAttributeSelectorComponent); fixture.componentRef.setInput('attributes', [sizeAttribute]); diff --git a/src/app/features/store/components/product-attribute-selector/product-attribute-selector.component.ts b/src/app/features/store/components/product-attribute-selector/product-attribute-selector.component.ts index 4d00543..b32384b 100644 --- a/src/app/features/store/components/product-attribute-selector/product-attribute-selector.component.ts +++ b/src/app/features/store/components/product-attribute-selector/product-attribute-selector.component.ts @@ -51,22 +51,15 @@ export class ProductAttributeSelectorComponent { const optionNormalized = this.normalizeText(option.value || option.label); const selectedForAttribute = selections[attribute.codigo] ?? []; - if ( - !attribute.allow_multi_select && - selectedForAttribute.length >= 1 && - !selectedForAttribute.includes(option.id) - ) { - availability[attribute.codigo][option.id] = false; - continue; - } - const isAvailable = variants.some((variant) => { if (!this.isVariantAvailable(variant)) return false; const variantAttrValues = this.getVariantAttributeValues(attribute, variant.values); - const desiredOptionIds = selectedForAttribute.includes(option.id) - ? selectedForAttribute - : [...selectedForAttribute, option.id]; + const desiredOptionIds = !attribute.allow_multi_select + ? [option.id] + : selectedForAttribute.includes(option.id) + ? selectedForAttribute + : [...selectedForAttribute, option.id]; const desiredValues = desiredOptionIds .map((id) => attribute.options.find((candidate) => candidate.id === id)) .filter((candidate): candidate is ProductAttributeOption => candidate !== undefined)