Repository navigation
fix(plugin-kanban): the swimlane layout takes its column width from the container, not the viewport - #8521
Merged
os-justin merged 2 commits intoSep 8, 2026
Conversation
…he container, not the viewport `KanbanBoardInner` derives `columnInlineStyle` from the board's own slot so an embedded board sizes to its slot; its docblock said this "replaces hard-coded `w-[85vw] sm:w-80`". It had exactly one consumer, `KanbanColumnView`, on the flat path. The swimlane layout paints plain column cells itself and has no column components to inherit from, so its header cells and lane cells kept the viewport classes — the sentence was true of one of the two layouts, and an embedded swimlane board (panel, drawer, pop-out) stayed viewport-sized. Both layouts now read one shared `columnWidthClasses(columnStyle)` helper and the same `columnInlineStyle` object, so the viewport classes step aside on either path when a container width exists and remain the fallback on either path when it does not. The flat layout's rendered width is unchanged. Pinned by `swimlaneContainerAwareColumnWidth-8508.test.tsx`: it reads the inline style rather than a rendered dimension (happy-dom performs no layout) and requires a real positive width at every width tier on both layouts, so deleting the viewport classes without wiring a width cannot pass. Refs: objectui#8508 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-justin
marked this pull request as ready for review
September 8, 2026 05:37
os-justin
enabled auto-merge
September 8, 2026 05:37
This was referenced Sep 8, 2026
Merged
Contributor
❌ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. Which half objected:
📦 Bundle Size Report
Size Limits
|
os-justin
deleted the
claude/issue-8508-swimlane-container-aware-columns
branch
September 8, 2026 15:00
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #8508
The defect, as measured
KanbanBoardInnerderivescolumnInlineStylefrom the board's own slot (useResizeObserveron a wrapper that encloses both layouts), and the docblock over it says this "replaces
hard-coded
w-[85vw] sm:w-80" so an embedded board sizes to its slot. That value had exactlyone consumer:
columnStyle={columnInlineStyle}, inside the non-swimlane branch. Theswimlane branch never read it — its header cells and its lane cells both carried the literal
the docblock says was replaced. So the sentence was true of one of the two layouts, and a
swimlane board embedded in a panel, a drawer or a pop-out — the case the feature exists for —
was still sized to the viewport.
Every line address on the card checks out on
origin/mainatd65b2baa4, including theternary boundary the card asked to confirm first: the swimlane branch opens at
:655, itsheader cells are
:687, its lane cells:721, the alternative opens at:738, and the onlycolumnInlineStyleconsumer is:751, inside it. The card's correction to the earlierreading is right: the swimlane header cells and lane cells carry byte-identical width
classes, so there is no intra-swimlane misalignment to fix.
The change
One shared
columnWidthClasses(columnStyle)helper now holds the two-way switch thatKanbanColumnViewused to spell inline, and both layouts call it with the samecolumnInlineStyleobject:KanbanColumnViewreads it through itscolumnStyleprop, exactly as before.directly, because that layout has no column components to inherit it from.
After the change the
w-[85vw] sm:w-80literal exists at one place in the repository —inside the shared helper's fallback arm — down from three code carriers.
Fallback behaviour is unchanged on both paths: with no measured width
columnInlineStyleisthe empty object and the viewport classes are what sizes a cell.
Pin — a prop, not a measured dimension
swimlaneContainerAwareColumnWidth-8508.test.tsxreads the inline style. It never reads arendered dimension: the sibling pin for objectui#7303 documents that the height defect there
was only catchable in a real Chromium, because happy-dom performs no layout, and this suite
deliberately does not re-attempt that.
The fixture supplies a board width through the one channel
useResizeObserveractually usesin this environment — the single synchronous
getBoundingClientRect()read inside its effect— by stubbing
Element.prototype.getBoundingClientRectfor the duration of a render. Afixture with no width is treated as the third state (
if (!boardWidth) return {}), pinnedseparately on both layouts, never as a stand-in for a sized one.
Nine cases: seven width tiers (both sides of each breakpoint, plus the
Math.max(..., 220)floor), a flat-layout non-regression case, and the third state. Nothing navigates by the width
class being changed — cells are reached through the region's accessible name, the header row's
position, a lane list's accessible name, and a flat column's
role="group".Ablations — run, not predicted; restored by state
Four legs, each mutated from the committed implementation, each proven on disk in both
directions (injected text present and removed text absent, plus a blob-hash inequality
against the HEAD blob), each restored and the restore proven by
git diff HEADempty andgit hash-objectequal to the HEAD blob. Every script carriedtrap ... EXIT INT TERMwithabsolute paths.
Read as failure modes rather than counts, the interesting reading is that the axis the card
predicted would discriminate the caricature — "a real width at each of the three tiers" — does
not discriminate it. In the sized states the caricature fails on exactly the assertion the
plain defect fails on. What separates them is the third-state case, which the defect leg
passes and the caricature leg fails, because deleting the classes leaves an unmeasured board's
cells with no width from any source. That case is therefore load-bearing, not scenery.
The half-fix leg exists because without it the "viewport classes must step aside" assertion
would never have fired independently, and would have shipped as a line that reads like a second
axis without being one. It fires, with its own message. The one assertion that could not fail
independently — a
parseFloat(...) > 0companion to the tier equality — was removed ratherthan kept and labelled.
The harness-kill leg answers the hazard the dispatch raised: when the fixture's width channel
dies, the suite reports the fixture, by name, and does not read as a component failure.
Verification
pnpm exec vitest run packages/plugin-kanban/— 34 files, 223 tests, all passing. The 34reconciles against 34 test files on disk under that package, so the new file is inside the
run; it also passes standalone (9 tests).
pnpm --filter @object-ui/plugin-kanban run type-check— clean, after building thedependency closure first (
pnpm --filter '@object-ui/plugin-kanban^...' build). Before thatbuild it reported 27 errors that were all downstream of unresolved
@object-ui/*types.The package's
tsconfig.test.jsonleg covers the new file:--listFilesnames it.--no-inline-configwas not used.check-control-bytes,check-changeset-presence,check-changeset-no-major,check-changeset-fixed: all green. A separate scan of the three changed files for controlbytes, zero-width characters and other irregular whitespace found 0 hits, with the same
instrument firing on planted samples.
pnpm lint/ full test farm is left to CI rather thanrun locally, since sibling agents share this container. That narrowing is safe to state
because
eslint.config.jsconfigures no type-aware linting (noprojectService, noparserOptions.project), so this diff cannot move the verdict on any file it does not touch.Out-of-scope observation, not filed
packages/plugin-kanban/src/useColumnWidths.tsis exported from the package barrel with itsColumnWidthConfigtype and its ownobjectui:kanban-column-widthsstorage key, and noboard reads it — the rendered column width comes entirely from
columnInlineStyle. It alsoappears in no docs under
content/. Two column-width policies, one of them unwired. Leftuntouched and handed to the PM to file, because the dedup search it needs is a call the
dispatch asked to conserve.
Generated by Claude Code