Skip to content

Commit c09248d

Browse files
authored
Merge 7792f8f into 4c1915e
2 parents 4c1915e + 7792f8f commit c09248d

3 files changed

Lines changed: 134 additions & 6 deletions

File tree

.changeset/moody-singers-doubt.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@lit-labs/virtualizer': patch
3+
---
4+
5+
Fixed an issue #3400 where Virtualizer incorrectly calculated its width effecting positioning of items in multi-column layouts.

packages/labs/virtualizer/src/Virtualizer.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -624,12 +624,16 @@ export class Virtualizer {
624624
bottom = window.innerHeight;
625625
right = window.innerWidth;
626626

627-
for (const ancestor of this._clippingAncestors) {
628-
const ancestorBounds = ancestor.getBoundingClientRect();
629-
top = Math.max(top, ancestorBounds.top);
630-
left = Math.max(left, ancestorBounds.left);
631-
bottom = Math.min(bottom, ancestorBounds.bottom);
632-
right = Math.min(right, ancestorBounds.right);
627+
const ancestorBounds = this._clippingAncestors.map((ancestor) =>
628+
ancestor.getBoundingClientRect()
629+
);
630+
ancestorBounds.unshift(hostElementBounds);
631+
632+
for (const bounds of ancestorBounds) {
633+
top = Math.max(top, bounds.top);
634+
left = Math.max(left, bounds.left);
635+
bottom = Math.min(bottom, bounds.bottom);
636+
right = Math.min(right, bounds.right);
633637
}
634638

635639
const scrollTop = top - hostElementBounds.top + hostElement.scrollTop;
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/**
2+
* @license
3+
* Copyright 2022 Google LLC
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
import {array, ignoreBenignErrors, until} from '../helpers.js';
8+
import {LitElement, css} from 'lit';
9+
import {customElement, property} from 'lit/decorators.js';
10+
import {LitVirtualizer} from '../../lit-virtualizer.js';
11+
import {expect, html, fixture} from '@open-wc/testing';
12+
import {grid} from '../../layouts/grid.js';
13+
14+
describe('Properly sizing virtualizer within host element', () => {
15+
ignoreBenignErrors(beforeEach, afterEach);
16+
17+
@customElement('custom-element-containing-lit-virtualizer')
18+
class CustomElementContainingLitVirtualizer extends LitElement {
19+
static styles = css`
20+
:host {
21+
display: block;
22+
position: absolute;
23+
box-sizing: border-box;
24+
top: 0;
25+
left: 0;
26+
bottom: 0;
27+
right: 0;
28+
padding: 50px;
29+
}
30+
lit-virtualizer {
31+
display: block;
32+
position: absolute;
33+
}
34+
.item {
35+
display: block;
36+
position: absolute;
37+
}
38+
`;
39+
40+
@property({type: Array})
41+
items = [];
42+
43+
render() {
44+
return html`
45+
<lit-virtualizer
46+
.layout=${grid({
47+
itemSize: {width: '25px', height: '25px'},
48+
direction: 'vertical',
49+
gap: '0px',
50+
flex: false,
51+
})}
52+
.items=${this.items}
53+
.renderItem=${(item: number) => html`
54+
<div class="item">[${item}]</div>
55+
`}
56+
>
57+
</lit-virtualizer>
58+
`;
59+
}
60+
}
61+
62+
it('should size the virtualizer width to the host element', async () => {
63+
const items = array(100);
64+
const root = await fixture(html`
65+
<div class="root">
66+
<style>
67+
.container {
68+
display: block;
69+
position: absolute;
70+
width: 200px;
71+
box-sizing: border-box;
72+
}
73+
</style>
74+
<div class="container">
75+
<custom-element-containing-lit-virtualizer .items=${items}>
76+
</custom-element-containing-lit-virtualizer>
77+
</div>
78+
</div>
79+
`);
80+
81+
await until(
82+
() =>
83+
root.querySelector(
84+
'custom-element-containing-lit-virtualizer'
85+
) instanceof CustomElementContainingLitVirtualizer
86+
);
87+
const ceclv = root.querySelector(
88+
'custom-element-containing-lit-virtualizer'
89+
)!;
90+
await until(
91+
() =>
92+
ceclv.shadowRoot?.querySelector('lit-virtualizer') instanceof
93+
LitVirtualizer
94+
);
95+
const litVirtualizer = ceclv.shadowRoot!.querySelector(
96+
'lit-virtualizer'
97+
)! as unknown as HTMLElement;
98+
await until(() => litVirtualizer.textContent?.includes('[4]'));
99+
100+
const renderedItems = [...litVirtualizer.querySelectorAll('.item')];
101+
const rects = renderedItems.map((i) => i.getBoundingClientRect());
102+
103+
const ceclvRect = ceclv.getBoundingClientRect();
104+
const leftOffset = ceclvRect.left;
105+
106+
expect(window.getComputedStyle(ceclv).width).to.equal('200px');
107+
expect(rects[0].left - leftOffset).to.equal(50);
108+
expect(rects[1].left - leftOffset).to.equal(75);
109+
expect(rects[2].left - leftOffset).to.equal(100);
110+
expect(rects[3].left - leftOffset).to.equal(125);
111+
112+
/**
113+
* Prior to fix in #3400, the following assertion would fail because
114+
* virtualizer would have not considered its bounding box properly
115+
* and positioned item 4 outside at 150px instead of expected 50px.
116+
*/
117+
expect(rects[4].left - leftOffset).to.equal(50);
118+
});
119+
});

0 commit comments

Comments
 (0)