From eefbfa5c984ccbfb280a6cff9972b7c94120b01a Mon Sep 17 00:00:00 2001 From: Jay Goss Date: Tue, 24 Feb 2026 14:32:46 -0600 Subject: [PATCH 1/7] feat(onboarding): Allow callers to specify feature flag for copy button gate Add optional featureFlag parameter to useCopySetupInstructionsEnabled and CopySetupInstructionsGate so project creation surfaces can use a separate flag from the existing onboarding surfaces. Refs ONB-13 Co-Authored-By: Claude --- .../onboardingCopyMarkdownButton.tsx | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/static/app/components/onboarding/gettingStartedDoc/onboardingCopyMarkdownButton.tsx b/static/app/components/onboarding/gettingStartedDoc/onboardingCopyMarkdownButton.tsx index 5223d8e7575982..f6bdb7d24d382a 100644 --- a/static/app/components/onboarding/gettingStartedDoc/onboardingCopyMarkdownButton.tsx +++ b/static/app/components/onboarding/gettingStartedDoc/onboardingCopyMarkdownButton.tsx @@ -90,20 +90,28 @@ export function OnboardingCopyMarkdownButton({ ); } -const FEATURE_FLAG = 'onboarding-copy-setup-instructions'; +const DEFAULT_FEATURE_FLAG = 'onboarding-copy-setup-instructions'; /** - * Feature-gated wrapper that renders its children only when the - * `onboarding-copy-setup-instructions` flag is enabled. Includes spacing - * so callsites don't render an empty Container when the flag is off. + * Returns whether the copy setup instructions button should be shown. + * Defaults to the `onboarding-copy-setup-instructions` flag, but callers + * can pass a different flag name (e.g. for project-creation-specific gating). */ -export function useCopySetupInstructionsEnabled(): boolean { +export function useCopySetupInstructionsEnabled( + featureFlag: string = DEFAULT_FEATURE_FLAG +): boolean { const organization = useOrganization(); - return organization.features.includes(FEATURE_FLAG); + return organization.features.includes(featureFlag); } -export function CopySetupInstructionsGate({children}: {children: React.ReactNode}) { - const enabled = useCopySetupInstructionsEnabled(); +export function CopySetupInstructionsGate({ + children, + featureFlag, +}: { + children: React.ReactNode; + featureFlag?: string; +}) { + const enabled = useCopySetupInstructionsEnabled(featureFlag); if (!enabled) { return null; } From 7257a0bfbdcbb01550ab407ed37687b89bf3caf7 Mon Sep 17 00:00:00 2001 From: Jay Goss Date: Tue, 24 Feb 2026 14:38:20 -0600 Subject: [PATCH 2/7] feat(onboarding): Add copy-as-markdown button to project creation and signup setup docs Add OnboardingCopyMarkdownButton to OnboardingLayout, which is shared by both the project creation getting-started page and the first-time signup wizard. Gated behind the new onboarding-copy-setup-instructions-project-creation feature flag. Refs ONB-13 Co-Authored-By: Claude --- .../gettingStartedDoc/onboardingLayout.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx b/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx index 8b5d8dbf6a835b..145909cbb4d984 100644 --- a/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx +++ b/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx @@ -1,13 +1,17 @@ import {Fragment, useEffect, useMemo} from 'react'; import styled from '@emotion/styled'; -import {Stack} from '@sentry/scraps/layout'; +import {Flex, Stack} from '@sentry/scraps/layout'; import {ExternalLink} from '@sentry/scraps/link'; import HookOrDefault from 'sentry/components/hookOrDefault'; import List from 'sentry/components/list'; import ListItem from 'sentry/components/list/listItem'; import {AuthTokenGeneratorProvider} from 'sentry/components/onboarding/gettingStartedDoc/authTokenGenerator'; +import { + OnboardingCopyMarkdownButton, + useCopySetupInstructionsEnabled, +} from 'sentry/components/onboarding/gettingStartedDoc/onboardingCopyMarkdownButton'; import {TabSelectionScope} from 'sentry/components/onboarding/gettingStartedDoc/selectedCodeTabContext'; import {Step} from 'sentry/components/onboarding/gettingStartedDoc/step'; import { @@ -62,6 +66,9 @@ export function OnboardingLayout({ }: OnboardingLayoutProps) { const api = useApi(); const organization = useOrganization(); + const copyEnabled = useCopySetupInstructionsEnabled( + 'onboarding-copy-setup-instructions-project-creation' + ); const {isPending: isLoadingRegistry, data: registryData} = useSourcePackageRegistries(organization); const selectedOptions = useUrlPlatformOptions(docsConfig.platformOptions); @@ -176,7 +183,15 @@ export function OnboardingLayout({ /> ) : null} - + + {copyEnabled && ( + + + + )}
{steps.map((step, index) => ( From 9c782fdaabb1ae127db4b59c52cb64e7be571173 Mon Sep 17 00:00:00 2001 From: Jay Goss Date: Wed, 25 Feb 2026 15:56:59 -0600 Subject: [PATCH 3/7] feat(onboarding): Use borderless style for copy-as-markdown button in project creation --- .../components/onboarding/gettingStartedDoc/onboardingLayout.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx b/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx index 145909cbb4d984..43344fa9326245 100644 --- a/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx +++ b/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx @@ -188,6 +188,7 @@ export function OnboardingLayout({ From 048646fa6c07ddcf5e021f859741080cc5ee64a0 Mon Sep 17 00:00:00 2001 From: Jay Goss Date: Wed, 25 Feb 2026 16:10:17 -0600 Subject: [PATCH 4/7] feat(onboarding): Move copy button inline with Install step header Place the "Copy instructions" button as trailingItems on the first step so it sits in the same row as the "Install" heading, matching the existing "Copy DSN" placement pattern on the Configure step. --- .../gettingStartedDoc/onboardingLayout.tsx | 51 +++++++++++++------ 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx b/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx index 43344fa9326245..cf92c586d4fb00 100644 --- a/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx +++ b/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx @@ -1,7 +1,7 @@ import {Fragment, useEffect, useMemo} from 'react'; import styled from '@emotion/styled'; -import {Flex, Stack} from '@sentry/scraps/layout'; +import {Stack} from '@sentry/scraps/layout'; import {ExternalLink} from '@sentry/scraps/link'; import HookOrDefault from 'sentry/components/hookOrDefault'; @@ -183,20 +183,40 @@ export function OnboardingLayout({ /> ) : null} - - {copyEnabled && ( - - - - )} +
- {steps.map((step, index) => ( - - ))} + {steps.map((step, index) => { + const copyButton = + copyEnabled && index === 0 ? ( + + ) : null; + + const trailingItems = copyButton ? ( + step.trailingItems ? ( + + {step.trailingItems} + {copyButton} + + ) : ( + copyButton + ) + ) : ( + step.trailingItems + ); + + return ( + + ); + })}
{nextSteps.length > 0 && ( @@ -235,13 +255,12 @@ export function OnboardingLayout({ ); } -const Divider = styled('hr')<{withBottomMargin?: boolean}>` +const Divider = styled('hr')` height: 1px; width: 100%; /* eslint-disable-next-line @sentry/scraps/use-semantic-token */ background: ${p => p.theme.tokens.border.primary}; border: none; - ${p => p.withBottomMargin && `margin-bottom: ${space(3)}`} `; const StyledStep = styled(Step)` From 1e9892de53776257a66e0fa80b3f3c2f58edfea6 Mon Sep 17 00:00:00 2001 From: Jay Goss Date: Wed, 25 Feb 2026 16:13:07 -0600 Subject: [PATCH 5/7] ref(onboarding): Export project-creation feature flag as a const --- .../gettingStartedDoc/onboardingCopyMarkdownButton.tsx | 2 ++ .../onboarding/gettingStartedDoc/onboardingLayout.tsx | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/static/app/components/onboarding/gettingStartedDoc/onboardingCopyMarkdownButton.tsx b/static/app/components/onboarding/gettingStartedDoc/onboardingCopyMarkdownButton.tsx index f6bdb7d24d382a..c1c1a032c3b23d 100644 --- a/static/app/components/onboarding/gettingStartedDoc/onboardingCopyMarkdownButton.tsx +++ b/static/app/components/onboarding/gettingStartedDoc/onboardingCopyMarkdownButton.tsx @@ -91,6 +91,8 @@ export function OnboardingCopyMarkdownButton({ } const DEFAULT_FEATURE_FLAG = 'onboarding-copy-setup-instructions'; +export const PROJECT_CREATION_FEATURE_FLAG = + 'onboarding-copy-setup-instructions-project-creation'; /** * Returns whether the copy setup instructions button should be shown. diff --git a/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx b/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx index cf92c586d4fb00..ed05a248042a8f 100644 --- a/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx +++ b/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx @@ -10,6 +10,7 @@ import ListItem from 'sentry/components/list/listItem'; import {AuthTokenGeneratorProvider} from 'sentry/components/onboarding/gettingStartedDoc/authTokenGenerator'; import { OnboardingCopyMarkdownButton, + PROJECT_CREATION_FEATURE_FLAG, useCopySetupInstructionsEnabled, } from 'sentry/components/onboarding/gettingStartedDoc/onboardingCopyMarkdownButton'; import {TabSelectionScope} from 'sentry/components/onboarding/gettingStartedDoc/selectedCodeTabContext'; @@ -66,9 +67,7 @@ export function OnboardingLayout({ }: OnboardingLayoutProps) { const api = useApi(); const organization = useOrganization(); - const copyEnabled = useCopySetupInstructionsEnabled( - 'onboarding-copy-setup-instructions-project-creation' - ); + const copyEnabled = useCopySetupInstructionsEnabled(PROJECT_CREATION_FEATURE_FLAG); const {isPending: isLoadingRegistry, data: registryData} = useSourcePackageRegistries(organization); const selectedOptions = useUrlPlatformOptions(docsConfig.platformOptions); From e6c265cdd83bf927ccf0374ed23aec649629de51 Mon Sep 17 00:00:00 2001 From: Jay Goss Date: Wed, 25 Feb 2026 16:39:09 -0600 Subject: [PATCH 6/7] fix(onboarding): Restore withBottomMargin prop on Divider styled component --- .../onboarding/gettingStartedDoc/onboardingLayout.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx b/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx index ed05a248042a8f..554261a9d60af9 100644 --- a/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx +++ b/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx @@ -254,12 +254,13 @@ export function OnboardingLayout({ ); } -const Divider = styled('hr')` +const Divider = styled('hr')<{withBottomMargin?: boolean}>` height: 1px; width: 100%; /* eslint-disable-next-line @sentry/scraps/use-semantic-token */ background: ${p => p.theme.tokens.border.primary}; border: none; + ${p => p.withBottomMargin && `margin-bottom: ${space(3)}`} `; const StyledStep = styled(Step)` From aaabf0c25f332de072cff2412671921a0f6f600e Mon Sep 17 00:00:00 2001 From: Jay Goss Date: Wed, 25 Feb 2026 16:42:48 -0600 Subject: [PATCH 7/7] fix(onboarding): Always apply bottom margin on divider above steps --- .../onboarding/gettingStartedDoc/onboardingLayout.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx b/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx index 554261a9d60af9..a1c7a4d5fa3c09 100644 --- a/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx +++ b/static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx @@ -182,14 +182,13 @@ export function OnboardingLayout({ /> ) : null} - +
{steps.map((step, index) => { const copyButton = copyEnabled && index === 0 ? ( ) : null;