feat(carousel): enhance product list layout with adaptive grid and simple vertical options
This commit is contained in:
@@ -53,6 +53,8 @@
|
|||||||
class="product-list"
|
class="product-list"
|
||||||
[class.product-list--row]="effectiveLayout() === 'row'"
|
[class.product-list--row]="effectiveLayout() === 'row'"
|
||||||
[class.product-list--column]="effectiveLayout() !== 'row'"
|
[class.product-list--column]="effectiveLayout() !== 'row'"
|
||||||
|
[class.product-list--adaptive]="groupLayout() !== 'simple_vertical'"
|
||||||
|
[class.product-list--simple-vertical]="groupLayout() === 'simple_vertical'"
|
||||||
>
|
>
|
||||||
@for (item of itemData(); track item.id; let index = $index) {
|
@for (item of itemData(); track item.id; let index = $index) {
|
||||||
<ng-container
|
<ng-container
|
||||||
|
|||||||
@@ -4,39 +4,34 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.product-list {
|
.product-list {
|
||||||
|
--product-list-item-min-width: 16rem;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
|
|
||||||
&--row {
|
&--row {
|
||||||
|
--product-list-item-min-width: 32rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--adaptive {
|
||||||
|
grid-template-columns: repeat(
|
||||||
|
auto-fit,
|
||||||
|
minmax(min(100%, var(--product-list-item-min-width)), 1fr)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--simple-vertical {
|
||||||
grid-template-columns: minmax(0, 1fr);
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
|
||||||
.product-list__item {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&--column {
|
|
||||||
container-name: product-list;
|
|
||||||
container-type: inline-size;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: stretch;
|
|
||||||
|
|
||||||
.product-list__item {
|
|
||||||
flex: 0 0 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__item {
|
&__item {
|
||||||
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
&--carousel {
|
&--carousel {
|
||||||
width: 100%;
|
|
||||||
min-width: clamp(15rem, 30vw, 18rem);
|
min-width: clamp(15rem, 30vw, 18rem);
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&--carousel-row {
|
&--carousel-row {
|
||||||
@@ -45,27 +40,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@container product-list (min-width: 544px) {
|
|
||||||
.product-list--column .product-list__item {
|
|
||||||
flex-basis: calc((100% - 1.5rem) / 2);
|
|
||||||
max-width: calc((100% - 1.5rem) / 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@container product-list (min-width: 828px) {
|
|
||||||
.product-list--column .product-list__item {
|
|
||||||
flex-basis: calc((100% - 3rem) / 3);
|
|
||||||
max-width: calc((100% - 3rem) / 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@container product-list (min-width: 1112px) {
|
|
||||||
.product-list--column .product-list__item {
|
|
||||||
flex-basis: calc((100% - 4.5rem) / 4);
|
|
||||||
max-width: calc((100% - 4.5rem) / 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.product-list__paginator {
|
.product-list__paginator {
|
||||||
margin-top: 1.5rem;
|
margin-top: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,11 +79,12 @@ describe('ProductListComponent', () => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
it('renders every row product at full available width', async () => {
|
it('renders row products in the adaptive grid', async () => {
|
||||||
const fixture = await render('row');
|
const fixture = await render('row');
|
||||||
const element = fixture.nativeElement as HTMLElement;
|
const element = fixture.nativeElement as HTMLElement;
|
||||||
|
|
||||||
expect(element.querySelector('.product-list--row')).not.toBeNull();
|
expect(element.querySelector('.product-list--row')).not.toBeNull();
|
||||||
|
expect(element.querySelector('.product-list--adaptive')).not.toBeNull();
|
||||||
expect(element.querySelectorAll('app-product-row-card')).toHaveLength(2);
|
expect(element.querySelectorAll('app-product-row-card')).toHaveLength(2);
|
||||||
expect(element.querySelector('app-product-column-with-image')).toBeNull();
|
expect(element.querySelector('app-product-column-with-image')).toBeNull();
|
||||||
});
|
});
|
||||||
@@ -143,9 +144,19 @@ describe('ProductListComponent', () => {
|
|||||||
const element = fixture.nativeElement as HTMLElement;
|
const element = fixture.nativeElement as HTMLElement;
|
||||||
|
|
||||||
expect(element.querySelectorAll('app-product-row-card')).toHaveLength(2);
|
expect(element.querySelectorAll('app-product-row-card')).toHaveLength(2);
|
||||||
|
expect(element.querySelector('.product-list--adaptive')).not.toBeNull();
|
||||||
expect(element.querySelector('app-paginator')).toBeNull();
|
expect(element.querySelector('app-paginator')).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('keeps simple vertical groups to one full-width item per row', async () => {
|
||||||
|
const fixture = await render('column_with_image', items, 'simple_vertical');
|
||||||
|
const element = fixture.nativeElement as HTMLElement;
|
||||||
|
|
||||||
|
expect(element.querySelector('.product-list--simple-vertical')).not.toBeNull();
|
||||||
|
expect(element.querySelector('.product-list--adaptive')).toBeNull();
|
||||||
|
expect(element.querySelectorAll('app-product-column-with-image')).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
it('renders carousel groups with the reusable carousel', async () => {
|
it('renders carousel groups with the reusable carousel', async () => {
|
||||||
const fixture = await render('column_with_image', items, 'carousel');
|
const fixture = await render('column_with_image', items, 'carousel');
|
||||||
const element = fixture.nativeElement as HTMLElement;
|
const element = fixture.nativeElement as HTMLElement;
|
||||||
|
|||||||
Reference in New Issue
Block a user