]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10188 Update project/organization header
authorStas Vilchik <stas.vilchik@sonarsource.com>
Thu, 14 Dec 2017 11:20:38 +0000 (12:20 +0100)
committerStas Vilchik <stas.vilchik@sonarsource.com>
Tue, 2 Jan 2018 09:38:10 +0000 (10:38 +0100)
97 files changed:
server/sonar-web/src/main/js/app/components/ComponentContainerNotFound.tsx
server/sonar-web/src/main/js/app/components/SimpleContainer.tsx
server/sonar-web/src/main/js/app/components/extensions/ExtensionNotFound.tsx
server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css
server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.tsx [deleted file]
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavHeader.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBreadcrumbs-test.tsx [deleted file]
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavHeader-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavHeader-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMeta-test.tsx.snap
server/sonar-web/src/main/js/app/styles/components/page.css
server/sonar-web/src/main/js/app/styles/init/base.css
server/sonar-web/src/main/js/app/styles/init/icons.css
server/sonar-web/src/main/js/app/styles/print.css
server/sonar-web/src/main/js/app/styles/style.css
server/sonar-web/src/main/js/app/types.ts
server/sonar-web/src/main/js/apps/about/components/AboutApp.js
server/sonar-web/src/main/js/apps/account/account.css
server/sonar-web/src/main/js/apps/account/components/Password.js
server/sonar-web/src/main/js/apps/account/components/Security.js
server/sonar-web/src/main/js/apps/account/notifications/GlobalNotifications.js
server/sonar-web/src/main/js/apps/account/notifications/Notifications.js
server/sonar-web/src/main/js/apps/account/notifications/Projects.js
server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/GlobalNotifications-test.js.snap
server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Notifications-test.js.snap
server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Projects-test.js.snap
server/sonar-web/src/main/js/apps/account/organizations/OrganizationsList.tsx
server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.tsx
server/sonar-web/src/main/js/apps/account/profile/Profile.js
server/sonar-web/src/main/js/apps/account/templates/account-tokens.hbs
server/sonar-web/src/main/js/apps/background-tasks/components/Tasks.js
server/sonar-web/src/main/js/apps/code/components/App.tsx
server/sonar-web/src/main/js/apps/code/components/Search.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js
server/sonar-web/src/main/js/apps/component-measures/components/App.js
server/sonar-web/src/main/js/apps/custom-measures/templates/custom-measures-list.hbs
server/sonar-web/src/main/js/apps/groups/templates/groups-list.hbs
server/sonar-web/src/main/js/apps/groups/templates/groups-search.hbs
server/sonar-web/src/main/js/apps/issues/components/App.js
server/sonar-web/src/main/js/apps/issues/components/PageActions.js
server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx
server/sonar-web/src/main/js/apps/marketplace/Search.tsx
server/sonar-web/src/main/js/apps/marketplace/style.css
server/sonar-web/src/main/js/apps/metrics/templates/metrics-layout.hbs
server/sonar-web/src/main/js/apps/organizations/components/MembersList.js
server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.js
server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/MembersList-test.js.snap
server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.js.snap
server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigationMeta.tsx
server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/OrganizationNavigationMeta-test.tsx
server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.tsx.snap
server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigationMeta-test.tsx.snap
server/sonar-web/src/main/js/apps/overview/components/OverviewApp.js
server/sonar-web/src/main/js/apps/permission-templates/components/List.js
server/sonar-web/src/main/js/apps/permissions/shared/components/HoldersList.tsx
server/sonar-web/src/main/js/apps/portfolio/components/App.tsx
server/sonar-web/src/main/js/apps/project-admin/key/Key.js
server/sonar-web/src/main/js/apps/project-admin/links/Table.js
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js
server/sonar-web/src/main/js/apps/projectBranches/components/App.tsx
server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap
server/sonar-web/src/main/js/apps/projectQualityProfiles/Table.tsx
server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/__snapshots__/Table-test.tsx.snap
server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx
server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeak.tsx
server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx
server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap
server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js
server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx
server/sonar-web/src/main/js/apps/settings/components/App.js
server/sonar-web/src/main/js/apps/users/UsersList.tsx
server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersList.tsx.snap
server/sonar-web/src/main/js/apps/web-api/components/Action.tsx
server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Action-test.tsx.snap
server/sonar-web/src/main/js/apps/web-api/styles/web-api.css
server/sonar-web/src/main/js/components/SourceViewer/styles.css
server/sonar-web/src/main/js/components/common/BranchStatus.css
server/sonar-web/src/main/js/components/common/BranchStatus.tsx
server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/BranchStatus-test.tsx.snap
server/sonar-web/src/main/js/components/controls/Favorite.tsx
server/sonar-web/src/main/js/components/controls/FavoriteBase.tsx
server/sonar-web/src/main/js/components/controls/FavoriteIssueFilter.js [deleted file]
server/sonar-web/src/main/js/components/controls/HomePageSelect.tsx
server/sonar-web/src/main/js/components/controls/__tests__/Favorite-test.tsx
server/sonar-web/src/main/js/components/controls/__tests__/FavoriteBase-test.tsx
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Favorite-test.tsx.snap
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/FavoriteBase-test.tsx.snap
server/sonar-web/src/main/js/components/nav/ContextNavBar.css
server/sonar-web/src/main/js/helpers/urls.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index e87bea11ccecf7661b50aa54d9ee427bd137973e..c097f03da9667b7a8e0c7d617c815ecb4af29bb8 100644 (file)
@@ -21,32 +21,16 @@ import * as React from 'react';
 import { Link } from 'react-router';
 import { translate } from '../../helpers/l10n';
 
