Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 0 additions & 128 deletions static/app/components/core/toast/toast.chonk.tsx

This file was deleted.

214 changes: 114 additions & 100 deletions static/app/components/core/toast/toast.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,26 @@
import {useTheme} from '@emotion/react';
import styled from '@emotion/styled';
import {type DO_NOT_USE_ChonkTheme} from '@emotion/react';
import * as Sentry from '@sentry/react';
import classNames from 'classnames';
import {motion} from 'framer-motion';
import {motion, type HTMLMotionProps} from 'framer-motion';

import {Container, Flex} from '@sentry/scraps/layout';

import type {Indicator} from 'sentry/actionCreators/indicator';
import {Button} from 'sentry/components/core/button';
import {chonkFor} from 'sentry/components/core/chonk';
import LoadingIndicator from 'sentry/components/loadingIndicator';
import TextOverflow from 'sentry/components/textOverflow';
import {IconCheckmark, IconRefresh, IconWarning} from 'sentry/icons';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import testableTransition from 'sentry/utils/testableTransition';
import {withChonk} from 'sentry/utils/theme/withChonk';

import {
ChonkToastContainer,
ChonkToastIconContainer,
ChonkToastLoadingIndicator,
ChonkToastMessage,
ChonkToastUndoButton,
ChonkToastUndoButtonContainer,
} from './toast.chonk';
import {chonkStyled} from 'sentry/utils/theme/theme.chonk';

export interface ToastProps {
indicator: Indicator;
onDismiss: (indicator: Indicator, event: React.MouseEvent) => void;
}

