feat: explorer link component - #74
Open
gabitoesmiapodo wants to merge 14 commits into
Open
Conversation
Renders an explorer URL as a safe external link: new tab, rel noopener noreferrer, an aria-hidden icon for its body. Composes no URLs; the caller supplies the href.
Builds explorer URLs for party, contract and update ids from a per-app ExplorerConfig. Canton has no chain registry and no canonical explorer, so the base URL comes from the app. Entity is inferred from the id shape, overridable.
The target/rel/name contract now lives in one place. Identifier's test asserts delegation; ExplorerLink's test pins the link's own behaviour.
parseEnv takes the source rather than reading import.meta.env directly so it stays testable. An unset var reaches Vite as an empty string, so the schema rejects that as well as a missing key.
Cards render the counterparty through Identifier instead of raw truncated text. config.ts parses the environment once at import so a misconfigured build fails on load rather than at first use.
Kit:
- ExplorerLink requires `aria-label`; an icon-only link had no accessible name
- pin `rel`/`target` behind the prop spread and drop them from the prop type
- widen the prop base to AnchorHTMLAttributes and accept a ref
- match `00`-prefixed contract ids down to their suffixless 66-character form
- give `.cnc-explorer-link` a focus ring and a display matching its Identifier slot
- restore the prop docs an earlier refactor dropped from IdentifierProps
dApp:
- default VITE_EXPLORER_URL to the local scan so a fresh clone still boots
- restrict it to http(s): the value lands in an href
- restore the from/to prefix and name the copy control per direction
- pass `announce={false}` where the Toaster already announces
- link the proposal counterparty to the explorer like the grant one
Kit: - drop the unused per-entity path override; deployments differ by host, not by route, so the base url is the only knob and the memo collapses with it - read the party-id shape off truncate.ts instead of restating the separator - give each theme property one home: the explorer link owns its appearance, the identifier link only its fit in the row dApp: - validate the environment in vite.config.ts and define the result back, so the client ships neither zod nor a re-check of a value Vite already inlined (-14.5 kB gzip, 12% of the bundle) - fold the repeated counterparty markup into one CounterpartyId - report env failures with zod's own formatter
`{id}` was the placeholder the removed per-entity override substituted into.
With fixed routes the table only needs the path segment.
Also restores the ProposalCard `direction` comment dropped along the way.
- root CLAUDE.md: the node floor is 24.15.0, pinned at 24.18.1 - canton-dappbooster/CLAUDE.md: ExplorerLink/ exists now - dapp/frontend/architecture.md: correct what src/lib holds, route counterparty ids through CounterpartyId, and record the build-time env seam
The href is optional per call site. Only the grant card links its counterparty, so CounterpartyId takes the href rather than building one.
One variable against one rule does not need a schema library. `new URL` plus a protocol check covers it, and the tests are unchanged.
Per-property comments on ExplorerLinkProps, IdentifierProps, TruncateOptions and CounterpartyIdProps are gone, the fenced code blocks in the explorer-link JSDoc are proper @example tags, and the entity-detection note is one comment instead of three.
10 tasks
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #9
dApps hand-roll explorer URLs for every party, contract and transaction they render, and Canton has no canonical explorer: the base URL is per-environment and per-SV.
This adds the missing pair.
getExplorerLink/useExplorerLinkturn an identifier into a URL, inferring party, contract or update from its shape;ExplorerLinkrenders the icon-only external link withtargetandrelpinned out of a consumer's reach.Identifier's link slot delegates to it, so the two stay one contract instead of two copies of the same anchor.The vesting dApp consumes both: counterparty ids on grant cards link out, resolved from one build-time config. That URL is validated when the app builds, so a bad value fails the build rather than the page load and the client ships no validator.
Changes
ExplorerLink: icon-only external link,targetandrelnot overridable.useExplorerLink/getExplorerLink: identifier to URL, entity inferred from shape.Identifier's link slot now rendersExplorerLink..cnc-explorer-link, split out of the identifier link rules.VITE_EXPLORER_URLresolved, defaulted and validated at build time.Outside the issue's scope
Identifier/truncateJSDoc and prop ordering: d1f2051.CLAUDE.mdNode engine row corrected: 0d0fb93.Acceptance criteria
rel="noopener".Diverged from the issue:
txis namedupdate, matching what Splice Scan calls a ledger transaction.ExplorerLinkcomposes no URLs.relisnoopener noreferrer, and neither it nortargetcan be overridden.Test plan
Automated tests
pnpm -C canton-dappbooster test— 18 new cases across the component and the hook.pnpm -C dapp/frontend test— 8 new cases on env parsing, includingjavascript:rejection.pnpm lint,typecheck,build,test,knip.Manual verification
pnpm run app:dev, open http://localhost:3012.scan.localhost:4000/party/<id>(scan currently not working in the mock app)Breaking changes
None.
Checklist
Screenshots