Skip to content

fix: merge partial style overrides into the defaults - #36

Merged
killerwolf merged 1 commit into
mainfrom
fix/merge-partial-style-overrides
Sep 6, 2026
Merged

fix: merge partial style overrides into the defaults#36
killerwolf merged 1 commit into
mainfrom
fix/merge-partial-style-overrides

Conversation

@killerwolf

Copy link
Copy Markdown
Owner

Found while writing the TypeScript declarations in #33.

The bug

The constructor spread user options one level too shallow:

focusPoint: {
  enabled: true,
  style: { width: "30px", height: "30px", border: "3px solid white", /* ... */ },
  ...options.focusPoint,   // replaces `style` wholesale
},

So this:

new VisualImageTool({ imageElement: img, focusPoint: { style: { width: "40px" } } });

left style as { width: "40px" } and nothing else. _createFocusMarker() then assigned marker.style.border = undefined, boxShadow = undefined, backgroundColor = undefined — so instead of a slightly larger marker you got one with no border, no shadow and no fill.

cropZone.style and cropZone.handleStyle had the same problem, affecting the crop overlay and all eight resize handles.

To change one property you had to restate every default, and nothing in the docs or the types said so.

The fix

The two style objects are merged a level deeper than the rest of the options, so an override layers onto the defaults. enabled and any other scalar keys keep behaving exactly as before. Passing no style at all, or an explicit undefined, leaves the defaults intact.

Tests

Five new cases: a partial override on the focus marker, the crop overlay and a resize handle, plus one guarding that scalar options still pass through and one for the no-style-given path.

I checked these actually catch the bug — reverting the source fix and re-running fails four of the five:

FAIL  partial style overrides > keeps the focus marker's other defaults
FAIL  partial style overrides > keeps the crop overlay's other defaults
FAIL  partial style overrides > keeps a resize handle's other defaults
FAIL  partial style overrides > still lets scalar options through untouched
Tests  4 failed | 14 passed (18)

Follow-up in #33

src/index.d.ts does not exist on main yet — it is part of #33, where the doc comments on FocusPointStyle, CropZoneStyle and CropHandleStyle currently describe the replace-wholesale behaviour this PR removes. I have updated those comments on the #33 branch, so whichever merges second is consistent. npm run types:check also lives in #33 and could not be run here; it passes on that branch with the updated comments.

Verification

npm test (18 passing), npm run lint:check and npm run format:check green.

🤖 Generated with Claude Code

Overriding a single style property replaced the entire style object, so
`focusPoint: { style: { width: "40px" } }` left the marker with no border,
shadow or background — the element creation code then assigned `undefined`
to each of them. Callers had to restate every default to change one of
them, and nothing said so.

The style objects are now merged a level deeper than the rest of the
options, so an override layers onto the defaults. `enabled` and the other
scalar keys are untouched.

Four of the five new tests fail against the previous behaviour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
killerwolf added a commit that referenced this pull request Sep 6, 2026
The declarations documented the shallow-merge behaviour these comments
were written against. #36 fixes that merge, so the note that a partial
override unsets everything it omits no longer holds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@killerwolf
killerwolf merged commit 7727019 into main Sep 6, 2026
2 checks passed
@killerwolf
killerwolf deleted the fix/merge-partial-style-overrides branch September 6, 2026 07:58
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.

1 participant