Skip to content

Fix multi-column Row NaN (default cells to column count) + border wid…#35

Merged
ivoIturrieta merged 2 commits into
mainfrom
fix/row-default-cells-and-border-px
Jun 28, 2026
Merged

Fix multi-column Row NaN (default cells to column count) + border wid…#35
ivoIturrieta merged 2 commits into
mainfrom
fix/row-default-cells-and-border-px

Conversation

@ivoIturrieta

@ivoIturrieta ivoIturrieta commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator

…th px unit

Two type-checks-but-renders-broken footguns found by cold-start agent testing:

  • A multi-column with no layout/cells defaulted cells to [1] regardless of column count, so the 2nd/3rd had no cell and rendered width="NaN". Default to one equal cell PER child, matching renderToJson. (Also makes a layout accidentally placed on harmless — the Row no longer NaNs.)

  • A number border width (borderTopWidth: 1) rendered border-top: 1 solid (invalid CSS the browser drops), even though BorderInput + its JSDoc accept a number. Normalize border *Width fields (number / unit-less numeric string → px) in the mapper, for both nested border objects and gathered flat side props.

+6 tests (shared mapper normalization + react render-level for both).

Summary

What does this PR do? Keep it brief.

Changes

Test Plan

  • pnpm build passes
  • pnpm test passes
  • Tested in Storybook (if UI change)
  • Added/updated tests for new behavior

Notes

Any additional context for reviewers.


📖 Storybook Preview: https://unlayer.github.io/elements/pr/35/

…th px unit

Two type-checks-but-renders-broken footguns found by cold-start agent testing:

- A multi-column <Row> with no `layout`/`cells` defaulted `cells` to `[1]`
  regardless of column count, so the 2nd/3rd <Column> had no cell and rendered
  width="NaN". Default to one equal cell PER <Column> child, matching
  renderToJson. (Also makes a `layout` accidentally placed on <Column> harmless
  — the Row no longer NaNs.)

- A number border width (`borderTopWidth: 1`) rendered `border-top: 1 solid`
  (invalid CSS the browser drops), even though BorderInput + its JSDoc accept a
  number. Normalize border *Width fields (number / unit-less numeric string → px)
  in the mapper, for both nested `border` objects and gathered flat side props.

+6 tests (shared mapper normalization + react render-level for both).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 28, 2026 20:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes two render-time “type-checks-but-breaks” cases in the React email renderer by (1) defaulting <Row> cell layouts to match the number of <Column> children (preventing NaN widths) and (2) normalizing border width values so unitless widths render as valid CSS (Npx).

Changes:

  • Default <Row> cells to one equal cell per direct <Column> child when neither layout nor cells is provided.
  • Normalize border*Width values (number / unitless numeric string → "Npx") inside the semantic-props mapper, including gathered flat side props.
  • Add regression tests covering both behaviors at the shared mapper level and at the React render-to-HTML/JSON level.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
packages/shared/src/utils/semantic-props.ts Adds border width normalization in mapSemanticProps after border-side prop gathering.
packages/shared/src/utils/semantic-props.test.ts Adds unit normalization tests for nested and gathered border width props.
packages/react/src/dx-footguns.test.tsx Adds regression tests ensuring no NaN widths for multi-column Rows and px units for border widths.
packages/react/src/components/Row.tsx Updates default cells behavior to match <Column> child count when layout/cells are omitted.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/shared/src/utils/semantic-props.ts
The width normalization mutated final.border in place, but final.border can
alias the caller's object (the values escape hatch is a shallow clone; a nested
border prop passes by reference) — so a reused const HAIRLINE would be rewritten
to '1px' as a side effect. Clone before rewriting, reassign. +purity test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@ivoIturrieta ivoIturrieta merged commit 770f1c6 into main Jun 28, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants