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:
committed by
dermotduffy
co-authored by
Yannik Lieblinger
parent
ab683df5e8
commit
3f5a35c7f6
@@ -286,8 +286,12 @@ export class MediaGridController {
|
|||||||
// Reset the column CSS sizes first.
|
// Reset the column CSS sizes first.
|
||||||
this._setColumnSizeStyles();
|
this._setColumnSizeStyles();
|
||||||
|
|
||||||
// Need to recreate the masonry layout since the column width will differ.
|
// Update the column width on the existing Masonry instance rather than
|
||||||
this._createMasonry();
|
// 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();
|
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 children = createChildren();
|
||||||
const parent = createParent({ children: children });
|
const parent = createParent({ children: children });
|
||||||
createController(parent);
|
createController(parent);
|
||||||
@@ -508,18 +508,16 @@ describe('MediaGridController', () => {
|
|||||||
// Clear mock state.
|
// Clear mock state.
|
||||||
vi.mocked(Masonry).mockClear();
|
vi.mocked(Masonry).mockClear();
|
||||||
vi.mocked(masonry.layout)?.mockClear();
|
vi.mocked(masonry.layout)?.mockClear();
|
||||||
|
vi.mocked(masonry.option)?.mockClear();
|
||||||
|
|
||||||
// Resize the host.
|
// Resize the host.
|
||||||
setElementWidth(parent, 3000);
|
setElementWidth(parent, 3000);
|
||||||
triggerResizeObserver('host');
|
triggerResizeObserver('host');
|
||||||
|
|
||||||
// Masonry should be reconstructed, styles set and layout called.
|
// Masonry should not be recreated, but column width should be updated
|
||||||
expect(Masonry).toBeCalledWith(
|
// via option() and layout should be called.
|
||||||
parent,
|
expect(Masonry).not.toBeCalled();
|
||||||
expect.objectContaining({
|
expect(masonry.option).toBeCalledWith({ columnWidth: 599 });
|
||||||
columnWidth: 599,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
expect(
|
expect(
|
||||||
parent.style.getPropertyValue('--advanced-camera-card-grid-column-size'),
|
parent.style.getPropertyValue('--advanced-camera-card-grid-column-size'),
|
||||||
).toBe('599px');
|
).toBe('599px');
|
||||||
@@ -528,10 +526,12 @@ describe('MediaGridController', () => {
|
|||||||
// Clear mock state.
|
// Clear mock state.
|
||||||
vi.mocked(Masonry).mockClear();
|
vi.mocked(Masonry).mockClear();
|
||||||
vi.mocked(masonry.layout)?.mockClear();
|
vi.mocked(masonry.layout)?.mockClear();
|
||||||
|
vi.mocked(masonry.option)?.mockClear();
|
||||||
|
|
||||||
// Triger with the same sizes.
|
// Trigger with the same sizes.
|
||||||
triggerResizeObserver('host');
|
triggerResizeObserver('host');
|
||||||
expect(Masonry).not.toBeCalled();
|
expect(Masonry).not.toBeCalled();
|
||||||
|
expect(masonry.option).not.toBeCalled();
|
||||||
expect(masonry.layout).not.toBeCalled();
|
expect(masonry.layout).not.toBeCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user