feat: enhance product variant handling and improve layout for better user interaction

This commit is contained in:
2026-08-10 09:58:51 -03:00
parent 1977df6765
commit 7c2a098696
10 changed files with 185 additions and 97 deletions

View File

@@ -13,30 +13,9 @@
<span class="product-vertical-with-cart-card__price">{{ formattedPrice() }}</span>
<app-quantity-selector [(quantity)]="quantity" />
</div>
} @else if (!hasMultipleVariantSelectors()) {
<div class="product-vertical-with-cart-card__single-variant">
<span class="product-vertical-with-cart-card__price">{{ formattedPrice() }}</span>
<div class="product-vertical-with-cart-card__single-variant-row">
@for (selector of variantSelectors(); track selector.key) {
<select
class="form-select product-vertical-with-cart-card__variant-select"
[attr.aria-label]="selector.label"
[ngModel]="selectedValues()[selector.key]"
(ngModelChange)="onVariantValueChange(selector.key, $event)"
>
@for (option of selector.options; track option) {
<option [ngValue]="option">{{ option }}</option>
}
</select>
}
<app-quantity-selector [(quantity)]="quantity" />
</div>
</div>
} @else {
<div class="product-vertical-with-cart-card__multiple-variants">
<div class="product-vertical-with-cart-card__summary-column">
<div class="product-vertical-with-cart-card__variants">
<div class="product-vertical-with-cart-card__summary">
<span class="product-vertical-with-cart-card__price">{{ formattedPrice() }}</span>
<app-quantity-selector [(quantity)]="quantity" />
</div>

View File

@@ -60,41 +60,20 @@
padding-inline: 12px;
}
&__single-variant {
&__variants {
display: grid;
justify-items: center;
gap: 16px;
}
&__single-variant-row {
&__variant-selectors {
display: flex;
align-items: center;
width: 100%;
gap: 8px;
}
&__multiple-variants {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
align-items: center;
gap: 16px;
}
&__summary-column,
&__variant-selectors {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
min-width: 0;
}
&__variant-selectors {
align-items: stretch;
}
&__variant-select {
flex: 1 1 0;
min-width: 0;
height: 40px;
border-color: #d8d8d8;
@@ -107,14 +86,6 @@
}
}
&__single-variant-row &__variant-select {
flex: 1 1 auto;
}
&__single-variant-row app-quantity-selector {
flex: 0 0 auto;
}
&__price {
color: var(--tenant-primary, #009933);
font-size: 22px;

View File

@@ -116,7 +116,7 @@ describe('ProductVerticalWithCartCardComponent', () => {
).not.toBeNull();
});
it('places a single variant selector in the same row as the quantity', async () => {
it('places price and quantity together above a single variant selector', async () => {
const fixture = await createComponent();
fixture.componentRef.setInput('variants', [
{ id: 10, values: { fecha: '10 de octubre' } },
@@ -125,16 +125,18 @@ describe('ProductVerticalWithCartCardComponent', () => {
fixture.detectChanges();
const element = fixture.nativeElement as HTMLElement;
const row = element.querySelector('.product-vertical-with-cart-card__single-variant-row');
const summary = element.querySelector('.product-vertical-with-cart-card__summary');
const selectors = element.querySelector('.product-vertical-with-cart-card__variant-selectors');
expect(row?.querySelectorAll('.product-vertical-with-cart-card__variant-select')).toHaveLength(
1,
);
expect(row?.querySelector('app-quantity-selector')).not.toBeNull();
expect(element.querySelector('.product-vertical-with-cart-card__multiple-variants')).toBeNull();
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'),
).toHaveLength(1);
expect(selectors?.querySelector('app-quantity-selector')).toBeNull();
});
it('uses separate summary and selector columns for multiple variant attributes', async () => {
it('places all variant selectors together below price and quantity', async () => {
const fixture = await createComponent();
fixture.componentRef.setInput('variants', [
{ id: 20, values: { fecha: '10 de octubre', turno: 'Mañana' } },
@@ -143,13 +145,13 @@ describe('ProductVerticalWithCartCardComponent', () => {
fixture.detectChanges();
const element = fixture.nativeElement as HTMLElement;
const layout = element.querySelector('.product-vertical-with-cart-card__multiple-variants');
const layout = element.querySelector('.product-vertical-with-cart-card__variants');
expect(layout?.querySelector('.product-vertical-with-cart-card__summary')).not.toBeNull();
expect(
layout?.querySelector('.product-vertical-with-cart-card__summary-column'),
).not.toBeNull();
expect(
layout?.querySelectorAll('.product-vertical-with-cart-card__variant-select'),
layout?.querySelectorAll(
'.product-vertical-with-cart-card__variant-selectors .product-vertical-with-cart-card__variant-select',
),
).toHaveLength(2);
});

View File

@@ -78,9 +78,6 @@ export class ProductVerticalWithCartCardComponent {
});
protected readonly hasVariants = computed(() => this.variantSelectors().length > 0);
protected readonly hasMultipleVariantSelectors = computed(
() => this.variantSelectors().length > 1,
);
constructor() {
effect(() => {