Skip to content
Merged
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
28 changes: 28 additions & 0 deletions static/app/components/dnd/dragReorderButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {Button, type ButtonProps} from '@sentry/scraps/button';

import {IconGrabbable} from 'sentry/icons';
import {t} from 'sentry/locale';
import type {IconSize} from 'sentry/utils/theme';

type DragReorderButtonProps = Omit<ButtonProps, 'children'> & {
iconSize?: IconSize;
};

export function DragReorderButton({
size = 'zero',
iconSize = 'xs',
ref,
...props
}: DragReorderButtonProps) {
return (
<Button
aria-label={t('Drag to reorder')}
priority="transparent"
size={size}
style={{cursor: 'grab'}}
icon={<IconGrabbable size={iconSize} />}
ref={ref}
{...props}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import type {SelectOption} from '@sentry/scraps/compactSelect';
import {Flex, Grid} from '@sentry/scraps/layout';
import {Text} from '@sentry/scraps/text';

import {DragReorderButton} from 'sentry/components/dnd/dragReorderButton';
import {QuestionTooltip} from 'sentry/components/questionTooltip';
import {IconDelete, IconGrabbable} from 'sentry/icons';
import {IconDelete} from 'sentry/icons';
import {t} from 'sentry/locale';
import {AssertionFormError} from 'sentry/views/alerts/rules/uptime/formErrors';
import {
Expand Down Expand Up @@ -85,13 +86,9 @@ export function OpContainer({
<Text size="sm" bold>
<label htmlFor={inputId}>{label}</label>
</Text>
<Button
size="zero"
priority="transparent"
icon={<IconGrabbable size="xs" />}
<DragReorderButton
aria-label={t('Reorder assertion')}
ref={setActivatorNodeRef}
style={{cursor: 'grab'}}
{...listeners}
{...attributes}
/>
Expand Down
8 changes: 3 additions & 5 deletions static/app/views/alerts/rules/uptime/assertions/opGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {Container, Stack} from '@sentry/scraps/layout';
import {OverlayTrigger} from '@sentry/scraps/overlayTrigger';
import {Text} from '@sentry/scraps/text';

import {IconAdd, IconDelete, IconGrabbable} from 'sentry/icons';
import {DragReorderButton} from 'sentry/components/dnd/dragReorderButton';
import {IconAdd, IconDelete} from 'sentry/icons';
import {t} from 'sentry/locale';
import {uniqueId} from 'sentry/utils/guid';
import {
Expand Down Expand Up @@ -231,10 +232,7 @@ export function AssertionOpGroup({
options={[{value: 'negated', label: t('Negate result')}]}
/>
</CompositeSelect>
<Button
size="zero"
priority="transparent"
icon={<IconGrabbable size="xs" />}
<DragReorderButton
aria-label={t('Reorder assertion group')}
ref={setActivatorNodeRef}
{...listeners}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {Fragment, type ReactNode} from 'react';
import type {DraggableSyntheticListeners, UseDraggableArguments} from '@dnd-kit/core';
import type {DraggableAttributes, DraggableSyntheticListeners} from '@dnd-kit/core';
import styled from '@emotion/styled';

import {Button} from '@sentry/scraps/button';

import {IconDelete, IconGrabbable} from 'sentry/icons';
import {DragReorderButton} from 'sentry/components/dnd/dragReorderButton';
import {IconDelete} from 'sentry/icons';
import {t} from 'sentry/locale';
import type {QueryFieldValue} from 'sentry/utils/discover/fields';
import {QueryField as TableQueryField} from 'sentry/views/discover/table/queryField';
Expand All @@ -14,7 +15,7 @@ export interface QueryFieldProps {
fieldOptions: React.ComponentProps<typeof TableQueryField>['fieldOptions'];
onChange: (newValue: QueryFieldValue) => void;
value: QueryFieldValue;
attributes?: UseDraggableArguments['attributes'];
attributes?: DraggableAttributes;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

While we're here 😄

canDelete?: boolean;
canDrag?: boolean;
disabled?: boolean;
Expand Down Expand Up @@ -53,16 +54,7 @@ export function QueryField({
<QueryFieldWrapper ref={ref} style={style}>
{isDragging ? null : (
<Fragment>
{canDrag && (
<DragAndReorderButton
{...listeners}
{...attributes}
aria-label={t('Drag to reorder')}
icon={<IconGrabbable size="xs" />}
size="zero"
priority="transparent"
/>
)}
{canDrag && <StyledDragReorderButton {...listeners} {...attributes} />}
<TableQueryField
placeholder={t('Select group')}
fieldValue={value}
Expand Down Expand Up @@ -91,7 +83,7 @@ export function QueryField({
);
}

const DragAndReorderButton = styled(Button)`
const StyledDragReorderButton = styled(DragReorderButton)`
height: ${p => p.theme.form.md.height};
`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ import {CSS} from '@dnd-kit/utilities';
import {useTheme} from '@emotion/react';
import styled from '@emotion/styled';

import {Button} from '@sentry/scraps/button';

import {IconGrabbable} from 'sentry/icons';
import {t} from 'sentry/locale';
import {DragReorderButton} from 'sentry/components/dnd/dragReorderButton';

export function SortableVisualizeFieldWrapper({
dragId,
Expand Down Expand Up @@ -47,22 +44,14 @@ export function SortableVisualizeFieldWrapper({
return (
<div ref={setNodeRef} style={style}>
{canDrag && (
<DragAndReorderButton
{...listeners}
{...attributes}
aria-label={t('Drag to reorder')}
icon={<IconGrabbable size="xs" />}
size="zero"
priority="transparent"
isDragging={isDragging}
/>
<StyledDragReorderButton {...listeners} {...attributes} isDragging={isDragging} />
)}
{children}
</div>
);
}

const DragAndReorderButton = styled(Button)<{isDragging: boolean}>`
const StyledDragReorderButton = styled(DragReorderButton)<{isDragging: boolean}>`
height: ${p => p.theme.form.md.height};

${p => p.isDragging && p.theme.visuallyHidden}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import styled from '@emotion/styled';

import {Button} from '@sentry/scraps/button';

import {IconDelete, IconGrabbable} from 'sentry/icons';
import {DragReorderButton} from 'sentry/components/dnd/dragReorderButton';
import {IconDelete} from 'sentry/icons';
import {t} from 'sentry/locale';
import type {SelectValue} from 'sentry/types/core';
import {
Expand Down Expand Up @@ -91,12 +92,7 @@ export function VisualizeGhostField({
return (
<Ghost>
<FieldRow>
<DragAndReorderButton
aria-label={t('Drag to reorder')}
icon={<IconGrabbable size="xs" />}
size="zero"
priority="transparent"
/>
<StyledDragReorderButton />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Inline cursor style overrides ghost's grabbing cursor

Medium Severity

The Ghost styled component uses button { cursor: grabbing; } to show a "grabbing" cursor on all buttons during an active drag. Previously, the old DragAndReorderButton didn't set any inline cursor style, so the Ghost's CSS worked. Now, DragReorderButton always applies style={{cursor: 'grab'}} as an inline style, which has higher specificity than any CSS class rule and cannot be overridden by the Ghost's button { cursor: grabbing; } selector. This causes the drag handle in the ghost overlay to incorrectly show a grab cursor instead of grabbing while dragging.

Additional Locations (1)
Fix in Cursor Fix in Web

Copy link
Copy Markdown
Member Author

@JoshuaKGoldberg JoshuaKGoldberg Mar 18, 2026

Choose a reason for hiding this comment

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

[Question] 🤔 I'm not sure which way is better... the custom ghost-y one before, or aligning to the new way?

Copy link
Copy Markdown
Member Author

@JoshuaKGoldberg JoshuaKGoldberg Mar 27, 2026

Choose a reason for hiding this comment

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

I believe this comment is wrong. This is already visually buggy. Both in dev and on prod, the standard cursor is shown and the ghost visualization is offset too far to the right:

Screenshot of dragging a visualization in the Widget builder, showing the default cursor and a ghost visualization row 50% of its width to the right

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

(I'm talking with product about filing an issue)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

<FieldBar>
{draggingField?.kind === FieldValueKind.EQUATION ? (
<StyledArithmeticInput
Expand Down Expand Up @@ -237,6 +233,6 @@ const Ghost = styled('div')`
}
`;

const DragAndReorderButton = styled(Button)`
const StyledDragReorderButton = styled(DragReorderButton)`
height: ${p => p.theme.form.md.height};
`;
11 changes: 4 additions & 7 deletions static/app/views/discover/table/columnEditCollection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import {Tooltip} from '@sentry/scraps/tooltip';

import {parseArithmetic} from 'sentry/components/arithmeticInput/parser';
import {SectionHeading} from 'sentry/components/charts/styles';
import {DragReorderButton} from 'sentry/components/dnd/dragReorderButton';
import {getOffsetOfElement} from 'sentry/components/performance/waterfall/utils';
import {IconAdd, IconDelete, IconGrabbable, IconWarning} from 'sentry/icons';
import {IconAdd, IconDelete, IconWarning} from 'sentry/icons';
import {t} from 'sentry/locale';
import type {Organization} from 'sentry/types/organization';
import {trackAnalytics} from 'sentry/utils/analytics';
Expand Down Expand Up @@ -484,13 +485,9 @@ class ColumnEditCollection extends Component<Props, State> {
className={isGhost ? '' : DRAG_CLASS}
>
{canDrag ? (
<DragAndReorderButton
aria-label={t('Drag to reorder')}
<StyledDragReorderButton
onMouseDown={event => this.startDrag(event, i)}
onTouchStart={event => this.startDrag(event, i)}
icon={<IconGrabbable size="xs" />}
size="zero"
priority="transparent"
/>
) : singleColumn && showAliasField ? null : (
<span />
Expand Down Expand Up @@ -789,7 +786,7 @@ const RemoveButton = styled(Button)`
height: ${p => p.theme.form.md.height};
`;

const DragAndReorderButton = styled(Button)`
const StyledDragReorderButton = styled(DragReorderButton)`
height: ${p => p.theme.form.md.height};
`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import {CompactSelect} from '@sentry/scraps/compactSelect';
import {OverlayTrigger} from '@sentry/scraps/overlayTrigger';
import {Tooltip} from '@sentry/scraps/tooltip';

import {DragReorderButton} from 'sentry/components/dnd/dragReorderButton';
import {IconAdd} from 'sentry/icons/iconAdd';
import {IconDelete} from 'sentry/icons/iconDelete';
import {IconGrabbable} from 'sentry/icons/iconGrabbable';
import {t} from 'sentry/locale';
import {getFieldDefinition} from 'sentry/utils/fields';
import {
Expand Down Expand Up @@ -83,15 +83,7 @@ export function ToolbarGroupByDropdown({
style={{transform: CSS.Transform.toString(transform)}}
{...attributes}
>
{canDelete ? (
<Button
aria-label={t('Drag to reorder')}
priority="transparent"
size="zero"
icon={<IconGrabbable size="sm" />}
{...listeners}
/>
) : null}
{canDelete ? <DragReorderButton iconSize="sm" {...listeners} /> : null}
<StyledCompactSelect
data-test-id="editor-column"
options={options}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import type {SelectKey, SelectOption} from '@sentry/scraps/compactSelect';
import {CompactSelect} from '@sentry/scraps/compactSelect';
import {Tooltip} from '@sentry/scraps/tooltip';

import {DragReorderButton} from 'sentry/components/dnd/dragReorderButton';
import {IconAdd} from 'sentry/icons';
import {IconDelete} from 'sentry/icons/iconDelete';
import {IconGrabbable} from 'sentry/icons/iconGrabbable';
import {t} from 'sentry/locale';
import type {ParsedFunction} from 'sentry/utils/discover/fields';
import {getFieldDefinition} from 'sentry/utils/fields';
Expand Down Expand Up @@ -80,13 +80,7 @@ export function ToolbarVisualizeDropdown({
{...attributes}
>
{dragColumnId === undefined ? null : (
<Button
aria-label={t('Drag to reorder')}
priority="transparent"
size="zero"
icon={<IconGrabbable size="sm" />}
{...listeners}
/>
<DragReorderButton iconSize="sm" {...listeners} />
)}
{label}
<AggregateCompactSelect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {Flex} from '@sentry/scraps/layout';
import {ArithmeticBuilder} from 'sentry/components/arithmeticBuilder';
import type {Expression} from 'sentry/components/arithmeticBuilder/expression';
import type {FunctionArgument} from 'sentry/components/arithmeticBuilder/types';
import {DragReorderButton} from 'sentry/components/dnd/dragReorderButton';
import {IconDelete} from 'sentry/icons/iconDelete';
import {IconGrabbable} from 'sentry/icons/iconGrabbable';
import {t} from 'sentry/locale';
import {EQUATION_PREFIX, stripEquationPrefix} from 'sentry/utils/discover/fields';
import {
Expand Down Expand Up @@ -98,13 +98,7 @@ export function VisualizeEquation({
{...attributes}
>
{dragColumnId === undefined ? null : (
<Button
aria-label={t('Drag to reorder')}
priority="transparent"
size="zero"
icon={<IconGrabbable size="sm" />}
{...listeners}
/>
<DragReorderButton iconSize="sm" {...listeners} />
)}
{label}
<Flex flex={1}>
Expand Down
15 changes: 7 additions & 8 deletions static/app/views/explore/tables/aggregateColumnEditorModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import type {ModalRenderProps} from 'sentry/actionCreators/modal';
import {ArithmeticBuilder} from 'sentry/components/arithmeticBuilder';
import type {Expression} from 'sentry/components/arithmeticBuilder/expression';
import type {FunctionArgument} from 'sentry/components/arithmeticBuilder/types';
import {DragReorderButton} from 'sentry/components/dnd/dragReorderButton';
import {DropdownMenu} from 'sentry/components/dropdownMenu';
import {SPAN_PROPS_DOCS_URL} from 'sentry/constants';
import {IconAdd} from 'sentry/icons/iconAdd';
import {IconDelete} from 'sentry/icons/iconDelete';
import {IconGrabbable} from 'sentry/icons/iconGrabbable';
import {t} from 'sentry/locale';
import type {TagCollection} from 'sentry/types/group';
import type {Organization} from 'sentry/types/organization';
Expand Down Expand Up @@ -230,13 +230,7 @@ function ColumnEditorRow({
}}
{...attributes}
>
<StyledButton
aria-label={t('Drag to reorder')}
priority="transparent"
size="sm"
icon={<IconGrabbable size="sm" />}
{...listeners}
/>
<StyledDragReorderButton size="sm" iconSize="sm" {...listeners} />
{isGroupBy(column.column) ? (
<GroupBySelector
groupBy={column.column}
Expand Down Expand Up @@ -538,6 +532,11 @@ const RowContainer = styled('div')`
}
`;

const StyledDragReorderButton = styled(DragReorderButton)`
padding-left: 0;
padding-right: 0;
`;

const StyledButton = styled(Button)`
padding-left: 0;
padding-right: 0;
Expand Down
15 changes: 7 additions & 8 deletions static/app/views/explore/tables/columnEditorModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import {Grid} from '@sentry/scraps/layout';
import {OverlayTrigger} from '@sentry/scraps/overlayTrigger';

import type {ModalRenderProps} from 'sentry/actionCreators/modal';
import {DragReorderButton} from 'sentry/components/dnd/dragReorderButton';
import {SPAN_PROPS_DOCS_URL} from 'sentry/constants';
import {IconAdd} from 'sentry/icons/iconAdd';
import {IconDelete} from 'sentry/icons/iconDelete';
import {IconGrabbable} from 'sentry/icons/iconGrabbable';
import {t} from 'sentry/locale';
import type {TagCollection} from 'sentry/types/group';
import {defined} from 'sentry/utils';
Expand Down Expand Up @@ -282,13 +282,7 @@ function ColumnEditorRow({
}}
{...attributes}
>
<StyledButton
aria-label={t('Drag to reorder')}
priority="transparent"
size="sm"
icon={<IconGrabbable size="sm" />}
{...listeners}
/>
<StyledDragReorderButton size="sm" iconSize="sm" {...listeners} />
<StyledCompactSelect
data-test-id="editor-column"
options={options}
Expand Down Expand Up @@ -331,6 +325,11 @@ const RowContainer = styled('div')`
}
`;

const StyledDragReorderButton = styled(DragReorderButton)`
padding-left: 0;
padding-right: 0;
`;

const StyledButton = styled(Button)`
padding-left: 0;
padding-right: 0;
Expand Down
Loading