Browse Source

feat(workflow): Tidy up projects page design (#33608)

* feat(workflow): Tidy up projects page design

Tidy up projects page design and update for mobile views. Also properly sort projects.

FIXES WOR-1784

* update test

* fix styles

* update padding

* remove min-width
Kelly Carino 2 years ago
parent
commit
7baf2151e0

+ 8 - 3
static/app/components/dropdownControl.tsx

@@ -63,6 +63,7 @@ type Props = DefaultProps & {
   buttonTooltipTitle?: string | null;
   className?: string;
   detached?: boolean;
+  fullWidth?: boolean;
 
   /**
    * String or element for the button contents.
@@ -150,10 +151,10 @@ class DropdownControl extends React.Component<Props> {
   }
 
   render() {
-    const {alwaysRenderMenu, className} = this.props;
+    const {alwaysRenderMenu, className, fullWidth} = this.props;
 
     return (
-      <Container className={className}>
+      <Container className={className} fullWidth={fullWidth ?? false}>
         <DropdownMenu alwaysRenderMenu={alwaysRenderMenu}>
           {({isOpen, getMenuProps, getActorProps}) => (
             <React.Fragment>
@@ -167,9 +168,13 @@ class DropdownControl extends React.Component<Props> {
   }
 }
 
-const Container = styled('div')`
+const Container = styled('div')<{fullWidth: boolean}>`
   display: inline-block;
   position: relative;
+
+  @media (max-width: ${p => p.theme.breakpoints[0]}) {
+    width: ${p => p.fullWidth && '100%'};
+  }
 `;
 
 const StyledDropdownButton = styled(DropdownButton)`

+ 21 - 5
static/app/views/alerts/rules/filter.tsx

@@ -59,6 +59,7 @@ type Props = {
   dropdownSections: DropdownSection[];
   header: React.ReactElement;
   onFilterChange: (section: string, filterSelection: Set<string>) => void;
+  fullWidth?: boolean;
   showMyTeamsDescription?: boolean;
 };
 
@@ -67,6 +68,7 @@ function Filter({
   header,
   dropdownSections,
   showMyTeamsDescription,
+  fullWidth = false,
 }: Props) {
   function toggleFilter(sectionId: string, value: string) {
     const section = dropdownSections.find(
@@ -114,6 +116,7 @@ function Filter({
   return (
     <DropdownControl
       menuWidth="240px"
+      fullWidth={fullWidth}
       blendWithActor
       alwaysRenderMenu={false}
       button={({isOpen, getActorProps}) => (
@@ -123,8 +126,11 @@ function Filter({
           icon={<IconUser />}
           priority="default"
           data-test-id="filter-button"
+          fullWidth={fullWidth}
         >
-          <DropdownButtonText>{filterDescription}</DropdownButtonText>
+          <DropdownButtonText fullWidth={fullWidth}>
+            {filterDescription}
+          </DropdownButtonText>
           {activeFilters.length > 1 && (
             <StyledBadge text={`+${activeFilters.length - 1}`} />
           )}
@@ -177,20 +183,30 @@ const Header = styled('div')`
   border-bottom: 1px solid ${p => p.theme.border};
 `;
 
-const StyledDropdownButton = styled(DropdownButton)`
+const StyledDropdownButton = styled(DropdownButton)<{fullWidth: boolean}>`
+  white-space: nowrap;
   display: flex;
   align-items: center;
-  white-space: nowrap;
-  max-width: 200px;
 
   z-index: ${p => p.theme.zIndex.dropdown};
+
+  ${p =>
+    p.fullWidth
+      ? `
+      width: 100%
+  `
+      : `max-width: 200px`}
 `;
 
-const DropdownButtonText = styled('span')`
+const DropdownButtonText = styled('span')<{fullWidth: boolean}>`
   white-space: nowrap;
   text-overflow: ellipsis;
   overflow: hidden;
   flex: 1;
+
+  @media (max-width: ${p => p.theme.breakpoints[0]}) {
+    text-align: ${p => p.fullWidth && 'start'};
+  }
 `;
 
 const StyledBadge = styled(Badge)`

+ 3 - 0
static/app/views/alerts/rules/teamFilter.tsx

@@ -15,6 +15,7 @@ import Filter from './filter';
 type Props = {
   handleChangeFilter: (sectionId: string, activeFilters: Set<string>) => void;
   selectedTeams: Set<string>;
+  fullWidth?: boolean;
   selectedStatus?: Set<string>;
   /**
    * only show teams user is a member of
@@ -36,6 +37,7 @@ function TeamFilter({
   showStatus = false,
   selectedStatus = new Set(),
   handleChangeFilter,
+  fullWidth = false,
   showIsMemberTeams = false,
   showMyTeamsAndUnassigned = true,
   showMyTeamsDescription = false,
@@ -88,6 +90,7 @@ function TeamFilter({
 
   return (
     <Filter
+      fullWidth={fullWidth}
       showMyTeamsDescription={showMyTeamsDescription}
       header={
         <InputWrapper>

+ 7 - 3
static/app/views/projectsDashboard/index.tsx

@@ -176,6 +176,7 @@ function Dashboard({
                   <TeamFilter
                     selectedTeams={selectedTeams}
                     handleChangeFilter={handleChangeFilter}
+                    fullWidth
                     showIsMemberTeams
                     showMyTeamsAndUnassigned={false}
                     showMyTeamsDescription
@@ -191,7 +192,7 @@ function Dashboard({
               {hasProjectRedesign ? (
                 <LazyLoad once debounce={50} height={300} offset={300}>
                   <ProjectCards>
-                    {filteredProjects.map(project => (
+                    {sortProjects(filteredProjects).map(project => (
                       <ProjectCard
                         data-test-id={project.slug}
                         key={project.slug}
@@ -247,17 +248,20 @@ const TeamLink = styled(Link)`
 
 const ProjectsHeader = styled(Layout.Header)`
   border-bottom: none;
+  align-items: end;
+
+  @media (min-width: ${p => p.theme.breakpoints[1]}) {
+    padding: 26px ${space(4)} 0 ${space(4)};
+  }
 `;
 
 const Title = styled(Layout.HeaderContent)`
   margin-bottom: 0;
-  padding-top: ${space(0.5)};
 `;
 
 const ButtonContainer = styled('div')`
   display: inline-flex;
   gap: ${space(1)};
-  padding-top: ${space(0.5)};
 `;
 
 const SearchAndSelectorWrapper = styled('div')`

+ 28 - 12
static/app/views/projectsDashboard/projectCard.tsx

@@ -138,11 +138,11 @@ class ProjectCard extends Component<Props> {
             <HeaderRow>
               <StyledIdBadge
                 project={project}
-                avatarSize={18}
+                avatarSize={32}
                 hideOverflow
                 disableLink={!hasProjectAccess}
               />
-              <BookmarkStar organization={organization} project={project} />
+              <StyledBookmarkStar organization={organization} project={project} />
             </HeaderRow>
             <SummaryLinks>
               {stats ? (
@@ -151,7 +151,7 @@ class ProjectCard extends Component<Props> {
                     data-test-id="project-errors"
                     to={`/organizations/${organization.slug}/issues/?project=${project.id}`}
                   >
-                    {t('errors: %s', formatAbbreviatedNumber(totalErrors))}
+                    {t('Errors: %s', formatAbbreviatedNumber(totalErrors))}
                   </Link>
                   {this.hasPerformance && (
                     <Fragment>
@@ -161,7 +161,7 @@ class ProjectCard extends Component<Props> {
                         to={`/organizations/${organization.slug}/performance/?project=${project.id}`}
                       >
                         {t(
-                          'transactions: %s',
+                          'Transactions: %s',
                           formatAbbreviatedNumber(totalTransactions)
                         )}
                         {zeroTransactions && (
@@ -301,14 +301,18 @@ const ChartContainer = styled('div')`
 `;
 
 const CardHeader = styled('div')`
-  margin: ${space(1.5)} ${space(2)};
+  margin: ${space(2)} 13px;
+  height: 32px;
+`;
+
+const StyledBookmarkStar = styled(BookmarkStar)`
+  padding: 0;
 `;
 
 const HeaderRow = styled('div')`
-  display: grid;
-  grid-template-columns: 1fr auto;
+  display: flex;
   justify-content: space-between;
-  align-items: center;
+  align-items: flex-start;
 
   ${p => p.theme.text.cardTitle};
   color: ${p => p.theme.headingColor};
@@ -392,22 +396,34 @@ const StyledIdBadge = styled(IdBadge)`
   overflow: hidden;
   white-space: nowrap;
   flex-shrink: 1;
+  & div {
+    align-items: flex-start;
+  }
+
+  & span {
+    padding: 0;
+    position: relative;
+    top: -1px;
+  }
 `;
 
 const SummaryLinks = styled('div')`
   display: flex;
+  position: relative;
+  top: -${space(2)};
   align-items: center;
+  font-weight: 400;
 
   color: ${p => p.theme.subText};
-  font-size: ${p => p.theme.fontSizeMedium};
+  font-size: ${p => p.theme.fontSizeSmall};
 
   /* Need to offset for the project icon and margin */
-  margin-left: 26px;
+  margin-left: 40px;
 
   a {
-    color: ${p => p.theme.formText};
+    color: ${p => p.theme.subText};
     :hover {
-      color: ${p => p.theme.subText};
+      color: ${p => p.theme.linkHoverColor};
     }
   }
   em {

+ 2 - 2
tests/js/spec/views/projectsDashboard/projectCard.spec.jsx

@@ -101,7 +101,7 @@ describe('ProjectCard', function () {
 
     const total = wrapper.find('a[data-test-id="project-errors"]');
     expect(total).toHaveLength(1);
-    expect(total.text()).toContain('errors: 6');
+    expect(total.text()).toContain('Errors: 6');
 
     // No transacions as the feature isn't set.
     const transactions = wrapper.find('a[data-test-id="project-transactions"]');
@@ -132,7 +132,7 @@ describe('ProjectCard', function () {
 
     const transactions = wrapper.find('a[data-test-id="project-transactions"]');
     expect(transactions).toHaveLength(1);
-    expect(transactions.text()).toContain('transactions: 8');
+    expect(transactions.text()).toContain('Transactions: 8');
   });
 
   it('renders loading placeholder card if there are no stats', function () {