Skip to content

Fix three bugs: font-size units, imported-SVG ungroup, duplicate jPicker dialogs - #1092

Merged
jfhenon merged 3 commits into
masterfrom
fix/issues-949-953-957
Jul 11, 2026
Merged

Fix three bugs: font-size units, imported-SVG ungroup, duplicate jPicker dialogs#1092
jfhenon merged 3 commits into
masterfrom
fix/issues-949-953-957

Conversation

@jfhenon

@jfhenon jfhenon commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Root causes & fixes

#949 - changeSelectedAttributeNoUndoMethod (packages/svgcanvas/core/undo.js) used isNaN(parseFloat(newValue)) to decide whether to store a parsed number or the raw string. parseFloat only parses the leading numeric portion of a string, so "10pt" parses to 10 without failing, silently dropping the unit. Switched to Number(newValue), which returns NaN unless the entire string is numeric - correctly rejecting "10pt" while still converting a plain numeric string like "10.5" to a real number (preserving the fix #935 made for #930).

#953 - ungroupSelectedElement (packages/svgcanvas/core/selected-elem.js) called convertToGroup() on a <use>/gsvg-tagged element and returned immediately, so a single Ungroup only swapped the wrapper for an equivalent <g> without ever flattening it. convertToGroup() now returns the group it produces, and ungroupSelectedElement reassigns its local g to that result and falls through to the existing flatten logic instead of returning early. Separately, TopPanel.js's context-menu enable/disable logic only recognized tagName === 'g' as ungroupable, hard-disabling the menu item (via pointer-events: none) for the <use> that imported content actually is - now also enabled for 'use'.

#957 - jQuery.jPicker.js's initialize() unconditionally appends a fresh #jPicker-table into its (persistent, shown/hidden-not-recreated) container on every open, with no check for a pre-existing one in the non-expandable branch used by the gradient stop-color picker. Added the same kind of cleanup already used for the toolbar fill/stroke swatch pickers (#1033), generalized to this container.

Test plan

🤖 Generated with Claude Code

Summary by Sourcery

Fix attribute handling, ungrouping of imported SVG content, and gradient color picker behavior, and add regression coverage for these cases.

Bug Fixes:

  • Preserve unit-suffixed attribute values like font-size="10pt" and stroke-width="2px" instead of truncating them to bare numbers.
  • Ensure a single Ungroup action fully unwraps imported SVG content represented as / and enable Ungroup in the context menu for such elements.
  • Prevent duplicate jPicker table elements from accumulating when reopening the gradient stop color picker.

Tests:

  • Add a regression test suite to verify changeSelectedAttribute preserves unit suffixes while still converting plain numeric strings.
  • Update ungroupSelectedElement tests to assert imported -based content is fully flattened by a single ungroup operation.

jfhenon and others added 3 commits July 11, 2026 14:45
…949)

changeSelectedAttributeNoUndoMethod used isNaN(parseFloat(newValue)) to
decide whether to store a value as a parsed number or as the original
string. parseFloat() only parses the leading numeric portion of a
string, so a unit-suffixed value like font-size="10pt" or
stroke-width="2px" was silently truncated to a bare "10"/"2",
stripping the unit - a regression from #935.

