Skip to content

Commit 6c060c4

Browse files
committed
ref(onboarding): Extract ScmAlertOptionCard and simplify layout
Extract AlertOptionCard into a shared ScmAlertOptionCard component using Grid for consistent alignment. Remove LayoutGroup and motion from scmProjectDetails since layout animations are not needed here. Narrow content width to 285px to match design.
1 parent 1c0eca8 commit 6c060c4

File tree

3 files changed

+177
-200
lines changed

3 files changed

+177
-200
lines changed
Lines changed: 61 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,25 @@
11
import {Input} from '@sentry/scraps/input';
2-
import {Container, Flex, Stack} from '@sentry/scraps/layout';
3-
import {Radio} from '@sentry/scraps/radio';
2+
import {Container, Grid, Stack} from '@sentry/scraps/layout';
43
import {Select} from '@sentry/scraps/select';
54
import {Text} from '@sentry/scraps/text';
65

76
import {IconClock, IconFix, IconWarning} from 'sentry/icons';
87
import {t} from 'sentry/locale';
8+
import {ScmAlertOptionCard} from 'sentry/views/onboarding/components/scmAlertOptionCard';
99
import {
1010
type AlertRuleOptions,
1111
INTERVAL_CHOICES,
1212
METRIC_CHOICES,
1313
RuleAction,
1414
} from 'sentry/views/projectInstall/issueAlertOptions';
1515

16-
import {ScmCardButton} from './scmCardButton';
17-
1816
interface ScmAlertFrequencyProps extends Partial<AlertRuleOptions> {
1917
onFieldChange: <K extends keyof AlertRuleOptions>(
2018
key: K,
2119
value: AlertRuleOptions[K]
2220
) => void;
2321
}
2422

