Skip to content

Commit

Permalink
Improve jump compensation at end
Browse files Browse the repository at this point in the history
  • Loading branch information
inokawa committed Jul 24, 2024
1 parent 4aec4e4 commit b563967
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 1 deletion.
36 changes: 36 additions & 0 deletions e2e/VList.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,42 @@ test.describe("check if scroll jump compensation works", () => {
}
});

test("resize at bottom", async ({ page, browserName }) => {
await page.goto(storyUrl("advanced-collapse--two-stage-render"));
const component = await getScrollable(page);
const container = await getVirtualizer(page);
await component.waitForElementState("stable");
await page.waitForTimeout(500);

// should reach to the bottom within the specified number of tries
for (let i = 0; i <= 1; i++) {
// scroll to bottom
await scrollToBottom(component);

const prevBottomItem = getLastItem(component);

// wait for resize completed
await page.waitForTimeout(500);
await container.waitForElementState("stable");

const bottomItem = getLastItem(component);

// check if distance from the bottom isn't changed by resizes
const prevBottom = (await prevBottomItem).bottom;
const bottom = (await bottomItem).bottom;
if (
browserName === "firefox"
? Math.abs(bottom - prevBottom) <= 2
: bottom === prevBottom
) {
// succeeded
return;
}
}

throw new Error(`couldn't reach the bottom`);
});

test("resize with smooth scroll", async ({ page }) => {
await page.goto(storyUrl("advanced-collapse--collapse-and-scroll"));
const component = await getScrollable(page);
Expand Down
4 changes: 3 additions & 1 deletion src/core/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,9 @@ export const createVirtualStore = (
return [
_flushedJump,
// Use absolute position not to exceed scrollable bounds
_scrollMode === SCROLL_BY_SHIFT,
_scrollMode === SCROLL_BY_SHIFT ||
// https://github.com/inokawa/virtua/discussions/475
getRelativeScrollOffset() + viewportSize >= getTotalSize(),
];
},
_subscribe(target, cb) {
Expand Down
46 changes: 46 additions & 0 deletions stories/react/advanced/Collapse.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,49 @@ const Collapser2 = ({ id, onHidden }: { id: number; onHidden: () => void }) => {
</div>
);
};

const TwoStageRenderItem = ({ index }: { index: number }) => {
const getRandomHeight = () => Math.floor(Math.random() * 100) + 50; // Random height between 50 and 150 px
const getRandomDelay = () => Math.floor(Math.random() * 300) + 100; // Random delay between 100 and 400 ms

const [immediateHeight] = useState(() => getRandomHeight());
const [delayedHeight, setDelayedHeight] = useState<number | null>(null);

useEffect(() => {
const delay = getRandomDelay();
const timer = setTimeout(() => {
setDelayedHeight(getRandomHeight());
}, delay);

return () => clearTimeout(timer);
}, []);

return (
<div style={{ border: "5px solid black", margin: "5px 0" }}>
<div style={{ height: immediateHeight, background: "#e0e0e0" }}>
Immediate Content {index} (Height: {immediateHeight}px)
</div>
{delayedHeight !== null ? (
<div style={{ height: delayedHeight, background: "#f0f0f0" }}>
Delayed Content {index} (Height: {delayedHeight}px)
</div>
) : (
<div style={{ padding: "10px", background: "#ffe0e0" }}>Loading...</div>
)}
</div>
);
};

export const TwoStageRender: StoryObj = {
render: () => {
return (
<div style={{ height: "400px", border: "1px solid black" }}>
<VList style={{ height: "100%", width: "100%" }}>
{Array.from({ length: 100 }).map((_, index) => (
<TwoStageRenderItem key={index} index={index} />
))}
</VList>
</div>
);
},
};

0 comments on commit b563967

Please sign in to comment.