Skip to content

Commit 5ba864f

Browse files
committed
test: adds infinite growing height test
1 parent 71ab0a3 commit 5ba864f

6 files changed

Lines changed: 220 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,42 @@
11
# @dschz/solid-uplot
22

3+
## 0.5.0
4+
5+
### Breaking Changes
6+
7+
#### DOM Structure Change
8+
9+
The internal DOM structure has changed to fix the infinite height growth bug when using `autoResize`.
10+
11+
**Before:**
12+
13+
```html
14+
<div class="solid-uplot">
15+
<!-- children -->
16+
<div class="uplot">...</div>
17+
</div>
18+
```
19+
20+
**After:**
21+
22+
```html
23+
<div class="solid-uplot">
24+
<!-- children -->
25+
<div class="solid-uplot-chart">
26+
<div class="uplot">...</div>
27+
</div>
28+
</div>
29+
```
30+
31+
**Migration:**
32+
33+
- If you have CSS targeting `.solid-uplot > .uplot`, update to `.solid-uplot-chart > .uplot` or `.solid-uplot .uplot`
34+
- The `ref` prop still points to the container where uPlot is rendered (now `.solid-uplot-chart`)
35+
36+
### Bug Fixes
37+
38+
- **Fixed infinite height growth bug**: Charts using `autoResize` in unconstrained containers (parent with `height: auto`) no longer grow infinitely. The chart container now uses `flex: 1 1 0` with `min-height: 0`, which allows it to fill available space without forcing parent height growth.
39+
340
## 0.4.0
441

542
### Minor Changes

e2e/resize-infinite-growth.test.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { expect, test } from "@playwright/test";
2+
3+
/**
4+
* Tests for the infinite height growth bug fix when using autoResize.
5+
*
6+
* Previously, when autoResize was enabled and the parent container had no explicit
7+
* height constraint, a feedback loop occurred causing infinite height growth.
8+
*
9+
* The fix uses `flex: 1 1 0` with `min-height: 0` on the chart container, which
10+
* prevents the chart from forcing parent height growth while still filling
11+
* available space.
12+
*
13+
* Both constrained and unconstrained containers should now maintain stable heights.
14+
*/
15+
test.describe("Infinite Height Growth Bug (Fixed)", () => {
16+
test.beforeEach(async ({ page }) => {
17+
await page.goto("/resize-bug-test");
18+
// Wait for charts to render
19+
await page.waitForSelector(".u-wrap");
20+
});
21+
22+
test("unconstrained container with autoResize maintains stable height", async ({ page }) => {
23+
const container = page.getByTestId("unconstrained-container");
24+
25+
// Get initial height after first render
26+
const initialHeight = await container.evaluate((el) => el.getBoundingClientRect().height);
27+
28+
// Initial height should be reasonable (not zero, not huge)
29+
expect(initialHeight).toBeGreaterThan(0);
30+
31+
// Wait to ensure no growth occurs
32+
await page.waitForTimeout(1000);
33+
34+
// Get final height
35+
const finalHeight = await container.evaluate((el) => el.getBoundingClientRect().height);
36+
37+
// Assert: height should NOT have grown significantly
38+
// Allow 50% tolerance for initial render adjustments, but no infinite growth
39+
expect(finalHeight).toBeLessThanOrEqual(initialHeight * 1.5);
40+
});
41+
42+
test("constrained container with autoResize maintains stable height", async ({ page }) => {
43+
const container = page.getByTestId("constrained-container");
44+
45+
// Get initial height (should be 400px as set in the container style)
46+
const initialHeight = await container.evaluate((el) => el.getBoundingClientRect().height);
47+
48+
// Verify initial height is the constrained value
49+
expect(initialHeight).toBe(400);
50+
51+
// Wait to ensure no unexpected growth
52+
await page.waitForTimeout(1000);
53+
54+
// Get final height
55+
const finalHeight = await container.evaluate((el) => el.getBoundingClientRect().height);
56+
57+
// Assert: height should remain stable at 400px
58+
expect(finalHeight).toBe(400);
59+
});
60+
});

playground/App.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Home } from "./pages/Home";
99
import { LegendShowcasePage } from "./pages/LegendShowcase";
1010
import { MultiPlotPage } from "./pages/MultiPlot";
1111
import { PluginsPage } from "./pages/Plugins";
12+
import { ResizeBugTest } from "./pages/ResizeBugTest";
1213
import { Streaming } from "./pages/Streaming";
1314
import { TooltipDialogPage } from "./pages/TooltipDialog";
1415
import { Sidebar } from "./Sidebar";
@@ -32,6 +33,8 @@ export const App: Component = () => {
3233
<Route path="/multi-plot" component={MultiPlotPage} />
3334
<Route path="/children-placement" component={ChildrenPlacementPlayground} />
3435
<Route path="/dynamic-resize" component={DynamicResize} />
36+
{/* Test page for E2E tests - not linked in sidebar */}
37+
<Route path="/resize-bug-test" component={ResizeBugTest} />
3538
<Route path="*" component={ErrorPage} />
3639
</Router>
3740
);

