From f2a2df85dd9f5deca1c6424a4efb06f3c2e4dacb Mon Sep 17 00:00:00 2001 From: Jeremy Davis Date: Fri, 22 Nov 2019 17:55:32 +0900 Subject: [PATCH] SONAR-12633 Move project info into sidedrawer --- .../js/app/components/ComponentContainer.tsx | 1 + .../components/nav/component/ComponentNav.tsx | 123 +- .../js/app/components/nav/component/Menu.css | 27 + .../js/app/components/nav/component/Menu.tsx | 33 +- .../component/__tests__/ComponentNav-test.tsx | 4 +- .../nav/component/__tests__/Menu-test.tsx | 70 +- .../__snapshots__/ComponentNav-test.tsx.snap | 50 +- .../ComponentNavMenu-test.tsx.snap | 1417 ----------------- .../__snapshots__/Menu-test.tsx.snap | 161 +- .../projectInformation/DrawerLink.tsx} | 30 +- .../projectInformation/InfoDrawer.css | 70 + .../projectInformation/InfoDrawer.tsx | 53 + .../projectInformation/InfoDrawerPage.tsx | 49 + .../projectInformation/ProjectInformation.css | 47 + .../projectInformation/ProjectInformation.tsx | 137 ++ .../ProjectInformationPages.ts | 24 + .../ProjectInformationRenderer.tsx | 123 ++ .../__tests__/DrawerLink-test.tsx | 40 + .../__tests__/InfoDrawer-test.tsx | 44 + .../__tests__/InfoDrawerPage-test.tsx | 44 + .../__tests__/ProjectInformation-test.tsx | 73 + .../ProjectInformationRenderer-test.tsx | 65 + .../__snapshots__/DrawerLink-test.tsx.snap | 13 + .../__snapshots__/InfoDrawer-test.tsx.snap | 54 + .../InfoDrawerPage-test.tsx.snap | 40 + .../ProjectInformation-test.tsx.snap | 241 +++ .../ProjectInformationRenderer-test.tsx.snap | 996 ++++++++++++ .../badges/BadgeButton.tsx | 0 .../badges/BadgeParams.tsx | 11 +- .../badges/ProjectBadges.tsx | 95 ++ .../badges/__tests__/BadgeButton-test.tsx | 0 .../badges/__tests__/BadgeParams-test.tsx | 2 +- .../badges/__tests__/ProjectBadges-test.tsx | 43 +- .../__snapshots__/BadgeButton-test.tsx.snap | 0 .../__snapshots__/BadgeParams-test.tsx.snap | 9 +- .../__snapshots__/ProjectBadges-test.tsx.snap | 69 + .../badges/__tests__/utils-test.ts | 0 .../projectInformation}/badges/styles.css | 10 +- .../projectInformation}/badges/utils.ts | 2 +- .../projectInformation}/meta/MetaKey.tsx | 2 +- .../projectInformation}/meta/MetaLink.tsx | 4 +- .../projectInformation}/meta/MetaLinks.tsx | 22 +- .../meta/MetaQualityGate.tsx | 13 +- .../meta/MetaQualityProfiles.tsx | 26 +- .../projectInformation/meta/MetaSize.tsx | 75 + .../projectInformation}/meta/MetaTags.tsx | 8 +- .../meta/MetaTagsSelector.tsx | 4 +- .../meta/__tests__/MetaLink-test.tsx | 0 .../__tests__/MetaQualityProfiles-test.tsx | 10 +- .../meta/__tests__/MetaSize-test.tsx | 46 + .../meta/__tests__/MetaTags-test.tsx | 2 +- .../meta/__tests__/MetaTagsSelector-test.tsx | 4 +- .../__snapshots__/MetaLink-test.tsx.snap | 0 .../MetaQualityProfiles-test.tsx.snap | 38 +- .../__snapshots__/MetaSize-test.tsx.snap | 72 + .../__snapshots__/MetaTags-test.tsx.snap | 4 +- .../notifications/ProjectNotifications.tsx | 92 ++ .../__tests__/ProjectNotifications-test.tsx} | 13 +- .../ProjectNotifications-test.tsx.snap | 75 + .../src/main/js/app/styles/init/misc.css | 18 +- .../js/apps/account/projects/ProjectCard.tsx | 4 +- .../js/apps/overview/badges/ProjectBadges.tsx | 127 -- .../__snapshots__/ProjectBadges-test.tsx.snap | 180 --- .../js/apps/overview/meta/MetaContainer.tsx | 171 -- .../main/js/apps/overview/meta/MetaSize.tsx | 109 -- .../meta/__tests__/MetaContainer-test.tsx | 69 - .../__snapshots__/MetaContainer-test.tsx.snap | 398 ----- .../notifications/ProjectNotifications.tsx | 116 -- .../ProjectNotifications.tsx.snap | 108 -- .../src/main/js/apps/overview/styles.css | 67 - .../src/main/js/apps/projectLinks/LinkRow.tsx | 2 +- .../src/main/js/apps/projectLinks/Table.tsx | 2 +- .../__tests__/projectLinks-test.ts} | 16 +- .../utils.ts => helpers/projectLinks.ts} | 0 .../resources/org/sonar/l10n/core.properties | 33 +- 75 files changed, 3179 insertions(+), 3021 deletions(-) create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/Menu.css delete mode 100644 server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap rename server/sonar-web/src/main/js/{apps/overview/meta/MetaOrganizationKey.tsx => app/components/nav/component/projectInformation/DrawerLink.tsx} (60%) create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/InfoDrawer.css create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/InfoDrawer.tsx create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/InfoDrawerPage.tsx create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformation.css create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformation.tsx create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformationPages.ts create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformationRenderer.tsx create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/DrawerLink-test.tsx create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/InfoDrawer-test.tsx create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/InfoDrawerPage-test.tsx create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/ProjectInformation-test.tsx create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/ProjectInformationRenderer-test.tsx create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/DrawerLink-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/InfoDrawer-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/InfoDrawerPage-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformation-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformationRenderer-test.tsx.snap rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/badges/BadgeButton.tsx (100%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/badges/BadgeParams.tsx (91%) create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/ProjectBadges.tsx rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/badges/__tests__/BadgeButton-test.tsx (100%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/badges/__tests__/BadgeParams-test.tsx (98%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/badges/__tests__/ProjectBadges-test.tsx (52%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/badges/__tests__/__snapshots__/BadgeButton-test.tsx.snap (100%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/badges/__tests__/__snapshots__/BadgeParams-test.tsx.snap (94%) create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/__snapshots__/ProjectBadges-test.tsx.snap rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/badges/__tests__/utils-test.ts (100%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/badges/styles.css (90%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/badges/utils.ts (97%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/meta/MetaKey.tsx (93%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/meta/MetaLink.tsx (95%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/meta/MetaLinks.tsx (80%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/meta/MetaQualityGate.tsx (71%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/meta/MetaQualityProfiles.tsx (83%) create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/MetaSize.tsx rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/meta/MetaTags.tsx (91%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/meta/MetaTagsSelector.tsx (93%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/meta/__tests__/MetaLink-test.tsx (100%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/meta/__tests__/MetaQualityProfiles-test.tsx (83%) create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaSize-test.tsx rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/meta/__tests__/MetaTags-test.tsx (95%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/meta/__tests__/MetaTagsSelector-test.tsx (93%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/meta/__tests__/__snapshots__/MetaLink-test.tsx.snap (100%) rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/meta/__tests__/__snapshots__/MetaQualityProfiles-test.tsx.snap (83%) create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaSize-test.tsx.snap rename server/sonar-web/src/main/js/{apps/overview => app/components/nav/component/projectInformation}/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap (90%) create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/notifications/ProjectNotifications.tsx rename server/sonar-web/src/main/js/{apps/overview/notifications/__tests__/ProjectNotifications.tsx => app/components/nav/component/projectInformation/notifications/__tests__/ProjectNotifications-test.tsx} (88%) create mode 100644 server/sonar-web/src/main/js/app/components/nav/component/projectInformation/notifications/__tests__/__snapshots__/ProjectNotifications-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/overview/badges/ProjectBadges.tsx delete mode 100644 server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/ProjectBadges-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx delete mode 100644 server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx delete mode 100644 server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaContainer-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaContainer-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/overview/notifications/ProjectNotifications.tsx delete mode 100644 server/sonar-web/src/main/js/apps/overview/notifications/__tests__/__snapshots__/ProjectNotifications.tsx.snap rename server/sonar-web/src/main/js/{apps/projectLinks/__tests__/utils-test.ts => helpers/__tests__/projectLinks-test.ts} (66%) rename server/sonar-web/src/main/js/{apps/projectLinks/utils.ts => helpers/projectLinks.ts} (100%) diff --git a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx index 453dd435028..4a8e7cdfdd6 100644 --- a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx @@ -326,6 +326,7 @@ export class ComponentContainer extends React.PureComponent { currentTaskOnSameBranch={currentTask && this.isSameBranch(currentTask, branchLike)} isInProgress={isInProgress} isPending={isPending} + onComponentChange={this.handleComponentChange} warnings={this.state.warnings} /> )} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx index 47a79ce22cb..29f8c596785 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx @@ -29,6 +29,8 @@ import ComponentNavBgTaskNotif from './ComponentNavBgTaskNotif'; import Header from './Header'; import HeaderMeta from './HeaderMeta'; import Menu from './Menu'; +import InfoDrawer from './projectInformation/InfoDrawer'; +import ProjectInformation from './projectInformation/ProjectInformation'; interface Props { branchLikes: BranchLike[]; @@ -38,24 +40,27 @@ interface Props { currentTaskOnSameBranch?: boolean; isInProgress?: boolean; isPending?: boolean; + onComponentChange: (changes: Partial) => void; warnings: string[]; } -export default class ComponentNav extends React.PureComponent { - mounted = false; +export default function ComponentNav(props: Props) { + const { + branchLikes, + component, + currentBranchLike, + currentTask, + currentTaskOnSameBranch, + isInProgress, + isPending, + warnings + } = props; + const { contextNavHeightRaw, globalNavHeightRaw } = rawSizes; - componentDidMount() { - this.populateRecentHistory(); - } - - componentDidUpdate(prevProps: Props) { - if (this.props.component.key !== prevProps.component.key) { - this.populateRecentHistory(); - } - } + const [displayProjectInfo, setDisplayProjectInfo] = React.useState(false); - populateRecentHistory = () => { - const { breadcrumbs } = this.props.component; + React.useEffect(() => { + const { breadcrumbs, key, name, organization } = component; const { qualifier } = breadcrumbs[breadcrumbs.length - 1]; if ( [ @@ -65,55 +70,53 @@ export default class ComponentNav extends React.PureComponent { ComponentQualifier.Developper ].includes(qualifier as ComponentQualifier) ) { - RecentHistory.add( - this.props.component.key, - this.props.component.name, - qualifier.toLowerCase(), - this.props.component.organization - ); + RecentHistory.add(key, name, qualifier.toLowerCase(), organization); } - }; + }, [component, component.key]); - render() { - const { component, currentBranchLike, currentTask, isInProgress, isPending } = this.props; - const contextNavHeight = rawSizes.contextNavHeightRaw; - let notifComponent; - if (isInProgress || isPending || (currentTask && currentTask.status === STATUSES.FAILED)) { - notifComponent = ( - - ); - } - return ( - -
-
- -
- - + let notifComponent; + if (isInProgress || isPending || (currentTask && currentTask.status === STATUSES.FAILED)) { + notifComponent = ( + ); } + + const contextNavHeight = notifComponent ? contextNavHeightRaw + 30 : contextNavHeightRaw; + + return ( + +
+
+ +
+ setDisplayProjectInfo(!displayProjectInfo)} + /> + setDisplayProjectInfo(false)} + top={globalNavHeightRaw + contextNavHeightRaw}> + + + + ); } diff --git a/server/sonar-web/src/main/js/app/components/nav/component/Menu.css b/server/sonar-web/src/main/js/app/components/nav/component/Menu.css new file mode 100644 index 00000000000..3a62e30ca89 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/Menu.css @@ -0,0 +1,27 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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. + */ +.navbar-tabs > li > a.menu-button { + color: var(--darkBlue); +} + +.navbar-tabs > li > a.menu-button:hover { + color: var(--blue); + border-bottom-color: transparent; +} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx index bdc83cc6db5..c1aea4a7bfa 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx @@ -21,6 +21,7 @@ import * as classNames from 'classnames'; import * as React from 'react'; import { Link } from 'react-router'; import Dropdown from 'sonar-ui-common/components/controls/Dropdown'; +import BulletListIcon from 'sonar-ui-common/components/icons/BulletListIcon'; import DropdownIcon from 'sonar-ui-common/components/icons/DropdownIcon'; import NavBarTabs from 'sonar-ui-common/components/ui/NavBarTabs'; import { hasMessage, translate } from 'sonar-ui-common/helpers/l10n'; @@ -29,6 +30,7 @@ import { getBranchLikeQuery, isMainBranch, isPullRequest } from '../../../../hel import { isSonarCloud } from '../../../../helpers/system'; import { BranchLike } from '../../../../types/branch-like'; import { ComponentQualifier } from '../../../../types/component'; +import './Menu.css'; const SETTINGS_URLS = [ '/project/admin', @@ -51,6 +53,7 @@ interface Props { appState: Pick; branchLike: BranchLike | undefined; component: T.Component; + onToggleProjectInfo: () => void; } export class Menu extends React.PureComponent { @@ -249,6 +252,31 @@ export class Menu extends React.PureComponent { ]; } + renderProjectInformationButton() { + if (isPullRequest(this.props.branchLike)) { + return null; + } + + return ( + (this.isProject() || this.isApplication()) && ( +
  • + ) => { + e.preventDefault(); + e.currentTarget.blur(); + this.props.onToggleProjectInfo(); + }} + role="button" + tabIndex={0}> + + {translate(this.isProject() ? 'project' : 'application', 'info.title')} + +
  • + ) + ); + } + renderSettingsLink() { if (!this.getConfiguration().showSettings || this.isApplication() || this.isPortfolio()) { return null; @@ -511,7 +539,10 @@ export class Menu extends React.PureComponent { {this.renderActivityLink()} {this.renderExtensions()} - {this.renderAdministration()} + + {this.renderAdministration()} + {this.renderProjectInformationButton()} + ); } diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx index 4b82c645143..205f7bf64ad 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx @@ -35,9 +35,11 @@ it('renders', () => { branchLikes={[]} component={component} currentBranchLike={undefined} + isInProgress={true} + isPending={true} + onComponentChange={jest.fn()} warnings={[]} /> ); - wrapper.setState({ isInProgress: true, isPending: true }); expect(wrapper).toMatchSnapshot(); }); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/Menu-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/Menu-test.tsx index 97067a7126e..7f825a0c9ef 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/Menu-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/Menu-test.tsx @@ -44,9 +44,7 @@ it('should work with extensions', () => { configuration: { showSettings: true, extensions: [{ key: 'foo', name: 'Foo' }] }, extensions: [{ key: 'component-foo', name: 'ComponentFoo' }] }; - const wrapper = shallow( - - ); + const wrapper = shallowRender({ component }); expect(wrapper.find('Dropdown[data-test="extensions"]')).toMatchSnapshot(); expect(wrapper.find('Dropdown[data-test="administration"]')).toMatchSnapshot(); }); @@ -66,9 +64,7 @@ it('should work with multiple extensions', () => { { key: 'component-bar', name: 'ComponentBar' } ] }; - const wrapper = shallow( - - ); + const wrapper = shallowRender({ component }); expect(wrapper.find('Dropdown[data-test="extensions"]')).toMatchSnapshot(); expect(wrapper.find('Dropdown[data-test="administration"]')).toMatchSnapshot(); }); @@ -88,30 +84,25 @@ it('should render correctly for security extensions', () => { { key: 'component-bar', name: 'ComponentBar' } ] }; - const wrapper = shallow( - - ); + const wrapper = shallowRender({ component }); expect(wrapper.find('Dropdown[data-test="extensions"]')).toMatchSnapshot(); expect(wrapper.find('Dropdown[data-test="security"]')).toMatchSnapshot(); }); it('should work for a branch', () => { - const branch = mockBranch({ + const branchLike = mockBranch({ name: 'release' }); [true, false].forEach(showSettings => expect( - shallow( - - ) + shallowRender({ + branchLike, + component: { + ...baseComponent, + configuration: { showSettings }, + extensions: [{ key: 'component-foo', name: 'ComponentFoo' }] + } + }) ).toMatchSnapshot() ); }); @@ -119,17 +110,14 @@ it('should work for a branch', () => { it('should work for pull requests', () => { [true, false].forEach(showSettings => expect( - shallow( - - ) + shallowRender({ + branchLike: mockPullRequest(), + component: { + ...baseComponent, + configuration: { showSettings }, + extensions: [{ key: 'component-foo', name: 'ComponentFoo' }] + } + }) ).toMatchSnapshot() ); }); @@ -145,10 +133,18 @@ it('should work for all qualifiers', () => { function checkWithQualifier(qualifier: string) { const component = { ...baseComponent, configuration: { showSettings: true }, qualifier }; - expect( - shallow( - - ) - ).toMatchSnapshot(); + expect(shallowRender({ component })).toMatchSnapshot(); } }); + +function shallowRender(props: Partial) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap index 2a9dfac5576..e4bd6ee6c20 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap @@ -2,11 +2,32 @@ exports[`renders 1`] = ` + } >
    + + + `; diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap deleted file mode 100644 index 940c071c240..00000000000 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap +++ /dev/null @@ -1,1417 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly for security extensions 1`] = ` - -
  • - - ComponentBar - -
  • - - } - tagName="li" -> - -
    -`; - -exports[`should render correctly for security extensions 2`] = `null`; - -exports[`should work for a branch 1`] = ` - -
  • - - overview.page - -
  • -
  • - - issues.page - -
  • -
  • - - layout.security_hotspots - -
  • -
  • - - layout.measures - -
  • -
  • - - code.page - -
  • -
  • - - project_activity.page - -
  • - -
  • - - project_settings.page - -
  • -
  • - - project_branch_pull_request.page - -
  • -
  • - - project_baseline.page - -
  • -
  • - - webhooks.page - -
  • -
  • - - deletion.page - -
  • - - } - tagName="li" - > - -
    -
    -`; - -exports[`should work for a branch 2`] = ` - -
  • - - overview.page - -
  • -
  • - - issues.page - -
  • -
  • - - layout.security_hotspots - -
  • -
  • - - layout.measures - -
  • -
  • - - code.page - -
  • -
  • - - project_activity.page - -
  • -
    -`; - -exports[`should work for all qualifiers 1`] = ` - -
  • - - overview.page - -
  • -
  • - - issues.page - -
  • -
  • - - layout.security_hotspots - -
  • -
  • - - layout.measures - -
  • -
  • - - code.page - -
  • -
  • - - project_activity.page - -
  • - -
  • - - project_settings.page - -
  • -
  • - - project_branch_pull_request.page - -
  • -
  • - - project_baseline.page - -
  • -
  • - - webhooks.page - -
  • -
  • - - deletion.page - -
  • - - } - tagName="li" - > - -
    -
    -`; - -exports[`should work for all qualifiers 2`] = ` - -
  • - - overview.page - -
  • -
  • - - issues.page - -
  • -
  • - - layout.measures - -
  • -
  • - - view_projects.page - -
  • -
  • - - project_activity.page - -
  • - -
  • - - deletion.page - -
  • - - } - tagName="li" - > - -
    -
    -`; - -exports[`should work for all qualifiers 3`] = ` - -
  • - - overview.page - -
  • -
  • - - issues.page - -
  • -
  • - - layout.measures - -
  • -
  • - - view_projects.page - -
  • -
  • - - project_activity.page - -
  • -
    -`; - -exports[`should work for all qualifiers 4`] = ` - -
  • - - overview.page - -
  • -
  • - - issues.page - -
  • -
  • - - layout.security_hotspots - -
  • -
  • - - layout.measures - -
  • -
  • - - view_projects.page - -
  • -
  • - - project_activity.page - -
  • - -
  • - - deletion.page - -
  • - - } - tagName="li" - > - -
    -
    -`; - -exports[`should work for pull requests 1`] = ` - -
  • - - overview.page - -
  • -
  • - - issues.page - -
  • -
  • - - layout.measures - -
  • -
  • - - code.page - -
  • -
    -`; - -exports[`should work for pull requests 2`] = ` - -
  • - - overview.page - -
  • -
  • - - issues.page - -
  • -
  • - - layout.measures - -
  • -
  • - - code.page - -
  • -
    -`; - -exports[`should work with extensions 1`] = ` - -
  • - - ComponentFoo - -
  • - - } - tagName="li" -> - -
    -`; - -exports[`should work with extensions 2`] = ` - -
  • - - project_settings.page - -
  • -
  • - - project_branch_pull_request.page - -
  • -
  • - - project_baseline.page - -
  • -
  • - - webhooks.page - -
  • -
  • - - Foo - -
  • -
  • - - deletion.page - -
  • - - } - tagName="li" -> - -
    -`; - -exports[`should work with multiple extensions 1`] = ` - -
  • - - ComponentFoo - -
  • -
  • - - ComponentBar - -
  • - - } - tagName="li" -> - -
    -`; - -exports[`should work with multiple extensions 2`] = ` - -
  • - - project_settings.page - -
  • -
  • - - project_branch_pull_request.page - -
  • -
  • - - project_baseline.page - -
  • -
  • - - webhooks.page - -
  • -
  • - - Foo - -
  • -
  • - - Bar - -
  • -
  • - - deletion.page - -
  • - - } - tagName="li" -> - -
    -`; diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Menu-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Menu-test.tsx.snap index 65839142a47..ddb2ec883eb 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Menu-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Menu-test.tsx.snap @@ -77,6 +77,24 @@ exports[`should work for a branch 1`] = ` issues.page +
  • + + layout.security_hotspots + +
  • +
  • + + + project.info.title + +
  • `; @@ -281,6 +312,24 @@ exports[`should work for a branch 2`] = ` issues.page +
  • + + layout.security_hotspots + +
  • - + +
  • + + + project.info.title + +
  • +
    `; @@ -380,6 +443,23 @@ exports[`should work for all qualifiers 1`] = ` issues.page +
  • + + layout.security_hotspots + +
  • +
  • + + + project.info.title + +
  • `; @@ -796,6 +889,23 @@ exports[`should work for all qualifiers 4`] = ` issues.page +
  • + + layout.security_hotspots + +
  • +
  • + + + application.info.title + +
  • `; @@ -924,6 +1047,24 @@ exports[`should work for pull requests 1`] = ` issues.page +
  • + + layout.security_hotspots + +
  • +
  • + + layout.security_hotspots + +
  • { + label: string; + onPageChange: (page: P) => void; + to: P; } -export default function MetaOrganizationKey({ organization }: Props) { +export function DrawerLink

    (props: DrawerLinkProps

    ) { + const { label, to } = props; + return ( - <> -

    {translate('organization_key')}

    -
    - - -
    - + props.onPageChange(to)} + role="link" + tabIndex={0}> + {label} + + ); } + +export default React.memo(DrawerLink); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/InfoDrawer.css b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/InfoDrawer.css new file mode 100644 index 00000000000..fbc2900f7cc --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/InfoDrawer.css @@ -0,0 +1,70 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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. + */ +:root { + --drawer-width: 380px; +} + +/* TODO: should we move this? Or handle it differently? */ +.navbar-inner-with-notif .info-drawer { + border-top: 1px solid var(--barBorderColor); +} + +.info-drawer-pane { + background-color: white; + right: calc(-1 * var(--drawer-width)); + width: var(--drawer-width); + transition: right 0.3s ease-in-out; + border-left: 1px solid var(--barBorderColor); + box-sizing: border-box; +} + +.info-drawer-pane.open { + right: 0; +} + +.info-drawer { + position: fixed; + /* top is defined programmatically by ComponentNav */ + bottom: 0; + z-index: var(--pageSideZIndex); +} + +.info-drawer .close-button { + position: absolute; + top: 0; + right: 0; + background: white; + padding: calc(2 * var(--gridSize)); + z-index: var(--normalZIndex); +} + +.info-drawer .back-button { + cursor: pointer; +} + +.info-drawer .back-button:hover { + color: var(--blue); +} + +.info-drawer-page { + position: absolute; + top: 0; + bottom: 0; +} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/InfoDrawer.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/InfoDrawer.tsx new file mode 100644 index 00000000000..3bd34ac517c --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/InfoDrawer.tsx @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 classNames from 'classnames'; +import * as React from 'react'; +import { ClearButton } from 'sonar-ui-common/components/controls/buttons'; +import EscKeydownHandler from 'sonar-ui-common/components/controls/EscKeydownHandler'; +import OutsideClickHandler from 'sonar-ui-common/components/controls/OutsideClickHandler'; +import './InfoDrawer.css'; + +export interface InfoDrawerProps { + children: React.ReactNode; + displayed: boolean; + onClose: () => void; + top: number; +} + +export default function InfoDrawer(props: InfoDrawerProps) { + const { children, displayed, onClose, top } = props; + + return ( +
    +
    + +
    + {displayed && ( + + +
    {children}
    +
    +
    + )} +
    + ); +} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/InfoDrawerPage.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/InfoDrawerPage.tsx new file mode 100644 index 00000000000..5fdea691c7a --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/InfoDrawerPage.tsx @@ -0,0 +1,49 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 classNames from 'classnames'; +import * as React from 'react'; +import BackIcon from 'sonar-ui-common/components/icons/BackIcon'; +import { translate } from 'sonar-ui-common/helpers/l10n'; + +export interface InfoDrawerPageProps { + children: React.ReactNode; + displayed: boolean; + onPageChange: () => void; +} + +export default function InfoDrawerPage(props: InfoDrawerPageProps) { + const { children, displayed, onPageChange } = props; + return ( +
    +

    onPageChange()}> + + {translate('back')} +

    + + {displayed &&
    {children}
    } +
    + ); +} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformation.css b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformation.css new file mode 100644 index 00000000000..1fdf957ec7c --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformation.css @@ -0,0 +1,47 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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. + */ + +.project-info-list > li { + /* 1px to not cut icons on the left */ + padding-left: 1px; + padding-bottom: 4px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.project-info-tags { + position: relative; +} + +.project-info-deleted-profile, +.project-info-deprecated-rules { + margin: 4px -6px 4px; + padding: 3px 6px !important; + border: 1px solid var(--alertBorderError); + border-radius: 3px; + background-color: var(--alertBackgroundError); +} + +.project-info-deleted-profile a, +.project-info-deprecated-rules a { + color: var(--veryDarkBlue); + border-color: darken(var(--lightBlue)); +} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformation.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformation.tsx new file mode 100644 index 00000000000..50dc52ca3b9 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformation.tsx @@ -0,0 +1,137 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { getMeasures } from '../../../../../api/measures'; +import { isLoggedIn } from '../../../../../helpers/users'; +import { fetchMetrics } from '../../../../../store/rootActions'; +import { getCurrentUser, getMetrics, Store } from '../../../../../store/rootReducer'; +import { BranchLike } from '../../../../../types/branch-like'; +import { ComponentQualifier } from '../../../../../types/component'; +import { MetricKey } from '../../../../../types/metrics'; +import ProjectBadges from './badges/ProjectBadges'; +import InfoDrawerPage from './InfoDrawerPage'; +import ProjectNotifications from './notifications/ProjectNotifications'; +import './ProjectInformation.css'; +import { ProjectInformationPages } from './ProjectInformationPages'; +import ProjectInformationRenderer from './ProjectInformationRenderer'; + +interface Props { + branchLike?: BranchLike; + component: T.Component; + currentUser: T.CurrentUser; + fetchMetrics: () => void; + onComponentChange: (changes: {}) => void; + metrics: T.Dict; +} + +interface State { + measures?: T.Measure[]; + page: ProjectInformationPages; +} + +export class ProjectInformation extends React.PureComponent { + mounted = false; + state: State = { + page: ProjectInformationPages.main + }; + + componentDidMount() { + this.mounted = true; + this.props.fetchMetrics(); + this.loadMeasures(); + } + + componentWillUnmount() { + this.mounted = false; + } + + setPage = (page: ProjectInformationPages = ProjectInformationPages.main) => { + this.setState({ page }); + }; + + loadMeasures = () => { + const { + component: { key } + } = this.props; + + return getMeasures({ + component: key, + metricKeys: [MetricKey.ncloc, MetricKey.projects].join() + }).then(measures => { + if (this.mounted) { + this.setState({ measures }); + } + }); + }; + + render() { + const { branchLike, component, currentUser, metrics } = this.props; + const { measures, page } = this.state; + + const canConfigureNotifications = isLoggedIn(currentUser); + const canUseBadges = + metrics !== undefined && + component.visibility !== 'private' && + (component.qualifier === ComponentQualifier.Application || + component.qualifier === ComponentQualifier.Project); + + return ( + <> + + {canUseBadges && ( + + + + )} + {canConfigureNotifications && ( + + + + )} + + ); + } +} + +const mapDispatchToProps = { fetchMetrics }; + +const mapStateToProps = (state: Store) => ({ + currentUser: getCurrentUser(state), + metrics: getMetrics(state) +}); + +export default connect(mapStateToProps, mapDispatchToProps)(ProjectInformation); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformationPages.ts b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformationPages.ts new file mode 100644 index 00000000000..3f2ea3d9590 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformationPages.ts @@ -0,0 +1,24 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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. + */ +export enum ProjectInformationPages { + main, + badges, + notifications +} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformationRenderer.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformationRenderer.tsx new file mode 100644 index 00000000000..4bee5cdeac6 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/ProjectInformationRenderer.tsx @@ -0,0 +1,123 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { translate } from 'sonar-ui-common/helpers/l10n'; +import PrivacyBadgeContainer from '../../../../../components/common/PrivacyBadgeContainer'; +import { ComponentQualifier } from '../../../../../types/component'; +import DrawerLink from './DrawerLink'; +import MetaKey from './meta/MetaKey'; +import MetaLinks from './meta/MetaLinks'; +import MetaQualityGate from './meta/MetaQualityGate'; +import MetaQualityProfiles from './meta/MetaQualityProfiles'; +import MetaSize from './meta/MetaSize'; +import MetaTags from './meta/MetaTags'; +import { ProjectInformationPages } from './ProjectInformationPages'; + +export interface ProjectInformationRendererProps { + canConfigureNotifications: boolean; + canUseBadges: boolean; + component: T.Component; + measures?: T.Measure[]; + onComponentChange: (changes: {}) => void; + onPageChange: (page: ProjectInformationPages) => void; +} + +export function ProjectInformationRenderer(props: ProjectInformationRendererProps) { + const { canConfigureNotifications, canUseBadges, component, measures = [] } = props; + + const isApp = component.qualifier === ComponentQualifier.Application; + + return ( + <> +
    +

    + {translate(isApp ? 'application' : 'project', 'info.title')} +

    +
    + +
    + {(component.description || !isApp) && ( +
    +
    +

    {translate('project.info.description')}

    + {component.visibility && ( + + )} +
    + + {component.description &&

    {component.description}

    } + + {!isApp && ( + + )} +
    + )} + +
    + +
    + + {(component.qualityGate || + (component.qualityProfiles && component.qualityProfiles.length > 0)) && ( + <> +
    + {component.qualityGate && } + + {component.qualityProfiles && component.qualityProfiles.length > 0 && ( + + )} +
    + + )} + + {!isApp && } + +
    + +
    + + {canUseBadges && ( + + )} + {canConfigureNotifications && ( + + )} +
    + + ); +} + +export default React.memo(ProjectInformationRenderer); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/DrawerLink-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/DrawerLink-test.tsx new file mode 100644 index 00000000000..ae91f153d61 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/DrawerLink-test.tsx @@ -0,0 +1,40 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; +import * as React from 'react'; +import { DrawerLink, DrawerLinkProps } from '../DrawerLink'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +it('should call onPageChange when clicked', () => { + const onPageChange = jest.fn(); + const to = 'target'; + const wrapper = shallowRender({ onPageChange, to }); + + wrapper.simulate('click'); + + expect(onPageChange).toBeCalledWith(to); +}); + +function shallowRender(props: Partial> = {}) { + return shallow(); +} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/InfoDrawer-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/InfoDrawer-test.tsx new file mode 100644 index 00000000000..93ed1679334 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/InfoDrawer-test.tsx @@ -0,0 +1,44 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; +import * as React from 'react'; +import InfoDrawer, { InfoDrawerProps } from '../InfoDrawer'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot('default'); + expect(shallowRender({ displayed: true })).toMatchSnapshot('displayed'); +}); + +it('should call onClose when button is clicked', () => { + const onClose = jest.fn(); + const wrapper = shallowRender({ onClose }); + + wrapper.find('ClearButton').simulate('click'); + + expect(onClose).toBeCalled(); +}); + +function shallowRender(props: Partial = {}) { + return shallow( + + content + + ); +} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/InfoDrawerPage-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/InfoDrawerPage-test.tsx new file mode 100644 index 00000000000..43e1611bc88 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/InfoDrawerPage-test.tsx @@ -0,0 +1,44 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; +import * as React from 'react'; +import InfoDrawerPage, { InfoDrawerPageProps } from '../InfoDrawerPage'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); + expect(shallowRender({ displayed: true })).toMatchSnapshot(); +}); + +it('should call onPageChange when clicked', () => { + const onPageChange = jest.fn(); + const wrapper = shallowRender({ onPageChange }); + + wrapper.find('.back-button').simulate('click'); + + expect(onPageChange).toBeCalledTimes(1); +}); + +function shallowRender(props: Partial = {}) { + return shallow( + +
    content
    +
    + ); +} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/ProjectInformation-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/ProjectInformation-test.tsx new file mode 100644 index 00000000000..54b41c118a1 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/ProjectInformation-test.tsx @@ -0,0 +1,73 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; +import * as React from 'react'; +import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; +import { + mockComponent, + mockCurrentUser, + mockLoggedInUser, + mockMetric +} from '../../../../../../helpers/testMocks'; +import { ProjectInformation } from '../ProjectInformation'; +import { ProjectInformationPages } from '../ProjectInformationPages'; + +jest.mock('../../../../../../api/measures', () => { + const { mockMeasure } = jest.requireActual('../../../../../../helpers/testMocks'); + return { + getMeasures: jest.fn().mockResolvedValue([mockMeasure()]) + }; +}); + +it('should render correctly', async () => { + expect(shallowRender()).toMatchSnapshot('default'); + expect(shallowRender({ currentUser: mockLoggedInUser() })).toMatchSnapshot('logged in user'); + expect(shallowRender({ component: mockComponent({ visibility: 'private' }) })).toMatchSnapshot( + 'private' + ); + const wrapper = shallowRender(); + await waitAndUpdate(wrapper); + expect(wrapper).toMatchSnapshot('measures loaded'); +}); + +it('should handle page change', async () => { + const wrapper = shallowRender(); + + wrapper.instance().setPage(ProjectInformationPages.badges); + + await waitAndUpdate(wrapper); + + expect(wrapper.state().page).toBe(ProjectInformationPages.badges); +}); + +function shallowRender(props: Partial = {}) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/ProjectInformationRenderer-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/ProjectInformationRenderer-test.tsx new file mode 100644 index 00000000000..577b2e387d2 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/ProjectInformationRenderer-test.tsx @@ -0,0 +1,65 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockComponent } from '../../../../../../helpers/testMocks'; +import { + ProjectInformationRenderer, + ProjectInformationRendererProps +} from '../ProjectInformationRenderer'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot('default'); + expect(shallowRender({ canConfigureNotifications: false })).toMatchSnapshot('with notifications'); + expect(shallowRender({ canUseBadges: false })).toMatchSnapshot('no badges'); + expect(shallowRender({ canConfigureNotifications: false, canUseBadges: false })).toMatchSnapshot( + 'no badges, no notifications' + ); +}); + +it('should render a private project correctly', () => { + expect(shallowRender({ component: mockComponent({ visibility: 'private' }) })).toMatchSnapshot(); +}); + +it('should render an app correctly', () => { + const component = mockComponent({ qualifier: 'APP' }); + expect(shallowRender({ component })).toMatchSnapshot('default'); +}); + +it('should handle missing quality profiles and quality gates', () => { + expect( + shallowRender({ + component: mockComponent({ qualityGate: undefined, qualityProfiles: undefined }) + }) + ).toMatchSnapshot(); +}); + +function shallowRender(props: Partial = {}) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/DrawerLink-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/DrawerLink-test.tsx.snap new file mode 100644 index 00000000000..56f55967256 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/DrawerLink-test.tsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` + + switch page + + +`; diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/InfoDrawer-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/InfoDrawer-test.tsx.snap new file mode 100644 index 00000000000..4bdb4cd7014 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/InfoDrawer-test.tsx.snap @@ -0,0 +1,54 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly: default 1`] = ` +
    +
    + +
    +
    +`; + +exports[`should render correctly: displayed 1`] = ` +
    +
    + +
    + + +
    + + content + +
    +
    +
    +
    +`; diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/InfoDrawerPage-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/InfoDrawerPage-test.tsx.snap new file mode 100644 index 00000000000..d7d34dd05a9 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/InfoDrawerPage-test.tsx.snap @@ -0,0 +1,40 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +
    +

    + + back +

    +
    +`; + +exports[`should render correctly 2`] = ` +
    +

    + + back +

    +
    +
    + content +
    +
    +
    +`; diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformation-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformation-test.tsx.snap new file mode 100644 index 00000000000..211df6ee16f --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformation-test.tsx.snap @@ -0,0 +1,241 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly: default 1`] = ` + + + + + + +`; + +exports[`should render correctly: logged in user 1`] = ` + + + + + + + + + +`; + +exports[`should render correctly: measures loaded 1`] = ` + + + + + + +`; + +exports[`should render correctly: private 1`] = ` + + + +`; diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformationRenderer-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformationRenderer-test.tsx.snap new file mode 100644 index 00000000000..f1d2b875e96 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/__tests__/__snapshots__/ProjectInformationRenderer-test.tsx.snap @@ -0,0 +1,996 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should handle missing quality profiles and quality gates 1`] = ` + +
    +

    + project.info.title +

    +
    +
    +
    +
    +

    + project.info.description +

    +
    + +
    +
    + +
    + +
    + +
    + + +
    +
    +`; + +exports[`should render a private project correctly 1`] = ` + +
    +

    + project.info.title +

    +
    +
    +
    +
    +

    + project.info.description +

    + +
    + +
    +
    + +
    +
    + + +
    + +
    + +
    + + +
    +
    +`; + +exports[`should render an app correctly: default 1`] = ` + +
    +

    + application.info.title +

    +
    +
    +
    + +
    +
    + + +
    +
    + +
    + + +
    +
    +`; + +exports[`should render correctly: default 1`] = ` + +
    +

    + project.info.title +

    +
    +
    +
    +
    +

    + project.info.description +

    + +
    + +
    +
    + +
    +
    + + +
    + +
    + +
    + + +
    +
    +`; + +exports[`should render correctly: no badges 1`] = ` + +
    +

    + project.info.title +

    +
    +
    +
    +
    +

    + project.info.description +

    + +
    + +
    +
    + +
    +
    + + +
    + +
    + +
    + +
    +
    +`; + +exports[`should render correctly: no badges, no notifications 1`] = ` + +
    +

    + project.info.title +

    +
    +
    +
    +
    +

    + project.info.description +

    + +
    + +
    +
    + +
    +
    + + +
    + +
    + +
    +
    +
    +`; + +exports[`should render correctly: with notifications 1`] = ` + +
    +

    + project.info.title +

    +
    +
    +
    +
    +

    + project.info.description +

    + +
    + +
    +
    + +
    +
    + + +
    + +
    + +
    + +
    +
    +`; diff --git a/server/sonar-web/src/main/js/apps/overview/badges/BadgeButton.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/BadgeButton.tsx similarity index 100% rename from server/sonar-web/src/main/js/apps/overview/badges/BadgeButton.tsx rename to server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/BadgeButton.tsx diff --git a/server/sonar-web/src/main/js/apps/overview/badges/BadgeParams.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/BadgeParams.tsx similarity index 91% rename from server/sonar-web/src/main/js/apps/overview/badges/BadgeParams.tsx rename to server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/BadgeParams.tsx index cc8f09944a4..d0a739ea04d 100644 --- a/server/sonar-web/src/main/js/apps/overview/badges/BadgeParams.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/BadgeParams.tsx @@ -21,7 +21,7 @@ import * as classNames from 'classnames'; import * as React from 'react'; import Select from 'sonar-ui-common/components/controls/Select'; import { getLocalizedMetricName, translate } from 'sonar-ui-common/helpers/l10n'; -import { fetchWebApi } from '../../../api/web-api'; +import { fetchWebApi } from '../../../../../../api/web-api'; import { BadgeColors, BadgeFormats, BadgeOptions, BadgeType } from './utils'; interface Props { @@ -54,9 +54,9 @@ export default class BadgeParams extends React.PureComponent { fetchWebApi(false).then( webservices => { if (this.mounted) { - const domain = webservices.find(domain => domain.path === 'api/project_badges'); - const ws = domain && domain.actions.find(ws => ws.key === 'measure'); - const param = ws && ws.params && ws.params.find(param => param.key === 'metric'); + const domain = webservices.find(d => d.path === 'api/project_badges'); + const ws = domain && domain.actions.find(w => w.key === 'measure'); + const param = ws && ws.params && ws.params.find(p => p.key === 'metric'); if (param && param.possibleValues) { this.setState({ badgeMetrics: param.possibleValues }); } @@ -129,6 +129,7 @@ export default class BadgeParams extends React.PureComponent {
  • {inner}
  • +
  • {inner}
  • ); } @@ -122,7 +118,7 @@ export class MetaQualityProfiles extends React.PureComponent -
  • {inner}
  • +
  • {inner}
  • ); } @@ -135,11 +131,9 @@ export class MetaQualityProfiles extends React.PureComponent -

    - {translate('overview.quality_profiles')} -

    +

    {translate('overview.quality_profiles')}

    -
      +
        {profiles.map(profile => this.renderProfile(profile))}
      diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/MetaSize.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/MetaSize.tsx new file mode 100644 index 00000000000..605db6a069e --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/MetaSize.tsx @@ -0,0 +1,75 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 SizeRating from 'sonar-ui-common/components/ui/SizeRating'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { formatMeasure, localizeMetric } from 'sonar-ui-common/helpers/measures'; +import DrilldownLink from '../../../../../../components/shared/DrilldownLink'; +import { ComponentQualifier } from '../../../../../../types/component'; +import { MetricKey } from '../../../../../../types/metrics'; + +export interface MetaSizeProps { + component: T.Component; + measures: T.Measure[]; +} + +export default function MetaSize({ component, measures }: MetaSizeProps) { + const isApp = component.qualifier === ComponentQualifier.Application; + const ncloc = measures.find(measure => measure.metric === MetricKey.ncloc); + const projects = isApp + ? measures.find(measure => measure.metric === MetricKey.projects) + : undefined; + + return ( + <> +

      {localizeMetric(MetricKey.ncloc)}

      +
      + {ncloc ? ( + <> + + {formatMeasure(ncloc.value, 'SHORT_INT')} + + + + + + + ) : ( + 0 + )} + + {isApp && ( + + {projects ? ( + + {formatMeasure(projects.value, 'SHORT_INT')} + + ) : ( + 0 + )} + + {translate('metric.projects.name')} + + + )} +
      + + ); +} diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaTags.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/MetaTags.tsx similarity index 91% rename from server/sonar-web/src/main/js/apps/overview/meta/MetaTags.tsx rename to server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/MetaTags.tsx index f578941406c..1e7b3496586 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaTags.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/MetaTags.tsx @@ -22,8 +22,8 @@ import { ButtonLink } from 'sonar-ui-common/components/controls/buttons'; import Dropdown from 'sonar-ui-common/components/controls/Dropdown'; import { PopupPlacement } from 'sonar-ui-common/components/ui/popups'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { setProjectTags } from '../../../api/components'; -import TagsList from '../../../components/tags/TagsList'; +import { setProjectTags } from '../../../../../../api/components'; +import TagsList from '../../../../../../components/tags/TagsList'; import MetaTagsSelector from './MetaTagsSelector'; interface Props { @@ -62,7 +62,7 @@ export default class MetaTags extends React.PureComponent { if (this.canUpdateTags()) { return ( -
      (this.card = card)}> +
      (this.card = card)}> { ); } else { return ( -
      +
      { +jest.mock('../../../../../../../api/rules', () => { return { searchRules: jest.fn().mockResolvedValue({ total: 10 @@ -38,8 +38,8 @@ it('should render correctly', async () => { await waitAndUpdate(wrapper); expect(wrapper).toMatchSnapshot(); - expect(wrapper.find('.overview-deprecated-rules').exists()).toBe(true); - expect(wrapper.find('.overview-deleted-profile').exists()).toBe(true); + expect(wrapper.find('.project-info-deprecated-rules').exists()).toBe(true); + expect(wrapper.find('.project-info-deleted-profile').exists()).toBe(true); expect(searchRules).toBeCalled(); }); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaSize-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaSize-test.tsx new file mode 100644 index 00000000000..4f991d039b3 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaSize-test.tsx @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockComponent, mockMeasure } from '../../../../../../../helpers/testMocks'; +import { ComponentQualifier } from '../../../../../../../types/component'; +import { MetricKey } from '../../../../../../../types/metrics'; +import MetaSize, { MetaSizeProps } from '../MetaSize'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot('project'); + expect( + shallowRender({ component: mockComponent({ qualifier: ComponentQualifier.Application }) }) + ).toMatchSnapshot('application'); +}); + +function shallowRender(props: Partial = {}) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaTags-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaTags-test.tsx similarity index 95% rename from server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaTags-test.tsx rename to server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaTags-test.tsx index 1cc4a8597bc..f8464ab66c5 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaTags-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaTags-test.tsx @@ -19,7 +19,7 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; -import { mockComponent } from '../../../../helpers/testMocks'; +import { mockComponent } from '../../../../../../../helpers/testMocks'; import MetaTags from '../MetaTags'; const component = mockComponent({ diff --git a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaTagsSelector-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaTagsSelector-test.tsx similarity index 93% rename from server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaTagsSelector-test.tsx rename to server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaTagsSelector-test.tsx index 0a6002d00e7..26729890ba0 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaTagsSelector-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaTagsSelector-test.tsx @@ -20,10 +20,10 @@ /* eslint-disable import/first */ import { mount, shallow } from 'enzyme'; import * as React from 'react'; -import { searchProjectTags } from '../../../../api/components'; +import { searchProjectTags } from '../../../../../../../api/components'; import MetaTagsSelector from '../MetaTagsSelector'; -jest.mock('../../../../api/components', () => ({ +jest.mock('../../../../../../../api/components', () => ({ searchProjectTags: jest.fn() })); diff --git a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaLink-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaLink-test.tsx.snap similarity index 100% rename from server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaLink-test.tsx.snap rename to server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaLink-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaQualityProfiles-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaQualityProfiles-test.tsx.snap similarity index 83% rename from server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaQualityProfiles-test.tsx.snap rename to server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaQualityProfiles-test.tsx.snap index aeef836890a..2e585fd377d 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaQualityProfiles-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaQualityProfiles-test.tsx.snap @@ -2,20 +2,18 @@ exports[`should render correctly 1`] = ` -

      +

      overview.quality_profiles -

      +
      • - (js) + ( + js + ) name
        @@ -38,7 +38,9 @@ exports[`should render correctly 1`] = ` - (CSS) + ( + CSS + ) -

        +

        overview.quality_profiles -

        +
        • - (js) + ( + js + ) name
          @@ -95,7 +97,7 @@ exports[`should render correctly 2`] = ` overlay="overview.deprecated_profile.10" >
        • - (CSS) + ( + CSS + ) +

          + metric.ncloc.name +

          +
          + + 1 + + + + + + + + 1 + + + + metric.projects.name + + +
          + +`; + +exports[`should render correctly: project 1`] = ` + +

          + metric.ncloc.name +

          +
          + + 1 + + + + +
          +
          +`; diff --git a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap similarity index 90% rename from server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap rename to server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap index b1bb719ee73..c415bdfdab7 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap @@ -2,7 +2,7 @@ exports[`should render with tags and admin rights 1`] = `
          { + props.addNotification({ project: component.key, channel, type }); + }; + + const handleRemoveNotification = ({ channel, type }: { channel: string; type: string }) => { + props.removeNotification({ + project: component.key, + channel, + type + }); + }; + + const getCheckboxId = (type: string, channel: string) => { + return `project-notification-${component.key}-${type}-${channel}`; + }; + + const projectNotifications = notifications.filter(n => n.project && n.project === component.key); + + return ( + <> +

          {translate('project.info.notifications')}

          + + + {translate('notification.dispatcher.information')} + + + + + + + + ))} + + + + +
          + {channels.map(channel => ( + +

          {translate('notification.channel', channel)}

          +
          +
          + + ); +} + +export default withNotifications(ProjectNotifications); diff --git a/server/sonar-web/src/main/js/apps/overview/notifications/__tests__/ProjectNotifications.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/notifications/__tests__/ProjectNotifications-test.tsx similarity index 88% rename from server/sonar-web/src/main/js/apps/overview/notifications/__tests__/ProjectNotifications.tsx rename to server/sonar-web/src/main/js/app/components/nav/component/projectInformation/notifications/__tests__/ProjectNotifications-test.tsx index 0aa226a9843..e45d446687b 100644 --- a/server/sonar-web/src/main/js/apps/overview/notifications/__tests__/ProjectNotifications.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/notifications/__tests__/ProjectNotifications-test.tsx @@ -17,9 +17,10 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/* eslint-disable sonarjs/no-duplicate-string */ import { shallow } from 'enzyme'; import * as React from 'react'; -import { mockComponent } from '../../../../helpers/testMocks'; +import { mockComponent } from '../../../../../../../helpers/testMocks'; import { ProjectNotifications } from '../ProjectNotifications'; it('should render correctly', () => { @@ -43,7 +44,7 @@ it('should add and remove a notification for the project', () => { }); function shallowRender(props = {}) { - const wrapper = shallow( + return shallow( ); - - // Get the modal element. We need to trigger the ModalButton's `modal` prop, - // which is a function. It will return our Modal component. - return shallow( - wrapper.find('ModalButton').prop('modal')({ - onClose: jest.fn() - }) - ); } diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/notifications/__tests__/__snapshots__/ProjectNotifications-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/notifications/__tests__/__snapshots__/ProjectNotifications-test.tsx.snap new file mode 100644 index 00000000000..5006fe6a96c --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/notifications/__tests__/__snapshots__/ProjectNotifications-test.tsx.snap @@ -0,0 +1,75 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` + +

          + project.info.notifications +

          + + notification.dispatcher.information + + + + + + + + + + +
          + +

          + notification.channel.channel1 +

          +
          +

          + notification.channel.channel2 +

          +
          +
          +
          +`; diff --git a/server/sonar-web/src/main/js/app/styles/init/misc.css b/server/sonar-web/src/main/js/app/styles/init/misc.css index 2342b4676e3..9fa692cbb93 100644 --- a/server/sonar-web/src/main/js/app/styles/init/misc.css +++ b/server/sonar-web/src/main/js/app/styles/init/misc.css @@ -137,7 +137,7 @@ th.hide-overflow { } .big-padded { - padding: calc(2 * var(--gridSize)); + padding: calc(2 * var(--gridSize)) !important; } .padded-top { @@ -152,10 +152,6 @@ th.hide-overflow { padding-top: calc(var(--gridSize) / 2) !important; } -.little-padded-bottom { - padding-bottom: calc(var(--gridSize) / 2) !important; -} - td.little-spacer-left { padding-left: 4px !important; } @@ -210,6 +206,10 @@ th.huge-spacer-right { float: right !important; } +.borderless { + border: none !important; +} + .bordered { border: 1px solid var(--barBorderColor); } @@ -234,6 +234,10 @@ th.huge-spacer-right { overflow: hidden !important; } +.overflow-y-auto { + overflow-y: auto !important; +} + .max-width-100 { max-width: 100% !important; } @@ -301,6 +305,10 @@ th.huge-spacer-right { width: 600px !important; } +.max-height-100 { + max-height: 100% !important; +} + .justify { margin-bottom: -1em; text-align: justify; diff --git a/server/sonar-web/src/main/js/apps/account/projects/ProjectCard.tsx b/server/sonar-web/src/main/js/apps/account/projects/ProjectCard.tsx index 10e7d21534d..65542a67fd8 100644 --- a/server/sonar-web/src/main/js/apps/account/projects/ProjectCard.tsx +++ b/server/sonar-web/src/main/js/apps/account/projects/ProjectCard.tsx @@ -25,8 +25,8 @@ import Level from 'sonar-ui-common/components/ui/Level'; import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; import DateFromNow from '../../../components/intl/DateFromNow'; import DateTimeFormatter from '../../../components/intl/DateTimeFormatter'; -import MetaLink from '../../overview/meta/MetaLink'; -import { orderLinks } from '../../projectLinks/utils'; +import MetaLink from '../../../app/components/nav/component/projectInformation/meta/MetaLink'; +import { orderLinks } from '../../../helpers/projectLinks'; interface Props { project: T.MyProject; diff --git a/server/sonar-web/src/main/js/apps/overview/badges/ProjectBadges.tsx b/server/sonar-web/src/main/js/apps/overview/badges/ProjectBadges.tsx deleted file mode 100644 index 29ff1212c15..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/badges/ProjectBadges.tsx +++ /dev/null @@ -1,127 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { Button, ResetButtonLink } from 'sonar-ui-common/components/controls/buttons'; -import Modal from 'sonar-ui-common/components/controls/Modal'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import CodeSnippet from '../../../components/common/CodeSnippet'; -import { getBranchLikeQuery } from '../../../helpers/branch-like'; -import { isSonarCloud } from '../../../helpers/system'; -import { BranchLike } from '../../../types/branch-like'; -import BadgeButton from './BadgeButton'; -import BadgeParams from './BadgeParams'; -import './styles.css'; -import { BadgeOptions, BadgeType, getBadgeSnippet, getBadgeUrl } from './utils'; - -interface Props { - branchLike?: BranchLike; - metrics: T.Dict; - project: string; - qualifier: string; -} - -interface State { - open: boolean; - selectedType: BadgeType; - badgeOptions: BadgeOptions; -} - -export default class ProjectBadges extends React.PureComponent { - state: State = { - open: false, - selectedType: BadgeType.measure, - badgeOptions: { color: 'white', metric: 'alert_status' } - }; - - handleClose = () => { - this.setState({ open: false }); - }; - - handleOpen = () => { - this.setState({ open: true }); - }; - - handleSelectBadge = (selectedType: BadgeType) => { - this.setState({ selectedType }); - }; - - handleUpdateOptions = (options: Partial) => { - this.setState(state => ({ badgeOptions: { ...state.badgeOptions, ...options } })); - }; - - render() { - const { branchLike, project, qualifier } = this.props; - const { selectedType, badgeOptions } = this.state; - const header = translate('overview.badges.title'); - const fullBadgeOptions = { project, ...badgeOptions, ...getBranchLikeQuery(branchLike) }; - const badges = isSonarCloud() - ? [BadgeType.measure, BadgeType.qualityGate, BadgeType.marketing] - : [BadgeType.measure, BadgeType.qualityGate]; - return ( -
          - - {this.state.open && ( - -
          -

          {header}

          -
          -
          -

          - {translate('overview.badges.description', qualifier)} -

          -
          - {badges.map(type => ( - - ))} -
          -

          - {translate('overview.badges', selectedType, 'description', qualifier)} -

          - - -
          -
          - - {translate('close')} - -
          -
          - )} -
          - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/ProjectBadges-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/ProjectBadges-test.tsx.snap deleted file mode 100644 index 43a0e7f7638..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/ProjectBadges-test.tsx.snap +++ /dev/null @@ -1,180 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should display the modal after click on sonarcloud 1`] = ` -
          - -
          -`; - -exports[`should display the modal after click on sonarcloud 2`] = ` - -
          -

          - overview.badges.title -

          -
          -
          -

          - overview.badges.description.TRK -

          -
          - - - -
          -

          - overview.badges.measure.description.TRK -

          - - -
          -
          - - close - -
          -
          -`; - -exports[`should display the modal after click on sonarqube 1`] = ` -
          - -
          -`; - -exports[`should display the modal after click on sonarqube 2`] = ` - -
          -

          - overview.badges.title -

          -
          -
          -

          - overview.badges.description.TRK -

          -
          - - -
          -

          - overview.badges.measure.description.TRK -

          - - -
          -
          - - close - -
          -
          -`; diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx deleted file mode 100644 index bd9fbfaca1a..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx +++ /dev/null @@ -1,171 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import PrivacyBadgeContainer from '../../../components/common/PrivacyBadgeContainer'; -import { hasPrivateAccess } from '../../../helpers/organizations'; -import { isLoggedIn } from '../../../helpers/users'; -import { - getAppState, - getCurrentUser, - getMyOrganizations, - getOrganizationByKey, - Store -} from '../../../store/rootReducer'; -import { BranchLike } from '../../../types/branch-like'; -import MetaKey from './MetaKey'; -import MetaLinks from './MetaLinks'; -import MetaOrganizationKey from './MetaOrganizationKey'; -import MetaQualityGate from './MetaQualityGate'; -import MetaQualityProfiles from './MetaQualityProfiles'; -import MetaTags from './MetaTags'; - -const ProjectBadges = lazyLoadComponent(() => import('../badges/ProjectBadges'), 'ProjectBadges'); -const ProjectNotifications = lazyLoadComponent( - () => import('../notifications/ProjectNotifications'), - 'ProjectNotifications' -); - -interface StateToProps { - appState: T.AppState; - currentUser: T.CurrentUser; - organization?: T.Organization; - userOrganizations: T.Organization[]; -} - -interface OwnProps { - branchLike?: BranchLike; - component: T.Component; - history?: { - [metric: string]: Array<{ date: Date; value?: string }>; - }; - measures?: T.MeasureEnhanced[]; - metrics?: T.Dict; - onComponentChange: (changes: {}) => void; -} - -type Props = OwnProps & StateToProps; - -export class Meta extends React.PureComponent { - renderQualityInfos() { - const { organizationsEnabled } = this.props.appState; - const { component, currentUser, organization, userOrganizations } = this.props; - const { qualifier, qualityProfiles, qualityGate } = component; - const isProject = qualifier === 'TRK'; - - if ( - !isProject || - (organizationsEnabled && !hasPrivateAccess(currentUser, organization, userOrganizations)) - ) { - return null; - } - - return ( -
          - {qualityGate && ( - - )} - - {qualityProfiles && qualityProfiles.length > 0 && ( - - )} -
          - ); - } - - render() { - const { organizationsEnabled } = this.props.appState; - const { branchLike, component, currentUser, metrics, organization } = this.props; - const { qualifier, description, visibility } = component; - - const isProject = qualifier === 'TRK'; - const isApp = qualifier === 'APP'; - const isPrivate = visibility === 'private'; - const canUseBadges = !isPrivate && (isProject || isApp); - const canConfigureNotifications = isLoggedIn(currentUser); - - return ( -
          -
          -

          - {translate('overview.about_this_project', qualifier)} - {component.visibility && ( - - )} -

          - {description !== undefined &&

          {description}

          } - {isProject && ( - - )} -
          - - {this.renderQualityInfos()} - - {isProject && } - -
          - - {organizationsEnabled && } -
          - - {(canUseBadges || canConfigureNotifications) && ( -
          - {canUseBadges && metrics !== undefined && ( - - )} - - {canConfigureNotifications && ( - - )} -
          - )} -
          - ); - } -} - -const mapStateToProps = (state: Store, { component }: OwnProps) => ({ - appState: getAppState(state), - currentUser: getCurrentUser(state), - organization: getOrganizationByKey(state, component.organization), - userOrganizations: getMyOrganizations(state) -}); - -export default connect(mapStateToProps)(Meta); diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx deleted file mode 100644 index 353ccfed13b..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx +++ /dev/null @@ -1,109 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 classNames from 'classnames'; -import * as React from 'react'; -import SizeRating from 'sonar-ui-common/components/ui/SizeRating'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import { formatMeasure } from 'sonar-ui-common/helpers/measures'; -import LanguageDistributionContainer from '../../../components/charts/LanguageDistributionContainer'; -import DrilldownLink from '../../../components/shared/DrilldownLink'; -import { BranchLike } from '../../../types/branch-like'; - -interface Props { - branchLike?: BranchLike; - component: T.LightComponent; - measures: T.MeasureEnhanced[]; -} - -export default class MetaSize extends React.PureComponent { - renderLoC = (ncloc?: T.MeasureEnhanced) => ( -
          - {ncloc && ( - - - - )} - {ncloc ? ( - - {formatMeasure(ncloc.value, 'SHORT_INT')} - - ) : ( - 0 - )} -
          - ); - - renderLoCDistribution = () => { - const languageDistribution = this.props.measures.find( - measure => measure.metric.key === 'ncloc_language_distribution' - ); - - const className = - this.props.component.qualifier === 'TRK' ? 'overview-meta-size-lang-dist' : 'big-spacer-top'; - - return languageDistribution && languageDistribution.value !== undefined ? ( -
          - -
          - ) : null; - }; - - renderProjects = () => { - const projects = this.props.measures.find(measure => measure.metric.key === 'projects'); - return ( -
          - {projects ? ( - - {formatMeasure(projects.value, 'SHORT_INT')} - - ) : ( - 0 - )} -
          {translate('metric.projects.name')}
          -
          - ); - }; - - render() { - const ncloc = this.props.measures.find(measure => measure.metric.key === 'ncloc'); - - if (ncloc == null && this.props.component.qualifier !== 'APP') { - return null; - } - - return ( -
          - {this.props.component.qualifier === 'APP' && this.renderProjects()} - {this.renderLoC(ncloc)} - {this.renderLoCDistribution()} -
          - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaContainer-test.tsx b/server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaContainer-test.tsx deleted file mode 100644 index b6eee2473bf..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaContainer-test.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import { - mockAppState, - mockComponent, - mockLoggedInUser, - mockOrganization -} from '../../../../helpers/testMocks'; -import { Meta } from '../MetaContainer'; - -it('should render correctly', () => { - const wrapper = shallowRender(); - expect(wrapper).toMatchSnapshot(); - expect(metaQualityGateRendered(wrapper)).toBe(true); -}); - -it('should hide QG and QP links if the organization has a paid plan, and the user is not a member', () => { - const wrapper = shallowRender({ - organization: mockOrganization({ key: 'other_key', subscription: 'PAID' }) - }); - expect(wrapper).toMatchSnapshot(); - expect(metaQualityGateRendered(wrapper)).toBe(false); -}); - -it('should show QG and QP links if the organization has a paid plan, and the user is a member', () => { - const wrapper = shallowRender({ - organization: mockOrganization({ subscription: 'PAID' }) - }); - expect(wrapper).toMatchSnapshot(); - expect(metaQualityGateRendered(wrapper)).toBe(true); -}); - -function metaQualityGateRendered(wrapper: any) { - return wrapper.find('#overview-meta-quality-gate').exists(); -} - -function shallowRender(props: Partial = {}) { - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaContainer-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaContainer-test.tsx.snap deleted file mode 100644 index 0ca8d471138..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaContainer-test.tsx.snap +++ /dev/null @@ -1,398 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should hide QG and QP links if the organization has a paid plan, and the user is not a member 1`] = ` -
          -
          -

          - overview.about_this_project.TRK -

          - -
          - -
          - - -
          -
          - - -
          -
          -`; - -exports[`should render correctly 1`] = ` -
          -
          -

          - overview.about_this_project.TRK -

          - -
          -
          - - -
          - -
          - - -
          -
          - - -
          -
          -`; - -exports[`should show QG and QP links if the organization has a paid plan, and the user is a member 1`] = ` -
          -
          -

          - overview.about_this_project.TRK -

          - -
          -
          - - -
          - -
          - - -
          -
          - - -
          -
          -`; diff --git a/server/sonar-web/src/main/js/apps/overview/notifications/ProjectNotifications.tsx b/server/sonar-web/src/main/js/apps/overview/notifications/ProjectNotifications.tsx deleted file mode 100644 index 875f0fc83b3..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/notifications/ProjectNotifications.tsx +++ /dev/null @@ -1,116 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { Button, ResetButtonLink } from 'sonar-ui-common/components/controls/buttons'; -import Modal from 'sonar-ui-common/components/controls/Modal'; -import ModalButton from 'sonar-ui-common/components/controls/ModalButton'; -import { Alert } from 'sonar-ui-common/components/ui/Alert'; -import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import { - withNotifications, - WithNotificationsProps -} from '../../../components/hoc/withNotifications'; -import NotificationsList from '../../account/notifications/NotificationsList'; - -interface Props { - className?: string; - component: T.Component; -} - -export function ProjectNotifications(props: WithNotificationsProps & Props) { - const { channels, className, component, loading, notifications, perProjectTypes } = props; - - const header = translate('my_account.notifications'); - - const handleAddNotification = ({ channel, type }: { channel: string; type: string }) => { - props.addNotification({ project: component.key, channel, type }); - }; - - const handleRemoveNotification = ({ channel, type }: { channel: string; type: string }) => { - props.removeNotification({ - project: component.key, - channel, - type - }); - }; - - const getCheckboxId = (type: string, channel: string) => { - return `project-notification-${component.key}-${type}-${channel}`; - }; - - const projectNotifications = notifications.filter(n => n.project && n.project === component.key); - - return ( -
          - ( - -
          -

          {header}

          -
          -
          - {translate('notification.dispatcher.information')} - - - - - - - ))} - - - - -
          - {channels.map(channel => ( - -

          {translate('notification.channel', channel)}

          -
          -
          -
          -
          - - {translate('close')} - -
          -
          - )}> - {({ onClick }) => ( - - )} -
          -
          - ); -} - -export default withNotifications(ProjectNotifications); diff --git a/server/sonar-web/src/main/js/apps/overview/notifications/__tests__/__snapshots__/ProjectNotifications.tsx.snap b/server/sonar-web/src/main/js/apps/overview/notifications/__tests__/__snapshots__/ProjectNotifications.tsx.snap deleted file mode 100644 index e542aa63bcb..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/notifications/__tests__/__snapshots__/ProjectNotifications.tsx.snap +++ /dev/null @@ -1,108 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` - -
          -

          - my_account.notifications -

          -
          -
          - - notification.dispatcher.information - - - - - - - - - - -
          - -

          - notification.channel.channel1 -

          -
          -

          - notification.channel.channel2 -

          -
          -
          -
          - -
          -`; diff --git a/server/sonar-web/src/main/js/apps/overview/styles.css b/server/sonar-web/src/main/js/apps/overview/styles.css index 6074716b4b5..e519db8d7d2 100644 --- a/server/sonar-web/src/main/js/apps/overview/styles.css +++ b/server/sonar-web/src/main/js/apps/overview/styles.css @@ -168,73 +168,6 @@ vertical-align: top; } -/* - * Meta - TODO REMOVE ME!! - */ - -.overview-meta { - background-color: var(--barBackgroundColor); -} - -.overview-meta-card { - min-width: 200px; - box-sizing: border-box; -} - -.overview-meta-card + .overview-meta-card { - margin-top: calc(2 * var(--gridSize)); - padding-top: calc(2 * var(--gridSize) - 1px); - border-top: 1px solid var(--barBorderColor); -} - -.overview-meta-description { - margin-top: calc(-0.5 * var(--gridSize)); - line-height: 1.5; - color: var(--secondFontColor); -} - -.overview-meta-header { - margin-bottom: calc(0.5 * var(--gridSize)); - color: var(--baseFontColor); -} - -.overview-meta-list > li { - /* 1px to not cut icons on the left */ - padding-left: 1px; - padding-bottom: 4px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.overview-meta-tags { - position: relative; -} - -.overview-meta-size-lang-dist { - display: inline-block; - vertical-align: middle; - width: 160px; - min-height: 40px; - border-left: 1px solid var(--barBorderColor); - box-sizing: border-box; -} - -.overview-key { - width: 100%; - background-color: transparent !important; -} - -.overview-deleted-profile, -.overview-deprecated-rules { - margin: 4px -6px 4px; - padding: 3px 6px !important; - border: 1px solid var(--alertBorderError); - border-radius: 3px; - background-color: var(--alertBackgroundError); -} - /* * Animations */ diff --git a/server/sonar-web/src/main/js/apps/projectLinks/LinkRow.tsx b/server/sonar-web/src/main/js/apps/projectLinks/LinkRow.tsx index d2677e7d184..3f40e9df66d 100644 --- a/server/sonar-web/src/main/js/apps/projectLinks/LinkRow.tsx +++ b/server/sonar-web/src/main/js/apps/projectLinks/LinkRow.tsx @@ -23,7 +23,7 @@ import ConfirmButton from 'sonar-ui-common/components/controls/ConfirmButton'; import ProjectLinkIcon from 'sonar-ui-common/components/icons/ProjectLinkIcon'; import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; import isValidUri from '../../app/utils/isValidUri'; -import { getLinkName, isProvided } from './utils'; +import { getLinkName, isProvided } from '../../helpers/projectLinks'; interface Props { link: T.ProjectLink; diff --git a/server/sonar-web/src/main/js/apps/projectLinks/Table.tsx b/server/sonar-web/src/main/js/apps/projectLinks/Table.tsx index fd7591f79b8..0ff18c285c0 100644 --- a/server/sonar-web/src/main/js/apps/projectLinks/Table.tsx +++ b/server/sonar-web/src/main/js/apps/projectLinks/Table.tsx @@ -19,8 +19,8 @@ */ import * as React from 'react'; import { translate } from 'sonar-ui-common/helpers/l10n'; +import { orderLinks } from '../../helpers/projectLinks'; import LinkRow from './LinkRow'; -import { orderLinks } from './utils'; interface Props { links: T.ProjectLink[]; diff --git a/server/sonar-web/src/main/js/apps/projectLinks/__tests__/utils-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/projectLinks-test.ts similarity index 66% rename from server/sonar-web/src/main/js/apps/projectLinks/__tests__/utils-test.ts rename to server/sonar-web/src/main/js/helpers/__tests__/projectLinks-test.ts index 3f81d40a3a6..3f0d9e3ef84 100644 --- a/server/sonar-web/src/main/js/apps/projectLinks/__tests__/utils-test.ts +++ b/server/sonar-web/src/main/js/helpers/__tests__/projectLinks-test.ts @@ -17,11 +17,11 @@ * 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 utils from '../utils'; +import { getLinkName, isProvided, orderLinks } from '../projectLinks'; it('#isProvided', () => { - expect(utils.isProvided({ type: 'homepage' })).toBe(true); - expect(utils.isProvided({ type: 'custom' })).toBe(false); + expect(isProvided({ type: 'homepage' })).toBe(true); + expect(isProvided({ type: 'custom' })).toBe(false); }); it('#orderLinks', () => { @@ -29,12 +29,12 @@ it('#orderLinks', () => { const issues = { type: 'issue' }; const foo = { name: 'foo', type: 'foo' }; const bar = { name: 'bar', type: 'bar' }; - expect(utils.orderLinks([foo, homepage, issues, bar])).toEqual([homepage, issues, bar, foo]); - expect(utils.orderLinks([foo, bar])).toEqual([bar, foo]); - expect(utils.orderLinks([issues, homepage])).toEqual([homepage, issues]); + expect(orderLinks([foo, homepage, issues, bar])).toEqual([homepage, issues, bar, foo]); + expect(orderLinks([foo, bar])).toEqual([bar, foo]); + expect(orderLinks([issues, homepage])).toEqual([homepage, issues]); }); it('#getLinkName', () => { - expect(utils.getLinkName({ type: 'homepage' })).toBe('project_links.homepage'); - expect(utils.getLinkName({ name: 'foo', type: 'custom' })).toBe('foo'); + expect(getLinkName({ type: 'homepage' })).toBe('project_links.homepage'); + expect(getLinkName({ name: 'foo', type: 'custom' })).toBe('foo'); }); diff --git a/server/sonar-web/src/main/js/apps/projectLinks/utils.ts b/server/sonar-web/src/main/js/helpers/projectLinks.ts similarity index 100% rename from server/sonar-web/src/main/js/apps/projectLinks/utils.ts rename to server/sonar-web/src/main/js/helpers/projectLinks.ts diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 539e962987e..8babd52b5da 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -1318,7 +1318,7 @@ project_quality_profile.successfully_updated={0} quality profile has been succes # #------------------------------------------------------------------------------ project_quality_gate.default_qgate=Default -project_quality_gate.successfully_updated=Quality gate has been successfully updated. +project_quality_gate.successfully_updated=Quality Gate has been successfully updated. #------------------------------------------------------------------------------ # @@ -1333,6 +1333,18 @@ projects_management.delete_selected_warning=You're about to delete {0} selected projects_management.delete_all_warning=You're about to delete all {0} items. projects_management.project_has_been_successfully_created=Project {project} has been successfully created. +#------------------------------------------------------------------------------ +# +# PROJECT INFORMATION DRAWER +# +#------------------------------------------------------------------------------ + +project.info.title=Project information +application.info.title=Application information +project.info.description=Description +project.info.quality_gate=Quality Gate used +project.info.to_notifications=Set notifications +project.info.notifications=Set notifications #------------------------------------------------------------------------------ # @@ -2654,7 +2666,7 @@ overview.you_should_define_quality_gate=You should define a quality gate on this overview.quality_gate.ignored_conditions=Some Quality Gate conditions on New Code were ignored because of the small number of New Lines overview.quality_gate.ignored_conditions.tooltip=At the start of a new code period, if very few lines have been added or modified, it might be difficult to reach the desired level of code coverage or duplications. To prevent Quality Gate failure when there's little that can be done about it, Quality Gate conditions about duplications in new code and coverage on new code are ignored until the number of new lines is at least 20. overview.quality_gate.conditions_on_new_code=Only conditions on new code that are defined in the Quality Gate are checked. See the {link} associated to the project for details. -overview.quality_profiles=Quality Profiles +overview.quality_profiles=Quality Profiles used overview.new_code_period_x=New Code: {0} overview.max_new_code_period_from_x=Max New Code from: {0} overview.started_x=Started {0} @@ -2724,11 +2736,10 @@ overview.complexity_tooltip.file={0} files have complexity around {1} overview.deprecated_profile=This quality profile uses {0} deprecated rules and should be updated. overview.deleted_profile={0} has been deleted since the last analysis. - overview.badges.get_badge.TRK=Get project badges overview.badges.get_badge.VW=Get portfolio badges overview.badges.get_badge.APP=Get application badges -overview.badges.title=Badges +overview.badges.title=Get project badges overview.badges.description.TRK=Show the status of your project metrics on your README or website. Pick your style: overview.badges.description.VW=Show the status of your portfolio metrics on your README or website. Pick your style: overview.badges.description.APP=Show the status of your application metrics on your README or website. Pick your style: @@ -2739,17 +2750,17 @@ overview.badges.options.colors.orange=Orange overview.badges.options.formats.md=Markdown overview.badges.options.formats.url=Image URL only overview.badges.measure.alt=Standard badge -overview.badges.measure.description.TRK=This badge dynamically displays the current status of one metric of your project. -overview.badges.measure.description.VW=This badge dynamically displays the current status of one metric of your portfolio. -overview.badges.measure.description.APP=This badge dynamically displays the current status of one metric of your application. +overview.badges.measure.description.TRK=Displays the current status of one metric of your project. +overview.badges.measure.description.VW=Displays the current status of one metric of your portfolio. +overview.badges.measure.description.APP=Displays the current status of one metric of your application. overview.badges.marketing.alt=Scanned on SonarCloud badge overview.badges.marketing.description=This badge lets you advertise that you're using SonarCloud for code quality. overview.badges.marketing.description.TRK=This badge lets you advertise that you're using SonarCloud for code quality. overview.badges.quality_gate.alt=Quality Gate badge -overview.badges.quality_gate.description=This badge dynamically displays the current quality gate status of your project. -overview.badges.quality_gate.description.APP=This badge dynamically displays the current quality gate status of your application. -overview.badges.quality_gate.description.TRK=This badge dynamically displays the current quality gate status of your project. -overview.badges.quality_gate.description.VW=This badge dynamically displays the current quality gate status of your portfolio. +overview.badges.quality_gate.description=Displays the current quality gate status of your project. +overview.badges.quality_gate.description.APP=Displays the current quality gate status of your application. +overview.badges.quality_gate.description.TRK=Displays the current quality gate status of your project. +overview.badges.quality_gate.description.VW=Displays the current quality gate status of your portfolio. #------------------------------------------------------------------------------ -- 2.39.5