Просмотр исходного кода

feat(workflow): Remove project breakdown by Team (#33288)

* feat(workflow): Remove project breakdown by Team

First part of the projects page redesign. This removes the team sections from the Projects page (currently behind a feature flag for the workflow team).

FIXES WOR-1756

* add lazyload and tests
Kelly Carino 2 лет назад
Родитель
Сommit
b37d5a4e1c

+ 97 - 34
static/app/views/projectsDashboard/index.tsx

@@ -15,7 +15,7 @@ import LoadingIndicator from 'sentry/components/loadingIndicator';
 import NoProjectMessage from 'sentry/components/noProjectMessage';
 import PageHeading from 'sentry/components/pageHeading';
 import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
-import {IconAdd} from 'sentry/icons';
+import {IconAdd, IconUser} from 'sentry/icons';
 import {t} from 'sentry/locale';
 import ProjectsStatsStore from 'sentry/stores/projectsStatsStore';
 import space from 'sentry/styles/space';
@@ -25,6 +25,7 @@ import withApi from 'sentry/utils/withApi';
 import withOrganization from 'sentry/utils/withOrganization';
 import withTeamsForUser from 'sentry/utils/withTeamsForUser';
 
+import ProjectCard from './projectCard';
 import Resources from './resources';
 import TeamSection from './teamSection';
 
@@ -58,7 +59,10 @@ function Dashboard({teams, params, organization, loadingTeams, error}: Props) {
   const favorites = projects.filter(project => project.isBookmarked);
 
   const canCreateProjects = organization.access.includes('project:admin');
+  const canJoinTeam = organization.access.includes('team:read');
   const hasTeamAdminAccess = organization.access.includes('team:admin');
+  const hasProjectAccess = organization.access.includes('project:read');
+  const hasProjectRedesign = organization.features.includes('projects-page-redesign');
 
   const showEmptyMessage = projects.length === 0 && favorites.length === 0;
   const showResources = projects.length === 1 && !projects[0].firstEvent;
@@ -76,44 +80,80 @@ function Dashboard({teams, params, organization, loadingTeams, error}: Props) {
         <Fragment>
           <ProjectsHeader>
             <PageHeading>{t('Projects')}</PageHeading>
-            <Button
-              size="small"
-              disabled={!canCreateProjects}
-              title={
-                !canCreateProjects
-                  ? t('You do not have permission to create projects')
-                  : undefined
-              }
-              to={`/organizations/${organization.slug}/projects/new/`}
-              icon={<IconAdd size="xs" isCircled />}
-              data-test-id="create-project"
-            >
-              {t('Create Project')}
-            </Button>
+            <ButtonContainer>
+              {hasProjectRedesign && (
+                <Button
+                  size="small"
+                  icon={<IconUser size="xs" />}
+                  title={
+                    canJoinTeam
+                      ? undefined
+                      : t('You do not have permission to join a team.')
+                  }
+                  disabled={!canJoinTeam}
+                  to={`/settings/${organization.slug}/teams/`}
+                  data-test-id="join-team"
+                >
+                  {t('Join a Team')}
+                </Button>
+              )}
+              <Button
+                size="small"
+                priority={hasProjectRedesign ? 'primary' : 'default'}
+                disabled={!canCreateProjects}
+                title={
+                  !canCreateProjects
+                    ? t('You do not have permission to create projects')
+                    : undefined
+                }
+                to={`/organizations/${organization.slug}/projects/new/`}
+                icon={<IconAdd size="xs" isCircled />}
+                data-test-id="create-project"
+              >
+                {t('Create Project')}
+              </Button>
+            </ButtonContainer>
           </ProjectsHeader>
         </Fragment>
       )}
 
-      {filteredTeams.map((team, index) => (
-        <LazyLoad key={team.slug} once debounce={50} height={300} offset={300}>
-          <TeamSection
-            orgId={params.orgId}
-            team={team}
-            showBorder={index !== teams.length - 1}
-            title={
-              hasTeamAdminAccess ? (
-                <TeamLink to={`/settings/${organization.slug}/teams/${team.slug}/`}>
-                  <IdBadge team={team} avatarSize={22} />
-                </TeamLink>
-              ) : (
-                <IdBadge team={team} avatarSize={22} />
-              )
-            }
-            projects={sortProjects(team.projects)}
-            access={new Set(organization.access)}
-          />
+      {hasProjectRedesign ? (
+        <LazyLoad once debounce={50} height={300} offset={300}>
+          <ProjectCardsContainer>
+            <ProjectCards>
+              {projects.map(project => (
+                <ProjectCard
+                  data-test-id={project.slug}
+                  key={project.slug}
+                  project={project}
+                  hasProjectAccess={hasProjectAccess}
+                />
+              ))}
+            </ProjectCards>
+          </ProjectCardsContainer>
         </LazyLoad>
-      ))}
+      ) : (
+        filteredTeams.map((team, index) => (
+          <LazyLoad key={team.slug} once debounce={50} height={300} offset={300}>
+            <TeamSection
+              orgId={params.orgId}
+              team={team}
+              showBorder={index !== teams.length - 1}
+              title={
+                hasTeamAdminAccess ? (
+                  <TeamLink to={`/settings/${organization.slug}/teams/${team.slug}/`}>
+                    <IdBadge team={team} avatarSize={22} />
+                  </TeamLink>
+                ) : (
+                  <IdBadge team={team} avatarSize={22} />
+                )
+              }
+              projects={sortProjects(team.projects)}
+              access={new Set(organization.access)}
+            />
+          </LazyLoad>
+        ))
+      )}
       {showResources && <Resources organization={organization} />}
     </Fragment>
   );
