Browse Source

ref(ui): Various CSS cleanups in Issue Group Header (#39009)

Evan Purkhiser 2 years ago
parent
commit
0168e97858

+ 3 - 1
fixtures/page_objects/issue_details.py

@@ -68,7 +68,9 @@ class IssueDetailsPage(BasePage):
         self.browser.wait_until('[data-test-id="unbookmark"]')
         self.browser.wait_until('[data-test-id="unbookmark"]')
 
 
     def assign_to(self, user):
     def assign_to(self, user):
-        assignee = self.browser.find_element(by=By.CSS_SELECTOR, value=".assigned-to")
+        assignee = self.browser.find_element(
+            by=By.CSS_SELECTOR, value='[data-test-id="assigned-to"]'
+        )
 
 
         # Open the assignee picker
         # Open the assignee picker
         assignee.find_element(by=By.CSS_SELECTOR, value='[role="button"]').click()
         assignee.find_element(by=By.CSS_SELECTOR, value='[role="button"]').click()

+ 1 - 7
static/app/components/seenByList.tsx

@@ -1,6 +1,5 @@
 import {Fragment} from 'react';
 import {Fragment} from 'react';
 import styled from '@emotion/styled';
 import styled from '@emotion/styled';
-import classNames from 'classnames';
 import moment from 'moment';
 import moment from 'moment';
 
 
 import AvatarList from 'sentry/components/avatar/avatarList';
 import AvatarList from 'sentry/components/avatar/avatarList';
@@ -45,10 +44,7 @@ const SeenByList = ({
 
 
   // Note className="seen-by" is required for responsive design
   // Note className="seen-by" is required for responsive design
   return (
   return (
-    <SeenByWrapper
+    <SeenByWrapper iconPosition={iconPosition} className={className}>
-      iconPosition={iconPosition}
-      className={classNames('seen-by', className)}
-    >
       <AvatarList
       <AvatarList
         users={displayUsers}
         users={displayUsers}
         avatarSize={avatarSize}
         avatarSize={avatarSize}
@@ -72,8 +68,6 @@ const SeenByList = ({
 
 
 const SeenByWrapper = styled('div')<{iconPosition: Props['iconPosition']}>`
 const SeenByWrapper = styled('div')<{iconPosition: Props['iconPosition']}>`
   display: flex;
   display: flex;
-  margin-top: 15px;
-  float: right;
   ${p => (p.iconPosition === 'left' ? 'flex-direction: row-reverse' : '')};
   ${p => (p.iconPosition === 'left' ? 'flex-direction: row-reverse' : '')};
 `;
 `;
 
 

+ 1 - 5
static/app/views/eventsV2/eventDetails/linkedIssue.tsx

@@ -75,7 +75,7 @@ class LinkedIssue extends AsyncComponent<
                 }
                 }
               />
               />
             </StyledLink>
             </StyledLink>
-            <StyledSeenByList seenBy={group.seenBy} maxVisibleAvatars={5} />
+            <SeenByList seenBy={group.seenBy} maxVisibleAvatars={5} />
           </IssueCardHeader>
           </IssueCardHeader>
           <IssueCardBody>
           <IssueCardBody>
             <GroupChart statsPeriod="30d" data={group} height={56} />
             <GroupChart statsPeriod="30d" data={group} height={56} />
@@ -114,10 +114,6 @@ const IssueCardBody = styled('div')`
   padding-top: ${space(1)};
   padding-top: ${space(1)};
 `;
 `;
 
 
-const StyledSeenByList = styled(SeenByList)`
-  margin: 0;
-`;
-
 const StyledShortId = styled(ShortId)`
 const StyledShortId = styled(ShortId)`
   font-size: ${p => p.theme.fontSizeMedium};
   font-size: ${p => p.theme.fontSizeMedium};
   color: ${p => p.theme.textColor};
   color: ${p => p.theme.textColor};

+ 0 - 1
static/app/views/organizationGroupDetails/actions/index.tsx

@@ -487,7 +487,6 @@ const Wrapper = styled('div')`
   justify-content: flex-start;
   justify-content: flex-start;
   align-items: center;
   align-items: center;
   grid-auto-flow: column;
   grid-auto-flow: column;
-  margin-top: ${space(2)};
   gap: ${space(0.5)};
   gap: ${space(0.5)};
   white-space: nowrap;
   white-space: nowrap;
 `;
 `;

+ 99 - 117
static/app/views/organizationGroupDetails/header.tsx

@@ -31,7 +31,7 @@ import Tooltip from 'sentry/components/tooltip';
 import {IconChat} from 'sentry/icons';
 import {IconChat} from 'sentry/icons';
 import {t} from 'sentry/locale';
 import {t} from 'sentry/locale';
 import space from 'sentry/styles/space';
 import space from 'sentry/styles/space';
-import {Group, IssueCategory, Organization, Project} from 'sentry/types';
+import {Group, IssueCategory, Organization, Project, User} from 'sentry/types';
 import {Event} from 'sentry/types/event';
 import {Event} from 'sentry/types/event';
 import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
 import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
 import {getUtcDateString} from 'sentry/utils/dates';
 import {getUtcDateString} from 'sentry/utils/dates';
@@ -56,12 +56,8 @@ type Props = WithRouterProps & {
   event?: Event;
   event?: Event;
 };
 };
 
 
-type MemberList = NonNullable<
-  React.ComponentProps<typeof AssigneeSelector>['memberList']
->;
-
 type State = {
 type State = {
-  memberList?: MemberList;
+  memberList?: User[];
 };
 };
 
 
 class GroupHeader extends Component<Props, State> {
 class GroupHeader extends Component<Props, State> {
@@ -173,26 +169,26 @@ class GroupHeader extends Component<Props, State> {
         >
         >
           {t('Details')}
           {t('Details')}
         </ListLink>
         </ListLink>
-        <StyledListLink
+        <ListLink
           to={`${baseUrl}activity/${location.search}`}
           to={`${baseUrl}activity/${location.search}`}
           isActive={() => currentTab === Tab.ACTIVITY}
           isActive={() => currentTab === Tab.ACTIVITY}
           disabled={disabledTabs.includes(Tab.ACTIVITY)}
           disabled={disabledTabs.includes(Tab.ACTIVITY)}
           onClick={() => this.tabClickAnalyticsEvent(Tab.ACTIVITY)}
           onClick={() => this.tabClickAnalyticsEvent(Tab.ACTIVITY)}
         >
         >
           {t('Activity')}
           {t('Activity')}
-          <Badge>
+          <IconBadge>
             {group.numComments}
             {group.numComments}
             <IconChat size="xs" />
             <IconChat size="xs" />
-          </Badge>
+          </IconBadge>
-        </StyledListLink>
+        </ListLink>
-        <StyledListLink
+        <ListLink
           to={`${baseUrl}feedback/${location.search}`}
           to={`${baseUrl}feedback/${location.search}`}
           isActive={() => currentTab === Tab.USER_FEEDBACK}
           isActive={() => currentTab === Tab.USER_FEEDBACK}
           disabled={disabledTabs.includes(Tab.USER_FEEDBACK)}
           disabled={disabledTabs.includes(Tab.USER_FEEDBACK)}
           onClick={() => this.tabClickAnalyticsEvent(Tab.USER_FEEDBACK)}
           onClick={() => this.tabClickAnalyticsEvent(Tab.USER_FEEDBACK)}
         >
         >
           {t('User Feedback')} <Badge text={group.userReportCount} />
           {t('User Feedback')} <Badge text={group.userReportCount} />
-        </StyledListLink>
+        </ListLink>
         {hasEventAttachments && (
         {hasEventAttachments && (
           <ListLink
           <ListLink
             to={`${baseUrl}attachments/${location.search}`}
             to={`${baseUrl}attachments/${location.search}`}
@@ -283,18 +279,18 @@ class GroupHeader extends Component<Props, State> {
         >
         >
           {t('Details')}
           {t('Details')}
         </ListLink>
         </ListLink>
-        <StyledListLink
+        <ListLink
           to={`${baseUrl}activity/${location.search}`}
           to={`${baseUrl}activity/${location.search}`}
           isActive={() => currentTab === Tab.ACTIVITY}
           isActive={() => currentTab === Tab.ACTIVITY}
           disabled={disabledTabs.includes(Tab.ACTIVITY)}
           disabled={disabledTabs.includes(Tab.ACTIVITY)}
           onClick={() => this.tabClickAnalyticsEvent(Tab.ACTIVITY)}
           onClick={() => this.tabClickAnalyticsEvent(Tab.ACTIVITY)}
         >
         >
           {t('Activity')}
           {t('Activity')}
-          <Badge>
+          <IconBadge>
             {group.numComments}
             {group.numComments}
             <IconChat size="xs" />
             <IconChat size="xs" />
-          </Badge>
+          </IconBadge>
-        </StyledListLink>
+        </ListLink>
         <ListLink
         <ListLink
           to={`${baseUrl}tags/${location.search}`}
           to={`${baseUrl}tags/${location.search}`}
           isActive={() => currentTab === Tab.TAGS}
           isActive={() => currentTab === Tab.TAGS}
@@ -344,14 +340,14 @@ class GroupHeader extends Component<Props, State> {
 
 
     const shortIdBreadCrumb = group.shortId && (
     const shortIdBreadCrumb = group.shortId && (
       <GuideAnchor target="issue_number" position="bottom">
       <GuideAnchor target="issue_number" position="bottom">
-        <IssueBreadcrumbWrapper>
+        <ShortIdBreadrcumb>
-          <BreadcrumbProjectBadge
+          <ProjectBadge
             project={project}
             project={project}
             avatarSize={16}
             avatarSize={16}
             hideName
             hideName
             avatarProps={{hasTooltip: true, tooltip: project.slug}}
             avatarProps={{hasTooltip: true, tooltip: project.slug}}
           />
           />
-          <StyledTooltip
+          <Tooltip
             className="help-link"
             className="help-link"
             title={t(
             title={t(
               'This identifier is unique across your organization, and can be used to reference an issue in various places, like commit messages.'
               'This identifier is unique across your organization, and can be used to reference an issue in various places, like commit messages.'
@@ -359,21 +355,21 @@ class GroupHeader extends Component<Props, State> {
             position="bottom"
             position="bottom"
           >
           >
             <StyledShortId shortId={group.shortId} />
             <StyledShortId shortId={group.shortId} />
-          </StyledTooltip>
+          </Tooltip>
           {group.issueCategory === IssueCategory.PERFORMANCE && (
           {group.issueCategory === IssueCategory.PERFORMANCE && (
             <FeatureBadge
             <FeatureBadge
               type="beta"
               type="beta"
               title="Performance issues are available for early adopters and may change"
               title="Performance issues are available for early adopters and may change"
             />
             />
           )}
           )}
-        </IssueBreadcrumbWrapper>
+        </ShortIdBreadrcumb>
       </GuideAnchor>
       </GuideAnchor>
     );
     );
 
 
     return (
     return (
       <Layout.Header>
       <Layout.Header>
         <div className={className}>
         <div className={className}>
-          <StyledBreadcrumbs
+          <Breadcrumbs
             crumbs={[
             crumbs={[
               {
               {
                 label: 'Issues',
                 label: 'Issues',
@@ -384,69 +380,57 @@ class GroupHeader extends Component<Props, State> {
               },
               },
             ]}
             ]}
           />
           />
-          <div className="row">
+          <HeaderRow>
-            <div className="col-sm-7">
+            <TitleWrapper>
-              <TitleWrapper>
+              <TitleHeading>
                 <h3>
                 <h3>
                   <StyledEventOrGroupTitle hasGuideAnchor data={group} />
                   <StyledEventOrGroupTitle hasGuideAnchor data={group} />
                 </h3>
                 </h3>
-                {group.inbox && (
+                {group.inbox && <InboxReason inbox={group.inbox} fontSize="md" />}
-                  <InboxReasonWrapper>
+              </TitleHeading>
-                    <InboxReason inbox={group.inbox} fontSize="md" />
-                  </InboxReasonWrapper>
-                )}
-              </TitleWrapper>
               <StyledTagAndMessageWrapper>
               <StyledTagAndMessageWrapper>
                 {group.level && <ErrorLevel level={group.level} size="11px" />}
                 {group.level && <ErrorLevel level={group.level} size="11px" />}
                 {group.isUnhandled && <UnhandledInboxTag />}
                 {group.isUnhandled && <UnhandledInboxTag />}
                 <EventMessage
                 <EventMessage
                   message={message}
                   message={message}
                   annotations={
                   annotations={
-                    <Fragment>
+                    group.logger && (
-                      {group.logger && (
+                      <EventAnnotation>
-                        <EventAnnotationWithSpace>
+                        <Link
-                          <Link
+                          to={{
-                            to={{
+                            pathname: `/organizations/${organization.slug}/issues/`,
-                              pathname: `/organizations/${organization.slug}/issues/`,
+                            query: {query: 'logger:' + group.logger},
-                              query: {query: 'logger:' + group.logger},
+                          }}
-                            }}
+                        >
-                          >
+                          {group.logger}
-                            {group.logger}
+                        </Link>
-                          </Link>
+                      </EventAnnotation>
-                        </EventAnnotationWithSpace>
+                    )
-                      )}
-                    </Fragment>
                   }
                   }
                 />
                 />
               </StyledTagAndMessageWrapper>
               </StyledTagAndMessageWrapper>
-            </div>
+            </TitleWrapper>
-
             <StatsWrapper>
             <StatsWrapper>
-              <div className="count align-right m-l-1">
+              <div className="count">
                 <h6 className="nav-header">{t('Events')}</h6>
                 <h6 className="nav-header">{t('Events')}</h6>
-                {disableActions ? (
+                <Link disabled={disableActions} to={eventRouteToObject}>
                   <Count className="count" value={group.count} />
                   <Count className="count" value={group.count} />
-                ) : (
+                </Link>
-                  <Link to={eventRouteToObject}>
-                    <Count className="count" value={group.count} />
-                  </Link>
-                )}
               </div>
               </div>
-              <div className="count align-right m-l-1">
+              <div className="count">
                 <h6 className="nav-header">{t('Users')}</h6>
                 <h6 className="nav-header">{t('Users')}</h6>
                 {userCount !== 0 ? (
                 {userCount !== 0 ? (
-                  disableActions ? (
+                  <Link
+                    disabled={disableActions}
+                    to={`${baseUrl}tags/user/${location.search}`}
+                  >
                     <Count className="count" value={userCount} />
                     <Count className="count" value={userCount} />
-                  ) : (
+                  </Link>
-                    <Link to={`${baseUrl}tags/user/${location.search}`}>
-                      <Count className="count" value={userCount} />
-                    </Link>
-                  )
                 ) : (
                 ) : (
                   <span>0</span>
                   <span>0</span>
                 )}
                 )}
               </div>
               </div>
-              <div className="assigned-to m-l-1">
+              <div data-test-id="assigned-to">
                 <h6 className="nav-header">{t('Assignee')}</h6>
                 <h6 className="nav-header">{t('Assignee')}</h6>
                 <AssigneeSelector
                 <AssigneeSelector
                   id={group.id}
                   id={group.id}
@@ -456,18 +440,20 @@ class GroupHeader extends Component<Props, State> {
                 />
                 />
               </div>
               </div>
             </StatsWrapper>
             </StatsWrapper>
-          </div>
+          </HeaderRow>
-          <SeenByList
+          <HeaderRow>
-            seenBy={group.seenBy}
+            <GroupActions
-            iconTooltip={t('People who have viewed this issue')}
+              group={group}
-          />
+              project={project}
-          <GroupActions
+              disabled={disableActions}
-            group={group}
+              event={event}
-            project={project}
+              query={location.query}
-            disabled={disableActions}
+            />
-            event={event}
+            <StyledSeenByList
-            query={location.query}
+              seenBy={group.seenBy}
-          />
+              iconTooltip={t('People who have viewed this issue')}
+            />
+          </HeaderRow>
           <NavTabs>
           <NavTabs>
             {group.issueCategory === IssueCategory.PERFORMANCE
             {group.issueCategory === IssueCategory.PERFORMANCE
               ? this.getPerformanceIssueTabs()
               ? this.getPerformanceIssueTabs()
@@ -481,73 +467,69 @@ class GroupHeader extends Component<Props, State> {
 
 
 export default withApi(withRouter(withOrganization(GroupHeader)));
 export default withApi(withRouter(withOrganization(GroupHeader)));
 
 
-const TitleWrapper = styled('div')`
+const ShortIdBreadrcumb = styled('div')`
   display: flex;
   display: flex;
-  line-height: 24px;
+  gap: ${space(1)};
+  align-items: center;
 `;
 `;
 
 
-const StyledEventOrGroupTitle = styled(EventOrGroupTitle)`
+const StyledShortId = styled(ShortId)`
-  font-size: inherit;
+  font-family: ${p => p.theme.text.family};
+  font-size: ${p => p.theme.fontSizeMedium};
+`;
+
+const HeaderRow = styled('div')`
+  display: flex;
+  gap: ${space(2)};
+  justify-content: space-between;
+  margin-top: ${space(2)};
+
+  @media (max-width: ${p => p.theme.breakpoints.small}) {
+    flex-direction: column;
+  }
 `;
 `;
 
 
-const StyledBreadcrumbs = styled(Breadcrumbs)`
+const TitleWrapper = styled('div')`
-  margin-bottom: ${space(2)};
+  @media (min-width: ${p => p.theme.breakpoints.small}) {
+    max-width: 65%;
+  }
 `;
 `;
 
 
-const IssueBreadcrumbWrapper = styled('div')`
+const TitleHeading = styled('div')`
   display: flex;
   display: flex;
-  align-items: center;
+  line-height: 2;
+  gap: ${space(1)};
 `;
 `;
 
 
-const StyledTooltip = styled(Tooltip)`
+const StyledSeenByList = styled(SeenByList)`
-  display: flex;
+  @media (max-width: ${p => p.theme.breakpoints.small}) {
+    display: none;
+  }
 `;
 `;
 
 
-const StyledShortId = styled(ShortId)`
+const StyledEventOrGroupTitle = styled(EventOrGroupTitle)`
-  font-family: ${p => p.theme.text.family};
+  font-size: inherit;
-  font-size: ${p => p.theme.fontSizeMedium};
 `;
 `;
 
 
 const StatsWrapper = styled('div')`
 const StatsWrapper = styled('div')`
   display: grid;
   display: grid;
-  justify-content: flex-end;
   grid-template-columns: repeat(3, min-content);
   grid-template-columns: repeat(3, min-content);
-  gap: ${space(3)};
+  gap: calc(${space(3)} + ${space(3)});
-  margin-right: 15px;
-`;
 
 
-const InboxReasonWrapper = styled('div')`
+  @media (min-width: ${p => p.theme.breakpoints.small}) {
-  margin-left: ${space(1)};
+    justify-content: flex-end;
+  }
 `;
 `;
 
 
 const StyledTagAndMessageWrapper = styled(TagAndMessageWrapper)`
 const StyledTagAndMessageWrapper = styled(TagAndMessageWrapper)`
-  display: grid;
+  display: flex;
-  grid-auto-flow: column;
   gap: ${space(1)};
   gap: ${space(1)};
   justify-content: flex-start;
   justify-content: flex-start;
   line-height: 1.2;
   line-height: 1.2;
-
-  @media (max-width: ${p => p.theme.breakpoints.small}) {
-    margin-bottom: ${space(2)};
-  }
 `;
 `;
 
 
-const StyledListLink = styled(ListLink)`
+const IconBadge = styled(Badge)`
-  svg {
+  display: flex;
-    margin-left: ${space(0.5)};
+  align-items: center;
-    margin-bottom: ${space(0.25)};
+  gap: ${space(0.5)};
-    vertical-align: middle;
-  }
-`;
-
-const StyledProjectBadge = styled(ProjectBadge)`
-  flex-shrink: 0;
-`;
-
-const BreadcrumbProjectBadge = styled(StyledProjectBadge)`
-  margin-right: ${space(0.75)};
-`;
-
-const EventAnnotationWithSpace = styled(EventAnnotation)`
-  margin-left: ${space(1)};
 `;
 `;

+ 0 - 4
static/less/group-detail.less

@@ -1326,10 +1326,6 @@ ul.crumbs {
     .is-assigned {
     .is-assigned {
       padding-top: 0;
       padding-top: 0;
     }
     }
-
-    .seen-by {
-      display: none;
-    }
   }
   }
 
 
   // Event detail columns
   // Event detail columns