fix: prevent infinite resize loop in media grid Masonry layout (#2413)

- Credit to @lieblinger.

---------

Co-authored-by: Yannik Lieblinger <yannik@lieblinger.de>
This commit is contained in:
Dermot Duffy
2026-06-30 17:44:49 -07:00
committed by dermotduffy
co-authored by Yannik Lieblinger
parent ab683df5e8
commit 3f5a35c7f6
2 changed files with 15 additions and 11 deletions
+6 -2
View File
@@ -286,8 +286,12 @@ export class MediaGridController {
// Reset the column CSS sizes first.
this._setColumnSizeStyles();
// Need to recreate the masonry layout since the column width will differ.
this._createMasonry();
// Update the column width on the existing Masonry instance rather than
// destroying and recreating it. A destroy resets the container height to
// 0, which can cause an ancestor scrollbar to appear/disappear, changing
// the available width and triggering an infinite resize oscillation.
this._masonry?.option?.({ columnWidth: this._getColumnSize() });
this._throttledLayout();
}
}
@@ -491,7 +491,7 @@ describe('MediaGridController', () => {
expect(masonry.layout).toBeCalled();
});
it('should re-create masonry when host size changes', () => {
it('should update masonry column width when host size changes', () => {
const children = createChildren();
const parent = createParent({ children: children });
createController(parent);
@@ -508,18 +508,16 @@ describe('MediaGridController', () => {
// Clear mock state.
vi.mocked(Masonry).mockClear();
vi.mocked(masonry.layout)?.mockClear();
vi.mocked(masonry.option)?.mockClear();
// Resize the host.
setElementWidth(parent, 3000);
triggerResizeObserver('host');
// Masonry should be reconstructed, styles set and layout called.
expect(Masonry).toBeCalledWith(
parent,
expect.objectContaining({
columnWidth: 599,
}),
);
// Masonry should not be recreated, but column width should be updated
// via option() and layout should be called.
expect(Masonry).not.toBeCalled();
expect(masonry.option).toBeCalledWith({ columnWidth: 599 });
expect(
parent.style.getPropertyValue('--advanced-camera-card-grid-column-size'),
).toBe('599px');
@@ -528,10 +526,12 @@ describe('MediaGridController', () => {
// Clear mock state.
vi.mocked(Masonry).mockClear();
vi.mocked(masonry.layout)?.mockClear();
vi.mocked(masonry.option)?.mockClear();
// Triger with the same sizes.
// Trigger with the same sizes.
triggerResizeObserver('host');
expect(Masonry).not.toBeCalled();
expect(masonry.option).not.toBeCalled();
expect(masonry.layout).not.toBeCalled();
});