]> source.dussan.org Git - sonarqube.git/commitdiff
SONARCLOUD-54 Better empty organization message for projects page
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Wed, 18 Jul 2018 13:09:43 +0000 (15:09 +0200)
committerSonarTech <sonartech@sonarsource.com>
Thu, 19 Jul 2018 18:21:26 +0000 (20:21 +0200)
server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
server/sonar-web/src/main/js/apps/projects/components/EmptyInstance.tsx
server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/EmptyInstance-test.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectsList-test.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/EmptyInstance-test.tsx.snap
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsList-test.tsx.snap
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 7f436bc9535b48e997743d1351963f792ed94a04..a97b447e3a1bfeab6524a45477867ba21c0ac727 100644 (file)
@@ -295,6 +295,7 @@ export default class AllProjects extends React.PureComponent<Props, State> {
         {this.state.projects && (
           <ProjectsList
             cardType={this.getView()}
+            currentUser={this.props.currentUser}
             isFavorite={this.props.isFavorite}
             isFiltered={hasFilterParams(this.state.query)}
             organization={this.props.organization}
index db24b9caabfcb8cda2f8a20734fa77af12a3fbc7..a9f09ca7664329c8f2fa1cf1fa136eaf9562e368 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import * as PropTypes from 'prop-types';
 import { translate } from '../../../helpers/l10n';
+import { Button } from '../../../components/ui/buttons';
+import { Organization, CurrentUser, isLoggedIn } from '../../../app/types';
+import { isSonarCloud } from '../../../helpers/system';
 
-export default function EmptyInstance() {
-  return (
-    <div className="projects-empty-list">
-      <h3>{translate('projects.no_projects.empty_instance')}</h3>
-    </div>
-  );
+interface Props {
+  organization?: Organization;
+  currentUser: CurrentUser;
+}
+
+export default class EmptyInstance extends React.PureComponent<Props> {
+  static contextTypes = {
+    openProjectOnboarding: PropTypes.func
+  };
+
+  render() {
+    const { currentUser, organization } = this.props;
+    const showNewProjectButton = isSonarCloud()
+      ? organization && organization.canProvisionProjects
+      : isLoggedIn(currentUser);
+
+    return (
+      <div className="projects-empty-list">
+        <h3>
+          {showNewProjectButton
+            ? translate('projects.no_projects.empty_instance.new_project')
+            : translate('projects.no_projects.empty_instance')}
+        </h3>
+        {showNewProjectButton && (
+          <div>
+            <p className="big-spacer-top">
+              {translate('projects.no_projects.empty_instance.how_to_add_projects')}
+            </p>
+            <p className="big-spacer-top">
+              <Button onClick={this.context.openProjectOnboarding}>
+                {translate('embed_docs.analyze_new_project')}
+              </Button>
+            </p>
+          </div>
+        )}
+      </div>
+    );
+  }
 }
index 23cf0206096836d168abf6854e2cae264f2ea79a..3567bf0c6205bd2cee3c8e3cf7b296bcf980416d 100644 (file)
@@ -28,10 +28,11 @@ import EmptyFavoriteSearch from './EmptyFavoriteSearch';
 import EmptySearch from '../../../components/common/EmptySearch';
 import { Project } from '../types';
 import { Query } from '../query';
-import { Organization } from '../../../app/types';
+import { Organization, CurrentUser } from '../../../app/types';
 
 interface Props {
   cardType?: string;
+  currentUser: CurrentUser;
   isFavorite: boolean;
   isFiltered: boolean;
   organization: Organization | undefined;
@@ -45,11 +46,15 @@ export default class ProjectsList extends React.PureComponent<Props> {
   };
 
   renderNoProjects() {
-    const { isFavorite, isFiltered, query } = this.props;
+    const { currentUser, isFavorite, isFiltered, organization, query } = this.props;
     if (isFiltered) {
       return isFavorite ? <EmptyFavoriteSearch query={query} /> : <EmptySearch />;
     }
-    return isFavorite ? <NoFavoriteProjects /> : <EmptyInstance />;
+    return isFavorite ? (
+      <NoFavoriteProjects />
+    ) : (
+      <EmptyInstance currentUser={currentUser} organization={organization} />
+    );
   }
 
   renderRow = ({ index, key, style }: ListRowProps) => {
index babf7c999e01c83bb6d14370c11b3c23bb03b5c6..a6894a80b0ab0e1f71a605a140c81085898608ba 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-/* eslint-disable import/order */
+/* eslint-disable import/order, camelcase */
 import * as React from 'react';
 import { shallow } from 'enzyme';
 import AllProjects, { Props } from '../AllProjects';
index df59bbe7d6624be591076c3cd0e704c66da68e23..8ecfec660f30d59a1ab8fee4e37ca0c49d2c4e06 100644 (file)
 import * as React from 'react';
 import { shallow } from 'enzyme';
 import EmptyInstance from '../EmptyInstance';
+import { isSonarCloud } from '../../../../helpers/system';
 
-it('renders', () => {
-  expect(shallow(<EmptyInstance />)).toMatchSnapshot();
+jest.mock('../../../../helpers/system', () => ({
+  isSonarCloud: jest.fn()
+}));
+
+it('renders correctly for SQ', () => {
+  (isSonarCloud as jest.Mock<any>).mockReturnValue(false);
+  expect(
+    shallow(<EmptyInstance currentUser={{ isLoggedIn: false }} organization={undefined} />)
+  ).toMatchSnapshot();
+  expect(
+    shallow(<EmptyInstance currentUser={{ isLoggedIn: true }} organization={undefined} />)
+  ).toMatchSnapshot();
+});
+
+it('renders correctly for SC', () => {
+  (isSonarCloud as jest.Mock<any>).mockReturnValue(true);
+  expect(
+    shallow(
+      <EmptyInstance
+        currentUser={{ isLoggedIn: false }}
+        organization={{ key: 'foo', name: 'Foo' }}
+      />
+    )
+  ).toMatchSnapshot();
+  expect(
+    shallow(
+      <EmptyInstance
+        currentUser={{ isLoggedIn: false }}
+        organization={{ canProvisionProjects: true, key: 'foo', name: 'Foo' }}
+      />
+    )
+  ).toMatchSnapshot();
 });
index fd28e6c24815b2f510488d4b5a529eaa78154fee..95e84383c53ed39a3dbf107c61c01e7ba0156147 100644 (file)
@@ -35,6 +35,7 @@ function shallowRender(props?: any) {
   return shallow(
     <ProjectsList
       cardType="overall"
+      currentUser={{ isLoggedIn: true }}
       isFavorite={false}
       isFiltered={false}
       organization={undefined}
index 88824ab0f4d2024d04b546055cfee6b48cb77a14..e2dca712a6ea74e0f4fa06ae27c57fcf04308aa7 100644 (file)
@@ -84,6 +84,11 @@ exports[`renders 1`] = `
     >
       <ProjectsList
         cardType="overall"
+        currentUser={
+          Object {
+            "isLoggedIn": true,
+          }
+        }
         isFavorite={false}
         isFiltered={false}
         projects={
index df541164173cd82f572093459882f3cc1d31bc3c..d903a5771aa0dac0c8eafdc5fb32feff8438f931 100644 (file)
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`renders 1`] = `
+exports[`renders correctly for SC 1`] = `
 <div
   className="projects-empty-list"
 >
@@ -9,3 +9,61 @@ exports[`renders 1`] = `
   </h3>
 </div>
 `;
+
+exports[`renders correctly for SC 2`] = `
+<div
+  className="projects-empty-list"
+>
+  <h3>
+    projects.no_projects.empty_instance.new_project
+  </h3>
+  <div>
+    <p
+      className="big-spacer-top"
+    >
+      projects.no_projects.empty_instance.how_to_add_projects
+    </p>
+    <p
+      className="big-spacer-top"
+    >
+      <Button>
+        embed_docs.analyze_new_project
+      </Button>
+    </p>
+  </div>
+</div>
+`;
+
+exports[`renders correctly for SQ 1`] = `
+<div
+  className="projects-empty-list"
+>
+  <h3>
+    projects.no_projects.empty_instance
+  </h3>
+</div>
+`;
+
+exports[`renders correctly for SQ 2`] = `
+<div
+  className="projects-empty-list"
+>
+  <h3>
+    projects.no_projects.empty_instance.new_project
+  </h3>
+  <div>
+    <p
+      className="big-spacer-top"
+    >
+      projects.no_projects.empty_instance.how_to_add_projects
+    </p>
+    <p
+      className="big-spacer-top"
+    >
+      <Button>
+        embed_docs.analyze_new_project
+      </Button>
+    </p>
+  </div>
+</div>
+`;
index 7dcb16317c27f739056681e5f7ca73c2db6344b3..3fa14ab99ee299ce00cc8b723d7e7ec11e88fdf2 100644 (file)
@@ -19,7 +19,13 @@ exports[`renders different types of "no projects" 1`] = `
 <div
   className="projects-list"
 >
-  <EmptyInstance />
+  <EmptyInstance
+    currentUser={
+      Object {
+        "isLoggedIn": true,
+      }
+    }
+  />
 </div>
 `;
 
index e007b3aea60e9ed2d06c83dcf0e990c207966423..e5d4e15fc1a88e2627a4b67af64ebcd99cf0cf3d 100644 (file)
@@ -739,7 +739,9 @@ issue_bulk_change.no_match=There is no issue matching your filter selection
 
 projects.page=Projects
 projects._projects=projects
-projects.no_projects.empty_instance=Once you analyze some projects, they will show up here.
+projects.no_projects.empty_instance=There is no visible project yet.
+projects.no_projects.empty_instance.new_project=Once you analyze some projects, they will show up here.
+projects.no_projects.empty_instance.how_to_add_projects=Here is how you can analyse new projects
 projects.no_favorite_projects=You don't have any favorite projects yet.
 projects.no_favorite_projects.engagement=Discover and mark as favorites projects you are interested in to have a quick access to them.
 projects.no_favorite_projects.how_to_add_projects=Here is how to add projects to this page