Number(), unlike parseFloat(), returns NaN unless the entire string is
numeric, so it correctly rejects "10pt" while still converting a
plain numeric string like "10.5" to a real number (preserving the
behavior #935 was originally fixing for issue #930).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Importing an SVG wraps its content in a <use> referencing a <symbol>
in <defs> (per importSvgString()). Ungrouping that content required
two separate actions: ungroupSelectedElement() only converted the
<use>/gsvg wrapper into an equivalent <g> via convertToGroup() and
returned, so a single Ungroup silently swapped one wrapper for
another instead of actually unwrapping anything - the toolbar
Ungroup button appeared to do nothing on the first click.

convertToGroup() now returns the group it produces (or created/reused
for the gsvg case), and ungroupSelectedElement() reassigns its local
`g` to that result and falls through to the existing flatten logic
instead of returning early, so the conversion and the flatten happen
in one call.

Separately, the right-click context menu's Ungroup item was
hard-disabled for anything whose tagName wasn't literally 'g', so it
was inert for the <use> element imported content actually is - even
though the underlying function already knew how to handle it. Enable
it for 'use' as well.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Double-clicking a gradient stop's color swatch invokes jPickerMethod()
on the same persistent container div each time (it's shown/hidden
between opens, never recreated). initialize()'s non-expandable branch
unconditionally appended a fresh #jPicker-table into that container
without removing any table already there, so each reopen stacked a
duplicate table on top of the previous ones instead of replacing it.

Remove any existing #jPicker-table from the container immediately
before building the new one, mirroring the same cleanup already done
for the toolbar fill/stroke swatch pickers (#1033) but generalized to
this container rather than hardcoded to those two element ids.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Reviewer's Guide

Refines attribute value handling, imported-SVG ungroup behavior, and jPicker dialog lifecycle, with accompanying regression tests and UI wiring updates.

Sequence diagram for updated imported-SVG ungroup behavior

sequenceDiagram
  actor User
  participant Editor
  participant svgCanvas
  participant convertToGroup

  User->>Editor: clickUngroup()
  Editor->>svgCanvas: ungroupSelectedElement()
  svgCanvas->>svgCanvas: getSelectedElems()
  alt gsvg_or_symbol_on_selected
    svgCanvas->>convertToGroup: convertToGroup(g)
    convertToGroup-->>svgCanvas: g
    svgCanvas->>svgCanvas: flatten_group(g)
  else use_without_dataStorage_symbol
    svgCanvas->>svgCanvas: getHref(g)
    svgCanvas->>svgCanvas: getElementById(href)
    svgCanvas->>svgCanvas: dataStorage.put(symbol)
    svgCanvas->>convertToGroup: convertToGroup(g)
    convertToGroup-->>svgCanvas: g
    svgCanvas->>svgCanvas: flatten_group(g)
  end
Loading

Flow diagram for jPicker container reuse and cleanup

flowchart TD
  A[jPickerMethod called] --> B{isExpandable}
  B -- yes --> C[use new floating container]
  B -- no --> D[container = that]
  D --> E{container has #jPicker-table}
  E -- yes --> F[remove existing #jPicker-table]
  E -- no --> G[skip removal]
  F --> H[create newDiv with controlHtml]
  G --> H
  H --> I[append newDiv children into container]
Loading

File-Level Changes

Change Details Files
Ensure changeSelectedAttribute preserves unit-suffixed attribute values while still coercing plain numeric strings.
  • Replaced parseFloat-based numeric detection with Number() and Number.isNaN to avoid stripping unit suffixes from attribute values like font-size and stroke-width.
  • Kept support for coercing strictly numeric strings to numbers to maintain prior fixes.
  • Added a regression unit test suite verifying unit preservation and numeric coercion behavior for changeSelectedAttribute.
packages/svgcanvas/core/undo.js
tests/unit/change-selected-attribute-units.test.js
Make a single Ungroup of imported SVG content fully flatten / wrappers and enable Ungroup in the context menu for such elements.
  • Updated convertToGroup() to return the created group (or null on unexpected input) and adjusted ungroupSelectedElement() to operate on that returned group instead of returning early.
  • Adjusted ungroupSelectedElement() logic for elements to store symbol metadata, call convertToGroup(), and then continue flattening the resulting group in the same call.
  • Expanded context-menu enablement logic so Ungroup is enabled for both and elements, matching ungroupSelectedElement() capabilities.
  • Updated the selected-elem regression test to assert that a single ungroupSelectedElement() fully unwraps imported -based content into its children rather than leaving an intermediate .
packages/svgcanvas/core/selected-elem.js
src/editor/panels/TopPanel.js
tests/unit/selected-elem.test.js
Prevent duplicate jPicker tables from accumulating when reopening the non-expandable gradient stop color picker.
  • In jPicker.initialize(), added a pre-initialization cleanup that removes any existing #jPicker-table inside the persistent container before injecting new controlHtml.
  • Reused the existing pattern used for toolbar swatch pickers but applied it to the gradient stop picker container, ensuring only a single table exists between opens.
src/editor/components/jgraduate/jQuery.jPicker.js

Assessment against linked issues

Issue Objective Addressed Explanation
#949 Preserve unit-suffixed font-size values (e.g. '10pt', '10px') and other attributes when changed, instead of truncating them to bare numbers due to parseFloat-based coercion in changeSelectedAttributeNoUndoMethod.
#953 Enable imported SVG content (represented as referencing a ) to be fully ungrouped/flattened in a single Ungroup action, including from the context menu.
#957 Ensure the linear/radial gradient color picker does not create or show multiple #jPicker-table elements when the gradient stop color picker is opened multiple times, so only a single active picker table exists per container.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot 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.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@jfhenon
jfhenon merged commit e066dad into master Jul 11, 2026
9 checks passed
@jfhenon
jfhenon deleted the fix/issues-949-953-957 branch July 11, 2026 12:54
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.

Multiple jPicker-table in Linear Gradient color picker Imported svg can't be ungrouped Font-size Property Restriction: No Support for 'pt' Units

1 participant