fix(cards): clear model-card min-width on the item element itself

This commit is contained in:
Will Miao
2026-08-07 16:09:51 +08:00
parent 381cd710a2
commit ac7707d0f6
3 changed files with 45 additions and 6 deletions

View File

@@ -533,10 +533,15 @@ export class MasonryScroller {
element.style.width = `${pos.width}px`;
element.style.height = `${pos.height}px`;
// Remove max-width constraint from model-card to allow dynamic sizing
const modelCard = element.querySelector('.model-card');
// Remove max-width/min-width constraints from the model-card to allow
// dynamic sizing. The card is either the item element itself (e.g.
// RecipeCard returns the .model-card root) or a descendant (ModelCard).
const modelCard = element.classList.contains('model-card')
? element
: element.querySelector('.model-card');
if (modelCard) {
modelCard.style.maxWidth = 'none';
modelCard.style.minWidth = '0';
}
return element;

View File

@@ -483,10 +483,18 @@ export class VirtualScroller {
element.style.width = `${this.itemWidth}px`;
element.style.height = `${this.itemHeight}px`;
// Remove max-width constraint from model-card to allow dynamic sizing
const modelCard = element.querySelector('.model-card');
// Remove max-width/min-width constraints from the model-card to allow
// dynamic sizing. The card is either the item element itself (e.g.
// RecipeCard returns the .model-card root) or a descendant (ModelCard).
// Without this, the CSS min-width of 200px forces compact-density cards
// wider than their allocated column, and the last column gets clipped
// by the grid's overflow-x: hidden.
const modelCard = element.classList.contains('model-card')
? element
: element.querySelector('.model-card');
if (modelCard) {
modelCard.style.maxWidth = 'none';
modelCard.style.minWidth = '0';
}
return element;

View File

@@ -40,7 +40,7 @@ function makeItems(dimensions) {
* Build a scroller attached to a stubbed container. clientWidth/clientHeight
* are 0 in jsdom, so they are defined explicitly for deterministic layout.
*/
function createScroller({ items = [], fetchItemsFn, overscan, viewportHeight = 600 } = {}) {
function createScroller({ items = [], fetchItemsFn, overscan, viewportHeight = 600, createItemFn: customCreateItemFn } = {}) {
const wrapper = document.createElement('div');
Object.defineProperty(wrapper, 'clientWidth', { value: CONTAINER_WIDTH, configurable: true });
Object.defineProperty(wrapper, 'clientHeight', { value: viewportHeight, configurable: true });
@@ -55,7 +55,7 @@ function createScroller({ items = [], fetchItemsFn, overscan, viewportHeight = 6
gridElement: grid,
containerElement: wrapper,
scrollContainer: wrapper,
createItemFn,
createItemFn: customCreateItemFn || createItemFn,
fetchItemsFn: fetchMock,
overscan,
});
@@ -206,9 +206,35 @@ describe('MasonryScroller', () => {
expect(el.style.left).toBe(`${scroller.positions[i].left}px`);
expect(el.style.top).toBe(`${scroller.positions[i].top}px`);
expect(el.querySelector('.model-card').style.maxWidth).toBe('none');
expect(el.querySelector('.model-card').style.minWidth).toBe('0');
});
});
it('clears model-card max-width/min-width when the item element is the card root', () => {
// Production shape for recipe cards: RecipeCard returns the .model-card
// element itself, so the scroller must clear constraints on the element
// rather than a descendant (querySelector would find nothing).
const items = makeItems([{ width: 100, height: 200 }]);
const cardRoot = document.createElement('div');
cardRoot.className = 'model-card';
const { scroller, grid, wrapper } = track(createScroller({
items,
viewportHeight: 3000,
createItemFn: () => cardRoot.cloneNode(true),
}));
scroller.refreshWithData(items, items.length, false);
wrapper.scrollTop = 0;
scroller.overscan = 5;
scroller.renderItems();
const rendered = grid.querySelectorAll('.virtual-scroll-item');
expect(rendered.length).toBe(1);
expect(rendered[0].style.maxWidth).toBe('none');
expect(rendered[0].style.minWidth).toBe('0');
});
it('triggers loadMoreItems when scrolled to the bottom', async () => {
const firstPage = makeItems([
{ width: 100, height: 200 },