Skip to content

Commit 7e79446

Browse files
committed
fix(homepage): resolve dashboard card rendering issues
Prevent the top-row homepage app focus ring from being clipped by the scroll container, and fix the network stats card so upload/download values keep their formatted icon rendering across store updates. Add a focused regression test covering consecutive network speed updates.
1 parent 1277756 commit 7e79446

3 files changed

Lines changed: 79 additions & 25 deletions

File tree

src/components/home/AppsGrid.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export default function AppGrid() {
127127
<TabsContent
128128
key={category}
129129
value={category}
130-
className="sm:mt-2 min-h-0 flex-1 overflow-y-auto scrollbar-hidden sm:scrollbar-default"
130+
className="sm:mt-2 min-h-0 flex-1 overflow-y-auto py-1 scrollbar-hidden sm:scrollbar-default"
131131
>
132132
<motion.div
133133
initial={{ opacity: 0, y: 10 }}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
2+
import { act } from 'react'
3+
import { createRoot, type Root } from 'react-dom/client'
4+
import { JSDOM } from 'jsdom'
5+
import SystemStatValue from './SystemStatValue'
6+
import { store } from './store'
7+
8+
describe('SystemStatValue network speed rendering', () => {
9+
let root: Root | undefined
10+
let container: HTMLDivElement
11+
12+
beforeEach(() => {
13+
const dom = new JSDOM('<!doctype html><html><body><div id="root"></div></body></html>', {
14+
url: 'http://localhost',
15+
})
16+
globalThis.IS_REACT_ACT_ENVIRONMENT = true
17+
globalThis.window = dom.window as typeof globalThis.window
18+
globalThis.document = dom.window.document
19+
globalThis.HTMLElement = dom.window.HTMLElement
20+
globalThis.SVGElement = dom.window.SVGElement
21+
globalThis.Node = dom.window.Node
22+
globalThis.navigator = dom.window.navigator
23+
globalThis.localStorage = dom.window.localStorage
24+
25+
container = dom.window.document.getElementById('root') as HTMLDivElement
26+
root = createRoot(container)
27+
28+
store.systemInfo.networkSpeedDownload.set(0)
29+
})
30+
31+
afterEach(() => {
32+
if (!root) return
33+
act(() => {
34+
root?.unmount()
35+
})
36+
})
37+
38+
test('keeps formatting download speed after consecutive updates', () => {
39+
act(() => {
40+
root.render(<SystemStatValue valueKey="networkSpeedDownload" type="download" />)
41+
})
42+
43+
act(() => {
44+
store.systemInfo.networkSpeedDownload.set(20 * 1024 * 1024)
45+
})
46+
47+
expect(container.innerHTML).toContain('20 MB/s')
48+
expect(container.innerHTML).not.toContain('20971520')
49+
50+
act(() => {
51+
store.systemInfo.networkSpeedDownload.set(21 * 1024 * 1024)
52+
})
53+
54+
expect(container.innerHTML).toContain('21 MB/s')
55+
expect(container.innerHTML).not.toContain('22020096')
56+
})
57+
})

src/components/home/SystemStatValue.tsx

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -41,31 +41,28 @@ function DisplayValue({
4141
type: SystemStatValueType
4242
}) {
4343
const displayValue = store.systemInfo[valueKey].useCompute(value => {
44-
if (type === 'duration') {
45-
return formatDuration(Number(value), { unit: 's' })
44+
switch (type) {
45+
case 'duration':
46+
return formatDuration(Number(value), { unit: 's' })
47+
case 'progress':
48+
return `${value}%`
49+
case 'upload':
50+
return (
51+
<>
52+
<IconArrowUp className="size-4 text-green-500" />{' '}
53+
{formatBytes(Number(value), { precision: 0, unit: '/s' })}
54+
</>
55+
)
56+
case 'download':
57+
return (
58+
<>
59+
<IconArrowDown className="size-4 text-red-500" />{' '}
60+
{formatBytes(Number(value), { precision: 0, unit: '/s' })}
61+
</>
62+
)
63+
default:
64+
return String(value)
4665
}
47-
if (type === 'progress') {
48-
return `${value}%`
49-
}
50-
if (type === 'upload') {
51-
type = 'text'
52-
return (
53-
<>
54-
<IconArrowUp className="size-4 text-green-500" />{' '}
55-
{formatBytes(Number(value), { precision: 0, unit: '/s' })}
56-
</>
57-
)
58-
}
59-
if (type === 'download') {
60-
type = 'text'
61-
return (
62-
<>
63-
<IconArrowDown className="size-4 text-red-500" />{' '}
64-
{formatBytes(Number(value), { precision: 0, unit: '/s' })}
65-
</>
66-
)
67-
}
68-
return String(value)
6966
})
7067
const isTextLike = type === 'text' || type === 'upload' || type === 'download'
7168
return (

0 commit comments

Comments
 (0)