-export default class ComponentContainerNotFound extends React.PureComponent {
-  componentDidMount() {
-    const html = document.querySelector('html');
-    if (html) {
-      html.classList.add('dashboard-page');
-    }
-  }
-
-  componentWillUnmount() {
-    const html = document.querySelector('html');
-    if (html) {
-      html.classList.remove('dashboard-page');
-    }
-  }
-
-  render() {
-    return (
-      <div id="bd" className="page-wrapper-simple">
-        <div id="nonav" className="page-simple">
-          <h2 className="big-spacer-bottom">{translate('dashboard.project_not_found')}</h2>
-          <p className="spacer-bottom">{translate('dashboard.project_not_found.2')}</p>
-          <p>
-            <Link to="/">Go back to the homepage</Link>
-          </p>
-        </div>
+export default function ComponentContainerNotFound() {
+  return (
+    <div id="bd" className="page-wrapper-simple">
+      <div id="nonav" className="page-simple">
+        <h2 className="big-spacer-bottom">{translate('dashboard.project_not_found')}</h2>
+        <p className="spacer-bottom">{translate('dashboard.project_not_found.2')}</p>
+        <p>
+          <Link to="/">Go back to the homepage</Link>
+        </p>
       </div>
-    );
-  }
+    </div>
+  );
 }
index 4e5e35b7cceb63211a7c6e262d8e0753fb0cc168..8c2efc977afc3022083561f11c1d7ab3abef04c8 100644 (file)
@@ -27,35 +27,19 @@ interface Props {
   hideLoggedInInfo?: boolean;
 }
 
-export default class SimpleContainer extends React.PureComponent<Props> {
-  componentDidMount() {
-    const html = document.querySelector('html');
-    if (html) {
-      html.classList.add('dashboard-page');
-    }
-  }
+export default function SimpleContainer(props: Props) {
+  return (
+    <div className="global-container">
+      <div className="page-wrapper" id="container">
+        <NavBar className="navbar-global" height={theme.globalNavHeightRaw} />
 
-  componentWillUnmount() {
-    const html = document.querySelector('html');
-    if (html) {
-      html.classList.remove('dashboard-page');
-    }
-  }
-
-  render() {
-    return (
-      <div className="global-container">
-        <div className="page-wrapper" id="container">
-          <NavBar className="navbar-global" height={theme.globalNavHeightRaw} />
-
-          <div id="bd" className="page-wrapper-simple">
-            <div id="nonav" className="page-simple">
-              {this.props.children}
-            </div>
+        <div id="bd" className="page-wrapper-simple">
+          <div id="nonav" className="page-simple">
+            {props.children}
           </div>
         </div>
-        <GlobalFooterContainer hideLoggedInInfo={this.props.hideLoggedInInfo} />
       </div>
-    );
-  }
+      <GlobalFooterContainer hideLoggedInInfo={props.hideLoggedInInfo} />
+    </div>
+  );
 }
index 154b674f6b9dce3faa39427fdfbfb10e022eac25..1cad19e3d2004bd698dbb5aea62783e4187f8a67 100644 (file)
 import * as React from 'react';
 import { Link } from 'react-router';
 
-export default class ExtensionNotFound extends React.PureComponent {
-  componentDidMount() {
-    const html = document.querySelector('html');
-    if (html) {
-      html.classList.add('dashboard-page');
-    }
-  }
-
-  componentWillUnmount() {
-    const html = document.querySelector('html');
-    if (html) {
-      html.classList.remove('dashboard-page');
-    }
-  }
-
-  render() {
-    return (
-      <div id="bd" className="page-wrapper-simple">
-        <div id="nonav" className="page-simple">
-          <h2 className="big-spacer-bottom">The page you were looking for does not exist.</h2>
-          <p className="spacer-bottom">
-            You may have mistyped the address or the page may have moved.
-          </p>
-          <p>
-            <Link to="/">Go back to the homepage</Link>
-          </p>
-        </div>
+export default function ExtensionNotFound() {
+  return (
+    <div id="bd" className="page-wrapper-simple">
+      <div id="nonav" className="page-simple">
+        <h2 className="big-spacer-bottom">The page you were looking for does not exist.</h2>
+        <p className="spacer-bottom">
+          You may have mistyped the address or the page may have moved.
+        </p>
+        <p>
+          <Link to="/">Go back to the homepage</Link>
+        </p>
       </div>
-    );
-  }
+    </div>
+  );
 }
index fd0f3a46df1fe9d08f36e4cb5329158a99697f0d..f9488b415e3eaa9ea6302d7e0bc31008159606dc 100644 (file)
@@ -1,9 +1,9 @@
 .navbar-context-branches {
-  display: inline-block;
-  vertical-align: top;
-  padding: var(--gridSize) 0;
+  display: inline-flex;
+  justify-content: center;
+  line-height: calc(2 * var(--gridSize));
   margin-left: calc(2 * var(--gridSize));
-  line-height: 16px;
+  font-size: var(--baseFontSize);
 }
 
 .navbar-context-meta-branch-menu-item {
index ff2205adbb6347ce3b8ba583c0a3b6daada86852..7d4cd23592515a822cb0d2c2765657b14f4c328f 100644 (file)
@@ -18,8 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import ComponentNavBranch from './ComponentNavBranch';
-import ComponentNavBreadcrumbs from './ComponentNavBreadcrumbs';
+import ComponentNavHeader from './ComponentNavHeader';
 import ComponentNavMeta from './ComponentNavMeta';
 import ComponentNavMenu from './ComponentNavMenu';
 import ComponentNavBgTaskNotif from './ComponentNavBgTaskNotif';
@@ -111,16 +110,13 @@ export default class ComponentNav extends React.PureComponent<Props, State> {
         id="context-navigation"
         height={notifComponent ? theme.contextNavHeightRaw + 20 : theme.contextNavHeightRaw}
         notif={notifComponent}>
-        <ComponentNavBreadcrumbs component={this.props.component} />
-        {this.props.currentBranch && (
-          <ComponentNavBranch
-            branches={this.props.branches}
-            component={this.props.component}
-            currentBranch={this.props.currentBranch}
-            // to close dropdown on any location change
-            location={this.props.location}
-          />
-        )}
+        <ComponentNavHeader
+          branches={this.props.branches}
+          component={this.props.component}
+          currentBranch={this.props.currentBranch}
+          // to close dropdown on any location change
+          location={this.props.location}
+        />
         <ComponentNavMeta branch={this.props.currentBranch} component={this.props.component} />
         <ComponentNavMenu
           branch={this.props.currentBranch}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.tsx
deleted file mode 100644 (file)
index c8a75e8..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import * as React from 'react';
-import { connect } from 'react-redux';
-import { Link } from 'react-router';
-import { Component, Organization } from '../../../types';
-import QualifierIcon from '../../../../components/shared/QualifierIcon';
-import { getOrganizationByKey, areThereCustomOrganizations } from '../../../../store/rootReducer';
-import OrganizationAvatar from '../../../../components/common/OrganizationAvatar';
-import OrganizationHelmet from '../../../../components/common/OrganizationHelmet';
-import OrganizationLink from '../../../../components/ui/OrganizationLink';
-import PrivateBadge from '../../../../components/common/PrivateBadge';
-import { collapsePath, limitComponentName } from '../../../../helpers/path';
-import { getProjectUrl } from '../../../../helpers/urls';
-
-interface StateProps {
-  organization?: Organization;
-  shouldOrganizationBeDisplayed: boolean;
-}
-
-interface OwnProps {
-  component: Component;
-}
-
-interface Props extends StateProps, OwnProps {}
-
-export function ComponentNavBreadcrumbs(props: Props) {
-  const { component, organization, shouldOrganizationBeDisplayed } = props;
-  const { breadcrumbs } = component;
-
-  const lastItem = breadcrumbs[breadcrumbs.length - 1];
-
-  const items: JSX.Element[] = [];
-  breadcrumbs.forEach((item, index) => {
-    const isPath = item.qualifier === 'DIR';
-    const itemName = isPath ? collapsePath(item.name, 15) : limitComponentName(item.name);
-
-    if (index === 0) {
-      items.push(
-        <QualifierIcon
-          className="spacer-right"
-          key={`qualifier-${item.key}`}
-          qualifier={lastItem.qualifier}
-        />
-      );
-    }
-
-    items.push(
-      <Link
-        className="link-base-color link-no-underline"
-        key={`name-${item.key}`}
-        title={item.name}
-        to={getProjectUrl(item.key)}>
-        {itemName}
-      </Link>
-    );
-
-    if (index < breadcrumbs.length - 1) {
-      items.push(<span className="slash-separator" key={`separator-${item.key}`} />);
-    }
-  });
-
-  return (
-    <header className="navbar-context-header">
-      <OrganizationHelmet
-        title={component.name}
-        organization={organization && shouldOrganizationBeDisplayed ? organization : undefined}
-      />
-      {organization &&
-        shouldOrganizationBeDisplayed && <OrganizationAvatar organization={organization} />}
-      {organization &&
-        shouldOrganizationBeDisplayed && (
-          <OrganizationLink
-            organization={organization}
-            className="link-base-color link-no-underline spacer-left">
-            {organization.name}
-          </OrganizationLink>
-        )}
-      {organization && shouldOrganizationBeDisplayed && <span className="slash-separator" />}
-      {items}
-      {component.visibility === 'private' && (
-        <PrivateBadge className="spacer-left" qualifier={component.qualifier} />
-      )}
-    </header>
-  );
-}
-
-const mapStateToProps = (state: any, ownProps: OwnProps): StateProps => ({
-  organization:
-    ownProps.component.organization && getOrganizationByKey(state, ownProps.component.organization),
-  shouldOrganizationBeDisplayed: areThereCustomOrganizations(state)
-});
-
-export default connect(mapStateToProps)(ComponentNavBreadcrumbs);
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavHeader.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavHeader.tsx
new file mode 100644 (file)
index 0000000..e042b01
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { connect } from 'react-redux';
+import { Link } from 'react-router';
+import ComponentNavBranch from './ComponentNavBranch';
+import { Component, Organization, Branch, Breadcrumb } from '../../../types';
+import QualifierIcon from '../../../../components/shared/QualifierIcon';
+import { getOrganizationByKey, areThereCustomOrganizations } from '../../../../store/rootReducer';
+import OrganizationAvatar from '../../../../components/common/OrganizationAvatar';
+import OrganizationHelmet from '../../../../components/common/OrganizationHelmet';
+import OrganizationLink from '../../../../components/ui/OrganizationLink';
+import PrivateBadge from '../../../../components/common/PrivateBadge';
+import { collapsePath, limitComponentName } from '../../../../helpers/path';
+import { getProjectUrl } from '../../../../helpers/urls';
+
+interface StateProps {
+  organization?: Organization;
+  shouldOrganizationBeDisplayed: boolean;
+}
+
+interface OwnProps {
+  branches: Branch[];
+  component: Component;
+  currentBranch?: Branch;
+  location?: any;
+}
+
+interface Props extends StateProps, OwnProps {}
+
+export function ComponentNavHeader(props: Props) {
+  const { component, organization, shouldOrganizationBeDisplayed } = props;
+
+  return (
+    <header className="navbar-context-header">
+      <OrganizationHelmet
+        title={component.name}
+        organization={organization && shouldOrganizationBeDisplayed ? organization : undefined}
+      />
+      {organization &&
+        shouldOrganizationBeDisplayed && (
+          <>
+            <OrganizationAvatar organization={organization} />
+            <OrganizationLink
+              organization={organization}
+              className="link-base-color link-no-underline spacer-left">
+              {organization.name}
+            </OrganizationLink>
+            <span className="slash-separator" />
+          </>
+        )}
+      {renderBreadcrumbs(component.breadcrumbs)}
+      {component.visibility === 'private' && (
+        <PrivateBadge className="spacer-left" qualifier={component.qualifier} />
+      )}
+      {props.currentBranch && (
+        <ComponentNavBranch
+          branches={props.branches}
+          component={component}
+          currentBranch={props.currentBranch}
+          // to close dropdown on any location change
+          location={props.location}
+        />
+      )}
+    </header>
+  );
+}
+
+function renderBreadcrumbs(breadcrumbs: Breadcrumb[]) {
+  const lastItem = breadcrumbs[breadcrumbs.length - 1];
+  return breadcrumbs.map((item, index) => {
+    const isPath = item.qualifier === 'DIR';
+    const itemName = isPath ? collapsePath(item.name, 15) : limitComponentName(item.name);
+
+    return (
+      <React.Fragment key={item.key}>
+        {index === 0 && <QualifierIcon className="spacer-right" qualifier={lastItem.qualifier} />}
+        <Link
+          className="link-base-color link-no-underline"
+          title={item.name}
+          to={getProjectUrl(item.key)}>
+          {itemName}
+        </Link>
+        {index < breadcrumbs.length - 1 && <span className="slash-separator" />}
+      </React.Fragment>
+    );
+  });
+}
+
+const mapStateToProps = (state: any, ownProps: OwnProps): StateProps => ({
+  organization:
+    ownProps.component.organization && getOrganizationByKey(state, ownProps.component.organization),
+  shouldOrganizationBeDisplayed: areThereCustomOrganizations(state)
+});
+
+export default connect(mapStateToProps)(ComponentNavHeader);
index 01be74777a6bf4048e3eaf7851cf279e2e257228..3b080ac9d4be6e8c79e3b6015fde72df09dcb9f1 100644 (file)
@@ -19,7 +19,7 @@
  */
 import * as React from 'react';
 import { connect } from 'react-redux';
-import { Branch, Component, CurrentUser, isLoggedIn } from '../../../types';
+import { Branch, Component, CurrentUser, isLoggedIn, HomePageType } from '../../../types';
 import BranchStatus from '../../../../components/common/BranchStatus';
 import DateTimeFormatter from '../../../../components/intl/DateTimeFormatter';
 import Favorite from '../../../../components/controls/Favorite';
@@ -60,14 +60,22 @@ export function ComponentNavMeta({ branch, component, currentUser }: Props) {
       {isLoggedIn(currentUser) &&
         mainBranch && (
           <div className="navbar-context-meta-secondary">
-            <Favorite component={component.key} favorite={Boolean(component.isFavorite)} />
+            <Favorite
+              component={component.key}
+              favorite={Boolean(component.isFavorite)}
+              qualifier={component.qualifier}
+            />
             <HomePageSelect
               className="spacer-left"
-              currentPage={{ type: 'project', key: component.key }}
+              currentPage={{ type: HomePageType.Project, parameter: component.key }}
             />
           </div>
         )}
-      {shortBranch && <BranchStatus branch={branch!} />}
+      {shortBranch && (
+        <div className="navbar-context-meta-secondary">
+          <BranchStatus branch={branch!} />
+        </div>
+      )}
     </div>
   );
 }
index 26f876d34f06e8e642f8dff38c436f79342e4f44..4c30ef3b3a495f50f5dea049a1c2411f1dfb7448 100644 (file)
@@ -29,9 +29,9 @@ jest.mock('../ComponentNavMeta', () => ({
   }
 }));
 
-jest.mock('../ComponentNavBreadcrumbs', () => ({
+jest.mock('../ComponentNavHeader', () => ({
   // eslint-disable-next-line
-  default: function ComponentNavBreadcrumbs() {
+  default: function ComponentNavHeader() {
     return null;
   }
 }));
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBreadcrumbs-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBreadcrumbs-test.tsx
deleted file mode 100644 (file)
index 78a2eed..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import * as React from 'react';
-import { shallow } from 'enzyme';
-import { ComponentNavBreadcrumbs } from '../ComponentNavBreadcrumbs';
-import { Visibility } from '../../../../types';
-
-it('should not render breadcrumbs with one element', () => {
-  const component = {
-    breadcrumbs: [{ key: 'my-project', name: 'My Project', qualifier: 'TRK' }],
-    key: 'my-project',
-    name: 'My Project',
-    organization: 'org',
-    qualifier: 'TRK',
-    visibility: 'public'
-  };
-  const result = shallow(
-    <ComponentNavBreadcrumbs component={component} shouldOrganizationBeDisplayed={false} />
-  );
-  expect(result).toMatchSnapshot();
-});
-
-it('should render organization', () => {
-  const component = {
-    breadcrumbs: [{ key: 'my-project', name: 'My Project', qualifier: 'TRK' }],
-    key: 'my-project',
-    name: 'My Project',
-    organization: 'foo',
-    qualifier: 'TRK',
-    visibility: 'public'
-  };
-  const organization = {
-    key: 'foo',
-    name: 'The Foo Organization',
-    projectVisibility: Visibility.Public
-  };
-  const result = shallow(
-    <ComponentNavBreadcrumbs
-      component={component}
-      organization={organization}
-      shouldOrganizationBeDisplayed={true}
-    />
-  );
-  expect(result).toMatchSnapshot();
-});
-
-it('renders private badge', () => {
-  const component = {
-    breadcrumbs: [{ key: 'my-project', name: 'My Project', qualifier: 'TRK' }],
-    key: 'my-project',
-    name: 'My Project',
-    organization: 'org',
-    qualifier: 'TRK',
-    visibility: 'private'
-  };
-  const result = shallow(
-    <ComponentNavBreadcrumbs component={component} shouldOrganizationBeDisplayed={false} />
-  );
-  expect(result.find('PrivateBadge')).toHaveLength(1);
-});
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavHeader-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavHeader-test.tsx
new file mode 100644 (file)
index 0000000..89d10f4
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import { ComponentNavHeader } from '../ComponentNavHeader';
+import { Visibility } from '../../../../types';
+
+it('should not render breadcrumbs with one element', () => {
+  const component = {
+    breadcrumbs: [{ key: 'my-project', name: 'My Project', qualifier: 'TRK' }],
+    key: 'my-project',
+    name: 'My Project',
+    organization: 'org',
+    qualifier: 'TRK',
+    visibility: 'public'
+  };
+  const result = shallow(
+    <ComponentNavHeader branches={[]} component={component} shouldOrganizationBeDisplayed={false} />
+  );
+  expect(result).toMatchSnapshot();
+});
+
+it('should render organization', () => {
+  const component = {
+    breadcrumbs: [{ key: 'my-project', name: 'My Project', qualifier: 'TRK' }],
+    key: 'my-project',
+    name: 'My Project',
+    organization: 'foo',
+    qualifier: 'TRK',
+    visibility: 'public'
+  };
+  const organization = {
+    key: 'foo',
+    name: 'The Foo Organization',
+    projectVisibility: Visibility.Public
+  };
+  const result = shallow(
+    <ComponentNavHeader
+      branches={[]}
+      component={component}
+      organization={organization}
+      shouldOrganizationBeDisplayed={true}
+    />
+  );
+  expect(result).toMatchSnapshot();
+});
+
+it('renders private badge', () => {
+  const component = {
+    breadcrumbs: [{ key: 'my-project', name: 'My Project', qualifier: 'TRK' }],
+    key: 'my-project',
+    name: 'My Project',
+    organization: 'org',
+    qualifier: 'TRK',
+    visibility: 'private'
+  };
+  const result = shallow(
+    <ComponentNavHeader branches={[]} component={component} shouldOrganizationBeDisplayed={false} />
+  );
+  expect(result.find('PrivateBadge')).toHaveLength(1);
+});
index 6fea2eff38eb4846c9737c232168397378889af2..25470b77544a41611005a719b512f1f2a6156272 100644 (file)
@@ -27,7 +27,8 @@ exports[`renders 1`] = `
     />
   }
 >
-  <ComponentNavBreadcrumbs
+  <ComponentNavHeader
+    branches={Array []}
     component={
       Object {
         "breadcrumbs": Array [
@@ -43,6 +44,7 @@ exports[`renders 1`] = `
         "qualifier": "TRK",
       }
     }
+    location={Object {}}
   />
   <ComponentNavMeta
     component={
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.tsx.snap
deleted file mode 100644 (file)
index e907a97..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should not render breadcrumbs with one element 1`] = `
-<header
-  className="navbar-context-header"
->
-  <OrganizationHelmet
-    title="My Project"
-  />
-  <QualifierIcon
-    className="spacer-right"
-    key="qualifier-my-project"
-    qualifier="TRK"
-  />
-  <Link
-    className="link-base-color link-no-underline"
-    key="name-my-project"
-    onlyActiveOnIndex={false}
-    style={Object {}}
-    title="My Project"
-    to={
-      Object {
-        "pathname": "/dashboard",
-        "query": Object {
-          "branch": undefined,
-          "id": "my-project",
-        },
-      }
-    }
-  >
-    My Project
-  </Link>
-</header>
-`;
-
-exports[`should render organization 1`] = `
-<header
-  className="navbar-context-header"
->
-  <OrganizationHelmet
-    organization={
-      Object {
-        "key": "foo",
-        "name": "The Foo Organization",
-        "projectVisibility": "public",
-      }
-    }
-    title="My Project"
-  />
-  <OrganizationAvatar
-    organization={
-      Object {
-        "key": "foo",
-        "name": "The Foo Organization",
-        "projectVisibility": "public",
-      }
-    }
-  />
-  <OrganizationLink
-    className="link-base-color link-no-underline spacer-left"
-    organization={
-      Object {
-        "key": "foo",
-        "name": "The Foo Organization",
-        "projectVisibility": "public",
-      }
-    }
-  >
-    The Foo Organization
-  </OrganizationLink>
-  <span
-    className="slash-separator"
-  />
-  <QualifierIcon
-    className="spacer-right"
-    key="qualifier-my-project"
-    qualifier="TRK"
-  />
-  <Link
-    className="link-base-color link-no-underline"
-    key="name-my-project"
-    onlyActiveOnIndex={false}
-    style={Object {}}
-    title="My Project"
-    to={
-      Object {
-        "pathname": "/dashboard",
-        "query": Object {
-          "branch": undefined,
-          "id": "my-project",
-        },
-      }
-    }
-  >
-    My Project
-  </Link>
-</header>
-`;
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavHeader-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavHeader-test.tsx.snap
new file mode 100644 (file)
index 0000000..eb990ac
--- /dev/null
@@ -0,0 +1,104 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should not render breadcrumbs with one element 1`] = `
+<header
+  className="navbar-context-header"
+>
+  <OrganizationHelmet
+    title="My Project"
+  />
+  <React.Fragment
+    key="my-project"
+  >
+    <QualifierIcon
+      className="spacer-right"
+      qualifier="TRK"
+    />
+    <Link
+      className="link-base-color link-no-underline"
+      onlyActiveOnIndex={false}
+      style={Object {}}
+      title="My Project"
+      to={
+        Object {
+          "pathname": "/dashboard",
+          "query": Object {
+            "branch": undefined,
+            "id": "my-project",
+          },
+        }
+      }
+    >
+      My Project
+    </Link>
+  </React.Fragment>
+</header>
+`;
+
+exports[`should render organization 1`] = `
+<header
+  className="navbar-context-header"
+>
+  <OrganizationHelmet
+    organization={
+      Object {
+        "key": "foo",
+        "name": "The Foo Organization",
+        "projectVisibility": "public",
+      }
+    }
+    title="My Project"
+  />
+  <React.Fragment>
+    <OrganizationAvatar
+      organization={
+        Object {
+          "key": "foo",
+          "name": "The Foo Organization",
+          "projectVisibility": "public",
+        }
+      }
+    />
+    <OrganizationLink
+      className="link-base-color link-no-underline spacer-left"
+      organization={
+        Object {
+          "key": "foo",
+          "name": "The Foo Organization",
+          "projectVisibility": "public",
+        }
+      }
+    >
+      The Foo Organization
+    </OrganizationLink>
+    <span
+      className="slash-separator"
+    />
+  </React.Fragment>
+  <React.Fragment
+    key="my-project"
+  >
+    <QualifierIcon
+      className="spacer-right"
+      qualifier="TRK"
+    />
+    <Link
+      className="link-base-color link-no-underline"
+      onlyActiveOnIndex={false}
+      style={Object {}}
+      title="My Project"
+      to={
+        Object {
+          "pathname": "/dashboard",
+          "query": Object {
+            "branch": undefined,
+            "id": "my-project",
+          },
+        }
+      }
+    >
+      My Project
+    </Link>
+  </React.Fragment>
+</header>
+`;
index 726f3168f9ff9cca949581bf0d0090a3e15db10b..437ab7a3477263c2e9574237b7ca818e9184ba51 100644 (file)
@@ -38,20 +38,24 @@ exports[`renders status of short-living branch 1`] = `
       date="2017-01-02T00:00:00.000Z"
     />
   </div>
-  <BranchStatus
-    branch={
-      Object {
-        "isMain": false,
-        "mergeBranch": "master",
-        "name": "feature",
-        "status": Object {
-          "bugs": 0,
-          "codeSmells": 2,
-          "vulnerabilities": 3,
-        },
-        "type": "SHORT",
+  <div
+    className="navbar-context-meta-secondary"
+  >
+    <BranchStatus
+      branch={
+        Object {
+          "isMain": false,
+          "mergeBranch": "master",
+          "name": "feature",
+          "status": Object {
+            "bugs": 0,
+            "codeSmells": 2,
+            "vulnerabilities": 3,
+          },
+          "type": "SHORT",
+        }
       }
-    }
-  />
+    />
+  </div>
 </div>
 `;
index 53855c6747f541a0fd6fa2e43160eb4844d5d088..1ef923cf2b9dcc26b5cf282d975845a69dadc5fa 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+.white-page {
+  background-color: #fff !important;
+}
+
 .global-container {
   display: flex;
   flex-direction: column;
   padding-left: calc(50vw - 370px + 10px) !important;
 }
 
-.page-footer-with-sidebar div {
+.page-footer-with-sidebar div,
+.page-footer-with-sidebar .page-footer-menu {
   max-width: 980px;
 }
 
index 4b290fb30963718468527ce65a866d526fa90c59..72a57e214d22389e179af45de487bb80b7faf9c8 100644 (file)
@@ -26,7 +26,7 @@
 
 html,
 body {
-  background-color: #fff;
+  background-color: var(--barBackgroundColor);
 }
 
 body {
index 2033873f7842097d0940b0a334ceca8f68cd1e85..407f8237df73aa883f684b42cb23804bf966a2d1 100644 (file)
@@ -485,6 +485,7 @@ a:hover > .icon-radio {
   stroke-width: 1.41421356;
   stroke-opacity: 1;
   fill-opacity: 0;
+  vector-effect: non-scaling-stroke;
   transition: all 0.2s ease;
 }
 
index 63fcfb1ee50d2e9da75083b597d1d860bbd890fc..696c300636a2cdb79891554bb2eec272ffcf0a1b 100644 (file)
     display: none !important;
   }
 
-  .dashboard-page,
-  .dashboard-page body {
-    background-color: #fff;
+  html,
+  body {
+    background-color: #fff !important;
   }
-
   .widget thead,
   .widget tfoot {
     display: table-row-group;
index 921f952f4ee662d97cdbbab99829130ab2c10550..a60b872bbc76463fd64c8867650d84da278ea5a8 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-.dashboard-page,
-.dashboard-page body {
-  background-color: var(--barBackgroundColor);
-}
-
 .tabs {
   height: 20px;
   border-bottom: 1px solid #ddd;
index 70df316dea2d038bd94db8058534ee6571a95912..f9f9be013ac2fcb5b9d3c541e62db5d8e6b3d455 100644 (file)
@@ -62,13 +62,15 @@ export interface Extension {
   name: string;
 }
 
+export interface Breadcrumb {
+  key: string;
+  name: string;
+  qualifier: string;
+}
+
 export interface Component {
   analysisDate?: string;
-  breadcrumbs: Array<{
-    key: string;
-    name: string;
-    qualifier: string;
-  }>;
+  breadcrumbs: Breadcrumb[];
   configuration?: ComponentConfiguration;
   description?: string;
   extensions?: Extension[];
@@ -141,13 +143,20 @@ export interface CurrentUser {
   showOnboardingTutorial?: boolean;
 }
 
+export enum HomePageType {
+  Project = 'PROJECT',
+  Organization = 'ORGANIZATION',
+  MyProjects = 'MY_PROJECTS',
+  MyIssues = 'MY_ISSUES'
+}
+
 export interface HomePage {
-  key?: string;
-  type: string;
+  parameter?: string;
+  type: HomePageType;
 }
 
 export function isSameHomePage(a: HomePage, b: HomePage) {
-  return a.type === b.type && a.key === b.key;
+  return a.type === b.type && a.parameter === b.parameter;
 }
 
 export interface LoggedInUser extends CurrentUser {
index 267d0fc7cf1b87d4ee291321eb725958e01c65ba..d0369971567b94232cc45c4782b7f34cc477a5bf 100644 (file)
@@ -76,11 +76,15 @@ class AboutApp extends React.PureComponent {
       window.location = 'https://about.sonarcloud.io';
     } else {
       this.loadData();
+      // $FlowFixMe
+      document.body.classList.add('white-page');
     }
   }
 
   componentWillUnmount() {
     this.mounted = false;
+    // $FlowFixMe
+    document.body.classList.remove('white-page');
   }
 
   loadProjects() {
index 12c3cabf57410abe2b000ad116100ab8a47e396e..73d42282e135e327cf33ed23ffe8bb2890c4b782 100644 (file)
@@ -8,7 +8,7 @@
   padding-top: 20px;
   padding-bottom: 20px;
   border-bottom: 1px solid var(--barBorderColor);
-  background-color: var(--barBackgroundColor);
+  background-color: #fff;
 }
 
 .account-nav {
index dec082bc69bfb2ffc76981b7e5908f257fe5fbbb..6e3bb3c3c695ad6f9912a3ad00ee2820843a1c26 100644 (file)
@@ -70,10 +70,10 @@ export default class Password extends Component {
     const { success, errors } = this.state;
 
     return (
-      <section>
+      <section className="boxed-group">
         <h2 className="spacer-bottom">{translate('my_profile.password.title')}</h2>
 
-        <form onSubmit={this.handleChangePassword}>
+        <form className="boxed-group-inner" onSubmit={this.handleChangePassword}>
           {success && (
             <div className="alert alert-success">{translate('my_profile.password.changed')}</div>
           )}
index d52a3b6e1eaf316f7ed0af441120451a93c64288..3006ca55f1a4d5337765bbc83668d4277ae89c84 100644 (file)
@@ -31,11 +31,7 @@ function Security(props) {
   return (
     <div className="account-body account-container">
       <Helmet title={translate('my_account.security')} />
-
       <Tokens user={user} />
-
-      {user.local && <hr className="account-separator" />}
-
       {user.local && <Password user={user} />}
     </div>
   );
index d7ba3d3519793c658b6c22f4d4abd521f63a127c..294c2c209c6fdb10334051d2d5c940744bd57da2 100644 (file)
@@ -46,30 +46,32 @@ type Props = {
 
 function GlobalNotifications(props /*: Props */) {
   return (
-    <section>
-      <h2 className="spacer-bottom">{translate('my_profile.overall_notifications.title')}</h2>
+    <section className="boxed-group">
+      <h2>{translate('my_profile.overall_notifications.title')}</h2>
 
-      <table className="form">
-        <thead>
-          <tr>
-            <th />
-            {props.channels.map(channel => (
-              <th key={channel} className="text-center">
-                <h4>{translate('notification.channel', channel)}</h4>
-              </th>
-            ))}
-          </tr>
-        </thead>
+      <div className="boxed-group-inner">
+        <table className="form">
+          <thead>
+            <tr>
+              <th />
+              {props.channels.map(channel => (
+                <th key={channel} className="text-center">
+                  <h4>{translate('notification.channel', channel)}</h4>
+                </th>
+              ))}
+            </tr>
+          </thead>
 
-        <NotificationsList
-          notifications={props.notifications}
-          channels={props.channels}
-          types={props.types}
-          checkboxId={(d, c) => `global-notification-${d}-${c}`}
-          onAdd={props.addNotification}
-          onRemove={props.removeNotification}
-        />
-      </table>
+          <NotificationsList
+            notifications={props.notifications}
+            channels={props.channels}
+            types={props.types}
+            checkboxId={(d, c) => `global-notification-${d}-${c}`}
+            onAdd={props.addNotification}
+            onRemove={props.removeNotification}
+          />
+        </table>
+      </div>
     </section>
   );
 }
index aa2c8d1179e95eb4d672aa3b3cf28b593e0b3097..6e5fed53bcc1fbdafd619de3b49e82566da04dd9 100644 (file)
@@ -40,13 +40,8 @@ class Notifications extends React.PureComponent {
     return (
       <div className="account-body account-container">
         <Helmet title={translate('my_account.notifications')} />
-
-        <p className="big-spacer-bottom">{translate('notification.dispatcher.information')}</p>
-
+        <p className="alert alert-info">{translate('notification.dispatcher.information')}</p>
         <GlobalNotifications />
-
-        <hr className="account-separator" />
-
         <Projects />
       </div>
     );
index 14a7f74020cd37273d87280347cfd20133d71ea5..5345b55591a6eb30739187ebcf4932970019ca24 100644 (file)
@@ -113,30 +113,32 @@ class Projects extends React.PureComponent {
     const allProjects = [...this.props.projects, ...this.state.addedProjects];
 
     return (
-      <section>
-        <h2 className="spacer-bottom">{translate('my_profile.per_project_notifications.title')}</h2>
-
-        {allProjects.length === 0 && (
-          <div className="note">{translate('my_account.no_project_notifications')}</div>
-        )}
-
-        {allProjects.map(project => <ProjectNotifications key={project.key} project={project} />)}
-
-        <div className="spacer-top panel bg-muted">
-          <span className="text-middle spacer-right">
-            {translate('my_account.set_notifications_for')}:
-          </span>
-          <AsyncSelect
-            autoload={false}
-            cache={false}
-            name="new_project"
-            style={{ width: '300px' }}
-            loadOptions={this.loadOptions}
-            minimumInput={2}
-            optionRenderer={this.renderOption}
-            onChange={this.handleAddProject}
-            placeholder={translate('my_account.search_project')}
-          />
+      <section className="boxed-group">
+        <h2>{translate('my_profile.per_project_notifications.title')}</h2>
+
+        <div className="boxed-group-inner">
+          {allProjects.length === 0 && (
+            <div className="note">{translate('my_account.no_project_notifications')}</div>
+          )}
+
+          {allProjects.map(project => <ProjectNotifications key={project.key} project={project} />)}
+
+          <div className="spacer-top panel bg-muted">
+            <span className="text-middle spacer-right">
+              {translate('my_account.set_notifications_for')}:
+            </span>
+            <AsyncSelect
+              autoload={false}
+              cache={false}
+              name="new_project"
+              style={{ width: '300px' }}
+              loadOptions={this.loadOptions}
+              minimumInput={2}
+              optionRenderer={this.renderOption}
+              onChange={this.handleAddProject}
+              placeholder={translate('my_account.search_project')}
+            />
+          </div>
         </div>
       </section>
     );
index 4a7d7d10c5a6ec4878cc18282aac8270c0632c76..64a62011d961649be3ec00415de5cecbd0e4bac3 100644 (file)
@@ -1,69 +1,73 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should match snapshot 1`] = `
-<section>
-  <h2
-    className="spacer-bottom"
-  >
+<section
+  className="boxed-group"
+>
+  <h2>
     my_profile.overall_notifications.title
   </h2>
-  <table
-    className="form"
+  <div
+    className="boxed-group-inner"
   >
-    <thead>
-      <tr>
-        <th />
-        <th
-          className="text-center"
-          key="channel1"
-        >
-          <h4>
-            notification.channel.channel1
-          </h4>
-        </th>
-        <th
-          className="text-center"
-          key="channel2"
-        >
-          <h4>
-            notification.channel.channel2
-          </h4>
-        </th>
-      </tr>
-    </thead>
-    <NotificationsList
-      channels={
-        Array [
-          "channel1",
-          "channel2",
-        ]
-      }
-      checkboxId={[Function]}
-      notifications={
-        Array [
-          Object {
-            "channel": "channel1",
-            "type": "type1",
-          },
-          Object {
-            "channel": "channel1",
-            "type": "type2",
-          },
-          Object {
-            "channel": "channel2",
-            "type": "type2",
-          },
-        ]
-      }
-      onAdd={[Function]}
-      onRemove={[Function]}
-      types={
-        Array [
-          "type1",
-          "type2",
-        ]
-      }
-    />
-  </table>
+    <table
+      className="form"
+    >
+      <thead>
+        <tr>
+          <th />
+          <th
+            className="text-center"
+            key="channel1"
+          >
+            <h4>
+              notification.channel.channel1
+            </h4>
+          </th>
+          <th
+            className="text-center"
+            key="channel2"
+          >
+            <h4>
+              notification.channel.channel2
+            </h4>
+          </th>
+        </tr>
+      </thead>
+      <NotificationsList
+        channels={
+          Array [
+            "channel1",
+            "channel2",
+          ]
+        }
+        checkboxId={[Function]}
+        notifications={
+          Array [
+            Object {
+              "channel": "channel1",
+              "type": "type1",
+            },
+            Object {
+              "channel": "channel1",
+              "type": "type2",
+            },
+            Object {
+              "channel": "channel2",
+              "type": "type2",
+            },
+          ]
+        }
+        onAdd={[Function]}
+        onRemove={[Function]}
+        types={
+          Array [
+            "type1",
+            "type2",
+          ]
+        }
+      />
+    </table>
+  </div>
 </section>
 `;
index 8541cc01be747eb11d7fa17a983300099aac5d47..855ccecb356e2e15663d46ae0ddffe3590fb5b15 100644 (file)
@@ -10,14 +10,11 @@ exports[`should match snapshot 1`] = `
     title="my_account.notifications"
   />
   <p
-    className="big-spacer-bottom"
+    className="alert alert-info"
   >
     notification.dispatcher.information
   </p>
   <Connect(GlobalNotifications) />
-  <hr
-    className="account-separator"
-  />
   <Connect(Projects) />
 </div>
 `;
index 3d6b8b28eb1fa039f23b5628163e034a7cc3355f..7f2fb250868ef97fa5e41ae6784fbe459964337a 100644 (file)
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should render projects 1`] = `
-<section>
-  <h2
-    className="spacer-bottom"
-  >
+<section
+  className="boxed-group"
+>
+  <h2>
     my_profile.per_project_notifications.title
   </h2>
-  <Connect(ProjectNotifications)
-    key="foo"
-    project={
-      Object {
-        "key": "foo",
-        "name": "Foo",
-      }
-    }
-  />
-  <Connect(ProjectNotifications)
-    key="bar"
-    project={
-      Object {
-        "key": "bar",
-        "name": "Bar",
-      }
-    }
-  />
   <div
-    className="spacer-top panel bg-muted"
+    className="boxed-group-inner"
   >
-    <span
-      className="text-middle spacer-right"
-    >
-      my_account.set_notifications_for
-      :
-    </span>
-    <AsyncSelect
-      autoload={false}
-      cache={false}
-      loadOptions={[Function]}
-      minimumInput={2}
-      name="new_project"
-      onChange={[Function]}
-      optionRenderer={[Function]}
-      placeholder="my_account.search_project"
-      style={
+    <Connect(ProjectNotifications)
+      key="foo"
+      project={
+        Object {
+          "key": "foo",
+          "name": "Foo",
+        }
+      }
+    />
+    <Connect(ProjectNotifications)
+      key="bar"
+      project={
         Object {
-          "width": "300px",
+          "key": "bar",
+          "name": "Bar",
         }
       }
     />
+    <div
+      className="spacer-top panel bg-muted"
+    >
+      <span
+        className="text-middle spacer-right"
+      >
+        my_account.set_notifications_for
+        :
+      </span>
+      <AsyncSelect
+        autoload={false}
+        cache={false}
+        loadOptions={[Function]}
+        minimumInput={2}
+        name="new_project"
+        onChange={[Function]}
+        optionRenderer={[Function]}
+        placeholder="my_account.search_project"
+        style={
+          Object {
+            "width": "300px",
+          }
+        }
+      />
+    </div>
   </div>
 </section>
 `;
 
 exports[`should render projects 2`] = `
-<section>
-  <h2
-    className="spacer-bottom"
-  >
+<section
+  className="boxed-group"
+>
+  <h2>
     my_profile.per_project_notifications.title
   </h2>
-  <Connect(ProjectNotifications)
-    key="foo"
-    project={
-      Object {
-        "key": "foo",
-        "name": "Foo",
-      }
-    }
-  />
-  <Connect(ProjectNotifications)
-    key="bar"
-    project={
-      Object {
-        "key": "bar",
-        "name": "Bar",
-      }
-    }
-  />
-  <Connect(ProjectNotifications)
-    key="qux"
-    project={
-      Object {
-        "key": "qux",
-        "name": "Qux",
-      }
-    }
-  />
   <div
-    className="spacer-top panel bg-muted"
+    className="boxed-group-inner"
   >
-    <span
-      className="text-middle spacer-right"
-    >
-      my_account.set_notifications_for
-      :
-    </span>
-    <AsyncSelect
-      autoload={false}
-      cache={false}
-      loadOptions={[Function]}
-      minimumInput={2}
-      name="new_project"
-      onChange={[Function]}
-      optionRenderer={[Function]}
-      placeholder="my_account.search_project"
-      style={
+    <Connect(ProjectNotifications)
+      key="foo"
+      project={
+        Object {
+          "key": "foo",
+          "name": "Foo",
+        }
+      }
+    />
+    <Connect(ProjectNotifications)
+      key="bar"
+      project={
+        Object {
+          "key": "bar",
+          "name": "Bar",
+        }
+      }
+    />
+    <Connect(ProjectNotifications)
+      key="qux"
+      project={
         Object {
-          "width": "300px",
+          "key": "qux",
+          "name": "Qux",
         }
       }
     />
+    <div
+      className="spacer-top panel bg-muted"
+    >
+      <span
+        className="text-middle spacer-right"
+      >
+        my_account.set_notifications_for
+        :
+      </span>
+      <AsyncSelect
+        autoload={false}
+        cache={false}
+        loadOptions={[Function]}
+        minimumInput={2}
+        name="new_project"
+        onChange={[Function]}
+        optionRenderer={[Function]}
+        placeholder="my_account.search_project"
+        style={
+          Object {
+            "width": "300px",
+          }
+        }
+      />
+    </div>
   </div>
 </section>
 `;
 
 exports[`should render projects 3`] = `
-<section>
-  <h2
-    className="spacer-bottom"
-  >
+<section
+  className="boxed-group"
+>
+  <h2>
     my_profile.per_project_notifications.title
   </h2>
-  <Connect(ProjectNotifications)
-    key="foo"
-    project={
-      Object {
-        "key": "foo",
-        "name": "Foo",
-      }
-    }
-  />
-  <Connect(ProjectNotifications)
-    key="bar"
-    project={
-      Object {
-        "key": "bar",
-        "name": "Bar",
-      }
-    }
-  />
-  <Connect(ProjectNotifications)
-    key="qux"
-    project={
-      Object {
-        "key": "qux",
-        "name": "Qux",
-      }
-    }
-  />
   <div
-    className="spacer-top panel bg-muted"
+    className="boxed-group-inner"
   >
-    <span
-      className="text-middle spacer-right"
-    >
-      my_account.set_notifications_for
-      :
-    </span>
-    <AsyncSelect
-      autoload={false}
-      cache={false}
-      loadOptions={[Function]}
-      minimumInput={2}
-      name="new_project"
-      onChange={[Function]}
-      optionRenderer={[Function]}
-      placeholder="my_account.search_project"
-      style={
+    <Connect(ProjectNotifications)
+      key="foo"
+      project={
         Object {
-          "width": "300px",
+          "key": "foo",
+          "name": "Foo",
         }
       }
     />
+    <Connect(ProjectNotifications)
+      key="bar"
+      project={
+        Object {
+          "key": "bar",
+          "name": "Bar",
+        }
+      }
+    />
+    <Connect(ProjectNotifications)
+      key="qux"
+      project={
+        Object {
+          "key": "qux",
+          "name": "Qux",
+        }
+      }
+    />
+    <div
+      className="spacer-top panel bg-muted"
+    >
+      <span
+        className="text-middle spacer-right"
+      >
+        my_account.set_notifications_for
+        :
+      </span>
+      <AsyncSelect
+        autoload={false}
+        cache={false}
+        loadOptions={[Function]}
+        minimumInput={2}
+        name="new_project"
+        onChange={[Function]}
+        optionRenderer={[Function]}
+        placeholder="my_account.search_project"
+        style={
+          Object {
+            "width": "300px",
+          }
+        }
+      />
+    </div>
   </div>
 </section>
 `;
index 5b0b8ccc3678624195b00f1e4bcc5356997ffea0..42273f32553475d9164747295f9838c488e29c30 100644 (file)
@@ -21,12 +21,17 @@ import * as React from 'react';
 import { sortBy } from 'lodash';
 import OrganizationCard from './OrganizationCard';
 import { Organization } from '../../../app/types';
+import { translate } from '../../../helpers/l10n';
 
 interface Props {
   organizations: Organization[];
 }
 
 export default function OrganizationsList({ organizations }: Props) {
+  if (organizations.length === 0) {
+    return <div>{translate('my_account.organizations.no_results')}</div>;
+  }
+
   return (
     <ul className="account-projects-list">
       {sortBy(organizations, organization => organization.name.toLocaleLowerCase()).map(
index e6f34fbd8e2a6658cccdf5dca46f6ce1a438ffbe..d63555b15745783630ac90a593fd2c66af3d804d 100644 (file)
@@ -87,29 +87,22 @@ class UserOrganizations extends React.PureComponent<Props, State> {
       <div className="account-body account-container">
         <Helmet title={translate('my_account.organizations')} />
 
-        <header className="page-header">
-          <h2 className="page-title">{translate('my_account.organizations')}</h2>
+        <div className="boxed-group">
           {canCreateOrganizations && (
-            <div className="page-actions">
-              <button onClick={this.handleCreateClick}>{translate('create')}</button>
+            <div className="clearfix">
+              <div className="boxed-group-actions">
+                <button onClick={this.handleCreateClick}>{translate('create')}</button>
+              </div>
             </div>
           )}
-          {this.props.organizations.length > 0 ? (
-            <div className="page-description">
-              {translate('my_account.organizations.description')}
-            </div>
-          ) : (
-            <div className="page-description">
-              {translate('my_account.organizations.no_results')}
-            </div>
-          )}
-        </header>
-
-        {this.state.loading ? (
-          <i className="spinner" />
-        ) : (
-          <OrganizationsList organizations={this.props.organizations} />
-        )}
+          <div className="boxed-group-inner">
+            {this.state.loading ? (
+              <i className="spinner" />
+            ) : (
+              <OrganizationsList organizations={this.props.organizations} />
+            )}
+          </div>
+        </div>
 
         {this.state.createOrganization && (
           <CreateOrganizationForm
index 4996bfcd124e96e4ea9e1a71de9f039c610d59ef..464f549d5bd48ecaf38b9df649eda42a2ea8fac1 100644 (file)
@@ -45,29 +45,31 @@ function Profile(props /*: Props */) {
 
   return (
     <div className="account-body account-container">
-      <div className="spacer-bottom">
-        {translate('login')}: <strong id="login">{user.login}</strong>
-      </div>
+      <div className="boxed-group boxed-group-inner">
+        <div className="spacer-bottom">
+          {translate('login')}: <strong id="login">{user.login}</strong>
+        </div>
+
+        {!user.local &&
+          user.externalProvider !== 'sonarqube' && (
+            <div id="identity-provider" className="spacer-bottom">
+              <UserExternalIdentity user={user} />
+            </div>
+          )}
 
-      {!user.local &&
-        user.externalProvider !== 'sonarqube' && (
-          <div id="identity-provider" className="spacer-bottom">
-            <UserExternalIdentity user={user} />
+        {!!user.email && (
+          <div className="spacer-bottom">
+            {translate('my_profile.email')}: <strong id="email">{user.email}</strong>
           </div>
         )}
 
-      {!!user.email && (
-        <div className="spacer-bottom">
-          {translate('my_profile.email')}: <strong id="email">{user.email}</strong>
-        </div>
-      )}
+        {!customOrganizations && <hr className="account-separator" />}
+        {!customOrganizations && <UserGroups groups={user.groups} />}
 
-      {!customOrganizations && <hr className="account-separator" />}
-      {!customOrganizations && <UserGroups groups={user.groups} />}
+        <hr />
 
-      <hr className="account-separator" />
-
-      <UserScmAccounts user={user} scmAccounts={user.scmAccounts} />
+        <UserScmAccounts user={user} scmAccounts={user.scmAccounts} />
+      </div>
     </div>
   );
 }
index 2c8da3e02977aa8d4b668450c3e172f065d88b26..65bbf91e160b0c344f53fee8371c0e1390746d1a 100644 (file)
@@ -1,77 +1,81 @@
-<h2 class="spacer-bottom">{{t 'users.tokens'}}</h2>
+<div class="boxed-group">
+  <h2>{{t 'users.tokens'}}</h2>
 
-<div class="big-spacer-bottom big-spacer-right markdown">
-  <p>{{t 'my_account.tokens_description'}}</p>
-</div>
+<div class="boxed-group-inner">
+  <div class="big-spacer-bottom big-spacer-right markdown">
+    <p>{{t 'my_account.tokens_description'}}</p>
+  </div>
 
-{{#notNull tokens}}
-  <table class="data">
-    <thead>
-    <tr>
-      <th>{{t 'name'}}</th>
-      <th class="text-right">{{t 'created'}}</th>
-      <th>&nbsp;</th>
-    </tr>
-    </thead>
-    <tbody>
-    {{#each tokens}}
-      <tr>
-        <td>
-          <div title="{{name}}">
-            {{limitString name}}
-          </div>
-        </td>
-        <td class="thin nowrap text-right">
-          {{d createdAt}}
-        </td>
-        <td class="thin nowrap text-right">
-          <div class="big-spacer-left">
-            <form class="js-revoke-token-form" data-token="{{name}}">
-              {{#if deleting}}
-                <button class="button-red active input-small">{{t 'users.tokens.sure'}}</button>
-              {{else}}
-                <button class="button-red input-small">{{t 'users.tokens.revoke'}}</button>
-              {{/if}}
-            </form>
-          </div>
-        </td>
-      </tr>
-    {{else}}
+  {{#notNull tokens}}
+    <table class="data">
+      <thead>
       <tr>
-        <td colspan="3">
-          <span class="note">{{t 'users.no_tokens'}}</span>
-        </td>
+        <th>{{t 'name'}}</th>
+        <th class="text-right">{{t 'created'}}</th>
+        <th>&nbsp;</th>
       </tr>
-    {{/each}}
-    </tbody>
-  </table>
-{{/notNull}}
+      </thead>
+      <tbody>
+      {{#each tokens}}
+        <tr>
+          <td>
+            <div title="{{name}}">
+              {{limitString name}}
+            </div>
+          </td>
+          <td class="thin nowrap text-right">
+            {{d createdAt}}
+          </td>
+          <td class="thin nowrap text-right">
+            <div class="big-spacer-left">
+              <form class="js-revoke-token-form" data-token="{{name}}">
+                {{#if deleting}}
+                  <button class="button-red active input-small">{{t 'users.tokens.sure'}}</button>
+                {{else}}
+                  <button class="button-red input-small">{{t 'users.tokens.revoke'}}</button>
+                {{/if}}
+              </form>
+            </div>
+          </td>
+        </tr>
+      {{else}}
+        <tr>
+          <td colspan="3">
+            <span class="note">{{t 'users.no_tokens'}}</span>
+          </td>
+        </tr>
+      {{/each}}
+      </tbody>
+    </table>
+  {{/notNull}}
 
-{{#each errors}}
-  <div class="alert alert-danger">{{msg}}</div>
-{{/each}}
+  {{#each errors}}
+    <div class="alert alert-danger">{{msg}}</div>
+  {{/each}}
 
-<form class="js-generate-token-form spacer-top panel bg-muted">
-  <label>{{t 'users.generate_new_token'}}:</label>
-  <input type="text" required maxlength="100" placeholder="{{t 'users.enter_token_name'}}">
-  <button>{{t 'users.generate'}}</button>
-</form>
+  <form class="js-generate-token-form spacer-top panel bg-muted">
+    <label>{{t 'users.generate_new_token'}}:</label>
+    <input type="text" required maxlength="100" placeholder="{{t 'users.enter_token_name'}}">
+    <button>{{t 'users.generate'}}</button>
+  </form>
 
-{{#if newToken}}
-  <div class="panel panel-white big-spacer-top">
-    <div class="alert alert-warning">
-      {{tp 'users.tokens.new_token_created' newToken.name}}
-    </div>
+  {{#if newToken}}
+    <div class="panel panel-white big-spacer-top">
+      <div class="alert alert-warning">
+        {{tp 'users.tokens.new_token_created' newToken.name}}
+      </div>
 
-    <table class="data">
-      <tr>
-        <td class="thin">
-          <button class="js-copy-to-clipboard" data-clipboard-text="{{newToken.token}}">{{t 'copy'}}</button>
-        </td>
-        <td class="nowrap">
-          <div class="monospaced text-success">{{newToken.token}}</div>
-        </td>
-      </tr>
-    </table>
-  </div>
-{{/if}}
+      <table class="data">
+        <tr>
+          <td class="thin">
+            <button class="js-copy-to-clipboard" data-clipboard-text="{{newToken.token}}">{{t 'copy'}}</button>
+          </td>
+          <td class="nowrap">
+            <div class="monospaced text-success">{{newToken.token}}</div>
+          </td>
+        </tr>
+      </table>
+    </div>
+  {{/if}}
+</div>
+</div>
index 0f0f843a1f50dca8bb9b48c8f0c941b07ed51c46..56f9d0508db37dddef1c0043683488d1a149751e 100644 (file)
@@ -49,34 +49,36 @@ export default class Tasks extends React.PureComponent {
     });
 
     return (
-      <table className={className}>
-        <thead>
-          <tr>
-            <th>{translate('background_tasks.table.status')}</th>
-            <th>{translate('background_tasks.table.task')}</th>
-            <th>{translate('background_tasks.table.id')}</th>
-            <th>&nbsp;</th>
-            <th className="text-right">{translate('background_tasks.table.submitted')}</th>
-            <th className="text-right">{translate('background_tasks.table.started')}</th>
-            <th className="text-right">{translate('background_tasks.table.finished')}</th>
-            <th className="text-right">{translate('background_tasks.table.duration')}</th>
-            <th>&nbsp;</th>
-          </tr>
-        </thead>
-        <tbody>
-          {tasks.map((task, index, tasks) => (
-            <Task
-              key={task.id}
-              task={task}
-              tasks={tasks}
-              component={component}
-              onCancelTask={onCancelTask}
-              onFilterTask={onFilterTask}
-              previousTask={index > 0 ? tasks[index - 1] : undefined}
-            />
-          ))}
-        </tbody>
-      </table>
+      <div className="boxed-group boxed-group-inner">
+        <table className={className}>
+          <thead>
+            <tr>
+              <th>{translate('background_tasks.table.status')}</th>
+              <th>{translate('background_tasks.table.task')}</th>
+              <th>{translate('background_tasks.table.id')}</th>
+              <th>&nbsp;</th>
+              <th className="text-right">{translate('background_tasks.table.submitted')}</th>
+              <th className="text-right">{translate('background_tasks.table.started')}</th>
+              <th className="text-right">{translate('background_tasks.table.finished')}</th>
+              <th className="text-right">{translate('background_tasks.table.duration')}</th>
+              <th>&nbsp;</th>
+            </tr>
+          </thead>
+          <tbody>
+            {tasks.map((task, index, tasks) => (
+              <Task
+                key={task.id}
+                task={task}
+                tasks={tasks}
+                component={component}
+                onCancelTask={onCancelTask}
+                onFilterTask={onFilterTask}
+                previousTask={index > 0 ? tasks[index - 1] : undefined}
+              />
+            ))}
+          </tbody>
+        </table>
+      </div>
     );
   }
 }
index 77110f20481db91707be847e5ee33f5c5d426a5d..0a5f41af9618fde17be0f6f524281cc55885b740 100644 (file)
@@ -200,7 +200,9 @@ export default class App extends React.PureComponent<Props, State> {
 
     const shouldShowBreadcrumbs = breadcrumbs.length > 1;
 
-    const componentsClassName = classNames('spacer-top', { 'new-loading': loading });
+    const componentsClassName = classNames('boxed-group', 'boxed-group-inner', 'spacer-top', {
+      'new-loading': loading
+    });
 
     return (
       <div className="page page-limited">
index 14539241c9aa6d9fcb53af37ace72f836dd139b0..fba3a124f5def6344fb9f26ad1cdeb1c3ab3e884 100644 (file)
@@ -182,12 +182,14 @@ export default class Search extends React.PureComponent<Props, State> {
         {loading && <i className="spinner spacer-left" />}
 
         {results != null && (
-          <Components
-            branch={this.props.branch}
-            components={results}
-            rootComponent={component}
-            selected={selected}
-          />
+          <div className="boxed-group boxed-group-inner spacer-top">
+            <Components
+              branch={this.props.branch}
+              components={results}
+              rootComponent={component}
+              selected={selected}
+            />
+          </div>
         )}
       </div>
     );
index 7b8eb343c9030079908bc6ffdc4deb644df86ff8..1c578244c010ad2c4c6f7d566f082f2283ff96ac 100644 (file)
@@ -44,6 +44,9 @@ class CodingRulesAppContainer extends React.PureComponent {
 */
 
   componentDidMount() {
+    // $FlowFixMe
+    document.body.classList.add('white-page');
+
     if (this.props.appState.organizationsEnabled && !this.props.params.organizationKey) {
       // redirect to organization-level rules page
       this.props.router.replace(
@@ -62,6 +65,9 @@ class CodingRulesAppContainer extends React.PureComponent {
   }
 
   componentWillUnmount() {
+    // $FlowFixMe
+    document.body.classList.remove('white-page');
+
     if (this.stop) {
       this.stop();
     }
index 84bd294f9e940ec380069599fd28f18d2cc11539..7611ea8b4c8d6496e23c730ca773444f7604f1e9 100644 (file)
@@ -74,6 +74,8 @@ export default class App extends React.PureComponent {
 
   componentDidMount() {
     this.mounted = true;
+    // $FlowFixMe
+    document.body.classList.add('white-page');
     this.props.fetchMetrics();
     this.fetchMeasures(this.props);
     key.setScope('measures-files');
@@ -95,6 +97,8 @@ export default class App extends React.PureComponent {
 
   componentWillUnmount() {
     this.mounted = false;
+    // $FlowFixMe
+    document.body.classList.remove('white-page');
     key.deleteScope('measures-files');
     const footer = document.getElementById('footer');
     if (footer) {
index 20ca1491fa3e09687574b338278e4d8fe27593a3..97c513248ebda20831db16bbdaed8df23e519dc8 100644 (file)
@@ -1,12 +1,14 @@
-<table class="data zebra">
-  <thead>
-  <tr>
-    <th>{{t 'custom_measures.metric'}}</th>
-    <th>{{t 'value'}}</th>
-    <th>{{t 'description'}}</th>
-    <th>{{t 'date'}}</th>
-    <th>&nbsp;</th>
-  </tr>
-  </thead>
-  <tbody></tbody>
-</table>
+<div class="boxed-group boxed-group-inner">
+  <table class="data zebra">
+    <thead>
+    <tr>
+      <th>{{t 'custom_measures.metric'}}</th>
+      <th>{{t 'value'}}</th>
+      <th>{{t 'description'}}</th>
+      <th>{{t 'date'}}</th>
+      <th>&nbsp;</th>
+    </tr>
+    </thead>
+    <tbody></tbody>
+  </table>
+</div>
index b96143e7caa11cbfb02d04b2ad73cecb2dd455a3..ac23f3c1f65cbb0083e90e73c79a2b9546e3904c 100644 (file)
@@ -1,4 +1,4 @@
-<div>
+<div class="boxed-group boxed-group-inner">
   {{#isNull organization}}
     <div class="panel panel-vertical js-anyone">
       <div class="display-inline-block text-top width-20">
index e0d8614362f748d39212feda64dd2e2137927095..ecf034da48e4309ed96f5ca27454db8306088a72 100644 (file)
@@ -1,4 +1,4 @@
-<div class="panel panel-vertical bordered-bottom spacer-bottom">
+<div class="big-spacer-bottom">
   <form id="groups-search-form" class="search-box">
     <input id="groups-search-query" class="search-box-input" type="text" name="q" placeholder="{{t 'search.search_by_name'}}" maxlength="100">
     <svg class="search-box-magnifier" width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
index cc68fbb41c9edbc0d721ecb74f7bfc068b07d9f9..c13b7a9b0ff06c19d6226327cc068aee3b171327 100644 (file)
@@ -151,6 +151,9 @@ export default class App extends React.PureComponent {
       return;
     }
 
+    // $FlowFixMe
+    document.body.classList.add('white-page');
+
     const footer = document.getElementById('footer');
     if (footer) {
       footer.classList.add('page-footer-with-sidebar');
@@ -206,6 +209,9 @@ export default class App extends React.PureComponent {
   componentWillUnmount() {
     this.detachShortcuts();
 
+    // $FlowFixMe
+    document.body.classList.remove('white-page');
+
     const footer = document.getElementById('footer');
     if (footer) {
       footer.classList.remove('page-footer-with-sidebar');
index 2aaca8517e887153b78078a1d9b6aa5b74b317bb..b800d271f3f2f279eb18fa4f7ac2246b67d41e9c 100644 (file)
@@ -22,6 +22,7 @@ import React from 'react';
 import IssuesCounter from './IssuesCounter';
 import ReloadButton from './ReloadButton';
 /*:: import type { Paging } from '../utils'; */
+import { HomePageType } from '../../../app/types';
 import DeferredSpinner from '../../../components/common/DeferredSpinner';
 import HomePageSelect from '../../../components/controls/HomePageSelect';
 import { translate } from '../../../helpers/l10n';
@@ -74,7 +75,10 @@ export default class PageActions extends React.PureComponent {
         </div>
 
         {this.props.canSetHome && (
-          <HomePageSelect className="huge-spacer-left" currentPage={{ type: 'my-issues' }} />
+          <HomePageSelect
+            className="huge-spacer-left"
+            currentPage={{ type: HomePageType.MyIssues }}
+          />
         )}
       </div>
     );
index 48924aba04ee59f9a204771b09b20a81a05ecfa1..c41ac4bf51629b0351529003393d0b06aa4c3305 100644 (file)
@@ -79,7 +79,7 @@ export default class PluginsList extends React.PureComponent<Props> {
 
   render() {
     return (
-      <div id="marketplace-plugins">
+      <div className="boxed-group boxed-group-inner" id="marketplace-plugins">
         <ul>
           {this.props.plugins.map(plugin => (
             <li key={plugin.key} className="panel panel-vertical">
index 1eaef17728ee8bbd43e032cb50f76242c0906b46..c114ebb66434918da3ce3e6a5a249c690cb63359 100644 (file)
@@ -48,7 +48,7 @@ export default class Search extends React.PureComponent<Props> {
       }
     ];
     return (
-      <div id="marketplace-search" className="panel panel-vertical bordered-bottom spacer-bottom">
+      <div id="marketplace-search" className="big-spacer-bottom">
         <div className="display-inline-block text-top nowrap abs-width-150 spacer-right">
           <RadioToggle
             name="marketplace-filter"
index b435f1c3367a3cdb55581ba82f981d9dde5ddc16..35aee754fed4c8f0492780552b3af94f1718d06f 100644 (file)
@@ -12,7 +12,6 @@
   display: flex;
   flex-direction: column;
   justify-content: space-between;
-  background-color: #f3f3f3;
   margin-left: 8px;
   margin-right: 8px;
 }
index 9aeeeede6b3300fa738f3cb1965b221ea478bb6c..179ee3ed06745c4b542c193f78a195eb662f9ce1 100644 (file)
@@ -1,5 +1,5 @@
 <div class="page page-limited">
   <div id="metrics-header"></div>
-  <div id="metrics-list"></div>
+  <div id="metrics-list" class="boxed-group boxed-group-inner"></div>
   <div id="metrics-list-footer"></div>
 </div>
index c40344c98bdebd5a5734a2f8f816e6604ddf9b0b..af7bf261a150ecf8cffe40bb63eaac0c53016f6f 100644 (file)
@@ -38,20 +38,22 @@ export default class MembersList extends React.PureComponent {
 
   render() {
     return (
-      <table className="data zebra">
-        <tbody>
-          {this.props.members.map(member => (
-            <MembersListItem
-              key={member.login}
-              member={member}
-              organizationGroups={this.props.organizationGroups}
-              organization={this.props.organization}
-              removeMember={this.props.removeMember}
-              updateMemberGroups={this.props.updateMemberGroups}
-            />
-          ))}
-        </tbody>
-      </table>
+      <div className="boxed-group boxed-group-inner">
+        <table className="data zebra">
+          <tbody>
+            {this.props.members.map(member => (
+              <MembersListItem
+                key={member.login}
+                member={member}
+                organizationGroups={this.props.organizationGroups}
+                organization={this.props.organization}
+                removeMember={this.props.removeMember}
+                updateMemberGroups={this.props.updateMemberGroups}
+              />
+            ))}
+          </tbody>
+        </table>
+      </div>
     );
   }
 }
index 5f7a3c867efd4a168507bdb7987edb53b0bdd881..92ceb30e062ab5828bbce4c633fb857b52186ed9 100644 (file)
@@ -107,87 +107,89 @@ class OrganizationEdit extends React.PureComponent {
           <h1 className="page-title">{title}</h1>
         </header>
 
-        <form onSubmit={this.handleSubmit}>
-          <div className="modal-field">
-            <label htmlFor="organization-name">
-              {translate('organization.name')}
-              <em className="mandatory">*</em>
-            </label>
-            <input
-              id="organization-name"
-              name="name"
-              required={true}
-              type="text"
-              maxLength="64"
-              value={this.state.name}
-              disabled={this.state.loading}
-              onChange={e => this.setState({ name: e.target.value })}
-            />
-            <div className="modal-field-description">
-              {translate('organization.name.description')}
-            </div>
-          </div>
-          <div className="modal-field">
-            <label htmlFor="organization-avatar">{translate('organization.avatar')}</label>
-            <input
-              id="organization-avatar"
-              name="avatar"
-              type="text"
-              maxLength="256"
-              value={this.state.avatar}
-              disabled={this.state.loading}
-              onChange={this.handleAvatarInputChange}
-            />
-            <div className="modal-field-description">
-              {translate('organization.avatar.description')}
+        <div className="boxed-group boxed-group-inner">
+          <form onSubmit={this.handleSubmit}>
+            <div className="modal-field">
+              <label htmlFor="organization-name">
+                {translate('organization.name')}
+                <em className="mandatory">*</em>
+              </label>
+              <input
+                id="organization-name"
+                name="name"
+                required={true}
+                type="text"
+                maxLength="64"
+                value={this.state.name}
+                disabled={this.state.loading}
+                onChange={e => this.setState({ name: e.target.value })}
+              />
+              <div className="modal-field-description">
+                {translate('organization.name.description')}
+              </div>
             </div>
-            {!!this.state.avatarImage && (
-              <div className="spacer-top spacer-bottom">
-                <div className="little-spacer-bottom">
-                  {translate('organization.avatar.preview')}
-                  {':'}
+            <div className="modal-field">
+              <label htmlFor="organization-avatar">{translate('organization.avatar')}</label>
+              <input
+                id="organization-avatar"
+                name="avatar"
+                type="text"
+                maxLength="256"
+                value={this.state.avatar}
+                disabled={this.state.loading}
+                onChange={this.handleAvatarInputChange}
+              />
+              <div className="modal-field-description">
+                {translate('organization.avatar.description')}
+              </div>
+              {!!this.state.avatarImage && (
+                <div className="spacer-top spacer-bottom">
+                  <div className="little-spacer-bottom">
+                    {translate('organization.avatar.preview')}
+                    {':'}
+                  </div>
+                  <img src={this.state.avatarImage} alt="" height={30} />
                 </div>
-                <img src={this.state.avatarImage} alt="" height={30} />
+              )}
+            </div>
+            <div className="modal-field">
+              <label htmlFor="organization-description">{translate('description')}</label>
+              <textarea
+                id="organization-description"
+                name="description"
+                rows="3"
+                maxLength="256"
+                value={this.state.description}
+                disabled={this.state.loading}
+                onChange={e => this.setState({ description: e.target.value })}
+              />
+              <div className="modal-field-description">
+                {translate('organization.description.description')}
+              </div>
+            </div>
+            <div className="modal-field">
+              <label htmlFor="organization-url">{translate('organization.url')}</label>
+              <input
+                id="organization-url"
+                name="url"
+                type="text"
+                maxLength="256"
+                value={this.state.url}
+                disabled={this.state.loading}
+                onChange={e => this.setState({ url: e.target.value })}
+              />
+              <div className="modal-field-description">
+                {translate('organization.url.description')}
               </div>
-            )}
-          </div>
-          <div className="modal-field">
-            <label htmlFor="organization-description">{translate('description')}</label>
-            <textarea
-              id="organization-description"
-              name="description"
-              rows="3"
-              maxLength="256"
-              value={this.state.description}
-              disabled={this.state.loading}
-              onChange={e => this.setState({ description: e.target.value })}
-            />
-            <div className="modal-field-description">
-              {translate('organization.description.description')}
             </div>
-          </div>
-          <div className="modal-field">
-            <label htmlFor="organization-url">{translate('organization.url')}</label>
-            <input
-              id="organization-url"
-              name="url"
-              type="text"
-              maxLength="256"
-              value={this.state.url}
-              disabled={this.state.loading}
-              onChange={e => this.setState({ url: e.target.value })}
-            />
-            <div className="modal-field-description">
-              {translate('organization.url.description')}
+            <div className="modal-field">
+              <button type="submit" disabled={this.state.loading}>
+                {translate('save')}
+              </button>
+              {this.state.loading && <i className="spinner spacer-left" />}
             </div>
-          </div>
-          <div className="modal-field">
-            <button type="submit" disabled={this.state.loading}>
-              {translate('save')}
-            </button>
-            {this.state.loading && <i className="spinner spacer-left" />}
-          </div>
-        </form>
+          </form>
+        </div>
       </div>
     );
   }
index 4bc29637672f2e1a486b09559e79524e4d00592f..fc8f227d95e3f9468c3317c6260ad59b83cc5307 100644 (file)
@@ -1,44 +1,48 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should render a list of members of an organization 1`] = `
-<table
-  className="data zebra"
+<div
+  className="boxed-group boxed-group-inner"
 >
-  <tbody>
-    <MembersListItem
-      key="admin"
-      member={
-        Object {
-          "avatar": "",
-          "groupCount": 3,
-          "login": "admin",
-          "name": "Admin Istrator",
+  <table
+    className="data zebra"
+  >
+    <tbody>
+      <MembersListItem
+        key="admin"
+        member={
+          Object {
+            "avatar": "",
+            "groupCount": 3,
+            "login": "admin",
+            "name": "Admin Istrator",
+          }
         }
-      }
-      organization={
-        Object {
-          "key": "foo",
-          "name": "Foo",
+        organization={
+          Object {
+            "key": "foo",
+            "name": "Foo",
+          }
         }
-      }
-    />
-    <MembersListItem
-      key="john"
-      member={
-        Object {
-          "avatar": "7daf6c79d4802916d83f6266e24850af",
-          "groupCount": 1,
-          "login": "john",
-          "name": "John Doe",
+      />
+      <MembersListItem
+        key="john"
+        member={
+          Object {
+            "avatar": "7daf6c79d4802916d83f6266e24850af",
+            "groupCount": 1,
+            "login": "john",
+            "name": "John Doe",
+          }
         }
-      }
-      organization={
-        Object {
-          "key": "foo",
-          "name": "Foo",
+        organization={
+          Object {
+            "key": "foo",
+            "name": "Foo",
+          }
         }
-      }
-    />
-  </tbody>
-</table>
+      />
+    </tbody>
+  </table>
+</div>
 `;
index 9dc712473f27df1ece253944eabd3174fab0279c..9c18fd59ac8f571616075fa8bb1b9e8ff35cbe0f 100644 (file)
@@ -18,118 +18,122 @@ exports[`smoke test 1`] = `
       organization.edit
     </h1>
   </header>
-  <form
-    onSubmit={[Function]}
+  <div
+    className="boxed-group boxed-group-inner"
   >
-    <div
-      className="modal-field"
+    <form
+      onSubmit={[Function]}
     >
-      <label
-        htmlFor="organization-name"
+      <div
+        className="modal-field"
       >
-        organization.name
-        <em
-          className="mandatory"
+        <label
+          htmlFor="organization-name"
+        >
+          organization.name
+          <em
+            className="mandatory"
+          >
+            *
+          </em>
+        </label>
+        <input
+          disabled={false}
+          id="organization-name"
+          maxLength="64"
+          name="name"
+          onChange={[Function]}
+          required={true}
+          type="text"
+          value="Foo"
+        />
+        <div
+          className="modal-field-description"
         >
-          *
-        </em>
-      </label>
-      <input
-        disabled={false}
-        id="organization-name"
-        maxLength="64"
-        name="name"
-        onChange={[Function]}
-        required={true}
-        type="text"
-        value="Foo"
-      />
+          organization.name.description
+        </div>
+      </div>
       <div
-        className="modal-field-description"
+        className="modal-field"
       >
-        organization.name.description
+        <label
+          htmlFor="organization-avatar"
+        >
+          organization.avatar
+        </label>
+        <input
+          disabled={false}
+          id="organization-avatar"
+          maxLength="256"
+          name="avatar"
+          onChange={[Function]}
+          type="text"
+          value=""
+        />
+        <div
+          className="modal-field-description"
+        >
+          organization.avatar.description
+        </div>
       </div>
-    </div>
-    <div
-      className="modal-field"
-    >
-      <label
-        htmlFor="organization-avatar"
-      >
-        organization.avatar
-      </label>
-      <input
-        disabled={false}
-        id="organization-avatar"
-        maxLength="256"
-        name="avatar"
-        onChange={[Function]}
-        type="text"
-        value=""
-      />
       <div
-        className="modal-field-description"
+        className="modal-field"
       >
-        organization.avatar.description
+        <label
+          htmlFor="organization-description"
+        >
+          description
+        </label>
+        <textarea
+          disabled={false}
+          id="organization-description"
+          maxLength="256"
+          name="description"
+          onChange={[Function]}
+          rows="3"
+          value=""
+        />
+        <div
+          className="modal-field-description"
+        >
+          organization.description.description
+        </div>
       </div>
-    </div>
-    <div
-      className="modal-field"
-    >
-      <label
-        htmlFor="organization-description"
-      >
-        description
-      </label>
-      <textarea
-        disabled={false}
-        id="organization-description"
-        maxLength="256"
-        name="description"
-        onChange={[Function]}
-        rows="3"
-        value=""
-      />
       <div
-        className="modal-field-description"
+        className="modal-field"
       >
-        organization.description.description
+        <label
+          htmlFor="organization-url"
+        >
+          organization.url
+        </label>
+        <input
+          disabled={false}
+          id="organization-url"
+          maxLength="256"
+          name="url"
+          onChange={[Function]}
+          type="text"
+          value=""
+        />
+        <div
+          className="modal-field-description"
+        >
+          organization.url.description
+        </div>
       </div>
-    </div>
-    <div
-      className="modal-field"
-    >
-      <label
-        htmlFor="organization-url"
-      >
-        organization.url
-      </label>
-      <input
-        disabled={false}
-        id="organization-url"
-        maxLength="256"
-        name="url"
-        onChange={[Function]}
-        type="text"
-        value=""
-      />
       <div
-        className="modal-field-description"
+        className="modal-field"
       >
-        organization.url.description
+        <button
+          disabled={false}
+          type="submit"
+        >
+          save
+        </button>
       </div>
-    </div>
-    <div
-      className="modal-field"
-    >
-      <button
-        disabled={false}
-        type="submit"
-      >
-        save
-      </button>
-    </div>
-  </form>
+    </form>
+  </div>
 </div>
 `;
 
@@ -151,133 +155,137 @@ exports[`smoke test 2`] = `
       organization.edit
     </h1>
   </header>
-  <form
-    onSubmit={[Function]}
+  <div
+    className="boxed-group boxed-group-inner"
   >
-    <div
-      className="modal-field"
+    <form
+      onSubmit={[Function]}
     >
-      <label
-        htmlFor="organization-name"
-      >
-        organization.name
-        <em
-          className="mandatory"
-        >
-          *
-        </em>
-      </label>
-      <input
-        disabled={false}
-        id="organization-name"
-        maxLength="64"
-        name="name"
-        onChange={[Function]}
-        required={true}
-        type="text"
-        value="New Foo"
-      />
       <div
-        className="modal-field-description"
+        className="modal-field"
       >
-        organization.name.description
+        <label
+          htmlFor="organization-name"
+        >
+          organization.name
+          <em
+            className="mandatory"
+          >
+            *
+          </em>
+        </label>
+        <input
+          disabled={false}
+          id="organization-name"
+          maxLength="64"
+          name="name"
+          onChange={[Function]}
+          required={true}
+          type="text"
+          value="New Foo"
+        />
+        <div
+          className="modal-field-description"
+        >
+          organization.name.description
+        </div>
       </div>
-    </div>
-    <div
-      className="modal-field"
-    >
-      <label
-        htmlFor="organization-avatar"
-      >
-        organization.avatar
-      </label>
-      <input
-        disabled={false}
-        id="organization-avatar"
-        maxLength="256"
-        name="avatar"
-        onChange={[Function]}
-        type="text"
-        value="foo-avatar"
-      />
       <div
-        className="modal-field-description"
+        className="modal-field"
       >
-        organization.avatar.description
+        <label
+          htmlFor="organization-avatar"
+        >
+          organization.avatar
+        </label>
+        <input
+          disabled={false}
+          id="organization-avatar"
+          maxLength="256"
+          name="avatar"
+          onChange={[Function]}
+          type="text"
+          value="foo-avatar"
+        />
+        <div
+          className="modal-field-description"
+        >
+          organization.avatar.description
+        </div>
+        <div
+          className="spacer-top spacer-bottom"
+        >
+          <div
+            className="little-spacer-bottom"
+          >
+            organization.avatar.preview
+            :
+          </div>
+          <img
+            alt=""
+            height={30}
+            src="foo-avatar-image"
+          />
+        </div>
       </div>
       <div
-        className="spacer-top spacer-bottom"
+        className="modal-field"
       >
+        <label
+          htmlFor="organization-description"
+        >
+          description
+        </label>
+        <textarea
+          disabled={false}
+          id="organization-description"
+          maxLength="256"
+          name="description"
+          onChange={[Function]}
+          rows="3"
+          value="foo-description"
+        />
         <div
-          className="little-spacer-bottom"
+          className="modal-field-description"
         >
-          organization.avatar.preview
-          :
+          organization.description.description
         </div>
-        <img
-          alt=""
-          height={30}
-          src="foo-avatar-image"
-        />
       </div>
-    </div>
-    <div
-      className="modal-field"
-    >
-      <label
-        htmlFor="organization-description"
-      >
-        description
-      </label>
-      <textarea
-        disabled={false}
-        id="organization-description"
-        maxLength="256"
-        name="description"
-        onChange={[Function]}
-        rows="3"
-        value="foo-description"
-      />
       <div
-        className="modal-field-description"
+        className="modal-field"
       >
-        organization.description.description
+        <label
+          htmlFor="organization-url"
+        >
+          organization.url
+        </label>
+        <input
+          disabled={false}
+          id="organization-url"
+          maxLength="256"
+          name="url"
+          onChange={[Function]}
+          type="text"
+          value="foo-url"
+        />
+        <div
+          className="modal-field-description"
+        >
+          organization.url.description
+        </div>
       </div>
-    </div>
-    <div
-      className="modal-field"
-    >
-      <label
-        htmlFor="organization-url"
-      >
-        organization.url
-      </label>
-      <input
-        disabled={false}
-        id="organization-url"
-        maxLength="256"
-        name="url"
-        onChange={[Function]}
-        type="text"
-        value="foo-url"
-      />
       <div
-        className="modal-field-description"
+        className="modal-field"
       >
-        organization.url.description
+        <button
+          disabled={false}
+          type="submit"
+        >
+          save
+        </button>
       </div>
-    </div>
-    <div
-      className="modal-field"
-    >
-      <button
-        disabled={false}
-        type="submit"
-      >
-        save
-      </button>
-    </div>
-  </form>
+    </form>
+  </div>
 </div>
 `;
 
@@ -299,135 +307,139 @@ exports[`smoke test 3`] = `
       organization.edit
     </h1>
   </header>
-  <form
-    onSubmit={[Function]}
+  <div
+    className="boxed-group boxed-group-inner"
   >
-    <div
-      className="modal-field"
+    <form
+      onSubmit={[Function]}
     >
-      <label
-        htmlFor="organization-name"
-      >
-        organization.name
-        <em
-          className="mandatory"
-        >
-          *
-        </em>
-      </label>
-      <input
-        disabled={true}
-        id="organization-name"
-        maxLength="64"
-        name="name"
-        onChange={[Function]}
-        required={true}
-        type="text"
-        value="New Foo"
-      />
       <div
-        className="modal-field-description"
+        className="modal-field"
       >
-        organization.name.description
+        <label
+          htmlFor="organization-name"
+        >
+          organization.name
+          <em
+            className="mandatory"
+          >
+            *
+          </em>
+        </label>
+        <input
+          disabled={true}
+          id="organization-name"
+          maxLength="64"
+          name="name"
+          onChange={[Function]}
+          required={true}
+          type="text"
+          value="New Foo"
+        />
+        <div
+          className="modal-field-description"
+        >
+          organization.name.description
+        </div>
       </div>
-    </div>
-    <div
-      className="modal-field"
-    >
-      <label
-        htmlFor="organization-avatar"
-      >
-        organization.avatar
-      </label>
-      <input
-        disabled={true}
-        id="organization-avatar"
-        maxLength="256"
-        name="avatar"
-        onChange={[Function]}
-        type="text"
-        value="foo-avatar"
-      />
       <div
-        className="modal-field-description"
+        className="modal-field"
       >
-        organization.avatar.description
+        <label
+          htmlFor="organization-avatar"
+        >
+          organization.avatar
+        </label>
+        <input
+          disabled={true}
+          id="organization-avatar"
+          maxLength="256"
+          name="avatar"
+          onChange={[Function]}
+          type="text"
+          value="foo-avatar"
+        />
+        <div
+          className="modal-field-description"
+        >
+          organization.avatar.description
+        </div>
+        <div
+          className="spacer-top spacer-bottom"
+        >
+          <div
+            className="little-spacer-bottom"
+          >
+            organization.avatar.preview
+            :
+          </div>
+          <img
+            alt=""
+            height={30}
+            src="foo-avatar-image"
+          />
+        </div>
       </div>
       <div
-        className="spacer-top spacer-bottom"
+        className="modal-field"
       >
+        <label
+          htmlFor="organization-description"
+        >
+          description
+        </label>
+        <textarea
+          disabled={true}
+          id="organization-description"
+          maxLength="256"
+          name="description"
+          onChange={[Function]}
+          rows="3"
+          value="foo-description"
+        />
         <div
-          className="little-spacer-bottom"
+          className="modal-field-description"
         >
-          organization.avatar.preview
-          :
+          organization.description.description
         </div>
-        <img
-          alt=""
-          height={30}
-          src="foo-avatar-image"
-        />
       </div>
-    </div>
-    <div
-      className="modal-field"
-    >
-      <label
-        htmlFor="organization-description"
-      >
-        description
-      </label>
-      <textarea
-        disabled={true}
-        id="organization-description"
-        maxLength="256"
-        name="description"
-        onChange={[Function]}
-        rows="3"
-        value="foo-description"
-      />
       <div
-        className="modal-field-description"
+        className="modal-field"
       >
-        organization.description.description
+        <label
+          htmlFor="organization-url"
+        >
+          organization.url
+        </label>
+        <input
+          disabled={true}
+          id="organization-url"
+          maxLength="256"
+          name="url"
+          onChange={[Function]}
+          type="text"
+          value="foo-url"
+        />
+        <div
+          className="modal-field-description"
+        >
+          organization.url.description
+        </div>
       </div>
-    </div>
-    <div
-      className="modal-field"
-    >
-      <label
-        htmlFor="organization-url"
-      >
-        organization.url
-      </label>
-      <input
-        disabled={true}
-        id="organization-url"
-        maxLength="256"
-        name="url"
-        onChange={[Function]}
-        type="text"
-        value="foo-url"
-      />
       <div
-        className="modal-field-description"
+        className="modal-field"
       >
-        organization.url.description
+        <button
+          disabled={true}
+          type="submit"
+        >
+          save
+        </button>
+        <i
+          className="spinner spacer-left"
+        />
       </div>
-    </div>
-    <div
-      className="modal-field"
-    >
-      <button
-        disabled={true}
-        type="submit"
-      >
-        save
-      </button>
-      <i
-        className="spinner spacer-left"
-      />
-    </div>
-  </form>
+    </form>
+  </div>
 </div>
 `;
index 077cc4a67ee67c89b3928ff3ede73b0864c6fd2c..eb49059477fdb50af04f73c8090c59ea3ea655ef 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { Organization } from '../../../app/types';
+import { connect } from 'react-redux';
+import { Organization, HomePageType } from '../../../app/types';
 import HomePageSelect from '../../../components/controls/HomePageSelect';
 import { translate } from '../../../helpers/l10n';
+import { getGlobalSettingValue } from '../../../store/rootReducer';
 
-interface Props {
+interface StateProps {
+  onSonarCloud: boolean;
+}
+
+interface Props extends StateProps {
   organization: Organization;
 }
 
-export default function OrganizationNavigationMeta({ organization }: Props) {
+export function OrganizationNavigationMeta({ onSonarCloud, organization }: Props) {
   return (
     <div className="navbar-context-meta">
       {organization.url != null && (
@@ -41,9 +47,23 @@ export default function OrganizationNavigationMeta({ organization }: Props) {
       <div className="text-muted">
         <strong>{translate('organization.key')}:</strong> {organization.key}
       </div>
-      <div className="navbar-context-meta-secondary">
-        <HomePageSelect currentPage={{ type: 'organization', key: organization.key }} />
-      </div>
+      {onSonarCloud && (
+        <div className="navbar-context-meta-secondary">
+          <HomePageSelect
+            currentPage={{ type: HomePageType.Organization, parameter: organization.key }}
+          />
+        </div>
+      )}
     </div>
   );
 }
+
+const mapStateToProps = (state: any): StateProps => {
+  const sonarCloudSetting = getGlobalSettingValue(state, 'sonar.sonarcloud.enabled');
+
+  return {
+    onSonarCloud: Boolean(sonarCloudSetting && sonarCloudSetting.value === 'true')
+  };
+};
+
+export default connect(mapStateToProps)(OrganizationNavigationMeta);
index 5b69f2c48535d6484b52d69571be1099d0b9e322..ef2d8fca1ddd3856e4f9b8c05f402332fa57ceaa 100644 (file)
  */
 import * as React from 'react';
 import { shallow } from 'enzyme';
-import OrganizationNavigationMeta from '../OrganizationNavigationMeta';
+import { OrganizationNavigationMeta } from '../OrganizationNavigationMeta';
 import { Visibility } from '../../../../app/types';
 
 it('renders', () => {
   expect(
     shallow(
       <OrganizationNavigationMeta
+        onSonarCloud={true}
         organization={{
           key: 'foo',
           name: 'Foo',
index eb04cc431fe1023942caa70cbef67a9ca1c056ac..07512698807af418de4f15d6ac0473149ce5b8f4 100644 (file)
@@ -14,7 +14,7 @@ exports[`render 1`] = `
       }
     }
   />
-  <OrganizationNavigationMeta
+  <Connect(OrganizationNavigationMeta)
     organization={
       Object {
         "key": "foo",
index bdd7594127b7b75002f114847d2032dd1081d4bf..207f8fe1faf27d4656e89e075940e0a249897c9a 100644 (file)
@@ -20,8 +20,8 @@ exports[`renders 1`] = `
     <Connect(HomePageSelect)
       currentPage={
         Object {
-          "key": "foo",
-          "type": "organization",
+          "parameter": "foo",
+          "type": "ORGANIZATION",
         }
       }
     />
index d5cb1b15caf36a9bb0fa5216f8116f0b9c70d4e0..16d88755c47fb1f9ebd700731c06b5a6d506750d 100644 (file)
@@ -68,10 +68,6 @@ export default class OverviewApp extends React.PureComponent {
 
   componentDidMount() {
     this.mounted = true;
-    const domElement = document.querySelector('html');
-    if (domElement) {
-      domElement.classList.add('dashboard-page');
-    }
     this.loadMeasures().then(this.loadHistory);
   }
 
@@ -86,10 +82,6 @@ export default class OverviewApp extends React.PureComponent {
 
   componentWillUnmount() {
     this.mounted = false;
-    const domElement = document.querySelector('html');
-    if (domElement) {
-      domElement.classList.remove('dashboard-page');
-    }
   }
 
   loadMeasures() {
index 9448e9d579726225094eb65c8af22ca0bb581b22..97182a9b41cad6dd616c2426b9892f039686e5a6 100644 (file)
@@ -44,10 +44,12 @@ export default class List extends React.PureComponent {
     ));
 
     return (
-      <table id="permission-templates" className="data zebra permissions-table">
-        <ListHeader organization={this.props.organization} permissions={this.props.permissions} />
-        <tbody>{permissionTemplates}</tbody>
-      </table>
+      <div className="boxed-group boxed-group-inner">
+        <table id="permission-templates" className="data zebra permissions-table">
+          <ListHeader organization={this.props.organization} permissions={this.props.permissions} />
+          <tbody>{permissionTemplates}</tbody>
+        </table>
+      </div>
     );
   }
 }
index 189351ba346b7f2155cb1a531854168d3dd71f4e..798e2156ceb58d6469cd0be32f2f04381bb35902 100644 (file)
@@ -92,14 +92,16 @@ export default class HoldersList extends React.PureComponent<Props> {
     ));
 
     return (
-      <table className="data zebra permissions-table">
-        {this.renderTableHeader()}
-        <tbody>
-          {users.length === 0 && groups.length === 0 && this.renderEmpty()}
-          {users}
-          {groups}
-        </tbody>
-      </table>
+      <div className="boxed-group boxed-group-inner">
+        <table className="data zebra permissions-table">
+          {this.renderTableHeader()}
+          <tbody>
+            {users.length === 0 && groups.length === 0 && this.renderEmpty()}
+            {users}
+            {groups}
+          </tbody>
+        </table>
+      </div>
     );
   }
 }
index 9f1ee5f2a76fd56a0102a63f72d32232bdf6f0c3..f4074fa61a1ae4b4bc9e4f10bae519ef5800822f 100644 (file)
@@ -50,10 +50,6 @@ export default class App extends React.PureComponent<Props, State> {
 
   componentDidMount() {
     this.mounted = true;
-    const html = document.querySelector('html');
-    if (html) {
-      html.classList.add('dashboard-page');
-    }
     this.fetchData();
   }
 
@@ -65,10 +61,6 @@ export default class App extends React.PureComponent<Props, State> {
 
   componentWillUnmount() {
     this.mounted = false;
-    const html = document.querySelector('html');
-    if (html) {
-      html.classList.remove('dashboard-page');
-    }
   }
 
   fetchData() {
index e94839f6b2eba6d4a725b9f5bac877a3c4b9ab0f..0288b2e9fb5d7aaa8c6d035ff031e88bf5dfbe2b 100644 (file)
@@ -101,7 +101,7 @@ class Key extends React.PureComponent {
         )}
 
         {hasModules && (
-          <div>
+          <div className="boxed-group boxed-group-inner">
             <div className="big-spacer-bottom">
               <ul className="tabs">
                 <li>
index 580413d4d1f20d072e6c6f437139617358fa08a7..453c93ad44a3cfe58c421976be635009fa8e6891 100644 (file)
@@ -54,10 +54,12 @@ export default class Table extends React.PureComponent {
     ));
 
     return (
-      <table id="project-links" className="data zebra">
-        {this.renderHeader()}
-        <tbody>{linkRows}</tbody>
-      </table>
+      <div className="boxed-group boxed-group-inner">
+        <table id="project-links" className="data zebra">
+          {this.renderHeader()}
+          <tbody>{linkRows}</tbody>
+        </table>
+      </div>
     );
   }
 }
index 080842999d868acfa4cf48943be795b3f13212c0..70c931e1cf773af79d18c031d8338442d580ff74 100644 (file)
@@ -94,8 +94,6 @@ export default class ProjectActivityAppContainer extends React.PureComponent {
 
   componentDidMount() {
     this.mounted = true;
-    const elem = document.querySelector('html');
-    elem && elem.classList.add('dashboard-page');
     if (this.shouldRedirect()) {
       const newQuery = { ...this.state.query, graph: getGraph() };
       if (isCustomGraph(newQuery.graph)) {
@@ -129,8 +127,6 @@ export default class ProjectActivityAppContainer extends React.PureComponent {
 
   componentWillUnmount() {
     this.mounted = false;
-    const elem = document.querySelector('html');
-    elem && elem.classList.remove('dashboard-page');
   }
 
   addCustomEvent = (analysis /*: string */, name /*: string */, category /*: ?string */) =>
index 19092afe65523a7fdfb26c1045215f8dceff21fd..f11038f2b1817506cb8f16fba4584b2121366b9d 100644 (file)
@@ -119,26 +119,30 @@ export default class App extends React.PureComponent<Props, State> {
           {this.renderBranchLifeTime()}
         </header>
 
-        <table className="data zebra zebra-hover">
-          <thead>
-            <tr>
-              <th>{translate('branch')}</th>
-              <th className="thin nowrap text-right">{translate('status')}</th>
-              <th className="thin nowrap text-right">{translate('branches.last_analysis_date')}</th>
-              <th className="thin nowrap text-right">{translate('actions')}</th>
-            </tr>
-          </thead>
-          <tbody>
-            {sortBranchesAsTree(branches).map(branch => (
-              <BranchRow
-                branch={branch}
-                component={component.key}
-                key={branch.name}
-                onChange={onBranchesChange}
-              />
-            ))}
-          </tbody>
-        </table>
+        <div className="boxed-group boxed-group-inner">
+          <table className="data zebra zebra-hover">
+            <thead>
+              <tr>
+                <th>{translate('branch')}</th>
+                <th className="thin nowrap text-right">{translate('status')}</th>
+                <th className="thin nowrap text-right">
+                  {translate('branches.last_analysis_date')}
+                </th>
+                <th className="thin nowrap text-right">{translate('actions')}</th>
+              </tr>
+            </thead>
+            <tbody>
+              {sortBranchesAsTree(branches).map(branch => (
+                <BranchRow
+                  branch={branch}
+                  component={component.key}
+                  key={branch.name}
+                  onChange={onBranchesChange}
+                />
+              ))}
+            </tbody>
+          </table>
+        </div>
       </div>
     );
   }
index 4914db6ffce4c74789cfbae889a427e3d14a9a66..e9d41bf777e18a22952462f0e51285fe25dbc97d 100644 (file)
@@ -41,69 +41,73 @@ exports[`renders sorted list of branches 1`] = `
       />
     </p>
   </header>
-  <table
-    className="data zebra zebra-hover"
+  <div
+    className="boxed-group boxed-group-inner"
   >
-    <thead>
-      <tr>
-        <th>
-          branch
-        </th>
-        <th
-          className="thin nowrap text-right"
-        >
-          status
-        </th>
-        <th
-          className="thin nowrap text-right"
-        >
-          branches.last_analysis_date
-        </th>
-        <th
-          className="thin nowrap text-right"
-        >
-          actions
-        </th>
-      </tr>
-    </thead>
-    <tbody>
-      <BranchRow
-        branch={
-          Object {
-            "isMain": true,
-            "name": "master",
+    <table
+      className="data zebra zebra-hover"
+    >
+      <thead>
+        <tr>
+          <th>
+            branch
+          </th>
+          <th
+            className="thin nowrap text-right"
+          >
+            status
+          </th>
+          <th
+            className="thin nowrap text-right"
+          >
+            branches.last_analysis_date
+          </th>
+          <th
+            className="thin nowrap text-right"
+          >
+            actions
+          </th>
+        </tr>
+      </thead>
+      <tbody>
+        <BranchRow
+          branch={
+            Object {
+              "isMain": true,
+              "name": "master",
+            }
           }
-        }
-        component="foo"
-        key="master"
-        onChange={[Function]}
-      />
-      <BranchRow
-        branch={
-          Object {
-            "isMain": false,
-            "mergeBranch": "master",
-            "name": "branch-1.0",
-            "type": "SHORT",
+          component="foo"
+          key="master"
+          onChange={[Function]}
+        />
+        <BranchRow
+          branch={
+            Object {
+              "isMain": false,
+              "mergeBranch": "master",
+              "name": "branch-1.0",
+              "type": "SHORT",
+            }
           }
-        }
-        component="foo"
-        key="branch-1.0"
-        onChange={[Function]}
-      />
-      <BranchRow
-        branch={
-          Object {
-            "isMain": false,
-            "name": "branch-1.0",
-            "type": "LONG",
+          component="foo"
+          key="branch-1.0"
+          onChange={[Function]}
+        />
+        <BranchRow
+          branch={
+            Object {
+              "isMain": false,
+              "name": "branch-1.0",
+              "type": "LONG",
+            }
           }
-        }
-        component="foo"
-        key="branch-1.0"
-        onChange={[Function]}
-      />
-    </tbody>
-  </table>
+          component="foo"
+          key="branch-1.0"
+          onChange={[Function]}
+        />
+      </tbody>
+    </table>
+  </div>
 </div>
 `;
index 88f51601856bdd5f0e08770e5284e9b76441d389..1e6374406ece2a89e7db60b8cc4af3374f68a5c6 100644 (file)
@@ -44,16 +44,18 @@ export default function Table(props: Props) {
   ));
 
   return (
-    <table className="data zebra">
-      <thead>
-        <tr>
-          <th className="thin nowrap">{translate('language')}</th>
-          <th className="thin nowrap">{translate('quality_profile')}</th>
-          {/* keep one empty cell for the spinner */}
-          <th>&nbsp;</th>
-        </tr>
-      </thead>
-      <tbody>{profileRows}</tbody>
-    </table>
+    <div className="boxed-group boxed-group-inner">
+      <table className="data zebra">
+        <thead>
+          <tr>
+            <th className="thin nowrap">{translate('language')}</th>
+            <th className="thin nowrap">{translate('quality_profile')}</th>
+            {/* keep one empty cell for the spinner */}
+            <th>&nbsp;</th>
+          </tr>
+        </thead>
+        <tbody>{profileRows}</tbody>
+      </table>
+    </div>
   );
 }
index 5ac9cea89da7a41769ea27242d3b9ce328890c97..b77fae2826b4dbd261f5437b2159482ba8acd782 100644 (file)
@@ -1,32 +1,65 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`renders 1`] = `
-<table
-  className="data zebra"
+<div
+  className="boxed-group boxed-group-inner"
 >
-  <thead>
-    <tr>
-      <th
-        className="thin nowrap"
-      >
-        language
-      </th>
-      <th
-        className="thin nowrap"
-      >
-        quality_profile
-      </th>
-      <th>
-        Â 
-      </th>
-    </tr>
-  </thead>
-  <tbody>
-    <ProfileRow
-      key="java"
-      onChangeProfile={[Function]}
-      possibleProfiles={
-        Array [
+  <table
+    className="data zebra"
+  >
+    <thead>
+      <tr>
+        <th
+          className="thin nowrap"
+        >
+          language
+        </th>
+        <th
+          className="thin nowrap"
+        >
+          quality_profile
+        </th>
+        <th>
+          Â 
+        </th>
+      </tr>
+    </thead>
+    <tbody>
+      <ProfileRow
+        key="java"
+        onChangeProfile={[Function]}
+        possibleProfiles={
+          Array [
+            Object {
+              "activeDeprecatedRuleCount": 0,
+              "activeRuleCount": 17,
+              "key": "foo-java",
+              "language": "java",
+              "languageName": "java",
+              "name": "foo-java",
+              "organization": "org",
+            },
+            Object {
+              "activeDeprecatedRuleCount": 0,
+              "activeRuleCount": 17,
+              "key": "bar-java",
+              "language": "java",
+              "languageName": "java",
+              "name": "bar-java",
+              "organization": "org",
+            },
+            Object {
+              "activeDeprecatedRuleCount": 0,
+              "activeRuleCount": 17,
+              "key": "baz-java",
+              "language": "java",
+              "languageName": "java",
+              "name": "baz-java",
+              "organization": "org",
+            },
+          ]
+        }
+        profile={
           Object {
             "activeDeprecatedRuleCount": 0,
             "activeRuleCount": 17,
@@ -35,44 +68,26 @@ exports[`renders 1`] = `
             "languageName": "java",
             "name": "foo-java",
             "organization": "org",
-          },
-          Object {
-            "activeDeprecatedRuleCount": 0,
-            "activeRuleCount": 17,
-            "key": "bar-java",
-            "language": "java",
-            "languageName": "java",
-            "name": "bar-java",
-            "organization": "org",
-          },
-          Object {
-            "activeDeprecatedRuleCount": 0,
-            "activeRuleCount": 17,
-            "key": "baz-java",
-            "language": "java",
-            "languageName": "java",
-            "name": "baz-java",
-            "organization": "org",
-          },
-        ]
-      }
-      profile={
-        Object {
-          "activeDeprecatedRuleCount": 0,
-          "activeRuleCount": 17,
-          "key": "foo-java",
-          "language": "java",
-          "languageName": "java",
-          "name": "foo-java",
-          "organization": "org",
+          }
+        }
+      />
+      <ProfileRow
+        key="js"
+        onChangeProfile={[Function]}
+        possibleProfiles={
+          Array [
+            Object {
+              "activeDeprecatedRuleCount": 0,
+              "activeRuleCount": 17,
+              "key": "foo-js",
+              "language": "js",
+              "languageName": "js",
+              "name": "foo-js",
+              "organization": "org",
+            },
+          ]
         }
-      }
-    />
-    <ProfileRow
-      key="js"
-      onChangeProfile={[Function]}
-      possibleProfiles={
-        Array [
+        profile={
           Object {
             "activeDeprecatedRuleCount": 0,
             "activeRuleCount": 17,
@@ -81,21 +96,10 @@ exports[`renders 1`] = `
             "languageName": "js",
             "name": "foo-js",
             "organization": "org",
-          },
-        ]
-      }
-      profile={
-        Object {
-          "activeDeprecatedRuleCount": 0,
-          "activeRuleCount": 17,
-          "key": "foo-js",
-          "language": "js",
-          "languageName": "js",
-          "name": "foo-js",
-          "organization": "org",
+          }
         }
-      }
-    />
-  </tbody>
-</table>
+      />
+    </tbody>
+  </table>
+</div>
 `;
index 15dc8f4e7caf6aced553a6275660c0df6e141ff6..b8c83faa629d5a4dad0626ff65fe891e13024063 100644 (file)
@@ -70,11 +70,6 @@ export default class AllProjects extends React.PureComponent<Props, State> {
   componentDidMount() {
     this.mounted = true;
 
-    const html = document.querySelector('html');
-    if (html) {
-      html.classList.add('dashboard-page');
-    }
-
     if (this.props.isFavorite && !isLoggedIn(this.props.currentUser)) {
       handleRequiredAuthentication();
       return;
@@ -95,11 +90,6 @@ export default class AllProjects extends React.PureComponent<Props, State> {
   componentWillUnmount() {
     this.mounted = false;
 
-    const html = document.querySelector('html');
-    if (html) {
-      html.classList.remove('dashboard-page');
-    }
-
     const footer = document.getElementById('footer');
     if (footer) {
       footer.classList.remove('page-footer-with-sidebar');
index 3f347339ad02c1ec3557e923f0d4a93c6e1e748e..0b7666c981649d24d7d552537aaa588d9dbd69ae 100644 (file)
@@ -23,7 +23,7 @@ import SearchFilterContainer from '../filters/SearchFilterContainer';
 import Tooltip from '../../../components/controls/Tooltip';
 import PerspectiveSelect from './PerspectiveSelect';
 import ProjectsSortingSelect from './ProjectsSortingSelect';
-import { CurrentUser, isLoggedIn } from '../../../app/types';
+import { CurrentUser, isLoggedIn, HomePageType } from '../../../app/types';
 import HomePageSelect from '../../../components/controls/HomePageSelect';
 import { translate } from '../../../helpers/l10n';
 import { RawQuery } from '../../../helpers/query';
@@ -101,12 +101,12 @@ export default function PageHeader(props: Props) {
         )}
       </div>
 
-      {props.onSonarCloud &&
-        isLoggedIn(currentUser) &&
-        props.isFavorite &&
-        !props.organization && (
-          <HomePageSelect className="huge-spacer-left" currentPage={{ type: 'my-projects' }} />
-        )}
+      {props.isFavorite && (
+        <HomePageSelect
+          className="huge-spacer-left"
+          currentPage={{ type: HomePageType.MyProjects }}
+        />
+      )}
     </header>
   );
 }
index 8a5b4f6d216f810527ece956b906487e988611d5..3c79acd0113d01227b73fcc40fe89bd1e82a9adb 100644 (file)
@@ -49,6 +49,7 @@ export default function ProjectCardLeak({ organization, project }: Props) {
             className="spacer-right"
             component={project.key}
             favorite={project.isFavorite}
+            qualifier="TRK"
           />
         )}
         <h2 className="project-card-name">
index d8ecbe86f66ee5d8bb146933b09a829f02a68fc3..79926daf0270a446d78cdc0981dc41418ec0dbec 100644 (file)
@@ -48,6 +48,7 @@ export default function ProjectCardOverall({ organization, project }: Props) {
             className="spacer-right"
             component={project.key}
             favorite={project.isFavorite}
+            qualifier="TRK"
           />
         )}
         <h2 className="project-card-name">
index d33996850666f756e9b2d659ff8497a8357b37f2..81a0bb7102fecb545ff0e8797a6d2c53685e8dce 100644 (file)
@@ -50,32 +50,34 @@ export default class Projects extends React.PureComponent<Props> {
 
   render() {
     return (
-      <table
-        className={classNames('data', 'zebra', { 'new-loading': !this.props.ready })}
-        id="projects-management-page-projects">
-        <thead>
-          <tr>
-            <th />
-            <th>{translate('name')}</th>
-            <th />
-            <th>{translate('key')}</th>
-            <th className="thin nowrap text-right">{translate('last_analysis')}</th>
-            <th />
-          </tr>
-        </thead>
-        <tbody>
-          {this.props.projects.map(project => (
-            <ProjectRow
-              currentUser={this.props.currentUser}
-              key={project.key}
-              onApplyTemplate={this.handleApplyTemplate}
-              onProjectCheck={this.onProjectCheck}
-              project={project}
-              selected={this.props.selection.includes(project.key)}
-            />
-          ))}
-        </tbody>
-      </table>
+      <div className="boxed-group boxed-group-inner">
+        <table
+          className={classNames('data', 'zebra', { 'new-loading': !this.props.ready })}
+          id="projects-management-page-projects">
+          <thead>
+            <tr>
+              <th />
+              <th>{translate('name')}</th>
+              <th />
+              <th>{translate('key')}</th>
+              <th className="thin nowrap text-right">{translate('last_analysis')}</th>
+              <th />
+            </tr>
+          </thead>
+          <tbody>
+            {this.props.projects.map(project => (
+              <ProjectRow
+                currentUser={this.props.currentUser}
+                key={project.key}
+                onApplyTemplate={this.handleApplyTemplate}
+                onProjectCheck={this.onProjectCheck}
+                project={project}
+                selected={this.props.selection.includes(project.key)}
+              />
+            ))}
+          </tbody>
+        </table>
+      </div>
     );
   }
 }
index 7a3728a28ad140d0e73a730153e143134c921b90..1503fed2c4e9a04c90ccf2d2931cfa4db29fbc64 100644 (file)
@@ -1,67 +1,71 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`renders list of projects 1`] = `
-<table
-  className="data zebra new-loading"
-  id="projects-management-page-projects"
+<div
+  className="boxed-group boxed-group-inner"
 >
-  <thead>
-    <tr>
-      <th />
-      <th>
-        name
-      </th>
-      <th />
-      <th>
-        key
-      </th>
-      <th
-        className="thin nowrap text-right"
-      >
-        last_analysis
-      </th>
-      <th />
-    </tr>
-  </thead>
-  <tbody>
-    <ProjectRow
-      currentUser={
-        Object {
-          "login": "foo",
+  <table
+    className="data zebra new-loading"
+    id="projects-management-page-projects"
+  >
+    <thead>
+      <tr>
+        <th />
+        <th>
+          name
+        </th>
+        <th />
+        <th>
+          key
+        </th>
+        <th
+          className="thin nowrap text-right"
+        >
+          last_analysis
+        </th>
+        <th />
+      </tr>
+    </thead>
+    <tbody>
+      <ProjectRow
+        currentUser={
+          Object {
+            "login": "foo",
+          }
         }
-      }
-      key="a"
-      onApplyTemplate={[Function]}
-      onProjectCheck={[Function]}
-      project={
-        Object {
-          "key": "a",
-          "name": "A",
-          "qualifier": "TRK",
-          "visibility": "public",
+        key="a"
+        onApplyTemplate={[Function]}
+        onProjectCheck={[Function]}
+        project={
+          Object {
+            "key": "a",
+            "name": "A",
+            "qualifier": "TRK",
+            "visibility": "public",
+          }
         }
-      }
-      selected={true}
-    />
-    <ProjectRow
-      currentUser={
-        Object {
-          "login": "foo",
+        selected={true}
+      />
+      <ProjectRow
+        currentUser={
+          Object {
+            "login": "foo",
+          }
         }
-      }
-      key="b"
-      onApplyTemplate={[Function]}
-      onProjectCheck={[Function]}
-      project={
-        Object {
-          "key": "b",
-          "name": "B",
-          "qualifier": "TRK",
-          "visibility": "public",
+        key="b"
+        onApplyTemplate={[Function]}
+        onProjectCheck={[Function]}
+        project={
+          Object {
+            "key": "b",
+            "name": "B",
+            "qualifier": "TRK",
+            "visibility": "public",
+          }
         }
-      }
-      selected={false}
-    />
-  </tbody>
-</table>
+        selected={false}
+      />
+    </tbody>
+  </table>
+</div>
 `;
index 0ec8d28794d832df7f060fd01a367ada29e4122f..4386f8cca8c7b3facee6db5c7446a3cec314aa09 100644 (file)
@@ -37,6 +37,8 @@ export default class QualityGatesApp extends Component {
 
   componentDidMount() {
     this.fetchQualityGates();
+    // $FlowFixMe
+    document.body.classList.add('white-page');
     const footer = document.getElementById('footer');
     if (footer) {
       footer.classList.add('page-footer-with-sidebar');
@@ -44,6 +46,8 @@ export default class QualityGatesApp extends Component {
   }
 
   componentWillUnmount() {
+    // $FlowFixMe
+    document.body.classList.remove('white-page');
     const footer = document.getElementById('footer');
     if (footer) {
       footer.classList.remove('page-footer-with-sidebar');
index 8f8f056be4e5f31ea2acd6f96179385f1597b0ca..fda9bb7ed26ed3e7db83286aa91e5aa154ec2685 100644 (file)
@@ -43,13 +43,6 @@ export default class App extends React.PureComponent<Props, State> {
   mounted: boolean;
   state: State = { loading: true };
 
-  componentWillMount() {
-    const html = document.querySelector('html');
-    if (html) {
-      html.classList.add('dashboard-page');
-    }
-  }
-
   componentDidMount() {
     this.mounted = true;
     this.loadData();
@@ -57,10 +50,6 @@ export default class App extends React.PureComponent<Props, State> {
 
   componentWillUnmount() {
     this.mounted = false;
-    const html = document.querySelector('html');
-    if (html) {
-      html.classList.remove('dashboard-page');
-    }
   }
 
   fetchProfiles() {
index 7df88bb9fb79aa44976eb4c74acb3a932b27b9bf..a2c05ac3e3f628d11aead29ae2335c643fa0375b 100644 (file)
@@ -47,10 +47,6 @@ export default class App extends React.PureComponent {
   state /*: State */ = { loaded: false };
 
   componentDidMount() {
-    const html = document.querySelector('html');
-    if (html) {
-      html.classList.add('dashboard-page');
-    }
     const componentKey = this.props.component ? this.props.component.key : null;
     this.props.fetchSettings(componentKey).then(() => this.setState({ loaded: true }));
   }
@@ -62,13 +58,6 @@ export default class App extends React.PureComponent {
     }
   }
 
-  componentWillUnmount() {
-    const html = document.querySelector('html');
-    if (html) {
-      html.classList.remove('dashboard-page');
-    }
-  }
-
   render() {
     if (!this.state.loaded) {
       return null;
index a81a9441af36a345bf66eccae143f21d625ae2c6..9609acd0a9b28381e416472fdaff12d4607df948 100644 (file)
@@ -38,31 +38,33 @@ export default function UsersList({
   users
 }: Props) {
   return (
-    <table id="users-list" className="data zebra">
-      <thead>
-        <tr>
-          <th />
-          <th className="nowrap" />
-          <th className="nowrap">{translate('my_profile.scm_accounts')}</th>
-          {!organizationsEnabled && <th className="nowrap">{translate('my_profile.groups')}</th>}
-          <th className="nowrap">{translate('users.tokens')}</th>
-          <th className="nowrap">&nbsp;</th>
-        </tr>
-      </thead>
-      <tbody>
-        {users.map(user => (
-          <UserListItem
-            identityProvider={identityProviders.find(
-              provider => user.externalProvider === provider.key
-            )}
-            isCurrentUser={currentUser.isLoggedIn && currentUser.login === user.login}
-            key={user.login}
-            onUpdateUsers={onUpdateUsers}
-            organizationsEnabled={organizationsEnabled}
-            user={user}
-          />
-        ))}
-      </tbody>
-    </table>
+    <div className="boxed-group boxed-group-inner">
+      <table id="users-list" className="data zebra">
+        <thead>
+          <tr>
+            <th />
+            <th className="nowrap" />
+            <th className="nowrap">{translate('my_profile.scm_accounts')}</th>
+            {!organizationsEnabled && <th className="nowrap">{translate('my_profile.groups')}</th>}
+            <th className="nowrap">{translate('users.tokens')}</th>
+            <th className="nowrap">&nbsp;</th>
+          </tr>
+        </thead>
+        <tbody>
+          {users.map(user => (
+            <UserListItem
+              identityProvider={identityProviders.find(
+                provider => user.externalProvider === provider.key
+              )}
+              isCurrentUser={currentUser.isLoggedIn && currentUser.login === user.login}
+              key={user.login}
+              onUpdateUsers={onUpdateUsers}
+              organizationsEnabled={organizationsEnabled}
+              user={user}
+            />
+          ))}
+        </tbody>
+      </table>
+    </div>
   );
 }
index 4b732ab0f3699d5438f82321207cb6a2c4160170..64c13cf48e9db06e8c589069ab9cb730727e8478 100644 (file)
@@ -1,64 +1,68 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should render correctly 1`] = `
-<table
-  className="data zebra"
-  id="users-list"
+<div
+  className="boxed-group boxed-group-inner"
 >
-  <thead>
-    <tr>
-      <th />
-      <th
-        className="nowrap"
-      />
-      <th
-        className="nowrap"
-      >
-        my_profile.scm_accounts
-      </th>
-      <th
-        className="nowrap"
-      >
-        users.tokens
-      </th>
-      <th
-        className="nowrap"
-      >
-        Â 
-      </th>
-    </tr>
-  </thead>
-  <tbody>
-    <UserListItem
-      isCurrentUser={true}
-      key="luke"
-      onUpdateUsers={[Function]}
-      organizationsEnabled={true}
-      user={
-        Object {
-          "active": true,
-          "local": false,
-          "login": "luke",
-          "name": "Luke",
-          "scmAccounts": Array [],
+  <table
+    className="data zebra"
+    id="users-list"
+  >
+    <thead>
+      <tr>
+        <th />
+        <th
+          className="nowrap"
+        />
+        <th
+          className="nowrap"
+        >
+          my_profile.scm_accounts
+        </th>
+        <th
+          className="nowrap"
+        >
+          users.tokens
+        </th>
+        <th
+          className="nowrap"
+        >
+          Â 
+        </th>
+      </tr>
+    </thead>
+    <tbody>
+      <UserListItem
+        isCurrentUser={true}
+        key="luke"
+        onUpdateUsers={[Function]}
+        organizationsEnabled={true}
+        user={
+          Object {
+            "active": true,
+            "local": false,
+            "login": "luke",
+            "name": "Luke",
+            "scmAccounts": Array [],
+          }
         }
-      }
-    />
-    <UserListItem
-      isCurrentUser={false}
-      key="obi"
-      onUpdateUsers={[Function]}
-      organizationsEnabled={true}
-      user={
-        Object {
-          "active": true,
-          "local": false,
-          "login": "obi",
-          "name": "One",
-          "scmAccounts": Array [],
+      />
+      <UserListItem
+        isCurrentUser={false}
+        key="obi"
+        onUpdateUsers={[Function]}
+        organizationsEnabled={true}
+        user={
+          Object {
+            "active": true,
+            "local": false,
+            "login": "obi",
+            "name": "One",
+            "scmAccounts": Array [],
+          }
         }
-      }
-    />
-  </tbody>
-</table>
+      />
+    </tbody>
+  </table>
+</div>
 `;
index f0b5eed39e69606d465841df6d4ecc1ebd76b91b..5126f39b525c626060de20e2de7fd1b8bd8e32f7 100644 (file)
@@ -120,7 +120,7 @@ export default class Action extends React.PureComponent<Props, State> {
       );
     }
 
-    return <hr />;
+    return null;
   }
 
   render() {
@@ -130,8 +130,8 @@ export default class Action extends React.PureComponent<Props, State> {
     const actionKey = getActionKey(domain.path, action.key);
 
     return (
-      <div id={actionKey} className="web-api-action">
-        <header className="web-api-action-header">
+      <div id={actionKey} className="boxed-group">
+        <header className="web-api-action-header boxed-group-header">
           <Link
             to={{ pathname: '/web_api/' + actionKey }}
             className="spacer-right link-no-underline">
@@ -161,26 +161,28 @@ export default class Action extends React.PureComponent<Props, State> {
           )}
         </header>
 
-        <div
-          className="web-api-action-description markdown"
-          dangerouslySetInnerHTML={{ __html: action.description }}
-        />
+        <div className="boxed-group-inner">
+          <div
+            className="web-api-action-description markdown"
+            dangerouslySetInnerHTML={{ __html: action.description }}
+          />
 
-        {this.renderTabs()}
+          {this.renderTabs()}
 
-        {showParams &&
-          action.params && (
-            <Params
-              params={action.params}
-              showDeprecated={this.props.showDeprecated}
-              showInternal={this.props.showInternal}
-            />
-          )}
+          {showParams &&
+            action.params && (
+              <Params
+                params={action.params}
+                showDeprecated={this.props.showDeprecated}
+                showInternal={this.props.showInternal}
+              />
+            )}
 
-        {showResponse &&
-          action.hasResponseExample && <ResponseExample domain={domain} action={action} />}
+          {showResponse &&
+            action.hasResponseExample && <ResponseExample domain={domain} action={action} />}
 
-        {showChangelog && <ActionChangelog changelog={action.changelog} />}
+          {showChangelog && <ActionChangelog changelog={action.changelog} />}
+        </div>
       </div>
     );
   }
index 02cc7f1b708ce73239d120bc9aa8f2f1c1e4941b..ac543cd86e35ecadb3db0deeeeeb3c1cb29dd560 100644 (file)
@@ -91,11 +91,11 @@ exports[`should display the response example 1`] = `
 
 exports[`should render correctly 1`] = `
 <div
-  className="web-api-action"
+  className="boxed-group"
   id="foo/foo"
 >
   <header
-    className="web-api-action-header"
+    className="web-api-action-header boxed-group-header"
   >
     <Link
       className="spacer-right link-no-underline"
@@ -118,43 +118,47 @@ exports[`should render correctly 1`] = `
     </h3>
   </header>
   <div
-    className="web-api-action-description markdown"
-    dangerouslySetInnerHTML={
-      Object {
-        "__html": "Foo Desc",
-      }
-    }
-  />
-  <ul
-    className="web-api-action-actions tabs"
+    className="boxed-group-inner"
   >
-    <li>
-      <a
-        className=""
-        href="#"
-        onClick={[Function]}
-      >
-        api_documentation.parameters
-      </a>
-    </li>
-    <li>
-      <a
-        className=""
-        href="#"
-        onClick={[Function]}
-      >
-        api_documentation.response_example
-      </a>
-    </li>
-    <li>
-      <a
-        className=""
-        href="#"
-        onClick={[Function]}
-      >
-        api_documentation.changelog
-      </a>
-    </li>
-  </ul>
+    <div
+      className="web-api-action-description markdown"
+      dangerouslySetInnerHTML={
+        Object {
+          "__html": "Foo Desc",
+        }
+      }
+    />
+    <ul
+      className="web-api-action-actions tabs"
+    >
+      <li>
+        <a
+          className=""
+          href="#"
+          onClick={[Function]}
+        >
+          api_documentation.parameters
+        </a>
+      </li>
+      <li>
+        <a
+          className=""
+          href="#"
+          onClick={[Function]}
+        >
+          api_documentation.response_example
+        </a>
+      </li>
+      <li>
+        <a
+          className=""
+          href="#"
+          onClick={[Function]}
+        >
+          api_documentation.changelog
+        </a>
+      </li>
+    </ul>
+  </div>
 </div>
 `;
index c99b781ab07b96b34b157e3ab8fefb1541331491..ea4ec21da9dd58ceebb83154f2fcc7c222c8c711 100644 (file)
 }
 
 .web-api-domain-actions {
-}
-
-.web-api-action {
-  padding-top: 30px;
+  margin-top: calc(2 * var(--gridSize));
 }
 
 .web-api-action-title {
index 8c9498bec49957dca2752ddfb0b890ddba640a64..c69d07eb48109db7e39dc2daad8dd2f3daa66569 100644 (file)
@@ -20,7 +20,7 @@
 .source-viewer {
   width: 100%;
   min-height: 200px;
-  border: 1px solid var(--barBorderColor);
+  border: 1px solid var(--gray80);
   box-sizing: border-box;
   background-color: #fff;
   overflow-x: auto;
index ccfccb351f108a69db75bf0123dd2554a9da6b53..ac0a177c6a58fe8dbdc390b8a5a6931ad29b05d4 100644 (file)
@@ -1,5 +1,8 @@
 .branch-status {
+  display: flex;
+  align-items: center;
   min-width: 64px;
+  line-height: calc(2 * var(--gridSize));
   text-align: right;
 }
 
index bb9069bc2348334483860ad03179a033e78f5a32..e0e4fa55b1d684e53dbde6ab5fc30a69d935a6a4 100644 (file)
@@ -44,26 +44,26 @@ export default function BranchStatus({ branch, concise = false }: Props) {
     const indicatorColor = totalIssues > 0 ? 'red' : 'green';
 
     return concise ? (
-      <ul className="list-inline branch-status">
+      <ul className="branch-status">
         <li>{totalIssues}</li>
         <li className="spacer-left">
           <StatusIndicator color={indicatorColor} size="small" />
         </li>
       </ul>
     ) : (
-      <ul className="list-inline branch-status">
+      <ul className="branch-status">
         <li className="spacer-right">
           <StatusIndicator color={indicatorColor} size="small" />
         </li>
-        <li>
+        <li className="spacer-left">
           {branch.status.bugs}
           <BugIcon />
         </li>
-        <li>
+        <li className="spacer-left">
           {branch.status.vulnerabilities}
           <VulnerabilityIcon />
         </li>
-        <li>
+        <li className="spacer-left">
           {branch.status.codeSmells}
           <CodeSmellIcon />
         </li>
index 8833bab3ceb84c7aa6b627cc398a8d286a822eac..d648e77db49a45938cf6939227b6b93b1691e879 100644 (file)
@@ -16,7 +16,7 @@ exports[`renders status of long-living branches 2`] = `
 
 exports[`renders status of short-living branches 1`] = `
 <ul
-  className="list-inline branch-status"
+  className="branch-status"
 >
   <li
     className="spacer-right"
@@ -26,15 +26,21 @@ exports[`renders status of short-living branches 1`] = `
       size="small"
     />
   </li>
-  <li>
+  <li
+    className="spacer-left"
+  >
     0
     <BugIcon />
   </li>
-  <li>
+  <li
+    className="spacer-left"
+  >
     0
     <VulnerabilityIcon />
   </li>
-  <li>
+  <li
+    className="spacer-left"
+  >
     0
     <CodeSmellIcon />
   </li>
@@ -43,7 +49,7 @@ exports[`renders status of short-living branches 1`] = `
 
 exports[`renders status of short-living branches 2`] = `
 <ul
-  className="list-inline branch-status"
+  className="branch-status"
 >
   <li
     className="spacer-right"
@@ -53,15 +59,21 @@ exports[`renders status of short-living branches 2`] = `
       size="small"
     />
   </li>
-  <li>
+  <li
+    className="spacer-left"
+  >
     0
     <BugIcon />
   </li>
-  <li>
+  <li
+    className="spacer-left"
+  >
     0
     <VulnerabilityIcon />
   </li>
-  <li>
+  <li
+    className="spacer-left"
+  >
     1
     <CodeSmellIcon />
   </li>
@@ -70,7 +82,7 @@ exports[`renders status of short-living branches 2`] = `
 
 exports[`renders status of short-living branches 3`] = `
 <ul
-  className="list-inline branch-status"
+  className="branch-status"
 >
   <li
     className="spacer-right"
@@ -80,15 +92,21 @@ exports[`renders status of short-living branches 3`] = `
       size="small"
     />
   </li>
-  <li>
+  <li
+    className="spacer-left"
+  >
     7
     <BugIcon />
   </li>
-  <li>
+  <li
+    className="spacer-left"
+  >
     6
     <VulnerabilityIcon />
   </li>
-  <li>
+  <li
+    className="spacer-left"
+  >
     3
     <CodeSmellIcon />
   </li>
index fb43626c05cd52818161dcf2fed6a6918b8f030a..5ac11f9527a255cdb4630bfafb9d4fef7ac40597 100644 (file)
@@ -25,13 +25,13 @@ interface Props {
   className?: string;
   component: string;
   favorite: boolean;
+  qualifier: string;
 }
 
-export default function Favorite({ favorite, component, ...other }: Props) {
+export default function Favorite({ component, ...other }: Props) {
   return (
     <FavoriteBase
       {...other}
-      favorite={favorite}
       addFavorite={() => addFavorite(component)}
       removeFavorite={() => removeFavorite(component)}
     />
index f802e0885f7f3c1dce507a1f6250ad72f6144ed8..a1f738dcba376ab7909ee595686a5560a218eb82 100644 (file)
@@ -23,10 +23,11 @@ import Tooltip from './Tooltip';
 import FavoriteIcon from '../icons-components/FavoriteIcon';
 import { translate } from '../../helpers/l10n';
 
-interface Props {
+export interface Props {
   addFavorite: () => Promise<void>;
   className?: string;
   favorite: boolean;
+  qualifier: string;
   removeFavorite: () => Promise<void>;
 }
 
@@ -83,10 +84,10 @@ export default class FavoriteBase extends React.PureComponent<Props, State> {
 
   render() {
     const tooltip = this.state.favorite
-      ? translate('favorite.current')
-      : translate('favorite.check');
+      ? translate('favorite.current', this.props.qualifier)
+      : translate('favorite.check', this.props.qualifier);
     return (
-      <Tooltip overlay={tooltip}>
+      <Tooltip overlay={tooltip} placement="left">
         <a
           className={classNames('display-inline-block', 'link-no-underline', this.props.className)}
           href="#"
diff --git a/server/sonar-web/src/main/js/components/controls/FavoriteIssueFilter.js b/server/sonar-web/src/main/js/components/controls/FavoriteIssueFilter.js
deleted file mode 100644 (file)
index c00c299..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import React from 'react';
-import PropTypes from 'prop-types';
-import FavoriteBase from './FavoriteBase';
-import { toggleIssueFilter } from '../../api/issue-filters';
-
-export default class FavoriteIssueFilter extends React.PureComponent {
-  static propTypes = {
-    favorite: PropTypes.bool.isRequired,
-    filter: PropTypes.shape({
-      id: PropTypes.string.isRequired
-    }).isRequired
-  };
-
-  render() {
-    return (
-      <FavoriteBase
-        favorite={this.props.favorite}
-        addFavorite={() => toggleIssueFilter(this.props.filter.id)}
-        removeFavorite={() => toggleIssueFilter(this.props.filter.id)}
-      />
-    );
-  }
-}
index 9d68ef7e1fca21406da0cae5a8966854315f2f23..572e65cb5aed5e086f4fe144300bdb0cc261df9b 100644 (file)
@@ -24,11 +24,12 @@ import Tooltip from './Tooltip';
 import HomeIcon from '../icons-components/HomeIcon';
 import { CurrentUser, isLoggedIn, HomePage, isSameHomePage } from '../../app/types';
 import { translate } from '../../helpers/l10n';
-import { getCurrentUser } from '../../store/rootReducer';
+import { getCurrentUser, getGlobalSettingValue } from '../../store/rootReducer';
 import { setHomePage } from '../../store/users/actions';
 
 interface StateProps {
   currentUser: CurrentUser;
+  onSonarCloud: boolean;
 }
 
 interface DispatchProps {
@@ -48,9 +49,9 @@ class HomePageSelect extends React.PureComponent<Props> {
   };
 
   render() {
-    const { currentPage, currentUser } = this.props;
+    const { currentPage, currentUser, onSonarCloud } = this.props;
 
-    if (!isLoggedIn(currentUser)) {
+    if (!isLoggedIn(currentUser) || !onSonarCloud) {
       return null;
     }
 
@@ -59,7 +60,7 @@ class HomePageSelect extends React.PureComponent<Props> {
     const tooltip = checked ? translate('homepage.current') : translate('homepage.check');
 
     return (
-      <Tooltip overlay={tooltip}>
+      <Tooltip overlay={tooltip} placement="left">
         {checked ? (
           <span className={classNames('display-inline-block', this.props.className)}>
             <HomeIcon filled={checked} />
@@ -81,9 +82,14 @@ class HomePageSelect extends React.PureComponent<Props> {
   }
 }
 
-const mapStateToProps = (state: any): StateProps => ({
-  currentUser: getCurrentUser(state)
-});
+const mapStateToProps = (state: any): StateProps => {
+  const sonarCloudSetting = getGlobalSettingValue(state, 'sonar.sonarcloud.enabled');
+
+  return {
+    currentUser: getCurrentUser(state),
+    onSonarCloud: Boolean(sonarCloudSetting && sonarCloudSetting.value === 'true')
+  };
+};
 
 const mapDispatchToProps: DispatchProps = { setHomePage };
 
index 0601fc5bd2982a1340a05204d99e6537ad2def09..05bfc43bfa9669c42f6b9e15d6d6aed8240aa9f0 100644 (file)
@@ -22,5 +22,5 @@ import { shallow } from 'enzyme';
 import Favorite from '../Favorite';
 
 it('renders', () => {
-  expect(shallow(<Favorite component="foo" favorite={true} />)).toMatchSnapshot();
+  expect(shallow(<Favorite component="foo" favorite={true} qualifier="TRK" />)).toMatchSnapshot();
 });
index a6231ceb7ffd63f9789ca0c6ded55a64002d62dd..3d93e7f91595145fc5fdde88615bcfa3cb530d2d 100644 (file)
@@ -19,7 +19,7 @@
  */
 import * as React from 'react';
 import { shallow } from 'enzyme';
-import FavoriteBase from '../FavoriteBase';
+import FavoriteBase, { Props } from '../FavoriteBase';
 import { click } from '../../../helpers/testUtils';
 
 it('should render favorite', () => {
@@ -46,8 +46,14 @@ it('should remove favorite', () => {
   expect(removeFavorite).toBeCalled();
 });
 
-function renderFavoriteBase(props?: any) {
+function renderFavoriteBase(props: Partial<Props> = {}) {
   return shallow(
-    <FavoriteBase favorite={true} addFavorite={jest.fn()} removeFavorite={jest.fn()} {...props} />
+    <FavoriteBase
+      favorite={true}
+      addFavorite={jest.fn()}
+      qualifier="TRK"
+      removeFavorite={jest.fn()}
+      {...props}
+    />
   );
 }
index dd978f6e7247320ec5109d5337bc18df54d6bc55..592afbf93983631a7aef22c393883c065886ea04 100644 (file)
@@ -4,6 +4,7 @@ exports[`renders 1`] = `
 <FavoriteBase
   addFavorite={[Function]}
   favorite={true}
+  qualifier="TRK"
   removeFavorite={[Function]}
 />
 `;
index bb842b986b068d50a49985ebdd6b68606931dd80..8186fad3a49d656c2211d171fbebf1b5bf5cb701 100644 (file)
@@ -2,8 +2,8 @@
 
 exports[`should render favorite 1`] = `
 <Tooltip
-  overlay="favorite.current"
-  placement="bottom"
+  overlay="favorite.current.TRK"
+  placement="left"
 >
   <a
     className="display-inline-block link-no-underline"
@@ -19,8 +19,8 @@ exports[`should render favorite 1`] = `
 
 exports[`should render not favorite 1`] = `
 <Tooltip
-  overlay="favorite.check"
-  placement="bottom"
+  overlay="favorite.check.TRK"
+  placement="left"
 >
   <a
     className="display-inline-block link-no-underline"
index 81a469b239bf6ee3c7381d1f595fc17875edf288..3042b4cf120a3a6e3910bac96ba22a8726427a65 100644 (file)
@@ -1,6 +1,6 @@
 .navbar-context,
 .navbar-context .navbar-inner {
-  background-color: var(--barBackgroundColor);
+  background-color: #fff;
   z-index: 420;
 }
 
@@ -14,7 +14,7 @@
 }
 
 .navbar-context-header {
-  display: inline-flex;
+  display: flex;
   align-items: center;
   height: calc(4 * var(--gridSize));
   font-size: var(--bigFontSize);
@@ -48,6 +48,7 @@
   top: 36px;
   right: 0;
   padding: 0 20px;
+  white-space: nowrap;
 }
 
 .navbar-context-description {
index 10ab02703cbb01fb1bd06d563ad097951dbedf94..f5e53d33c26cf99d7e3d63b67a9171cfe319e6c6 100644 (file)
@@ -21,7 +21,7 @@ import { stringify } from 'querystring';
 import { omitBy, isNil } from 'lodash';
 import { isShortLivingBranch } from './branches';
 import { getProfilePath } from '../apps/quality-profiles/utils';
-import { Branch, HomePage } from '../app/types';
+import { Branch, HomePage, HomePageType } from '../app/types';
 
 interface Query {
   [x: string]: string | undefined;
@@ -174,13 +174,13 @@ export function getOrganizationUrl(organization: string) {
 
 export function getHomePageUrl(homepage: HomePage) {
   switch (homepage.type) {
-    case 'project':
-      return getProjectUrl(homepage.key!);
-    case 'organization':
-      return getOrganizationUrl(homepage.key!);
-    case 'my-projects':
+    case HomePageType.Project:
+      return getProjectUrl(homepage.parameter!);
+    case HomePageType.Organization:
+      return getOrganizationUrl(homepage.parameter!);
+    case HomePageType.MyProjects:
       return '/projects';
-    case 'my-issues':
+    case HomePageType.MyIssues:
       return { pathname: '/issues', query: { resolved: 'false' } };
   }
 
index b23f715de55112d970c9b08aa7120202b5b8878c..f6a72667d62be0285d5d8d1906a8708156467ea6 100644 (file)
@@ -1318,7 +1318,7 @@ email_configuration.test.email_was_sent_to_x=Email was sent to {0}
 #
 #------------------------------------------------------------------------------
 notification.channel.EmailNotificationChannel=Email
-notification.dispatcher.information=Receive notifications when specific types of events occur. A notification is never sent to the author of the event.
+notification.dispatcher.information=A notification is never sent to the author of the event.
 notification.dispatcher.ChangesOnMyIssue=Changes in issues assigned to me
 notification.dispatcher.NewIssues=New issues
 notification.dispatcher.NewAlerts=New quality gate status
@@ -2736,5 +2736,24 @@ homepage.check=Check to make the current page your homepage
 # FAVORITE
 #
 #------------------------------------------------------------------------------
-favorite.current=This is your favorite component. Click to unset.
-favorite.check=Click to mark this component as favorite.
+favorite.check.TRK=Click to mark this project as favorite.
+favorite.check.BRC=Click to mark this sub-project as favorite.
+favorite.check.DIR=Click to mark this directory as favorite.
+favorite.check.PAC=Click to mark this package as favorite.
+favorite.check.VW=Click to mark this portfolio as favorite.
+favorite.check.SVW=Click to mark this sub-ortfolio as favorite.
+favorite.check.APP=Click to mark this application as favorite.
+favorite.check.FIL=Click to mark this file as favorite.
+favorite.check.CLA=Click to mark this file as favorite.
+favorite.check.UTS=Click to mark this test file as favorite.
+
+favorite.current.TRK=This project is marked as favorite.
+favorite.current.BRC=This sub-project is marked as favorite.
+favorite.current.DIR=This directory is marked as favorite.
+favorite.current.PAC=This package is marked as favorite.
+favorite.current.VW=This portfolio is marked as favorite.
+favorite.current.SVW=This sub-ortfolio is marked as favorite.
+favorite.current.APP=This application is marked as favorite.
+favorite.current.FIL=This file is marked as favorite.
+favorite.current.CLA=This file is marked as favorite.
+favorite.current.UTS=This test file is marked as favorite.