export function Toast({indicator, onDismiss, ...props}: ToastProps) {
const theme = useTheme();

return (
<ToastContainer
onClick={
Expand All @@ -43,20 +33,20 @@ export function Toast({indicator, onDismiss, ...props}: ToastProps) {
{...props}
>
<ToastIcon type={indicator.type} />
<ToastMessage>
<Container padding="lg">
<TextOverflow>{indicator.message}</TextOverflow>
</ToastMessage>
</Container>
{indicator.options.undo && typeof indicator.options.undo === 'function' ? (
<ToastUndoButtonContainer type={indicator.type}>
<ToastUndoButton
priority={theme.isChonk ? 'default' : 'link'}
size={theme.isChonk ? 'xs' : undefined}
<Flex align="center" justify="center" padding="0 lg">
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved some of the styled wrappers to just layout components

<Button
priority="default"
size="xs"
onClick={indicator.options.undo}
icon={<IconRefresh size="xs" />}
>
{t('Undo')}
</ToastUndoButton>
</ToastUndoButtonContainer>
</Button>
</Flex>
) : null}
</ToastContainer>
);
Expand Down Expand Up @@ -103,82 +93,106 @@ function ToastIcon({type}: {type: Indicator['type']}) {
}
}

const ToastContainer = withChonk(
styled(motion.div)<React.HTMLAttributes<HTMLDivElement> & {type: Indicator['type']}>`
display: flex;
align-items: center;
height: 40px;
max-width: calc(100vw - ${space(4)} * 2);
padding: 0 15px 0 10px;
margin-top: 15px;
background: ${p => p.theme.inverted.background};
color: ${p => p.theme.inverted.textColor};
border-radius: 44px 7px 7px 44px;
box-shadow: ${p => p.theme.dropShadowHeavy};
position: relative;
`,
ChonkToastContainer
);

const ToastIconContainer = withChonk(
styled('div')<{type: Indicator['type']}>`
margin-right: ${space(0.75)};

svg {
width: 16px;
height: 16px;
color: ${p =>
p.type === 'success'
? p.theme.successText
: p.type === 'error'
? p.theme.errorText
: p.theme.textColor};
}
`,
ChonkToastIconContainer
);

const ToastMessage = withChonk(
styled('div')`
flex: 1;
`,
ChonkToastMessage
);

const ToastUndoButton = withChonk(
styled(Button)`
display: flex;
align-items: center;
gap: ${space(0.5)};
color: ${p => p.theme.inverted.linkColor};
margin-left: ${space(2)};

&:hover {
color: ${p => p.theme.inverted.linkHoverColor};
}
`,
ChonkToastUndoButton
);

const ToastUndoButtonContainer = withChonk(
styled('div')<{type: Indicator['type']}>`
color: ${p => p.theme.inverted.linkColor};
function getContainerTheme(
theme: DO_NOT_USE_ChonkTheme,
type: Indicator['type']
): React.CSSProperties {
switch (type) {
case 'success':
return {
background: theme.colors.green100,
borderBottom: `2px solid ${theme.colors.border.success}`,
border: `1px solid ${chonkFor(theme, theme.colors.chonk.green400)}`,
boxShadow: `0 3px 0 0px ${chonkFor(theme, theme.colors.chonk.green400)}`,
};
case 'error':
return {
background: theme.colors.red100,
borderBottom: `2px solid ${theme.colors.border.danger}`,
border: `1px solid ${chonkFor(theme, theme.colors.chonk.red400)}`,
boxShadow: `0 3px 0 0px ${chonkFor(theme, theme.colors.chonk.red400)}`,
};
default:
return {
background: theme.colors.background.primary,
borderBottom: `2px solid ${theme.colors.border.accent}`,
border: `1px solid ${chonkFor(theme, theme.colors.chonk.blue400)}`,
boxShadow: `0 3px 0 0px ${chonkFor(theme, theme.colors.chonk.blue400)}`,
};
}
}

&:hover {
color: ${p => p.theme.inverted.linkHoverColor};
}
`,
ChonkToastUndoButtonContainer
);
interface ChonkToastContainerProps extends HTMLMotionProps<'div'> {
children: React.ReactNode;
type: Indicator['type'];
}

const ToastLoadingIndicator = withChonk(
styled(LoadingIndicator)`
margin: 0;
const ToastContainer = chonkStyled((props: ChonkToastContainerProps) => {
const {type, children, ...rest} = props;
return (
<ToastOuterContainer type={type} {...rest}>
<ToastInnerContainer type={type}>{children}</ToastInnerContainer>
</ToastOuterContainer>
);
})<ChonkToastContainerProps>``;

const ToastOuterContainer = chonkStyled(motion.div)<{type: Indicator['type']}>`
overflow: hidden;
/* The outer container is a separate element because the colors are not opaque,
* so we set the background color here to the background color so that the
* toast is not see-through.
*/
background: ${p => p.theme.colors.background.primary};
border-radius: ${p => p.theme.radius.lg};
border: ${p => getContainerTheme(p.theme, p.type).border};
box-shadow: ${p => getContainerTheme(p.theme, p.type).boxShadow};
`;

const ToastInnerContainer = chonkStyled('div')<{type: Indicator['type']}>`
display: flex;
align-items: stretch;
background: ${p => getContainerTheme(p.theme, p.type).background};
`;
Comment on lines +151 to +155
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to make this a layout component too but the problem is we can’t pass that background we’re calculating here because we are intentionally very limited on type-level in what we can pass.

That raises the general question: how will we do these things in the future? Will we keep using styled? Should this be a styled(Flex) ? Should we just use inline style={{}} attributes for things we can’t pass to Flex ?


function getToastIconContainerTheme(
theme: DO_NOT_USE_ChonkTheme,
type: Indicator['type']
): React.CSSProperties {
switch (type) {
case 'success':
return {
background: theme.colors.chonk.green400,
borderRight: `1px solid ${chonkFor(theme, theme.colors.chonk.green400)}`,
};
case 'error':
return {
background: theme.colors.chonk.red400,
borderRight: `1px solid ${chonkFor(theme, theme.colors.chonk.red400)}`,
};
default:
return {
background: theme.colors.background.primary,
borderRight: `1px solid ${chonkFor(theme, theme.colors.chonk.blue400)}`,
};
}
}
const ToastIconContainer = chonkStyled('div')<{type: Indicator['type']}>`
display: flex;
align-items: center;
justify-content: center;
padding: ${p => p.theme.space.lg} ${p => p.theme.space.xl};
position: relative;
${p => ({...getToastIconContainerTheme(p.theme, p.type)})};

svg {
width: 16px;
height: 16px;
color: ${p => (p.type === 'success' ? p.theme.colors.black : p.type === 'error' ? p.theme.colors.white : undefined)} !important;
`;
Comment on lines +181 to +191
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The ToastIconContainer styled component has an unclosed svg CSS block within its template literal.
Severity: CRITICAL | Confidence: High

🔍 Detailed Analysis

The ToastIconContainer styled component in static/app/components/core/toast/toast.tsx contains a syntax error. The svg CSS block, opened on line 187, is not properly closed with a } before the template literal's closing backtick on line 191. This unclosed block will prevent the TypeScript/JavaScript compiler from successfully transpiling the code, leading to a build failure and preventing the application from running.

💡 Suggested Fix

Add a closing } for the svg block before the template literal's closing backtick on line 191. The corrected code should include } before the final backtick.

🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: static/app/components/core/toast/toast.tsx#L179-L191

Potential issue: The `ToastIconContainer` styled component in
`static/app/components/core/toast/toast.tsx` contains a syntax error. The `svg` CSS
block, opened on line 187, is not properly closed with a `}` before the template
literal's closing backtick on line 191. This unclosed block will prevent the
TypeScript/JavaScript compiler from successfully transpiling the code, leading to a
build failure and preventing the application from running.

Did we get this right? 👍 / 👎 to inform future reviews.
Reference ID: 4918173


const ToastLoadingIndicator = chonkStyled(LoadingIndicator)`
margin: 0;
.loading-indicator {

.loading-indicator {
border-color: ${p => p.theme.inverted.border};
border-left-color: ${p => p.theme.inverted.purple300};
}
`,
ChonkToastLoadingIndicator
);
}
`;
Loading
Loading