From 79514f3755f680199222a3e2938dfbb0e59b67a0 Mon Sep 17 00:00:00 2001 From: Jay Goss Date: Wed, 24 Jun 2026 12:07:52 -0500 Subject: [PATCH 1/9] ref(project-creation): Align SCM alert frequency with updated design Rework the alert-frequency options to match the latest Figma. The custom threshold form now lives inside the selected card instead of below it with a left-border accent bar, and only mounts when Custom is selected. The threshold input and metric select are equal width at the standard form size. Add the High priority issues description line and a footer note that alerts can be changed after project creation. Update the option copy to Custom threshold and I'll set up alerts later. ScmAlertOptionCard now wraps the radio row and its children in one bordered card and accepts an optional description. Claude-Session: https://claude.ai/code/session_01F8yRVeJRYS7BKpoxQHASq2 --- .../components/scmAlertFrequency.tsx | 116 +++++++++--------- .../components/scmAlertOptionCard.tsx | 38 ++++-- .../onboarding/scmProjectDetails.spec.tsx | 10 +- 3 files changed, 92 insertions(+), 72 deletions(-) diff --git a/static/app/views/onboarding/components/scmAlertFrequency.tsx b/static/app/views/onboarding/components/scmAlertFrequency.tsx index a17e07c47b67..2dc1b8ac328d 100644 --- a/static/app/views/onboarding/components/scmAlertFrequency.tsx +++ b/static/app/views/onboarding/components/scmAlertFrequency.tsx @@ -1,8 +1,9 @@ import {Input} from '@sentry/scraps/input'; -import {Container, Grid, Stack} from '@sentry/scraps/layout'; +import {Flex, Grid, Stack} from '@sentry/scraps/layout'; import {Select} from '@sentry/scraps/select'; import {Text} from '@sentry/scraps/text'; +import {IconInfo} from 'sentry/icons/iconInfo'; import {t} from 'sentry/locale'; import {ScmAlertOptionCard} from 'sentry/views/onboarding/components/scmAlertOptionCard'; import { @@ -31,72 +32,71 @@ export function ScmAlertFrequency({ const isLaterSelected = alertSetting === RuleAction.CREATE_ALERT_LATER; return ( - - onFieldChange('alertSetting', RuleAction.DEFAULT_ALERT)} - /> + + + onFieldChange('alertSetting', RuleAction.DEFAULT_ALERT)} + /> - onFieldChange('alertSetting', RuleAction.CUSTOMIZED_ALERTS)} - > - - - - + onFieldChange('alertSetting', RuleAction.CUSTOMIZED_ALERTS)} + > + {isCustomSelected && ( + + {t('When there are more than')} - - - onFieldChange('threshold', e.target.value)} - disabled={!isCustomSelected} - /> - onFieldChange('threshold', e.target.value)} + /> + onFieldChange('interval', option.value)} - disabled={!isCustomSelected} - /> + p.theme.space.lg} ${p => p.theme.space.lg}; - padding-left: calc(${p => p.theme.space.lg} + 20px + ${p => p.theme.space.md}); + padding-left: calc(${p => p.theme.space.lg} + 24px + ${p => p.theme.space.md}); `; From 21fa9d0bd6908bbf32f136374666d4ee599a2b5d Mon Sep 17 00:00:00 2001 From: Jay Goss Date: Wed, 24 Jun 2026 14:41:20 -0500 Subject: [PATCH 7/9] test(project-creation): Match renamed alert label in SCM acceptance test The alert option label was renamed from "I'll create my own alerts later" to "I'll set up alerts later" earlier in this branch, but the back-nav acceptance test still selected the radio by the old text and timed out. Update its xpath substrings to match the current label. --- tests/acceptance/test_scm_onboarding.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/acceptance/test_scm_onboarding.py b/tests/acceptance/test_scm_onboarding.py index 9680c17d8664..395f92887ff9 100644 --- a/tests/acceptance/test_scm_onboarding.py +++ b/tests/acceptance/test_scm_onboarding.py @@ -744,12 +744,10 @@ def test_scm_back_from_setup_docs_active_project_alert_changed(self) -> None: self.browser.click('[aria-label="Back"]') self.browser.wait_until('[data-test-id="onboarding-step-scm-project-details"]') - # Switch alerts from "High priority issues" to "create later". - self.browser.click( - xpath='//button[@role="radio"][contains(., "create my own alerts later")]' - ) + # Switch alerts from "High priority issues" to "set up later". + self.browser.click(xpath='//button[@role="radio"][contains(., "set up alerts later")]') self.browser.wait_until( - xpath='//button[@role="radio"][@aria-checked="true"][contains(., "create my own alerts later")]' + xpath='//button[@role="radio"][@aria-checked="true"][contains(., "set up alerts later")]' ) self.browser.wait_until_clickable(xpath='//button[contains(., "Create project")]') self.browser.click(xpath='//button[contains(., "Create project")]') From 692505a7f1810d1f826adb33425cb4c92aed2d0c Mon Sep 17 00:00:00 2001 From: Jay Goss Date: Thu, 25 Jun 2026 16:42:13 -0500 Subject: [PATCH 8/9] ref(project-creation): Use smaller radio and text in alert option cards Shrink the option-card radio to xs and the label/description to sm, and align the radio with a Flex instead of a centered grid row, tightening the alert-frequency cards. --- .../components/scmAlertOptionCard.tsx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/static/app/views/onboarding/components/scmAlertOptionCard.tsx b/static/app/views/onboarding/components/scmAlertOptionCard.tsx index 1e98aa7e371f..77982889edf2 100644 --- a/static/app/views/onboarding/components/scmAlertOptionCard.tsx +++ b/static/app/views/onboarding/components/scmAlertOptionCard.tsx @@ -1,6 +1,6 @@ import styled from '@emotion/styled'; -import {Container, Grid, Stack} from '@sentry/scraps/layout'; +import {Container, Flex, Grid, Stack} from '@sentry/scraps/layout'; import {Radio} from '@sentry/scraps/radio'; import {Text} from '@sentry/scraps/text'; @@ -38,23 +38,22 @@ export function ScmAlertOptionCard({ - - - + + + - + {label} {description && ( - + {description} @@ -73,11 +72,11 @@ export function ScmAlertOptionCard({ ); } -// The body indents to line up under the label (button padding + the md radio's -// 24px width + grid column gap) and carries its own right/bottom padding so +// The body indents to line up under the label (button padding + the xs radio's +// 12px width + grid column gap) and carries its own right/bottom padding so // input focus rings clear the animated overflow:hidden bounds. The top gap // comes from the header button's own bottom padding. const ExpandedBody = styled('div')` padding: 0 ${p => p.theme.space.lg} ${p => p.theme.space.lg}; - padding-left: calc(${p => p.theme.space.lg} + 24px + ${p => p.theme.space.md}); + padding-left: calc(${p => p.theme.space.lg} + 12px + ${p => p.theme.space.md}); `; From b79f0bde4c58e319e9fe5ea84dd9dc6cb4259bb2 Mon Sep 17 00:00:00 2001 From: Jay Goss Date: Fri, 26 Jun 2026 11:31:15 -0500 Subject: [PATCH 9/9] ref(project-creation): Keep the collapsible reveal from clipping content ScmCollapsibleReveal kept overflow:hidden on the animated container even after expanding, so it clipped focus rings at the edges and open select menus past the settled bounds. Keep overflow hidden only while the height tween runs, then switch to visible once open and settled, and back to hidden on collapse. Reword the ScmAlertOptionCard body comment accordingly: its right/bottom padding now just insets to match the header, not to clear the clipped bounds. --- .../components/scmAlertOptionCard.tsx | 8 ++++---- .../components/scmCollapsibleReveal.tsx | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/static/app/views/onboarding/components/scmAlertOptionCard.tsx b/static/app/views/onboarding/components/scmAlertOptionCard.tsx index 77982889edf2..93dd490615bf 100644 --- a/static/app/views/onboarding/components/scmAlertOptionCard.tsx +++ b/static/app/views/onboarding/components/scmAlertOptionCard.tsx @@ -72,10 +72,10 @@ export function ScmAlertOptionCard({ ); } -// The body indents to line up under the label (button padding + the xs radio's -// 12px width + grid column gap) and carries its own right/bottom padding so -// input focus rings clear the animated overflow:hidden bounds. The top gap -// comes from the header button's own bottom padding. +// The body indents to line up under the label (header button padding + the xs +// radio's 12px width + grid column gap) and insets its right/bottom to match +// the header's padding, since the card itself carries none. The top gap comes +// from the header button's own bottom padding. const ExpandedBody = styled('div')` padding: 0 ${p => p.theme.space.lg} ${p => p.theme.space.lg}; padding-left: calc(${p => p.theme.space.lg} + 12px + ${p => p.theme.space.md}); diff --git a/static/app/views/onboarding/components/scmCollapsibleReveal.tsx b/static/app/views/onboarding/components/scmCollapsibleReveal.tsx index a2268f3d9baa..7fd39af60d3e 100644 --- a/static/app/views/onboarding/components/scmCollapsibleReveal.tsx +++ b/static/app/views/onboarding/components/scmCollapsibleReveal.tsx @@ -1,3 +1,4 @@ +import {useState} from 'react'; import {AnimatePresence, motion} from 'framer-motion'; interface ScmCollapsibleRevealProps { @@ -16,6 +17,14 @@ interface ScmCollapsibleRevealProps { * initial={false} renders the open state without animating on mount. */ export function ScmCollapsibleReveal({open, id, children}: ScmCollapsibleRevealProps) { + // overflow:hidden is needed while the height tween runs so the content clips + // cleanly, but kept on it would also clip anything that extends past the + // settled bounds, e.g. a focus ring at the edge or an open select menu below. + // Switch to visible once open and settled, back to hidden whenever animating. + const [overflow, setOverflow] = useState<'hidden' | 'visible'>( + open ? 'visible' : 'hidden' + ); + return ( {open && ( @@ -26,7 +35,13 @@ export function ScmCollapsibleReveal({open, id, children}: ScmCollapsibleRevealP animate={{height: 'auto', opacity: 1}} exit={{height: 0, opacity: 0}} transition={{duration: 0.2, ease: 'easeOut'}} - style={{overflow: 'hidden', width: '100%'}} + onAnimationStart={() => setOverflow('hidden')} + onAnimationComplete={() => { + if (open) { + setOverflow('visible'); + } + }} + style={{overflow, width: '100%'}} > {children}