Keep usage overflow chip width stable as hidden count changes (#26873)

The "+N" overflow chip in the status bar changed width whenever its count changed, so the density probe that decides how many usage chips fit kept re-measuring. Reserve the width of the provider-count label with an invisible sizer span, so the chip keeps the same size across counts.

- Pass the total provider count into UsageOverflowChip and render it in an invisible, grid-stacked sizer span.
- Overlay the visible "+N" label on the same grid cell so the chip width tracks the total, not the hidden subset.
- Add an e2e test that loads ten usage providers, shrinks the viewport to collapse them, and checks the chip width stays stable and no "Maximum update depth" or React #185 errors occur.
This commit is contained in:
Jinjing
2026-10-09 05:58:50 -07:00
committed by GitHub
parent 05ab94bd65
commit c9c6a00449
4 changed files with 113 additions and 3 deletions
@@ -95,9 +95,11 @@ export function getUsageTone(p: ProviderRateLimits): UsageTone {
*/
export function UsageOverflowChip({
hidden,
providerCount,
display
}: {
hidden: readonly ProviderRateLimits[]
providerCount: number
display: UsagePercentageDisplay
}): React.JSX.Element {
const tones = hidden.map(getUsageTone)
@@ -128,9 +130,13 @@ export function UsageOverflowChip({
value0: names
}
)}
className="inline-flex h-4 items-center rounded-full border border-border px-1.5 text-[11px] font-medium tabular-nums text-foreground data-[tone=urgent]:border-destructive/40 data-[tone=urgent]:text-destructive data-[tone=warning]:border-status-warning-border data-[tone=warning]:text-status-warning data-[usage-collapsed=true]:invisible data-[usage-collapsed=true]:absolute"
className="inline-grid h-4 items-center justify-items-center rounded-full border border-border px-1.5 text-[11px] font-medium tabular-nums text-foreground data-[tone=urgent]:border-destructive/40 data-[tone=urgent]:text-destructive data-[tone=warning]:border-status-warning-border data-[tone=warning]:text-status-warning data-[usage-collapsed=true]:invisible data-[usage-collapsed=true]:absolute"
>
+{Math.max(1, hidden.length)}
{/* Why: count changes must not invalidate density measurements and restart probing. */}
<span aria-hidden="true" className="invisible col-start-1 row-start-1">
+{providerCount}
</span>
<span className="col-start-1 row-start-1">+{Math.max(1, hidden.length)}</span>
</span>
)
}
@@ -161,6 +161,7 @@ export function StatusBarSurface({
))}
{collapseUsage ? (
<UsageOverflowChip
providerCount={rosterProviders.length}
hidden={rosterProviders.filter((p) =>
collapsedUsageProviders.includes(p.provider)
)}
@@ -80,7 +80,9 @@ describe('Cursor compact usage headline', () => {
expect(getUsageTone(p)).toBe('urgent')
expect(getTightestUsageSection(p)?.label).toBe(bucket)
expect(getUsageHeadlineSection(p)?.label).toBe('Cursor Models')
const overflow = renderToStaticMarkup(<UsageOverflowChip hidden={[p]} display="used" />)
const overflow = renderToStaticMarkup(
<UsageOverflowChip hidden={[p]} providerCount={1} display="used" />
)
expect(overflow).toContain('data-tone="urgent"')
expect(
pickCollapsedUsageChips(
@@ -0,0 +1,101 @@
import type { ProviderRateLimits } from '../../src/shared/rate-limit-types'
import { expect, test } from './helpers/orca-app'
test.use({ seedTestRepo: false })
test('ten usage providers survive a narrow viewport and expand again', async ({
orcaPage: page
}, testInfo) => {
const densityErrors: string[] = []
const captureDensityError = (message: string): void => {
if (/Maximum update depth|Minified React error #185/.test(message)) {
densityErrors.push(message)
}
}
page.on('pageerror', (error) => captureDensityError(error.message))
page.on('console', (message) => captureDensityError(message.text()))
await page.setViewportSize({ width: 1800, height: 800 })
await page.evaluate(() => {
const store = window.__store
if (!store) {
throw new Error('store unavailable')
}
const limits = (provider: ProviderRateLimits['provider']): ProviderRateLimits => ({
provider,
session: { usedPercent: 25, windowMinutes: 300, resetsAt: null, resetDescription: null },
weekly: null,
status: 'ok',
updatedAt: Date.now(),
error: null
})
store.setState({
statusBarVisible: true,
statusBarUsageMode: 'verbose',
detectedAgentIds: null,
statusBarItems: [
'claude',
'codex',
'gemini',
'antigravity',
'opencode-go',
'kimi',
'minimax',
'grok',
'cursor',
'zcode',
'resource-usage',
'ports',
'ssh'
],
statusBarCompactChangeNoticeDismissed: true,
usagePercentageDisplayChangeNoticeDismissed: true,
rateLimits: {
...store.getState().rateLimits,
claude: limits('claude'),
codex: limits('codex'),
gemini: limits('gemini'),
antigravity: limits('antigravity'),
opencodeGo: limits('opencode-go'),
kimi: limits('kimi'),
minimax: limits('minimax'),
grok: limits('grok'),
cursor: limits('cursor'),
zcode: limits('zcode')
}
})
})
const usage = page.getByRole('button', { name: 'Usage', exact: true })
const collapsed = usage.locator('[data-usage-chip][data-usage-collapsed=true]')
const overflow = usage.locator('[data-usage-more]')
await expect(usage).toBeVisible()
await expect(usage.locator('[data-usage-chip]')).toHaveCount(10)
await expect(collapsed).toHaveCount(0)
let badgeWidth = 0
for (const width of [180, 600, 180, 1800]) {
await page.setViewportSize({ width, height: 800 })
await expect(usage).toBeVisible()
if (width === 180) {
await expect(collapsed).toHaveCount(10)
await expect(overflow).toBeVisible()
await expect.poll(() => overflow.innerText()).toBe('+10')
badgeWidth = await overflow.evaluate((element) => element.getBoundingClientRect().width)
} else if (width === 600) {
await expect(overflow).toBeVisible()
await expect.poll(() => overflow.innerText()).toMatch(/^\+[1-9]$/)
await expect
.poll(() => overflow.evaluate((element) => element.getBoundingClientRect().width))
.toBeCloseTo(badgeWidth)
} else if (width === 1800) {
await expect(collapsed).toHaveCount(0)
await expect(overflow).toHaveCount(0)
}
expect(densityErrors).toEqual([])
await testInfo.attach(`status-bar-${width}`, {
body: await page.screenshot(),
contentType: 'image/png'
})
}
})