Skip to content

Commit b1a59e9

Browse files
authored
feat(issue-views): Improve drag handle and safari interactions (#87119)
This PR makes a couple of minor improvements to issue views, specifically related to the newly added grab handle, and safari improvements: 1. Hover states in Safari are no longer super janky. This was caused by some CSS selectors not being supported by safari. 2. The drag handle now has an interaction state layer to make it more clear that it is draggable 3. The background opacity of each issue view has been slightly decreased so that an item being dragged will slightly show the items beneath it. Note that there is still an issue where triggering a reorder while dragging in safari will cause the hover state to be lost. This seems to be an [intentional decision by Safari](https://stackoverflow.com/questions/52133842/safari-only-css-hover-event-not-triggered-on-drag), but I could not figure out a reasonable fix in the timebox I gave myself.
1 parent c9af920 commit b1a59e9

File tree

4 files changed

+34
-20
lines changed

4 files changed

+34
-20
lines changed

Diff for: static/app/components/nav/issueViews/issueViewNavItemContent.tsx

+19-14
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {motion, Reorder, useDragControls} from 'framer-motion';
44
import type {Location} from 'history';
55
import isEqual from 'lodash/isEqual';
66

7+
import InteractionStateLayer from 'sentry/components/interactionStateLayer';
78
import {useNavContext} from 'sentry/components/nav/context';
89
import {GrabHandleIcon} from 'sentry/components/nav/issueViews/grabHandleIcon';
910
import IssueViewNavEditableTitle from 'sentry/components/nav/issueViews/issueViewNavEditableTitle';
@@ -38,7 +39,7 @@ export interface IssueViewNavItemContentProps {
3839
/**
3940
* Whether an item is being dragged.
4041
*/
41-
isDragging: boolean;
42+
isDragging: string | null;
4243
/**
4344
* Whether the item is the last view in the list.
4445
* This will be removed once view sharing/starring is implemented.
@@ -63,7 +64,7 @@ export interface IssueViewNavItemContentProps {
6364
/**
6465
* A callback function that updates the isDragging state.
6566
*/
66-
setIsDragging: (isDragging: boolean) => void;
67+
setIsDragging: (isDragging: string | null) => void;
6768
/**
6869
* The issue view to display
6970
*/
@@ -137,11 +138,11 @@ export function IssueViewNavItemContent({
137138
dragTransition={{bounceStiffness: 400, bounceDamping: 40}}
138139
value={view}
139140
onDragStart={() => {
140-
setIsDragging(true);
141+
setIsDragging(view.id);
141142
startInteraction();
142143
}}
143144
onDragEnd={() => {
144-
setIsDragging(false);
145+
setIsDragging(null);
145146
onReorderComplete();
146147
endInteraction();
147148
}}
@@ -168,6 +169,7 @@ export function IssueViewNavItemContent({
168169
e.preventDefault();
169170
}}
170171
>
172+
<StyledInteractionStateLayer isPressed={isDragging === view.id} />
171173
<GrabHandleIcon color="gray300" />
172174
</GrabHandleWrapper>
173175
<ProjectIcon projectPlatforms={projectPlatforms} />
@@ -218,7 +220,7 @@ export function IssueViewNavItemContent({
218220
});
219221
}}
220222
setIsEditing={setIsEditing}
221-
isDragging={isDragging}
223+
isDragging={!!isDragging}
222224
/>
223225
{view.unsavedChanges && (
224226
<Tooltip
@@ -344,7 +346,13 @@ const hasUnsavedChanges = (
344346
// but we need to ensure the item is relatively positioned and has a background color for it to work
345347
const StyledReorderItem = styled(Reorder.Item)`
346348
position: relative;
347-
background-color: ${p => p.theme.surface200};
349+
background-color: ${p => p.theme.translucentSurface200};
350+
border-radius: ${p => p.theme.borderRadius};
351+
`;
352+
353+
const StyledInteractionStateLayer = styled(InteractionStateLayer)`
354+
height: 120%;
355+
border-radius: 4px;
348356
`;
349357

350358
const TrailingItemsWrapper = styled('div')`
@@ -357,14 +365,12 @@ const StyledSecondaryNavItem = styled(SecondaryNav.Item)`
357365
position: relative;
358366
padding-right: ${space(0.5)};
359367
360-
/* Hide the ellipsis menu if not hovered, or if it's not expanded */
361-
:not(:hover):not(:has([data-ellipsis-menu-trigger][aria-expanded='true'])) {
362-
[data-ellipsis-menu-trigger] {
368+
/* Hide the ellipsis menu if the item is not hovered */
369+
:not(:hover) {
370+
[data-ellipsis-menu-trigger]:not([aria-expanded='true']) {
363371
${p => p.theme.visuallyHidden}
364372
}
365-
}
366373
367-
:not(:hover) {
368374
[data-drag-icon] {
369375
${p => p.theme.visuallyHidden}
370376
}
@@ -381,8 +387,7 @@ const StyledSecondaryNavItem = styled(SecondaryNav.Item)`
381387
}
382388
383389
/* Hide the query count if the ellipsis menu is expanded */
384-
&:has([data-ellipsis-menu-trigger][aria-expanded='true'])
385-
[data-issue-view-query-count] {
390+
:has([data-ellipsis-menu-trigger][aria-expanded='true']) [data-issue-view-query-count] {
386391
${p => p.theme.visuallyHidden}
387392
}
388393
`;
@@ -413,13 +418,13 @@ const LeadingItemsWrapper = styled('div')`
413418
display: flex;
414419
align-items: center;
415420
justify-content: center;
421+
margin-right: ${space(0.75)};
416422
`;
417423

418424
const GrabHandleWrapper = styled(motion.div)`
419425
display: flex;
420426
align-items: center;
421427
justify-content: center;
422-
margin-right: ${space(0.75)};
423428
width: 18px;
424429
height: 18px;
425430
cursor: grab;

Diff for: static/app/components/nav/issueViews/issueViewNavItems.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export function IssueViewNavItems({
4242
* source of truth for the view state, rather than a separate state
4343
*/
4444
const [views, setViews] = useState<IssueView[]>(loadedViews);
45-
const [isDragging, setIsDragging] = useState(false);
45+
const [isDragging, setIsDragging] = useState<string | null>(null);
4646
const queryClient = useQueryClient();
4747

4848
useEffect(() => {

Diff for: static/app/components/nav/projectIcon.tsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import styled from '@emotion/styled';
22
import PlatformIcon from 'platformicons/build/platformIcon';
33

44
import {IconAllProjects} from 'sentry/components/nav/iconAllProjects';
5-
import {space} from 'sentry/styles/space';
65

76
interface ProjectIconProps {
87
projectPlatforms: string[];
8+
className?: string;
99
}
1010

11-
function ProjectIcon({projectPlatforms}: ProjectIconProps) {
11+
function ProjectIcon({projectPlatforms, className}: ProjectIconProps) {
1212
let renderedIcons: React.ReactNode;
1313

1414
switch (projectPlatforms.length) {
@@ -36,7 +36,11 @@ function ProjectIcon({projectPlatforms}: ProjectIconProps) {
3636
);
3737
}
3838

39-
return <IconWrap data-project-icon>{renderedIcons}</IconWrap>;
39+
return (
40+
<IconWrap className={className} data-project-icon>
41+
{renderedIcons}
42+
</IconWrap>
43+
);
4044
}
4145

4246
const IconWrap = styled('div')`
@@ -45,7 +49,6 @@ const IconWrap = styled('div')`
4549
flex-direction: column;
4650
align-items: center;
4751
justify-content: center;
48-
margin-right: ${space(0.75)};
4952
`;
5053

5154
const IconContainer = styled('div')`

Diff for: static/app/views/insights/navigation.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import {Fragment} from 'react';
2+
import styled from '@emotion/styled';
23

34
import {NAV_GROUP_LABELS} from 'sentry/components/nav/constants';
45
import {usePrefersStackedNav} from 'sentry/components/nav/prefersStackedNav';
56
import ProjectIcon from 'sentry/components/nav/projectIcon';
67
import {SecondaryNav} from 'sentry/components/nav/secondary';
78
import {PrimaryNavGroup} from 'sentry/components/nav/types';
89
import {t} from 'sentry/locale';
10+
import {space} from 'sentry/styles/space';
911
import useOrganization from 'sentry/utils/useOrganization';
1012
import useProjects from 'sentry/utils/useProjects';
1113
import {
@@ -70,7 +72,7 @@ function InsightsSecondaryNav({children}: InsightsNavigationProps) {
7072
key={project.id}
7173
to={`${baseUrl}/projects/${project.slug}/`}
7274
leadingItems={
73-
<ProjectIcon
75+
<StyledProjectIcon
7476
projectPlatforms={project.platform ? [project.platform] : []}
7577
/>
7678
}
@@ -98,3 +100,7 @@ export default function InsightsNavigation({children}: InsightsNavigationProps)
98100

99101
return <InsightsSecondaryNav>{children}</InsightsSecondaryNav>;
100102
}
103+
104+
const StyledProjectIcon = styled(ProjectIcon)`
105+
margin-right: ${space(0.75)};
106+
`;

0 commit comments

Comments
 (0)