Skip to content

Commit 69400a6

Browse files
committed
fix(#429): also skip the logical inline-size and the rest of the table box tree
My earlier #429 fix only skipped the physical width on <td>/<th>, but getComputedStyle also emits the logical inline-size, which re-froze the cell, and freezing the <table>/<tr>/<tbody> used widths pins the whole auto table. When the rasterized SVG falls back to a wider font (system-ui isn't available when an SVG renders as an <img>) the cells can't grow and the text wraps at the space (the "✅ 2024-09-16" case) — even though the SVG itself renders fine in a tab. I now skip both width and inline-size (min/max included) across the table box tree, so the cloned table re-runs its own layout. Column widths from the width attribute / <col> still survive.
1 parent 33b81eb commit 69400a6

2 files changed

Lines changed: 16 additions & 9 deletions

File tree

__tests__/snapdom.tableCellWidth.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ describe('#429 table cell width is not frozen', () => {
3535
for (const tag of tds) {
3636
// no inline width frozen on the tag
3737
const inline = (tag.match(/style="([^"]*)"/) || [])[1] || ''
38-
expect(/(?:^|;)\s*(min-|max-)?width\s*:/.test(inline)).toBe(false)
38+
expect(/(?:^|;)\s*(min-|max-)?(width|inline-size)\s*:/.test(inline)).toBe(false)
3939
// no width in the generated class either
4040
const cls = (tag.match(/class="([^"]*)"/) || [])[1] || ''
4141
for (const c of cls.split(/\s+/).filter(Boolean)) {
4242
const body = rules[c] || ''
43-
expect(/(?:^|;)\s*(min-|max-)?width\s*:/.test(body)).toBe(false)
43+
expect(/(?:^|;)\s*(min-|max-)?(width|inline-size)\s*:/.test(body)).toBe(false)
4444
}
4545
}
4646
})

src/utils/css.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,15 +156,22 @@ export function getStyleKey(snapshot, tagName) {
156156
// Tags that size to text content; grid/flex blockify them but we should not constrain
157157
// width (causes wrap when font-weight makes text wider than captured width, e.g. "Timestamp demo")
158158
const INLINE_SIZED_TAGS = new Set(['span', 'small', 'em', 'strong', 'b', 'i', 'u', 's', 'code', 'cite', 'mark', 'sub', 'sup'])
159-
// #429: table cells get their used width from the table layout algorithm, not from CSS.
160-
// getComputedStyle reports that fractional used width (e.g. 113.484px); freezing it as an
161-
// explicit width pins the auto-sized cell, so any sub-pixel rendering difference in the clone
162-
// (dpr=2 rounding, font metrics) no longer fits and the text wraps. Let the table size cells.
163-
const TABLE_CELL_TAGS = new Set(['td', 'th'])
164-
const skipWidth = isInline || INLINE_SIZED_TAGS.has(tagName) || TABLE_CELL_TAGS.has(tagName)
159+
// #429: the table box tree (table / row groups / rows / cells) gets its width from the table
160+
// layout algorithm, not from CSS. getComputedStyle reports that resolved used width (e.g.
161+
// 113.484px). Freezing it pins the auto table, so when the rasterized SVG falls back to a
162+
// wider font (system-ui isn't available when an SVG renders as an <img>), the content no longer
163+
// fits and wraps (e.g. "✅ 2024-09-16" breaks at the space). Letting the cloned table re-run
164+
// its own layout keeps cells sized to their content. Column widths set via the `width`
165+
// attribute / <col> still survive (they're attributes, not computed CSS).
166+
const TABLE_TAGS = new Set(['table', 'thead', 'tbody', 'tfoot', 'tr', 'td', 'th'])
167+
const skipWidth = isInline || INLINE_SIZED_TAGS.has(tagName) || TABLE_TAGS.has(tagName)
168+
// Skip the physical AND logical width longhands — getComputedStyle emits both `width` and
169+
// `inline-size`; dropping only one leaves the other to re-freeze the box.
170+
const isWidthProp = (p) => p === 'width' || p === 'min-width' || p === 'max-width' ||
171+
p === 'inline-size' || p === 'min-inline-size' || p === 'max-inline-size'
165172
for (let [prop, value] of Object.entries(snapshot)) {
166173
if (shouldIgnoreProp(prop)) continue
167-
if (skipWidth && (prop === 'width' || prop === 'min-width' || prop === 'max-width')) continue
174+
if (skipWidth && isWidthProp(prop)) continue
168175
const def = defaults[prop]
169176
if (value && value !== def) entries.push(`${prop}:${value}`)
170177
}

0 commit comments

Comments
 (0)