feat: enhance variant handling in selector and layout components for improved display and selection

This commit is contained in:
2026-08-10 16:34:02 -03:00
parent 0769bb8683
commit 9ea85b7924
5 changed files with 86 additions and 6 deletions

View File

@@ -6,7 +6,7 @@ import { StoreFooterComponent, StoreFooterSection } from './store-footer/store-f
import { StoreHeaderComponent } from './store-header/store-header.component';
import { CartComponent, CartItemMock } from '../../../shared/components/cart/cart.component';
import { ButtonComponent } from '../../../shared/components/button/button.component';
import { CartItem } from '../../services/cart/cart.interface';
import { CartItem, CartItemVariantValue } from '../../services/cart/cart.interface';
import { AuthService } from '../../services/auth/auth.service';
import { findMenu } from '../../services/menu.utils';
import { CheckoutService } from '../../services/checkout.service';
@@ -80,7 +80,7 @@ export class StoreLayoutComponent implements OnInit {
if (selectedVariant) {
attributes = Object.entries(selectedVariant.values).map(([label, value]) => ({
label: this.formatAttributeLabel(label),
value: Array.isArray(value) ? value.join(', ') : value,
value: this.formatAttributeValue(value),
}));
}
@@ -103,6 +103,11 @@ export class StoreLayoutComponent implements OnInit {
return label.charAt(0).toUpperCase() + label.slice(1);
}
private formatAttributeValue(value: CartItemVariantValue): string {
const values = Array.isArray(value) ? value : [value];
return values.map((item) => (typeof item === 'string' ? item : item.label)).join(', ');
}
protected readonly currentYear = new Date().getFullYear();
protected readonly tenant = this.tenantService.tenant;
protected readonly user = this.authService.user;

View File

@@ -4,11 +4,21 @@ export interface CartItemProduct {
variants?: CartItemVariant[];
}
export interface CartItemVariantOption {
value: string;
label: string;
}
export type CartItemVariantValue =
| string
| CartItemVariantOption
| Array<string | CartItemVariantOption>;
export interface CartItemVariant {
id: number;
precio: string;
stock_tecnico: number | null;
values: Record<string, string | string[]>;
values: Record<string, CartItemVariantValue>;
}
export interface CartItem {

View File

@@ -5,6 +5,7 @@
class="form-select variant-selector__select"
[attr.aria-label]="selector.label"
[disabled]="disabled()"
[compareWith]="compareValues"
[ngModel]="selectedValues()[selector.key]"
(ngModelChange)="onValueChange(selector.key, $event)"
>

View File

@@ -38,4 +38,45 @@ describe('VariantSelectorComponent', () => {
servicio: 'Cena',
});
});
it('renders backend option labels and compares equivalent option objects by value', async () => {
await TestBed.configureTestingModule({
imports: [VariantSelectorComponent],
}).compileComponents();
const fixture = TestBed.createComponent(VariantSelectorComponent);
fixture.componentRef.setInput('variants', [
{
value: 1,
values: {
color: { value: 'red', label: 'Rojo' },
talle: { value: 's', label: 'Small' },
},
},
{
value: 2,
values: {
color: { value: 'red', label: 'Rojo' },
talle: { value: 'm', label: 'Medium' },
},
},
]);
fixture.componentRef.setInput('selectedVariant', 2);
fixture.detectChanges();
const selects = Array.from(
fixture.nativeElement.querySelectorAll('select'),
) as HTMLSelectElement[];
expect(selects).toHaveLength(2);
expect(selects[0].selectedOptions[0]?.textContent).toBe('Rojo');
expect(selects[1].selectedOptions[0]?.textContent).toBe('Medium');
expect(fixture.nativeElement.textContent).not.toContain('[object Object]');
(fixture.componentInstance as any).onValueChange('talle', {
value: 's',
label: 'Small',
});
expect(fixture.componentInstance.selectedVariant()).toBe(1);
});
});

View File

@@ -10,7 +10,13 @@ import {
} from '@angular/core';
import { FormsModule } from '@angular/forms';
export type VariantAttributeValue = string | string[];
export interface VariantAttributeOption {
value: string;
label: string;
}
export type VariantAttributeScalar = string | VariantAttributeOption;
export type VariantAttributeValue = VariantAttributeScalar | VariantAttributeScalar[];
export interface VariantSelectorVariant {
value: unknown;
@@ -44,6 +50,10 @@ export class VariantSelectorComponent {
readonly compact = input(false);
protected readonly selectedValues = signal<Record<string, VariantAttributeValue>>({});
protected readonly compareValues = (
left: VariantAttributeValue | null,
right: VariantAttributeValue | null,
): boolean => left !== null && right !== null && this.sameValue(left, right);
protected readonly attributeKeys = computed(() =>
Array.from(new Set(this.variants().flatMap((variant) => Object.keys(variant.values)))),
);
@@ -132,7 +142,7 @@ export class VariantSelectorComponent {
if (!options.has(optionKey)) {
options.set(optionKey, {
key: optionKey,
label: Array.isArray(value) ? value.join(', ') : value,
label: this.valueLabel(value),
value,
});
}
@@ -151,7 +161,20 @@ export class VariantSelectorComponent {
}
private valueKey(value: VariantAttributeValue): string {
return JSON.stringify(value);
const comparableValue = Array.isArray(value)
? value.map((item) => this.scalarValue(item))
: this.scalarValue(value);
return JSON.stringify(comparableValue);
}
private valueLabel(value: VariantAttributeValue): string {
const values = Array.isArray(value) ? value : [value];
return values.map((item) => (typeof item === 'string' ? item : item.label)).join(', ');
}
private scalarValue(value: VariantAttributeScalar): string {
return typeof value === 'string' ? value : value.value;
}
private formatVariantLabel(key: string): string {