Skip to content

fix(tokenlogo): flag clip-path ids collide when a flag renders more than once - #1037

Merged
bordalix merged 1 commit into
arkade-os:masterfrom
Delgado74:fix/tokenlogo-clippath-ids
Sep 30, 2026
Merged

bordalix merged 1 commit into
arkade-os:masterfrom
Delgado74:fix/tokenlogo-clippath-ids

Conversation

@Delgado74

Copy link
Copy Markdown
Contributor

Closes #1035

Problem

Three flag logos in src/components/TokenLogo.tsx — UnitedStatesFlagLogo, BrazilFlagLogo and UnitedKingdomFlagLogo — each define a clipPath with a hardcoded id and reference it through url(#id).

SVG fragment references resolve against the whole document, not the local <svg>. So when the same flag renders more than once on a page, the second instance's url(#us-flag-circle) resolves to the first element with that id in document order.

This is reachable in normal use: TokenLogo is rendered by TransactionsList (once per transaction), SwapRouteIcon (once per route hop), AssetCard, Swap/Index, Send/Form, BitcoinDetail and Settings/Currency. A transaction list with two USD entries, or a multi-hop swap, produces duplicate ids.

The visual result happens to be correct today, because every instance of a given flag defines an identical circle. That is what makes this worth fixing now rather than later: it is invalid HTML, and the next edit that gives one flag a different clip shape will silently start clipping against the wrong element. Duplicate ids also break document.getElementById for anything that reaches for them.

Fix

Scope each id to its component instance with React's useId(), keeping a readable prefix so the ids stay greppable:

function useClipId(name: string) {
  return `${name}-${useId().replace(/:/g, '')}`
}

The colons are stripped because useId() returns values like :r0:, which make the url(#…) reference awkward to read and to search for.

Tests

New src/test/components/token-logo.test.tsx:

  • two instances of the same flag must produce two distinct clipPath ids (checked for all three flags)
  • each g[clip-path] must resolve to a clipPath defined inside its own <svg>

The second test is the one that catches the actual bug: it fails when useClipId is replaced by a constant, which is how the fix was verified.

src/test/screens/wallet/swap.test.tsx asserted the literal id #br-flag-circle; it now matches by prefix instead.

Note on scope

This is deliberately limited to the three flags that exist on master. The Cuban flag and the CUP ticker are part of the fiat work in #991, so they are not touched here — that branch carries the same useId treatment for the flags it adds.

Validation

  • pnpm test — 84/84 files, 748/748 tests
  • pnpm run format:check — clean
  • pnpm run lint — clean
  • pnpm exec tsc --noEmit — clean apart from one pre-existing error in src/providers/lnSwaps.tsx:99, present on master
  • pnpm build — succeeds

Hardcoded clipPath ids (us/br/cu/gb-flag-circle) collide when the same
flag renders more than once on a page (e.g. transaction lists or swap
routes), so the second instance would reference the wrong clip path.
Each flag now generates a unique id per component instance via useId
and points its <g clipPath> at that scoped id.

(cherry picked from commit d0bdd3c)
@coderabbitai

coderabbitai Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 53 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: e11c48a4-60bf-4da6-91bf-d44adc9c9b9f

📥 Commits

Reviewing files that changed from the base of the PR and between 46c5888 and 23f7477.

📒 Files selected for processing (3)
  • src/components/TokenLogo.tsx
  • src/test/components/token-logo.test.tsx
  • src/test/screens/wallet/swap.test.tsx

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@arkana-ai-bot arkana-ai-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review

Correct diagnosis and clean implementation. No protocol-critical paths touched.

Fix (src/components/TokenLogo.tsx)

useClipId hook — correct (TokenLogo.tsx:7-9).
useId() (React 18, confirmed in package.json) generates a per-render-tree-stable, document-unique id. The global colon-strip (.replace(/:/g, '')) is the right approach: React's :r0: format produces readable ids like us-flag-circle-r0, and a global replace stays robust if the internal format ever gains more colons. The hook is called before return in each component function, satisfying Rules of Hooks.

Three flags updated (TokenLogo.tsx:116,190,239) — US, Brazil, UK. Switzerland, China, EU, Japan, and Bitcoin/Tether/USDC logos either use a bounding <circle> or <g> without a clipPath reference, so they are correctly left untouched. The comment about the Cuban flag / #991 branch is a useful heads-up for reviewers of that PR.

Tests (src/test/components/token-logo.test.tsx)

Well-structured. Three positive aspects:

  1. Uniqueness assertion (line ~43-52): renders two of each flag, collects clipPath[id] values per prefix, and asserts both count = 2 and Set size = 2. Catches the regression directly.

  2. Scoping assertion (line ~54-68): for every <svg> in the rendered output, resolves the clip-path id and asserts the matching clipPath lives inside that svg. This is the test that would have caught the original bug — good that it is the explicit regression case, not just a snapshot.

  3. tokenLogoTickerForTicker coverage (lines ~5-19): tests normalisation (trim + uppercase) and the unknown-ticker undefined path. The .toBe() comparison is correct for string value equality.

Swap test update (src/test/screens/wallet/swap.test.tsx:178)

The old document.querySelector('#br-flag-circle') assertion was testing an implementation detail that this PR deliberately invalidates. Switching to clipPath[id^="br-flag-circle"] preserves the intent (the Brazil flag rendered) while being compatible with dynamic ids.

Cross-repo impact

No consumers of TokenLogo, us-flag-circle, br-flag-circle, or gb-flag-circle exist outside the wallet repo — confirmed by grep across all SDK clones.

Minor observations (non-blocking)

  • UnitedStatesFlagLogo is exported from TokenLogo.tsx but the new test file does not import it directly — it reaches it through <TokenLogo ticker='USD' />. Coverage is equivalent; just noting it in case someone later wants a direct UnitedStatesFlagLogo test.
  • The useClipId helper is file-private (no export), which is the right default. If a future component in a different file needs the same treatment, it can be promoted to a shared util then.

LGTM.

@bordalix
bordalix merged commit 0c50d82 into arkade-os:master Sep 30, 2026
6 checks passed
@Delgado74
Delgado74 deleted the fix/tokenlogo-clippath-ids branch October 1, 2026 03:55
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.

fix(tokenlogo): flag clip-path ids collide when the same flag renders more than once

3 participants