Skip to content

Fix/image objsrc width and dx footguns#34

Merged
ivoIturrieta merged 5 commits into
mainfrom
fix/image-objsrc-width-and-dx-footguns
Jun 28, 2026
Merged

Fix/image objsrc width and dx footguns#34
ivoIturrieta merged 5 commits into
mainfrom
fix/image-objsrc-width-and-dx-footguns

Conversation

@ivoIturrieta

@ivoIturrieta ivoIturrieta commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator

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/34/

ivoIturrieta and others added 2 commits June 28, 2026 19:11
Image round-trip: a fixed width now pins whether set via the flat `width`
prop OR the object-src / documented `values` full-control form
(`src={{ width: 300 }}`). Previously only the flat prop pinned; the object
form serialized autoWidth:true and still resized to the original on click in
the Builder. Both now emit the editor's canonical pin (autoWidth:false +
percent of the column slot), verified against the editor's own
imageRendering functions (holds across the on-click natural-dimension
reload). An explicit `autoWidth` on `src` is still honored.

DX footguns surfaced by cold-start agent testing (valid, type-checking input
that produced broken output):
- Button: `{ name, attrs: { href } }` (the shape the canonical Href type
  advertises) rendered href="" — normalizeLinkValue now reads href/target
  from `attrs` too, and `||` lets the schema's empty default href fall
  through. Genuine custom attrs are still spread.
- Social: `iconSize`/`spacing` as px strings ("34px") rendered max-width:NaNpx
  — the exporter does arithmetic on them; relax the type to number|string and
  coerce to a number in the mapper.
- Image: a string-url image inherited the placeholder's 1600x400 aspect and
  emitted a wrong height attr; drop the default height so it's height:auto.

Polish:
- Export the input building-block types (SizeInput, BorderInput,
  TextStyleProps, FontFamilyInput, FontWeightInput, HeadingLevel,
  ImageSrcInput) — referenced by public prop types but not importable (TS2459).
- Fix a JSDoc import example (@unlayer-internal/shared-elements -> the public
  package) and add a Button href example.

Tests: new dx-footguns + object-src round-trip cases; updated the two tests
that encoded the old object-src=natural behavior; type guards for the exports;
refreshed two snapshots (only the wrong height attr removed). 369 pass,
typecheck clean, bundle 63.6KB < 68KB.

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

- shared: normalizeLinkValue reads href from attrs, an empty values.href
  falls through to attrs (the || vs ?? fix), custom attrs preserved.
- react: the attrs-href fix works for Image action + Menu, not just Button.

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

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 addresses several “type-checks but renders wrong” DX footguns across link normalization, Social sizing, and Image sizing/HTML output, and adds public type exports to make author-written helpers easier to annotate.

Changes:

  • Extend link normalization to support Href.attrs.href/target and preserve custom attributes; add tests and cross-component regression coverage.
  • Improve Social DX by accepting px-string inputs for iconSize/spacing and coercing them for exporter arithmetic; add footgun regression tests.
  • Adjust Image defaults and sizing intent resolution (including object-src.width parity with flat width) and update snapshots/tests accordingly.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/shared/src/utils/semantic-props.ts Link-value normalization now reads from attrs and preserves non-link attrs.
packages/shared/src/utils/semantic-props.test.ts Adds unit tests for new attrs link behavior and edge cases.
packages/shared/src/layouts.ts Updates JSDoc usage import path to the public React package name.
packages/react/src/index.ts Re-exports additional “input building block” types from the public entry.
packages/react/src/dx-types.test-d.tsx Adds a dts-only guard ensuring those input types remain publicly exported.
packages/react/src/dx-footguns.test.tsx New integration-style tests covering prior silent-broken-output footguns.
packages/react/src/dx-behaviors.test.tsx Updates expected Image sizing behavior in multi-column layout.
packages/react/src/components/Social.tsx Accepts iconSize/spacing as SizeInput and coerces for exporter math.
packages/react/src/components/Image.width-roundtrip.test.tsx Adds round-trip parity tests for object-src.width pinning behavior.
packages/react/src/components/Image.tsx Drops placeholder height default and treats object-src.width as display intent.
packages/react/src/components/Image.test.tsx Updates Image expectations to match new pinning semantics for object src.
packages/react/src/components/Button.tsx JSDoc example updated to document href string ergonomics.
packages/react/src/components/snapshots/snapshots.test.tsx.snap Snapshot updates for removed height attribute on responsive images.
packages/react/src/snapshots/golden-template.test.tsx.snap Snapshot updates reflecting image height attribute omission changes.

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

Comment thread packages/shared/src/utils/semantic-props.ts Outdated
Comment thread packages/react/src/components/Social.tsx
ivoIturrieta and others added 3 commits June 28, 2026 19:30
…ditor

The render-time fix made { name, attrs:{ href } } render a working anchor,
but renderToJson preserved the storage shape (empty values.href + attrs),
and the Builder reads values.href — so the link was lost on import. Move an
attrs href/target into values.href/target at the mapper (both flat prop and
values escape hatch), keeping genuine custom attrs. Found while loading a
test design into the live editor. +4 tests.

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

The story embedded an inline-SVG data URI with double quotes (xmlns="…")
inside a double-quoted style="…", so the first inner quote closed the
attribute and '); opacity: 0.1; "> leaked as visible text. Replaced the grain
with a valid CSS radial-gradient dot pattern and dropped the onmouseover/out
inline JS (can't run in a rendered email; XSS pattern) from the affected
stories. Added a guard test over all Html story HTML (no inline handlers, no
url() with a raw double quote) and a security note: <Html> renders verbatim,
not sanitized — pass toSafeHtml via UnlayerProvider to sanitize like the editor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- normalizeLinkValue: only normalize a {name} object when it actually carries
  values or attrs, so a bare {name} (or accidental {name:…}) falls through to
  undefined per the documented contract instead of becoming {url:""}.
- Social coerceSizes: parse iconSize/spacing strictly (number or px string);
  drop a non-px unit ("50%", "1.5em") so it falls back to the schema default
  rather than being silently parseFloat-ed to a wrong px count.
Both caught in review. +2 tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ivoIturrieta ivoIturrieta merged commit 878d1aa into main Jun 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants