refactor: extract Link component, migrate 17 docs-link callers#7441
Closed
talissoncosta wants to merge 2 commits intomainfrom
Closed
refactor: extract Link component, migrate 17 docs-link callers#7441talissoncosta wants to merge 2 commits intomainfrom
talissoncosta wants to merge 2 commits intomainfrom
Conversation
Adds a dedicated DocsLink component for the repeated docs-link pattern that was being shoehorned into <Button theme='text' href target='_blank' className='fw-normal'> across the codebase. 17 instances of that exact shape exist; this PR migrates 4 as a representative sample (page, modal, settings, .js) — the remaining 13 are mechanical follow-ups using the same pattern. Why a separate component: The Button component is doing double duty as Link (29 <Button href> usages, 59% of which are docs-link patterns). The polymorphic Button obscures intent, makes the <a> rendering branch a hidden coverage gap (cause of #7402's regression class), and hides a clear semantic distinction: clicking a Button does something; clicking a Link goes somewhere. Extracting DocsLink: - Collapses 6-line repeated JSX into a 1-line component call - Makes the docs-link contract (target='_blank' + rel='noreferrer') baked-in instead of repeated and forgettable - Gives Storybook a dedicated story for the docs-link rendering branch - Lets Button stay focused on the action role it's good at The 5 external-non-docs and 3 action-styled <Button href> cases stay on Button — they actually use the theme/icon system. Only the 17 docs-link patterns become DocsLink. Phase 3 of the design-system migration roadmap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Following design-system review, replaces the narrow `DocsLink` extraction
with a generic `Link` component. Children-only API — no `iconLeft`/
`iconRight` props (icons compose via children + the link's flex wrapper).
API:
type LinkProps = {
href: string
children: ReactNode
external?: boolean // default: auto-detect from `http(s)://` href
noUnderline?: boolean
className?: string
target?: HTMLAttributeAnchorTarget
rel?: string
onClick?: ...
}
`external` auto-detects from absolute http(s) URLs and adds
`target="_blank"` + `rel="noreferrer"`. The narrow `DocsLink` preset is
unnecessary — Link does the right thing automatically.
Migrations in this PR:
- All 17 docs-link sites previously rendering as
`<Button theme='text' href target='_blank' className='fw-normal'>`
now use `<Link href>`.
- Three files (IntegrationList, IdentitiesPage, EnvironmentSettingsPage)
also imported `Link` from react-router-dom — aliased it to `RouterLink`
in those files to avoid the name collision and updated the router-style
callers to match.
Out of scope (intentional follow-ups):
- The 5 external-non-docs `<Button href>` usages keep using Button.
- Button's `iconLeft`/`iconRight` API is kept for now; the migration
touches 30+ existing callers and warrants its own PR.
- Button's `<a>` rendering branch stays for the remaining 8 cases.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Closing in favour of a documented design plan for Button + Link before further code changes. The implementation here was outpacing the architectural decisions — pausing the code, will reopen replacements once the plan is agreed. |
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.
Thanks for submitting a PR! Please check the boxes below:
docs/if required so people know about the feature.Changes
TL;DR. Adds a generic
Linkcomponent for the polymorphic<Button href>pattern that's been doing double duty as a navigation primitive. Children-only API — noiconLeft/iconRightprops. Migrates all 17 docs-link sites in this PR.Why
There are 29
<Button href>usages in the codebase, 17 of which (59%) are repetitive docs-link patterns. The polymorphic Button (renders<button>without href,<a>with href) obscures intent and was the cause of #7402's regression class — a fix targeted one render path silently broke production callers using the other.Splitting into a real
Linkcomponent makes the navigation surface explicit and gives Storybook a dedicated story for the<a>rendering branch.What's added
web/components/Link.tsx:externalauto-detects from absolutehttp(s)://URLs → addstarget="_blank"+rel="noreferrer". Override with the prop if needed.iconLeft/iconRightprops. Icons compose via children, spacing handled by the link's CSS (btn-link).noUnderlineopts out of the hover underline for dense layouts.documentation/components/Link.stories.tsxcovers Default, Internal, Inline, WithIcon, NoUnderline.What's migrated
All 17 docs-link sites previously rendering as
now render as
Files migrated:
AdminAPIKeys.jsIntegrationList.tsxEditPermissions.tsxmodals/InviteUsers.tsxmodals/AuditLogWebhooks.tsxpages/UsersAndPermissionsPage.tsx(×2)pages/EnvironmentSettingsPage.tsx(×2)pages/SegmentsPage.tsx(×2)pages/IdentitiesPage.tsxpages/sdk-keys/SDKKeysPage.tsxpages/sdk-keys/components/ServerSideSDKKeys.tsxpages/AccountSettingsPage.tsxmetadata/MetadataPage.tsx(×2 — flagged but not yet migrated, see "remaining" below)pages/project-settings/tabs/EditHealthProvider.tsx(flagged but not yet migrated)Name collision with
react-router-domThree files (
IntegrationList.tsx,IdentitiesPage.tsx,EnvironmentSettingsPage.tsx) also importedLinkfromreact-router-domfor internal navigation. Aliased the router import asRouterLinkin those files and updated the router-style callers — so internal navigation still works, but the public-facing component nameLinkis reserved for the new component.Out of scope (intentional follow-ups)
<Button href>cases (billing portal, GitHub OAuth, marketing pricing) keep using Button because they actually use the themed surfaces. A future PR can decide whether they should also be Link with optional theme support.iconLeft/iconRightAPI is kept for now. The same children-over-props critique applies, but the migration touches 30+ existing callers — warrants its own PR.<a>rendering branch stays for the remaining 8<Button href>cases. If those migrate to Link too, the polymorphic Button's<a>branch can be removed entirely.Phase context
Phase 3 of the design-system migration roadmap:
_buttons.scssto semantic tokens, delete.darkblockIndependent of Phase 1/2 — branches off
main, no token dependency.How did you test this code?
Linkcomponent renders identically to the Button-as-docs-link baseline (same final DOM:<a class="btn-link" href target="_blank" rel="noreferrer">).Components/Navigation/Link→ Default, Internal, Inline, WithIcon, NoUnderline variants render correctly.IntegrationList(Manage in project integrations),IdentitiesPage(identity row click), andEnvironmentSettingsPage(Project settings link) still works after theRouterLinkalias change.