playground/pages/ResizeBugTest.tsx

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { type Component, createSignal } from "solid-js";
2+
import uPlot from "uplot";
3+
4+
import { SolidUplot } from "../../src/SolidUplot";
5+
6+
const generateData = (): uPlot.AlignedData => {
7+
const length = 10;
8+
const x = Array.from({ length }, (_, i) => i);
9+
const y = Array.from({ length }, () => Math.random() * 100);
10+
return [x, y];
11+
};
12+
13+
/**
14+
* Regression test page for the infinite height growth bug fix.
15+
* This page is NOT linked in the sidebar - access via direct URL only.
16+
*
17+
* Previously, when autoResize was enabled in an unconstrained container (no explicit height),
18+
* the chart would enter a feedback loop where it kept growing indefinitely.
19+
*
20+
* The fix uses `flex: 1 1 0` with `min-height: 0` on the chart container, which prevents
21+
* the chart from forcing parent height growth while still filling available space.
22+
*
23+
* Both scenarios below should now maintain stable heights.
24+
*/
25+
export const ResizeBugTest: Component = () => {
26+
const [data] = createSignal(generateData());
27+
28+
return (
29+
<div class="p-8">
30+
<h1 class="mb-4 text-2xl font-bold">Resize Regression Test Page</h1>
31+
<p class="mb-8 text-gray-600">
32+
This page verifies the fix for the infinite height growth bug when using autoResize. Both
33+
scenarios below should maintain stable heights. Access via direct URL only.
34+
</p>
35+
36+
{/* Previously Bug Case: Unconstrained container - now fixed */}
37+
<section class="mb-8">
38+
<h2 class="mb-2 text-lg font-semibold text-blue-700">
39+
Unconstrained Container (Previously Buggy - Now Fixed)
40+
</h2>
41+
<p class="mb-2 text-sm text-blue-600">
42+
This container has no height constraint. Previously this caused infinite growth, but the
43+
fix prevents the chart from forcing parent height growth.
44+
</p>
45+
<div data-testid="unconstrained-container">
46+
<SolidUplot
47+
autoResize
48+
data={data()}
49+
series={[{}, { label: "Series", stroke: "#3b82f6", width: 2 }]}
50+
scales={{ x: { time: false } }}
51+
childrenPlacement="top"
52+
>
53+
<div
54+
style={{
55+
height: "50px",
56+
background: "#dbeafe",
57+
padding: "8px",
58+
display: "flex",
59+
"align-items": "center",
60+
"justify-content": "center",
61+
}}
62+
>
63+
Child content that adds height
64+
</div>
65+
</SolidUplot>
66+
</div>
67+
</section>
68+
69+
{/* Safe Case: Constrained container */}
70+
<section>
71+
<h2 class="mb-2 text-lg font-semibold text-green-700">Constrained Container</h2>
72+
<p class="mb-2 text-sm text-green-600">
73+
This container has explicit height (400px) - autoResize fills the available space
74+
correctly.
75+
</p>
76+
<div
77+
data-testid="constrained-container"
78+
style={{ height: "400px", border: "2px dashed #22c55e" }}
79+
>
80+
<SolidUplot
81+
autoResize
82+
data={data()}
83+
series={[{}, { label: "Series", stroke: "#22c55e", width: 2 }]}
84+
scales={{ x: { time: false } }}
85+
childrenPlacement="top"
86+
>
87+
<div
88+
style={{
89+
height: "50px",
90+
background: "#dcfce7",
91+
padding: "8px",
92+
display: "flex",
93+
"align-items": "center",
94+
"justify-content": "center",
95+
}}
96+
>
97+
Child content that adds height
98+
</div>
99+
</SolidUplot>
100+
</div>
101+
</section>
102+
</div>
103+
);
104+
};

src/SolidUplot.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,6 @@ export const SolidUplot = <T extends VoidStruct = VoidStruct>(
262262
return (
263263
<div
264264
id="solid-uplot-root"
265-
ref={mergeRefs(local.ref, (el) => (container = el))}
266265
class={classes()}
267266
style={{
268267
display: "flex",
@@ -272,12 +271,26 @@ export const SolidUplot = <T extends VoidStruct = VoidStruct>(
272271
width: "100%",
273272
height: "100%",
274273
"min-width": "0",
275-
"min-height": "0",
276274
}),
277275
...local.style,
278276
}}
279277
>
280278
{local.children}
279+
<div
280+
ref={mergeRefs(local.ref, (el) => (container = el))}
281+
class="solid-uplot-chart"
282+
style={{
283+
position: "relative",
284+
// When autoResize is enabled, use flex to fill remaining space
285+
// flex-basis: 0 prevents content from dictating size (fixes infinite height bug)
286+
// min-height uses the height prop as fallback for unconstrained containers
287+
...(local.autoResize && {
288+
flex: "1 1 0",
289+
"min-height": `${updateableOptions.height}px`,
290+
"min-width": "0",
291+
}),
292+
}}
293+
/>
281294
</div>
282295
);
283296
};

vite.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { defineConfig as defineViteConfig, mergeConfig } from "vite";
44
import solidPlugin from "vite-plugin-solid";
55
import { configDefaults, defineConfig as defineVitestConfig } from "vitest/config";
66

7-
const TEST_EXCLUDES = [...configDefaults.exclude, "src/index.tsx", "playground"];
7+
const TEST_EXCLUDES = [...configDefaults.exclude, "src/index.tsx", "playground", "e2e"];
88
const COVERAGE_EXCLUDE = [...TEST_EXCLUDES, "**/*.test.{ts,tsx}"];
99

1010
const viteConfig = defineViteConfig({

0 commit comments

Comments
 (0)