25-
interface AlertOptionCardProps {
26-
icon: React.ReactNode;
27-
isSelected: boolean;
28-
label: string;
29-
onSelect: () => void;
30-
children?: React.ReactNode;
31-
}
32-
33-
function AlertOptionCard({
34-
label,
35-
icon,
36-
isSelected,
37-
onSelect,
38-
children,
39-
}: AlertOptionCardProps) {
40-
return (
41-
<ScmCardButton aria-checked={isSelected} onClick={onSelect}>
42-
<Stack gap="md">
43-
<Container
44-
border={isSelected ? 'accent' : 'secondary'}
45-
padding="lg"
46-
radius="md"
47-
style={isSelected ? {marginBottom: 1} : {borderBottomWidth: 2}}
48-
>
49-
<Flex gap="md" align="center">
50-
<Container padding="xs 0 0 0">
51-
<Radio size="sm" readOnly checked={isSelected} tabIndex={-1} />
52-
</Container>
53-
<Text
54-
bold={isSelected}
55-
style={{flex: 1, lineHeight: '22px'}}
56-
size="md"
57-
density="comfortable"
58-
>
59-
{label}
60-
</Text>
61-
<Flex align="center" style={{paddingTop: 2}}>
62-
{icon}
63-
</Flex>
64-
</Flex>
65-
</Container>
66-
{children}
67-
</Stack>
68-
</ScmCardButton>
69-
);
70-
}
71-
7223
export function ScmAlertFrequency({
7324
alertSetting = RuleAction.DEFAULT_ALERT,
7425
interval = '1m',
@@ -82,80 +33,74 @@ export function ScmAlertFrequency({
8233

8334
return (
8435
<Stack gap="xl" role="radiogroup" aria-label={t('Alert frequency')}>
85-
<Stack gap="lg">
86-
<AlertOptionCard
87-
label={t('High priority issues')}
88-
icon={
89-
<IconWarning size="md" variant={isDefaultSelected ? 'accent' : 'secondary'} />
90-
}
91-
isSelected={isDefaultSelected}
92-
onSelect={() => onFieldChange('alertSetting', RuleAction.DEFAULT_ALERT)}
93-
/>
36+
<ScmAlertOptionCard
37+
label={t('High priority issues')}
38+
icon={
39+
<IconWarning size="md" variant={isDefaultSelected ? 'accent' : 'secondary'} />
40+
}
41+
isSelected={isDefaultSelected}
42+
onSelect={() => onFieldChange('alertSetting', RuleAction.DEFAULT_ALERT)}
43+
/>
9444

95-
<AlertOptionCard
96-
label={t('Custom')}
97-
icon={<IconFix size="md" variant={isCustomSelected ? 'accent' : 'secondary'} />}
98-
isSelected={isCustomSelected}
99-
onSelect={() => onFieldChange('alertSetting', RuleAction.CUSTOMIZED_ALERTS)}
100-
>
101-
<Flex paddingLeft="3xl">
102-
<Stack
103-
gap="md"
104-
paddingLeft="3xl"
105-
width="100%"
106-
style={{
107-
borderLeft: `2px solid var(--border-accent, var(--accent400))`,
108-
}}
109-
>
110-
<Stack gap="xs" width="100%">
45+
<ScmAlertOptionCard
46+
label={t('Custom')}
47+
icon={<IconFix size="md" variant={isCustomSelected ? 'accent' : 'secondary'} />}
48+
isSelected={isCustomSelected}
49+
onSelect={() => onFieldChange('alertSetting', RuleAction.CUSTOMIZED_ALERTS)}
50+
>
51+
<Container paddingLeft="2xl">
52+
<Stack
53+
gap="lg"
54+
padding="sm 0 0 2xl"
55+
width="100%"
56+
borderLeft={isCustomSelected ? 'accent' : 'secondary'}
57+
>
58+
<Stack gap="xs" width="100%">
59+
<Container>
11160
<Text size="md" density="comfortable">
11261
{t('When there are more than')}
11362
</Text>
114-
<Flex gap="md" width="100%">
115-
<div style={{width: 91}}>
116-
<Input
117-
size="sm"
118-
type="number"
119-
min="0"
120-
placeholder="10"
121-
value={threshold}
122-
onChange={e => onFieldChange('threshold', e.target.value)}
123-
/>
124-
</div>
125-
<div style={{flex: 1}}>
126-
<Select
127-
size="sm"
128-
value={metric}
129-
options={METRIC_CHOICES}
130-
onChange={option => onFieldChange('metric', option.value)}
131-
/>
132-
</div>
133-
</Flex>
134-
</Stack>
135-
<Stack gap="xs" width="100%">
136-
<Text size="md" density="comfortable">
137-
{t('a unique error in')}
138-
</Text>
63+
</Container>
64+
<Grid gap="md" width="100%" columns=".35fr .65fr">
65+
<Input
66+
size="sm"
67+
type="number"
68+
min="0"
69+
placeholder="10"
70+
value={threshold}
71+
onChange={e => onFieldChange('threshold', e.target.value)}
72+
/>
13973
<Select
14074
size="sm"
141-
value={interval}
142-
options={INTERVAL_CHOICES}
143-
onChange={option => onFieldChange('interval', option.value)}
75+
value={metric}
76+
options={METRIC_CHOICES}
77+
onChange={option => onFieldChange('metric', option.value)}
14478
/>
145-
</Stack>
79+
</Grid>
14680
</Stack>
147-
</Flex>
148-
</AlertOptionCard>
81+
<Stack gap="xs" width="100%">
82+
<Container>
83+
<Text size="md" density="comfortable">
84+
{t('a unique error in')}
85+
</Text>
86+
</Container>
87+
<Select
88+
size="sm"
89+
value={interval}
90+
options={INTERVAL_CHOICES}
91+
onChange={option => onFieldChange('interval', option.value)}
92+
/>
93+
</Stack>
94+
</Stack>
95+
</Container>
96+
</ScmAlertOptionCard>
14997

150-
<AlertOptionCard
151-
label={t("I'll create my own alerts later")}
152-
icon={
153-
<IconClock size="md" variant={isLaterSelected ? 'accent' : 'secondary'} />
154-
}
155-
isSelected={isLaterSelected}
156-
onSelect={() => onFieldChange('alertSetting', RuleAction.CREATE_ALERT_LATER)}
157-
/>
158-
</Stack>
98+
<ScmAlertOptionCard
99+
label={t("I'll create my own alerts later")}
100+
icon={<IconClock size="md" variant={isLaterSelected ? 'accent' : 'secondary'} />}
101+
isSelected={isLaterSelected}
102+
onSelect={() => onFieldChange('alertSetting', RuleAction.CREATE_ALERT_LATER)}
103+
/>
159104
</Stack>
160105
);
161106
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import {Container, Flex, Grid, Stack} from '@sentry/scraps/layout';
2+
import {Radio} from '@sentry/scraps/radio';
3+
import {Text} from '@sentry/scraps/text';
4+
5+
import {ScmCardButton} from 'sentry/views/onboarding/components/scmCardButton';
6+
7+
interface ScmAlertOptionCardProps {
8+
icon: React.ReactNode;
9+
isSelected: boolean;
10+
label: string;
11+
onSelect: () => void;
12+
children?: React.ReactNode;
13+
}
14+
15+
export function ScmAlertOptionCard({
16+
label,
17+
icon,
18+
isSelected,
19+
onSelect,
20+
children,
21+
}: ScmAlertOptionCardProps) {
22+
return (
23+
<ScmCardButton aria-checked={isSelected} onClick={onSelect}>
24+
<Stack gap="lg">
25+
<Container
26+
border={isSelected ? 'accent' : 'secondary'}
27+
padding="lg"
28+
radius="md"
29+
style={isSelected ? {marginBottom: 1} : {borderBottomWidth: 2}}
30+
>
31+
<Grid gap="md" align="center" columns="min-content 1fr min-content">
32+
<Radio size="sm" readOnly checked={isSelected} tabIndex={-1} />
33+
<Text bold={isSelected} size="md" density="comfortable">
34+
{label}
35+
</Text>
36+
<Flex align="center">{icon}</Flex>
37+
</Grid>
38+
</Container>
39+
{children}
40+
</Stack>
41+
</ScmCardButton>
42+
);
43+
}

0 commit comments

Comments
 (0)