Skip to content
Open
22 changes: 9 additions & 13 deletions static/app/components/badge/groupPriority.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -247,26 +247,22 @@ export function GroupPriorityDropdown({
);
}

const DropdownButton = styled(Button)`
font-weight: ${p => p.theme.font.weight.sans.regular};
border: none;
padding: 0;
height: unset;
border-radius: 20px;
box-shadow: none;

> span > div {
border-radius: 20px;
}
`;

const StyledTag = styled(Tag)`
gap: ${p => p.theme.space['2xs']};
position: relative;
height: 24px;
overflow: hidden;
`;

const DropdownButton = styled(Button)`
padding: 0;
border-radius: ${p => p.theme.radius.full};

${StyledTag} {
border-radius: ${p => p.theme.radius.full};
}
`;

const InlinePlaceholder = styled(Placeholder)`
display: inline-block;
vertical-align: middle;
Expand Down
31 changes: 31 additions & 0 deletions static/app/components/core/button/button.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,37 @@ describe('Button', () => {

expect(spy).not.toHaveBeenCalled();
});

it('does not call `onClick` on busy buttons', async () => {
const spy = jest.fn();
render(
<Button onClick={spy} busy>
Click me
</Button>
);
await userEvent.click(screen.getByText('Click me'));

expect(spy).not.toHaveBeenCalled();
});

it('shows spinner when busy', () => {
render(<Button busy>Busy Button</Button>);

const button = screen.getByRole('button', {name: 'Busy Button'});
expect(button).toHaveAttribute('aria-busy', 'true');
const spinner = button.querySelector('[aria-hidden="true"]');
expect(spinner).toBeInTheDocument();
});

it('hides spinner when not busy', () => {
render(<Button>Normal Button</Button>);

const button = screen.getByRole('button', {name: 'Normal Button'});
expect(button).not.toHaveAttribute('aria-busy');

const spinner = button.querySelector('[aria-hidden="true"]');
expect(spinner).not.toBeInTheDocument();
});
});

describe('LinkButton', () => {
Expand Down
32 changes: 32 additions & 0 deletions static/app/components/core/button/button.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {keyframes} from '@emotion/react';
import styled from '@emotion/styled';

import {Flex} from '@sentry/scraps/layout';
Expand Down Expand Up @@ -55,6 +56,7 @@ export function Button({
minWidth="0"
height="100%"
whiteSpace="nowrap"
visibility={busy ? 'hidden' : undefined}
>
{props.icon && (
<Flex
Expand All @@ -71,6 +73,17 @@ export function Button({
</Flex>
)}
{props.children}
{busy && (
<Flex
align="center"
justify="center"
position="absolute"
visibility="visible"
inset={0}
>
{({className}) => <BusySpinner className={className} aria-hidden />}
</Flex>
)}
</Flex>
</StyledButton>
</Tooltip>
Expand All @@ -80,3 +93,22 @@ export function Button({
const StyledButton = styled('button')<ButtonProps>`
${p => getButtonStyles(p as any)}
`;

const spin = keyframes`
to {
transform: rotate(360deg);
}
`;

const BusySpinner = styled('span')`
&::after {
content: '';
display: block;
width: 1em;
height: 1em;
border-radius: 50%;
border: 2px solid currentColor;
border-top-color: transparent;
animation: ${spin} 0.6s linear infinite;
}
`;
4 changes: 4 additions & 0 deletions static/app/components/core/layout/container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ interface ContainerLayoutProps {
alignSelf?: Responsive<React.CSSProperties['alignSelf']>;
justifySelf?: Responsive<React.CSSProperties['justifySelf']>;

visibility?: Responsive<'visible' | 'hidden' | 'collapse'>;

// Text Wrapping
whiteSpace?: Responsive<
'break-spaces' | 'normal' | 'nowrap' | 'pre' | 'pre-line' | 'pre-wrap'
Expand Down Expand Up @@ -225,6 +227,7 @@ const omitContainerProps = new Set<keyof ContainerLayoutProps | 'as'>([
'right',
'row',
'top',
'visibility',
'width',
'whiteSpace',
]);
Expand Down Expand Up @@ -314,6 +317,7 @@ export const Container = styled(
${p => rc('border-left', p.borderLeft, p.theme, getBorder)};
${p => rc('border-right', p.borderRight, p.theme, getBorder)};

${p => rc('visibility', p.visibility, p.theme)};
${p => rc('white-space', p.whiteSpace, p.theme)};

/**
Expand Down
Loading