Skip to content

Commit 7fd84e7

Browse files
authored
ref(badges): Move a bunch of badge* components into one folder (#68843)
Moved a bunch of things named `Badge*` and tags.tsx into a happy folder together. A lot of them render about the same, so I'm thinking there's loads of duplicate code all over the place. Lots of teams got tagged because imports got auto fixed for me, so i grouped up all the moves together into the one PR Also, I added some stories for AlertBadge which turned out surprising: <img width="764" alt="SCR-20240412-noza" src="https://github.com/getsentry/sentry/assets/187460/bfd9c058-581c-4266-baa2-954149bb414a">
1 parent 82af73b commit 7fd84e7

File tree

101 files changed

+516
-460
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+516
-460
lines changed

static/app/components/badge.tsx

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,8 @@
1-
import type {Theme} from '@emotion/react';
2-
import styled from '@emotion/styled';
3-
4-
import {space} from 'sentry/styles/space';
5-
6-
interface Props extends React.HTMLAttributes<HTMLSpanElement> {
7-
text?: string | number | null;
8-
type?: keyof Theme['badge'];
9-
}
10-
11-
const Badge = styled(({children, text, ...props}: Props) => (
12-
<span {...props}>{children ?? text}</span>
13-
))<Props>`
14-
display: inline-block;
15-
height: 20px;
16-
min-width: 20px;
17-
line-height: 20px;
18-
border-radius: 20px;
19-
padding: 0 5px;
20-
margin-left: ${space(0.5)};
21-
font-size: 75%;
22-
font-weight: 600;
23-
text-align: center;
24-
color: ${p => p.theme.badge[p.type ?? 'default'].color};
25-
background: ${p => p.theme.badge[p.type ?? 'default'].background};
26-
transition: background 100ms linear;
27-
28-
position: relative;
29-
`;
1+
import Badge from 'sentry/components/badge/badge';
302

3+
/**
4+
* @deprecated Import from `sentry/components/badge/badge` instead
5+
*
6+
* TODO(ryan953): remove this shim once getsentry imports are updated
7+
*/
318
export default Badge;

static/app/components/alertBadge.spec.tsx renamed to static/app/components/badge/alertBadge.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {render, screen} from 'sentry-test/reactTestingLibrary';
22

3-
import AlertBadge from 'sentry/components/alertBadge';
3+
import AlertBadge from 'sentry/components/badge/alertBadge';
44
import {IncidentStatus} from 'sentry/views/alerts/types';
55

66
describe('AlertBadge', () => {
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {Fragment} from 'react';
2+
3+
import AlertBadge from 'sentry/components/badge/alertBadge';
4+
import Matrix from 'sentry/components/stories/matrix';
5+
import storyBook from 'sentry/stories/storyBook';
6+
import {IncidentStatus} from 'sentry/views/alerts/types';
7+
8+
export default storyBook(AlertBadge, story => {
9+
story('Default', () => <AlertBadge />);
10+
11+
const props = {
12+
status: [
13+
IncidentStatus.OPENED,
14+
IncidentStatus.CLOSED,
15+
IncidentStatus.WARNING,
16+
IncidentStatus.CRITICAL,
17+
],
18+
withText: [false, true],
19+
isIssue: [false, true],
20+
};
21+
story('Props', () => (
22+
<Fragment>
23+
<Matrix
24+
render={AlertBadge}
25+
selectedProps={['withText', 'status']}
26+
propMatrix={props}
27+
/>
28+
<Matrix
29+
render={AlertBadge}
30+
selectedProps={['isIssue', 'withText']}
31+
propMatrix={props}
32+
/>
33+
<Matrix
34+
render={AlertBadge}
35+
selectedProps={['isIssue', 'status']}
36+
propMatrix={props}
37+
/>
38+
</Fragment>
39+
));
40+
});

static/app/components/badge.stories.tsx renamed to static/app/components/badge/badge.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Badge from 'sentry/components/badge';
1+
import Badge from 'sentry/components/badge/badge';
22
import JSXProperty from 'sentry/components/stories/jsxProperty';
33
import SideBySide from 'sentry/components/stories/sideBySide';
44
import storyBook from 'sentry/stories/storyBook';

static/app/components/badge/badge.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type {Theme} from '@emotion/react';
2+
import styled from '@emotion/styled';
3+
4+
import {space} from 'sentry/styles/space';
5+
6+
interface Props extends React.HTMLAttributes<HTMLSpanElement> {
7+
text?: string | number | null;
8+
type?: keyof Theme['badge'];
9+
}
10+
11+
const Badge = styled(({children, text, ...props}: Props) => (
12+
<span {...props}>{children ?? text}</span>
13+
))<Props>`
14+
display: inline-block;
15+
height: 20px;
16+
min-width: 20px;
17+
line-height: 20px;
18+
border-radius: 20px;
19+
padding: 0 5px;
20+
margin-left: ${space(0.5)};
21+
font-size: 75%;
22+
font-weight: 600;
23+
text-align: center;
24+
color: ${p => p.theme.badge[p.type ?? 'default'].color};
25+
background: ${p => p.theme.badge[p.type ?? 'default'].background};
26+
transition: background 100ms linear;
27+
28+
position: relative;
29+
`;
30+
31+
export default Badge;

static/app/components/deployBadge.spec.tsx renamed to static/app/components/badge/deployBadge.spec.tsx

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {RouterContextFixture} from 'sentry-fixture/routerContextFixture';
22

33
import {render, screen} from 'sentry-test/reactTestingLibrary';
44

5-
import DeployBadge from 'sentry/components/deployBadge';
5+
import DeployBadge from 'sentry/components/badge/deployBadge';
66
import type {Deploy} from 'sentry/types';
77

88
const deploy: Deploy = {
@@ -16,14 +16,6 @@ const deploy: Deploy = {
1616
};
1717

1818
describe('DeployBadge', () => {
19-
it('renders', () => {
20-
render(<DeployBadge deploy={deploy} />);
21-
22-
expect(screen.getByText('production')).toBeInTheDocument();
23-
expect(screen.queryByRole('link')).not.toBeInTheDocument();
24-
expect(screen.queryByTestId('deploy-open-icon')).not.toBeInTheDocument();
25-
});
26-
2719
it('renders with link', () => {
2820
const projectId = 1;
2921
render(
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import DeployBadge from 'sentry/components/badge/deployBadge';
2+
import storyBook from 'sentry/stories/storyBook';
3+
import type {Deploy} from 'sentry/types';
4+
5+
const deploy: Deploy = {
6+
name: '85fedddce5a61a58b160fa6b3d6a1a8451e94eb9 to prod',
7+
url: '',
8+
environment: 'production',
9+
dateStarted: '2020-05-11T18:12:00.025928Z',
10+
dateFinished: '2020-05-11T18:12:00.025928Z',
11+
version: '4.2.0',
12+
id: '6348842',
13+
};
14+
15+
export default storyBook(DeployBadge, story => {
16+
story('Renders with a link', () => (
17+
<DeployBadge deploy={deploy} orgSlug="sentry" version="1.2.3" projectId={1} />
18+
));
19+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import Link from 'sentry/components/links/link';
2+
import {Tag} from 'sentry/components/tag';
3+
import {t} from 'sentry/locale';
4+
import type {Deploy} from 'sentry/types';
5+
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
6+
7+
type Props = {
8+
deploy: Deploy;
9+
orgSlug: string;
10+
projectId: number;
11+
version: string;
12+
};
13+
14+
export default function DeployBadge({deploy, orgSlug, projectId, version}: Props) {
15+
return (
16+
<Link
17+
to={{
18+
pathname: `/organizations/${orgSlug}/issues/`,
19+
query: {
20+
project: projectId,
21+
environment: deploy.environment,
22+
query: new MutableSearch([`release:${version!}`]).formatString(),
23+
},
24+
}}
25+
>
26+
<Tag type="highlight" textMaxWidth={80} tooltipText={t('Open In Issues')}>
27+
{deploy.environment}
28+
</Tag>
29+
</Link>
30+
);
31+
}

static/app/components/featureBadge.spec.tsx renamed to static/app/components/badge/featureBadge.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {render, screen} from 'sentry-test/reactTestingLibrary';
22

3-
import FeatureBadge from 'sentry/components/featureBadge';
3+
import FeatureBadge from 'sentry/components/badge/featureBadge';
44

55
describe('FeatureBadge', function () {
66
it('auto-hides when expired', function () {

static/app/components/featureBadge.stories.tsx renamed to static/app/components/badge/featureBadge.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type {ComponentProps} from 'react';
22
import {Fragment} from 'react';
33

4-
import FeatureBadge from 'sentry/components/featureBadge';
4+
import FeatureBadge from 'sentry/components/badge/featureBadge';
55
import Matrix from 'sentry/components/stories/matrix';
66
import SideBySide from 'sentry/components/stories/sideBySide';
77
import storyBook from 'sentry/stories/storyBook';
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import {Fragment} from 'react';
2+
import {useTheme} from '@emotion/react';
3+
import styled from '@emotion/styled';
4+
import {captureException, withScope} from '@sentry/react';
5+
import type {SeverityLevel} from '@sentry/types';
6+
7+
import Badge from 'sentry/components/badge/badge';
8+
import CircleIndicator from 'sentry/components/circleIndicator';
9+
import type {TooltipProps} from 'sentry/components/tooltip';
10+
import {Tooltip} from 'sentry/components/tooltip';
11+
import {t} from 'sentry/locale';
12+
import type {ValidSize} from 'sentry/styles/space';
13+
import {space} from 'sentry/styles/space';
14+
15+
export type BadgeType = 'alpha' | 'beta' | 'new' | 'experimental';
16+
17+
type BadgeProps = {
18+
type: BadgeType;
19+
condensed?: boolean;
20+
expiresAt?: Date;
21+
title?: string;
22+
tooltipProps?: Partial<TooltipProps>;
23+
variant?: 'badge' | 'indicator' | 'short';
24+
};
25+
26+
type Props = Omit<React.HTMLAttributes<HTMLDivElement>, keyof BadgeProps> & BadgeProps;
27+
28+
const defaultTitles: Record<BadgeType, string> = {
29+
alpha: t('This feature is internal and available for QA purposes'),
30+
beta: t('This feature is available for early adopters and may change'),
31+
new: t('This feature is new! Try it out and let us know what you think'),
32+
experimental: t(
33+
'This feature is experimental! Try it out and let us know what you think. No promises!'
34+
),
35+
};
36+
37+
const labels: Record<BadgeType, string> = {
38+
alpha: t('alpha'),
39+
beta: t('beta'),
40+
new: t('new'),
41+
experimental: t('experimental'),
42+
};
43+
44+
const shortLabels: Record<BadgeType, string> = {
45+
alpha: 'A',
46+
beta: 'B',
47+
new: 'N',
48+
experimental: 'E',
49+
};
50+
51+
function BaseFeatureBadge({
52+
type,
53+
variant = 'badge',
54+
title,
55+
tooltipProps,
56+
expiresAt,
57+
...props
58+
}: Props) {
59+
const theme = useTheme();
60+
if (expiresAt && expiresAt.valueOf() < Date.now()) {
61+
// Only get 1% of events as we don't need many to know that a badge needs to be cleaned up.
62+
if (Math.random() < 0.01) {
63+
withScope(scope => {
64+
scope.setTag('title', title);
65+
scope.setTag('type', type);
66+
scope.setLevel('warning' as SeverityLevel);
67+
captureException(new Error('Expired Feature Badge'));
68+
});
69+
}
70+
return null;
71+
}
72+
73+
return (
74+
<div {...props}>
75+
<Tooltip title={title ?? defaultTitles[type]} position="right" {...tooltipProps}>
76+
<Fragment>
77+
{variant === 'badge' && <StyledBadge type={type} text={labels[type]} />}
78+
{variant === 'short' && <StyledBadge type={type} text={shortLabels[type]} />}
79+
{variant === 'indicator' && (
80+
<CircleIndicator color={theme.badge[type].indicatorColor} size={8} />
81+
)}
82+
</Fragment>
83+
</Tooltip>
84+
</div>
85+
);
86+
}
87+
88+
const StyledBadge = styled(Badge)`
89+
margin: 0;
90+
padding: 0 ${space(0.75)};
91+
line-height: ${space(2)};
92+
height: ${space(2)};
93+
font-weight: normal;
94+
font-size: ${p => p.theme.fontSizeExtraSmall};
95+
vertical-align: middle;
96+
`;
97+
98+
const FeatureBadge = styled(BaseFeatureBadge)<{space?: ValidSize}>`
99+
display: inline-flex;
100+
align-items: center;
101+
margin-left: ${p => space(p.space ?? 0.75)};
102+
`;
103+
104+
export default FeatureBadge;

static/app/components/group/groupPriority.spec.tsx renamed to static/app/components/badge/groupPriority.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {UserFixture} from 'sentry-fixture/user';
44
import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
55
import {textWithMarkupMatcher} from 'sentry-test/utils';
66

7-
import {GroupPriorityDropdown} from 'sentry/components/group/groupPriority';
7+
import {GroupPriorityDropdown} from 'sentry/components/badge/groupPriority';
88
import {GroupActivityType, PriorityLevel} from 'sentry/types';
99

1010
describe('GroupPriority', () => {

static/app/components/group/groupPriority.stories.tsx renamed to static/app/components/badge/groupPriority.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {useState} from 'react';
33
import {
44
GroupPriorityBadge,
55
GroupPriorityDropdown,
6-
} from 'sentry/components/group/groupPriority';
6+
} from 'sentry/components/badge/groupPriority';
77
import SideBySide from 'sentry/components/stories/sideBySide';
88
import storyBook from 'sentry/stories/storyBook';
99
import {PriorityLevel} from 'sentry/types';

static/app/components/group/groupPriority.tsx renamed to static/app/components/badge/groupPriority.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import styled from '@emotion/styled';
55
import bannerStar from 'sentry-images/spot/banner-star.svg';
66

77
import {usePrompt} from 'sentry/actionCreators/prompts';
8+
import Tag from 'sentry/components/badge/tag';
89
import {Button, LinkButton} from 'sentry/components/button';
910
import {Chevron} from 'sentry/components/chevron';
1011
import type {MenuItemProps} from 'sentry/components/dropdownMenu';
@@ -13,7 +14,6 @@ import {DropdownMenuFooter} from 'sentry/components/dropdownMenu/footer';
1314
import useFeedbackWidget from 'sentry/components/feedback/widget/useFeedbackWidget';
1415
import HookOrDefault from 'sentry/components/hookOrDefault';
1516
import Placeholder from 'sentry/components/placeholder';
16-
import {Tag} from 'sentry/components/tag';
1717
import {Tooltip} from 'sentry/components/tooltip';
1818
import {IconClose} from 'sentry/icons';
1919
import {t, tct} from 'sentry/locale';

static/app/components/tag.spec.tsx renamed to static/app/components/badge/tag.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {RouterContextFixture} from 'sentry-fixture/routerContextFixture';
22

33
import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
44

5-
import {Tag} from 'sentry/components/tag';
5+
import Tag from 'sentry/components/badge/tag';
66
import {IconFire} from 'sentry/icons';
77

88
describe('Tag', () => {

static/app/components/tag.stories.tsx renamed to static/app/components/badge/tag.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import {Fragment, useState} from 'react';
22

3+
import Tag from 'sentry/components/badge/tag';
34
import {Button} from 'sentry/components/button';
45
import JSXNode from 'sentry/components/stories/jsxNode';
56
import JSXProperty from 'sentry/components/stories/jsxProperty';
67
import SizingWindow from 'sentry/components/stories/sizingWindow';
7-
import {Tag} from 'sentry/components/tag';
88
import {IconCheckmark, IconFire, IconSentry, IconStar} from 'sentry/icons';
99
import storyBook from 'sentry/stories/storyBook';
1010
import useDismissAlert from 'sentry/utils/useDismissAlert';

0 commit comments

Comments
 (0)