@@ -137,6 +177,29 @@ const ProjectsHeader = styled('div')`
   justify-content: space-between;
 `;
 
+const ButtonContainer = styled('div')`
+  display: inline-flex;
+  gap: ${space(1)};
+`;
+
+const ProjectCardsContainer = styled('div')`
+  padding: 20px 30px 0 30px;
+`;
+
+const ProjectCards = styled('div')`
+  display: grid;
+  grid-template-columns: minmax(100px, 1fr);
+  gap: ${space(3)};
+
+  @media (min-width: ${p => p.theme.breakpoints[0]}) {
+    grid-template-columns: repeat(2, minmax(100px, 1fr));
+  }
+
+  @media (min-width: ${p => p.theme.breakpoints[3]}) {
+    grid-template-columns: repeat(3, minmax(100px, 1fr));
+  }
+`;
+
 const OrganizationDashboardWrapper = styled('div')`
   display: flex;
   flex: 1;

+ 33 - 1
tests/js/spec/views/projectsDashboard/index.spec.jsx

@@ -1,5 +1,5 @@
 import {mountWithTheme} from 'sentry-test/enzyme';
-import {act} from 'sentry-test/reactTestingLibrary';
+import {act, render, screen} from 'sentry-test/reactTestingLibrary';
 
 import * as projectsActions from 'sentry/actionCreators/projects';
 import ProjectsStatsStore from 'sentry/stores/projectsStatsStore';
@@ -116,6 +116,38 @@ describe('ProjectsDashboard', function () {
       expect(wrapper.find('Resources').exists()).toBe(false);
     });
 
+    it('renders users projects without team section for redesign', function () {
+      const projects = [
+        TestStubs.Project({
+          teams,
+          firstEvent: true,
+        }),
+
+        TestStubs.Project({
+          slug: 'project2',
+          teams,
+          isBookmarked: true,
+          firstEvent: true,
+        }),
+      ];
+
+      const userTeams = [TestStubs.Team({projects})];
+
+      render(
+        <Dashboard
+          teams={userTeams}
+          organization={{...org, features: [...org.features, 'projects-page-redesign']}}
+          params={{orgId: org.slug}}
+        />,
+        {routerContext}
+      );
+
+      expect(screen.getByTestId('join-team')).toBeInTheDocument();
+      expect(screen.getByTestId('create-project')).toBeInTheDocument();
+      expect(screen.queryByTestId('team')).not.toBeInTheDocument();
+      expect(screen.queryByText('Resources')).not.toBeInTheDocument();
+    });
+
     it('renders bookmarked projects first in team list', function () {
       const projects = [
         TestStubs.Project({