Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/ui-svg-images/src/InlineSVG/__tests__/InlineSVG.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,22 @@ describe('<InlineSVG />', () => {

expect(svg).toHaveAttribute('focusable', 'true')
})

it('should correctly parse hyphenated attributes like stroke-width', () => {
const svgWithHyphenatedAttrs = `<svg stroke-width="2" stroke-linecap="round"><path d="M12 2l3 6"/></svg>`
const { container } = render(<InlineSVG src={svgWithHyphenatedAttrs} />)
const svg = container.querySelector('svg')

expect(svg).toHaveAttribute('stroke-width', '2')
expect(svg).toHaveAttribute('stroke-linecap', 'round')
})

it('should correctly parse unquoted attribute values (non-standard format)', () => {
const svgWithUnquotedAttrs = `<svg stroke=currentColor stroke-width=2><circle cx=12 cy=12 r=10/></svg>`
const { container } = render(<InlineSVG src={svgWithUnquotedAttrs} />)
const svg = container.querySelector('svg')

expect(svg).toHaveAttribute('stroke', 'currentColor')
expect(svg).toHaveAttribute('stroke-width', '2')
})
})
12 changes: 6 additions & 6 deletions packages/ui-svg-images/src/InlineSVG/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,10 @@ class InlineSVG extends Component<InlineSVGProps> {
function parseAttributes(src: InlineSVGProps['src']) {
const attributes: Record<string, string> = {}
const SVGAttributesRegExp = /<svg\s+([^>]*)\s*>/
const namesAndValuesRegExp =
/(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']?/g
// Match either quoted or unquoted attribute values
// Handles both standard format: attr="value" and non-standard: attr=value
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would skip handling the non-standard format but we have to support it for backward compatibility.

// (\S+?) = attribute name, (?:["']([^"']*)["']|(\S+)) = quoted (group 2) OR unquoted (group 3) value
const namesAndValuesRegExp = /(\S+?)=(?:["']([^"']*)["']|(\S+))/g

if (typeof src === 'string') {
const attributesMatches = SVGAttributesRegExp.exec(src)
Expand All @@ -211,10 +213,8 @@ function parseAttributes(src: InlineSVGProps['src']) {

while (match != null) {
if (excludes.indexOf(match[1]) === -1) {
attributes[match[1]] =
match[2] ||
(match[3] ? match[3] : match[4] ? match[4] : match[5]) ||
match[1]
Comment on lines -214 to -217
Copy link
Collaborator

Choose a reason for hiding this comment

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

I dont understand the old code here... how can there be 5 matches?

Copy link
Contributor Author

@joyenjoyer joyenjoyer Feb 24, 2026

Choose a reason for hiding this comment

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

I tried it and match[4] and match[5] was always undefined for me. I guess the developer improved the regex by trial and error forgot to remove these obsolete capture groups.

// match[1] = attribute name, match[2] = quoted value, match[3] = unquoted value
attributes[match[1]] = match[2] || match[3]
}
match = namesAndValuesRegExp.exec(attributesString)
}
Expand Down
Loading