aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorStas Vilchik <stas.vilchik@sonarsource.com>2017-09-22 13:56:45 +0200
committerGitHub <noreply@github.com>2017-09-22 13:56:45 +0200
commit94f500e0091bc438c306ee76454c5395d223fe30 (patch)
tree524b75969a1455f6410c5c6d14ccc9979e871c1b /server/sonar-web
parent93044147ce1178f75054ed27eb520c3ec8fb30dc (diff)
downloadsonarqube-94f500e0091bc438c306ee76454c5395d223fe30.tar.gz
sonarqube-94f500e0091bc438c306ee76454c5395d223fe30.zip
apply branches feedback (#2535)
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/js/app/components/ComponentContainer.tsx32
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx17
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css10
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx9
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranch.tsx49
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranchesMenu.tsx42
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranchesMenuItem.tsx131
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx51
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx6
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/DeleteBranchModal.tsx (renamed from server/sonar-web/src/main/js/apps/projectBranches/components/DeleteBranchModal.tsx)7
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/NoBranchSupportPopup.tsx48
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/RenameBranchModal.tsx (renamed from server/sonar-web/src/main/js/apps/projectBranches/components/RenameBranchModal.tsx)7
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx6
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranch-test.tsx34
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranchesMenu-test.tsx9
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranchesMenuItem-test.tsx98
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMenu-test.tsx84
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMeta-test.tsx15
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/DeleteBranchModal-test.tsx (renamed from server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/DeleteBranchModal-test.tsx)8
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/NoBranchSupportPopup-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/RenameBranchModal-test.tsx (renamed from server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/RenameBranchModal-test.tsx)8
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranch-test.tsx.snap41
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenu-test.tsx.snap7
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenuItem-test.tsx.snap12
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap54
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/DeleteBranchModal-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/DeleteBranchModal-test.tsx.snap)0
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/NoBranchSupportPopup-test.tsx.snap37
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/RenameBranchModal-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/RenameBranchModal-test.tsx.snap)0
-rw-r--r--server/sonar-web/src/main/js/app/types.ts2
-rw-r--r--server/sonar-web/src/main/js/app/utils/startReactApp.js2
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/App.js10
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/App.tsx60
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/BranchRow.tsx139
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/App-test.tsx34
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/BranchRow-test.tsx65
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap73
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/BranchRow-test.tsx.snap100
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/routes.ts30
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx42
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx23
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap347
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/App.js2
-rw-r--r--server/sonar-web/src/main/js/components/common/BranchStatus.tsx52
-rw-r--r--server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/BranchStatus-test.tsx.snap48
45 files changed, 735 insertions, 1144 deletions
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 085b4be1979..a1d01845343 100644
--- a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
@@ -122,36 +122,36 @@ export default class ComponentContainer extends React.PureComponent<Props, State
const { query } = this.props.location;
const { branches, component, loading } = this.state;
- if (loading) {
- return <i className="spinner" />;
- }
-
- if (!component) {
+ if (!loading && !component) {
return <ComponentContainerNotFound />;
}
const branch = branches.find(b => (query.branch ? b.name === query.branch : b.isMain));
- const isFile = ['FIL', 'UTS'].includes(component.qualifier);
- const configuration = component.configuration || {};
return (
<div>
- {!isFile && (
+ {component &&
+ !['FIL', 'UTS'].includes(component.qualifier) && (
<ComponentNav
branches={branches}
currentBranch={branch}
component={component}
- conf={configuration}
location={this.props.location}
+ onBranchesChange={this.handleBranchesChange}
/>
)}
- {React.cloneElement(this.props.children, {
- branch,
- branches,
- component: component,
- onBranchesChange: this.handleBranchesChange,
- onComponentChange: this.handleComponentChange
- })}
+ {loading ? (
+ <div className="page page-limited">
+ <i className="spinner" />
+ </div>
+ ) : (
+ React.cloneElement(this.props.children, {
+ branch,
+ branches,
+ component,
+ onComponentChange: this.handleComponentChange
+ })
+ )}
</div>
);
}
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx
index 105931959f9..01ff6e3a4fa 100644
--- a/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx
@@ -106,20 +106,3 @@ it("doesn't load branches portfolio", () => {
expect(wrapper.find(Inner).exists()).toBeTruthy();
});
});
-
-it('updates branches on change', () => {
- (getBranches as jest.Mock<any>).mockImplementation(() => Promise.resolve([]));
- const wrapper = shallow(
- <ComponentContainer location={{ query: { id: 'portfolioKey' } }}>
- <Inner />
- </ComponentContainer>
- );
- (wrapper.instance() as ComponentContainer).mounted = true;
- wrapper.setState({
- branches: [{ isMain: true }],
- component: { breadcrumbs: [{ key: 'projectKey', name: 'project', qualifier: 'TRK' }] },
- loading: false
- });
- (wrapper.find(Inner).prop('onBranchesChange') as Function)();
- expect(getBranches).toBeCalledWith('projectKey');
-});
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css
index 5f3958f35bd..347ea1a3df7 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css
+++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css
@@ -25,4 +25,14 @@
.navbar-context-meta-branch-menu-item {
display: flex !important;
justify-content: space-between;
+ align-items: center;
+}
+
+.navbar-context-meta-branch-menu-item-name {
+ flex: 1;
+}
+
+.navbar-context-meta-branch-menu-item-actions {
+ height: 12px;
+ margin-left: 32px;
}
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 1c7a748ecef..5d78833af2d 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
@@ -24,7 +24,7 @@ import ComponentNavMeta from './ComponentNavMeta';
import ComponentNavMenu from './ComponentNavMenu';
import ComponentNavBranch from './ComponentNavBranch';
import RecentHistory from '../../RecentHistory';
-import { Branch, Component, ComponentConfiguration } from '../../../types';
+import { Branch, Component } from '../../../types';
import ContextNavBar from '../../../../components/nav/ContextNavBar';
import { getTasksForComponent } from '../../../../api/ce';
import { STATUSES } from '../../../../apps/background-tasks/constants';
@@ -34,8 +34,8 @@ interface Props {
branches: Branch[];
currentBranch?: Branch;
component: Component;
- conf: ComponentConfiguration;
location: {};
+ onBranchesChange: () => void;
}
interface State {
@@ -102,17 +102,17 @@ export default class ComponentNav extends React.PureComponent<Props, State> {
{this.props.currentBranch && (
<ComponentNavBranch
branches={this.props.branches}
+ component={this.props.component}
currentBranch={this.props.currentBranch}
// to close dropdown on any location change
location={this.props.location}
- project={this.props.component}
+ onBranchesChange={this.props.onBranchesChange}
/>
)}
<ComponentNavMeta
branch={this.props.currentBranch}
component={this.props.component}
- conf={this.props.conf}
incremental={this.state.incremental}
isInProgress={this.state.isInProgress}
isFailed={this.state.isFailed}
@@ -122,7 +122,6 @@ export default class ComponentNav extends React.PureComponent<Props, State> {
<ComponentNavMenu
branch={this.props.currentBranch}
component={this.props.component}
- conf={this.props.conf}
// to re-render selected menu item
location={this.props.location}
/>
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranch.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranch.tsx
index dd5ecaa3020..21010a1ec69 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranch.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranch.tsx
@@ -22,7 +22,6 @@ import * as classNames from 'classnames';
import * as PropTypes from 'prop-types';
import ComponentNavBranchesMenu from './ComponentNavBranchesMenu';
import SingleBranchHelperPopup from './SingleBranchHelperPopup';
-import NoBranchSupportPopup from './NoBranchSupportPopup';
import { Branch, Component } from '../../../types';
import BranchIcon from '../../../../components/icons-components/BranchIcon';
import { isShortLivingBranch } from '../../../../helpers/branches';
@@ -33,9 +32,10 @@ import Tooltip from '../../../../components/controls/Tooltip';
interface Props {
branches: Branch[];
+ component: Component;
currentBranch: Branch;
location?: any;
- project: Component;
+ onBranchesChange: () => void;
}
interface State {
@@ -53,8 +53,7 @@ export default class ComponentNavBranch extends React.PureComponent<Props, State
};
static contextTypes = {
- branchesEnabled: PropTypes.bool.isRequired,
- onSonarCloud: PropTypes.bool
+ branchesEnabled: PropTypes.bool.isRequired
};
componentDidMount() {
@@ -63,8 +62,8 @@ export default class ComponentNavBranch extends React.PureComponent<Props, State
componentWillReceiveProps(nextProps: Props) {
if (
- nextProps.project !== this.props.project ||
- nextProps.currentBranch !== this.props.currentBranch ||
+ nextProps.component !== this.props.component ||
+ this.differentBranches(nextProps.currentBranch, this.props.currentBranch) ||
nextProps.location !== this.props.location
) {
this.setState({ dropdownOpen: false, singleBranchPopupOpen: false });
@@ -75,11 +74,16 @@ export default class ComponentNavBranch extends React.PureComponent<Props, State
this.mounted = false;
}
+ differentBranches(a: Branch, b: Branch) {
+ // if main branch changes name, we should not close the dropdown
+ return a.isMain && b.isMain ? false : a.name !== b.name;
+ }
+
handleClick = (event: React.SyntheticEvent<HTMLElement>) => {
event.preventDefault();
event.stopPropagation();
event.currentTarget.blur();
- this.setState({ dropdownOpen: true });
+ this.setState(state => ({ dropdownOpen: !state.dropdownOpen }));
};
closeDropdown = () => {
@@ -117,12 +121,15 @@ export default class ComponentNavBranch extends React.PureComponent<Props, State
};
renderDropdown = () => {
+ const { configuration } = this.props.component;
return this.state.dropdownOpen ? (
<ComponentNavBranchesMenu
branches={this.props.branches}
+ canAdmin={configuration && configuration.showSettings}
+ component={this.props.component}
currentBranch={this.props.currentBranch}
+ onBranchesChange={this.props.onBranchesChange}
onClose={this.closeDropdown}
- project={this.props.project}
/>
) : null;
};
@@ -160,35 +167,11 @@ export default class ComponentNavBranch extends React.PureComponent<Props, State
</div>
);
- renderNoBranchSupportPopup = () => (
- <div className="display-inline-block spacer-left">
- <a className="link-no-underline" href="#" onClick={this.handleNoBranchSupportClick}>
- <HelpIcon fill="#cdcdcd" />
- </a>
- <BubblePopupHelper
- isOpen={this.state.noBranchSupportPopupOpen}
- position="bottomleft"
- popup={<NoBranchSupportPopup />}
- togglePopup={this.toggleNoBranchSupportPopup}
- />
- </div>
- );
-
render() {
const { branches, currentBranch } = this.props;
- if (this.context.onSonarCloud && !this.context.branchesEnabled) {
- return null;
- }
-
if (!this.context.branchesEnabled) {
- return (
- <div className="navbar-context-branches">
- <BranchIcon branch={currentBranch} className="little-spacer-right" color="#cdcdcd" />
- <span className="note">{currentBranch.name}</span>
- {this.renderNoBranchSupportPopup()}
- </div>
- );
+ return null;
}
if (branches.length < 2) {
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranchesMenu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranchesMenu.tsx
index 9f41411bbad..f4ea43b0bc6 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranchesMenu.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranchesMenu.tsx
@@ -28,14 +28,15 @@ import {
} from '../../../../helpers/branches';
import { translate } from '../../../../helpers/l10n';
import { getProjectBranchUrl } from '../../../../helpers/urls';
-import { Link } from 'react-router';
import Tooltip from '../../../../components/controls/Tooltip';
interface Props {
branches: Branch[];
+ canAdmin?: boolean;
+ component: Component;
currentBranch: Branch;
+ onBranchesChange: () => void;
onClose: () => void;
- project: Component;
}
interface State {
@@ -65,7 +66,9 @@ export default class ComponentNavBranchesMenu extends React.PureComponent<Props,
);
handleClickOutside = (event: Event) => {
- if (!this.node || !this.node.contains(event.target as HTMLElement)) {
+ // do not close when rename or delete branch modal is open
+ const modal = document.querySelector('.modal');
+ if (!modal && (!this.node || !this.node.contains(event.target as HTMLElement))) {
this.props.onClose();
}
};
@@ -125,11 +128,23 @@ export default class ComponentNavBranchesMenu extends React.PureComponent<Props,
};
getSelected = () => {
+ if (this.state.selected) {
+ return this.state.selected;
+ }
+
const branches = this.getFilteredBranches();
- return this.state.selected || (branches.length > 0 && branches[0].name);
+ if (branches.find(b => b.name === this.props.currentBranch.name)) {
+ return this.props.currentBranch.name;
+ }
+
+ if (branches.length > 0) {
+ return branches[0].name;
+ }
+
+ return undefined;
};
- getProjectBranchUrl = (branch: Branch) => getProjectBranchUrl(this.props.project.key, branch);
+ getProjectBranchUrl = (branch: Branch) => getProjectBranchUrl(this.props.component.key, branch);
isSelected = (branch: Branch) => branch.name === this.getSelected();
@@ -179,8 +194,10 @@ export default class ComponentNavBranchesMenu extends React.PureComponent<Props,
menu.push(
<ComponentNavBranchesMenuItem
branch={branch}
- component={this.props.project}
+ canAdmin={this.props.canAdmin}
+ component={this.props.component}
key={branch.name}
+ onBranchesChange={this.props.onBranchesChange}
onSelect={this.handleSelect}
selected={branch.name === selected}
/>
@@ -191,23 +208,10 @@ export default class ComponentNavBranchesMenu extends React.PureComponent<Props,
};
render() {
- const { project } = this.props;
- const showManageLink =
- project.qualifier === 'TRK' && project.configuration && project.configuration.showSettings;
-
return (
<div className="dropdown-menu dropdown-menu-shadow" ref={node => (this.node = node)}>
{this.renderSearch()}
{this.renderBranchesList()}
- {showManageLink && (
- <div className="dropdown-bottom-hint text-right">
- <Link
- className="text-muted"
- to={{ pathname: '/project/branches', query: { id: project.key } }}>
- {translate('branches.manage')}
- </Link>
- </div>
- )}
</div>
);
}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranchesMenuItem.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranchesMenuItem.tsx
index fa49b53d2d8..a1e02a1aa8f 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranchesMenuItem.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranchesMenuItem.tsx
@@ -20,48 +20,125 @@
import * as React from 'react';
import { Link } from 'react-router';
import * as classNames from 'classnames';
+import DeleteBranchModal from './DeleteBranchModal';
+import RenameBranchModal from './RenameBranchModal';
import BranchStatus from '../../../../components/common/BranchStatus';
import { Branch, Component } from '../../../types';
import BranchIcon from '../../../../components/icons-components/BranchIcon';
+import ChangeIcon from '../../../../components/icons-components/ChangeIcon';
+import DeleteIcon from '../../../../components/icons-components/DeleteIcon';
import { isShortLivingBranch } from '../../../../helpers/branches';
import { translate } from '../../../../helpers/l10n';
import { getProjectBranchUrl } from '../../../../helpers/urls';
-interface Props {
+export interface Props {
branch: Branch;
+ canAdmin?: boolean;
component: Component;
+ onBranchesChange: () => void;
onSelect: (branch: Branch) => void;
selected: boolean;
}
-export default function ComponentNavBranchesMenuItem({ branch, ...props }: Props) {
- const handleMouseEnter = () => {
- props.onSelect(branch);
+interface State {
+ deleteBranchModal: boolean;
+ renameBranchModal: boolean;
+}
+
+export default class ComponentNavBranchesMenuItem extends React.PureComponent<Props, State> {
+ state: State = { deleteBranchModal: false, renameBranchModal: false };
+
+ handleMouseEnter = () => {
+ this.props.onSelect(this.props.branch);
+ };
+
+ handleDeleteBranchClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ this.setState({ deleteBranchModal: true });
+ };
+
+ handleDeleteBranchClose = () => {
+ this.setState({ deleteBranchModal: false });
+ };
+
+ handleBranchDelete = () => {
+ this.props.onBranchesChange();
+ this.setState({ deleteBranchModal: false });
+ };
+
+ handleRenameBranchClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ this.setState({ renameBranchModal: true });
};
- return (
- <li key={branch.name} onMouseEnter={handleMouseEnter}>
- <Link
- className={classNames('navbar-context-meta-branch-menu-item', {
- active: props.selected
- })}
- to={getProjectBranchUrl(props.component.key, branch)}>
- <div>
- <BranchIcon
+ handleRenameBranchClose = () => {
+ this.setState({ renameBranchModal: false });
+ };
+
+ handleBranchRename = () => {
+ this.props.onBranchesChange();
+ this.setState({ renameBranchModal: false });
+ };
+
+ render() {
+ const { branch } = this.props;
+ return (
+ <li key={branch.name} onMouseEnter={this.handleMouseEnter}>
+ <Link
+ className={classNames('navbar-context-meta-branch-menu-item', {
+ active: this.props.selected
+ })}
+ to={getProjectBranchUrl(this.props.component.key, branch)}>
+ <div className="navbar-context-meta-branch-menu-item-name">
+ <BranchIcon
+ branch={branch}
+ className={classNames('little-spacer-right', {
+ 'big-spacer-left': isShortLivingBranch(branch) && !branch.isOrphan
+ })}
+ />
+ {branch.name}
+ {branch.isMain && (
+ <div className="outline-badge spacer-left">{translate('branches.main_branch')}</div>
+ )}
+ </div>
+ <div className="big-spacer-left note">
+ <BranchStatus branch={branch} concise={true} />
+ </div>
+ {this.props.canAdmin && (
+ <div className="navbar-context-meta-branch-menu-item-actions">
+ {branch.isMain ? (
+ <button className="js-rename button-link" onClick={this.handleRenameBranchClick}>
+ <ChangeIcon />
+ </button>
+ ) : (
+ <button className="js-delete button-link" onClick={this.handleDeleteBranchClick}>
+ <DeleteIcon />
+ </button>
+ )}
+ </div>
+ )}
+ </Link>
+
+ {this.state.deleteBranchModal && (
+ <DeleteBranchModal
branch={branch}
- className={classNames('little-spacer-right', {
- 'big-spacer-left': isShortLivingBranch(branch) && !branch.isOrphan
- })}
+ component={this.props.component.key}
+ onClose={this.handleDeleteBranchClose}
+ onDelete={this.handleBranchDelete}
/>
- {branch.name}
- {branch.isMain && (
- <div className="outline-badge spacer-left">{translate('branches.main_branch')}</div>
- )}
- </div>
- <div className="big-spacer-left note">
- <BranchStatus branch={branch} concise={true} />
- </div>
- </Link>
- </li>
- );
+ )}
+
+ {this.state.renameBranchModal && (
+ <RenameBranchModal
+ branch={branch}
+ component={this.props.component.key}
+ onClose={this.handleRenameBranchClose}
+ onRename={this.handleBranchRename}
+ />
+ )}
+ </li>
+ );
+ }
}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx
index 625043eb923..270829acee0 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx
@@ -21,7 +21,7 @@ import * as React from 'react';
import { Link } from 'react-router';
import * as classNames from 'classnames';
import * as PropTypes from 'prop-types';
-import { Branch, Component, ComponentExtension, ComponentConfiguration } from '../../../types';
+import { Branch, Component, ComponentExtension } from '../../../types';
import NavBarTabs from '../../../../components/nav/NavBarTabs';
import {
isShortLivingBranch,
@@ -48,7 +48,6 @@ const SETTINGS_URLS = [
interface Props {
branch?: Branch;
component: Component;
- conf: ComponentConfiguration;
location?: any;
}
@@ -74,6 +73,10 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
return this.props.component.qualifier === 'APP';
}
+ getConfiguration() {
+ return this.props.component.configuration || {};
+ }
+
renderDashboardLink() {
if (this.props.branch && isShortLivingBranch(this.props.branch)) {
return null;
@@ -193,7 +196,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
renderAdministration() {
const { branch } = this.props;
- if (!this.props.conf.showSettings || (branch && isShortLivingBranch(branch))) {
+ if (!this.getConfiguration().showSettings || (branch && isShortLivingBranch(branch))) {
return null;
}
@@ -209,7 +212,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
pathname: '/project/settings',
query: { branch: getBranchName(branch), id: this.props.component.key }
}}>
- {translate('layout.settings')}&nbsp;
+ {translate('branches.branch_settings')}
</Link>
</li>
);
@@ -238,7 +241,6 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
renderAdministrationLinks() {
return [
this.renderSettingsLink(),
- this.renderBranchesLink(),
this.renderProfilesLink(),
this.renderQualityGateLink(),
this.renderCustomMeasuresLink(),
@@ -252,7 +254,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
}
renderSettingsLink() {
- if (!this.props.conf.showSettings || this.isApplication() || this.isPortfolio()) {
+ if (!this.getConfiguration().showSettings || this.isApplication() || this.isPortfolio()) {
return null;
}
return (
@@ -272,23 +274,8 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
);
}
- renderBranchesLink() {
- if (!this.context.branchesEnabled || !this.isProject() || !this.props.conf.showSettings) {
- return null;
- }
- return (
- <li key="branches">
- <Link
- to={{ pathname: '/project/branches', query: { id: this.props.component.key } }}
- activeClassName="active">
- {translate('project_branches.page')}
- </Link>
- </li>
- );
- }
-
renderProfilesLink() {
- if (!this.props.conf.showQualityProfiles) {
+ if (!this.getConfiguration().showQualityProfiles) {
return null;
}
return (
@@ -303,7 +290,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
}
renderQualityGateLink() {
- if (!this.props.conf.showQualityGates) {
+ if (!this.getConfiguration().showQualityGates) {
return null;
}
return (
@@ -318,7 +305,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
}
renderCustomMeasuresLink() {
- if (!this.props.conf.showManualMeasures) {
+ if (!this.getConfiguration().showManualMeasures) {
return null;
}
return (
@@ -333,7 +320,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
}
renderLinksLink() {
- if (!this.props.conf.showLinks) {
+ if (!this.getConfiguration().showLinks) {
return null;
}
return (
@@ -348,7 +335,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
}
renderPermissionsLink() {
- if (!this.props.conf.showPermissions) {
+ if (!this.getConfiguration().showPermissions) {
return null;
}
return (
@@ -363,7 +350,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
}
renderBackgroundTasksLink() {
- if (!this.props.conf.showBackgroundTasks) {
+ if (!this.getConfiguration().showBackgroundTasks) {
return null;
}
return (
@@ -378,7 +365,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
}
renderUpdateKeyLink() {
- if (!this.props.conf.showUpdateKey) {
+ if (!this.getConfiguration().showUpdateKey) {
return null;
}
return (
@@ -395,7 +382,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
renderDeletionLink() {
const { qualifier } = this.props.component;
- if (!this.props.conf.showSettings) {
+ if (!this.getConfiguration().showSettings) {
return null;
}
@@ -426,7 +413,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
};
renderAdminExtensions() {
- const extensions = this.props.conf.extensions || [];
+ const extensions = this.getConfiguration().extensions || [];
return extensions.map(e => this.renderExtension(e, true));
}
@@ -446,9 +433,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> {
{translate('more')}&nbsp;
<i className="icon-dropdown" />
</a>
- <ul className="dropdown-menu">
- {extensions.map(e => this.renderExtension(e, false))}
- </ul>
+ <ul className="dropdown-menu">{extensions.map(e => this.renderExtension(e, false))}</ul>
</li>
);
}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx
index 4c1e485000c..c5721aa4e24 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx
@@ -20,7 +20,7 @@
import * as React from 'react';
import IncrementalBadge from './IncrementalBadge';
import BranchStatus from '../../../../components/common/BranchStatus';
-import { Branch, Component, ComponentConfiguration } from '../../../types';
+import { Branch, Component } from '../../../types';
import Tooltip from '../../../../components/controls/Tooltip';
import PendingIcon from '../../../../components/icons-components/PendingIcon';
import DateTimeFormatter from '../../../../components/intl/DateTimeFormatter';
@@ -30,7 +30,6 @@ import { isShortLivingBranch } from '../../../../helpers/branches';
interface Props {
branch?: Branch;
component: Component;
- conf: ComponentConfiguration;
incremental?: boolean;
isInProgress?: boolean;
isFailed?: boolean;
@@ -39,7 +38,8 @@ interface Props {
export default function ComponentNavMeta(props: Props) {
const metaList = [];
- const canSeeBackgroundTasks = props.conf.showBackgroundTasks;
+ const canSeeBackgroundTasks =
+ props.component.configuration != undefined && props.component.configuration.showBackgroundTasks;
const backgroundTasksUrl =
(window as any).baseUrl +
`/project/background_tasks?id=${encodeURIComponent(props.component.key)}`;
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/DeleteBranchModal.tsx b/server/sonar-web/src/main/js/app/components/nav/component/DeleteBranchModal.tsx
index 66d14ed260f..2273692a9a8 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/DeleteBranchModal.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/DeleteBranchModal.tsx
@@ -19,9 +19,9 @@
*/
import * as React from 'react';
import Modal from 'react-modal';
-import { deleteBranch } from '../../../api/branches';
-import { Branch } from '../../../app/types';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { deleteBranch } from '../../../../api/branches';
+import { Branch } from '../../../../app/types';
+import { translate, translateWithParameters } from '../../../../helpers/l10n';
interface Props {
branch: Branch;
@@ -66,6 +66,7 @@ export default class DeleteBranchModal extends React.PureComponent<Props, State>
handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
event.preventDefault();
+ event.stopPropagation();
this.props.onClose();
};
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/NoBranchSupportPopup.tsx b/server/sonar-web/src/main/js/app/components/nav/component/NoBranchSupportPopup.tsx
deleted file mode 100644
index db44804e214..00000000000
--- a/server/sonar-web/src/main/js/app/components/nav/component/NoBranchSupportPopup.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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 BubblePopup from '../../../../components/common/BubblePopup';
-import { translate } from '../../../../helpers/l10n';
-
-interface Props {
- popupPosition?: any;
-}
-
-export default function NoBranchSupportPopup(props: Props) {
- return (
- <BubblePopup position={props.popupPosition} customClass="bubble-popup-bottom">
- <div className="abs-width-400">
- <h6 className="spacer-bottom">{translate('branches.no_support.header')}</h6>
- <p className="big-spacer-bottom markdown">{translate('branches.no_support.header.text')}</p>
- <p>
- <a href="https://redirect.sonarsource.com/doc/branches.html" target="_blank">
- {translate('learn_more')}
- </a>
- <a
- className="button spacer-left"
- href="https://www.sonarsource.com/company/contact/"
- target="_blank">
- {translate('buy_developer_pack')}
- </a>
- </p>
- </div>
- </BubblePopup>
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/RenameBranchModal.tsx b/server/sonar-web/src/main/js/app/components/nav/component/RenameBranchModal.tsx
index 181fee72365..b17401c4e08 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/RenameBranchModal.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/RenameBranchModal.tsx
@@ -19,9 +19,9 @@
*/
import * as React from 'react';
import Modal from 'react-modal';
-import { renameBranch } from '../../../api/branches';
-import { Branch } from '../../../app/types';
-import { translate } from '../../../helpers/l10n';
+import { renameBranch } from '../../../../api/branches';
+import { Branch } from '../../../../app/types';
+import { translate } from '../../../../helpers/l10n';
interface Props {
branch: Branch;
@@ -70,6 +70,7 @@ export default class RenameBranchModal extends React.PureComponent<Props, State>
handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
event.preventDefault();
+ event.stopPropagation();
this.props.onClose();
};
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 6ae5fb1c768..6adc8751910 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
@@ -62,13 +62,15 @@ const component = {
it('loads status', () => {
getTasksForComponent.mockClear();
- mount(<ComponentNav branches={[]} component={component} conf={{}} location={{}} />);
+ mount(
+ <ComponentNav branches={[]} component={component} location={{}} onBranchesChange={jest.fn()} />
+ );
expect(getTasksForComponent).toBeCalledWith('component');
});
it('renders', () => {
const wrapper = shallow(
- <ComponentNav branches={[]} component={component} conf={{}} location={{}} />
+ <ComponentNav branches={[]} component={component} location={{}} onBranchesChange={jest.fn()} />
);
wrapper.setState({
incremental: true,
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranch-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranch-test.tsx
index 28931e0fc51..5a2343bb4c5 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranch-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranch-test.tsx
@@ -38,8 +38,9 @@ it('renders main branch', () => {
shallow(
<ComponentNavBranch
branches={[branch, fooBranch]}
+ component={component}
currentBranch={branch}
- project={component}
+ onBranchesChange={jest.fn()}
/>,
{ context: { branchesEnabled: true } }
)
@@ -59,8 +60,9 @@ it('renders short-living branch', () => {
shallow(
<ComponentNavBranch
branches={[branch, fooBranch]}
+ component={component}
currentBranch={branch}
- project={component}
+ onBranchesChange={jest.fn()}
/>,
{ context: { branchesEnabled: true } }
)
@@ -73,8 +75,9 @@ it('opens menu', () => {
const wrapper = shallow(
<ComponentNavBranch
branches={[branch, fooBranch]}
+ component={component}
currentBranch={branch}
- project={component}
+ onBranchesChange={jest.fn()}
/>,
{ context: { branchesEnabled: true } }
);
@@ -87,7 +90,12 @@ it('renders single branch popup', () => {
const branch: MainBranch = { isMain: true, name: 'master' };
const component = {} as Component;
const wrapper = shallow(
- <ComponentNavBranch branches={[branch]} currentBranch={branch} project={component} />,
+ <ComponentNavBranch
+ branches={[branch]}
+ component={component}
+ currentBranch={branch}
+ onBranchesChange={jest.fn()}
+ />,
{ context: { branchesEnabled: true } }
);
expect(wrapper).toMatchSnapshot();
@@ -96,29 +104,17 @@ it('renders single branch popup', () => {
expect(wrapper.find('BubblePopupHelper').prop('isOpen')).toBe(true);
});
-it('renders no branch support popup', () => {
+it('renders nothing when no branch support', () => {
const branch: MainBranch = { isMain: true, name: 'master' };
const component = {} as Component;
const wrapper = shallow(
<ComponentNavBranch
branches={[branch, fooBranch]}
+ component={component}
currentBranch={branch}
- project={component}
+ onBranchesChange={jest.fn()}
/>,
{ context: { branchesEnabled: false } }
);
- expect(wrapper).toMatchSnapshot();
- expect(wrapper.find('BubblePopupHelper').prop('isOpen')).toBe(false);
- click(wrapper.find('a'));
- expect(wrapper.find('BubblePopupHelper').prop('isOpen')).toBe(true);
-});
-
-it('renders nothing on SonarCloud without branch support', () => {
- const branch: MainBranch = { isMain: true, name: 'master' };
- const component = {} as Component;
- const wrapper = shallow(
- <ComponentNavBranch branches={[branch]} currentBranch={branch} project={component} />,
- { context: { branchesEnabled: false, onSonarCloud: true } }
- );
expect(wrapper.type()).toBeNull();
});
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranchesMenu-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranchesMenu-test.tsx
index a4eb5bfbf82..13e251e059a 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranchesMenu-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranchesMenu-test.tsx
@@ -36,9 +36,10 @@ it('renders list', () => {
shallow(
<ComponentNavBranchesMenu
branches={[mainBranch(), shortBranch('foo'), longBranch('bar'), shortBranch('baz', true)]}
+ component={project}
currentBranch={mainBranch()}
+ onBranchesChange={jest.fn()}
onClose={jest.fn()}
- project={project}
/>
)
).toMatchSnapshot();
@@ -48,9 +49,10 @@ it('searches', () => {
const wrapper = shallow(
<ComponentNavBranchesMenu
branches={[mainBranch(), shortBranch('foo'), shortBranch('foobar'), longBranch('bar')]}
+ component={project}
currentBranch={mainBranch()}
+ onBranchesChange={jest.fn()}
onClose={jest.fn()}
- project={project}
/>
);
wrapper.setState({ query: 'bar' });
@@ -61,9 +63,10 @@ it('selects next & previous', () => {
const wrapper = shallow(
<ComponentNavBranchesMenu
branches={[mainBranch(), shortBranch('foo'), shortBranch('foobar'), longBranch('bar')]}
+ component={project}
currentBranch={mainBranch()}
+ onBranchesChange={jest.fn()}
onClose={jest.fn()}
- project={project}
/>
);
elementKeydown(wrapper.find('input'), 40);
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranchesMenuItem-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranchesMenuItem-test.tsx
index 2e1d56e2115..c8ff060fa56 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranchesMenuItem-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranchesMenuItem-test.tsx
@@ -19,63 +19,61 @@
*/
import * as React from 'react';
import { shallow } from 'enzyme';
-import ComponentNavBranchesMenuItem from '../ComponentNavBranchesMenuItem';
+import ComponentNavBranchesMenuItem, { Props } from '../ComponentNavBranchesMenuItem';
import { BranchType, MainBranch, ShortLivingBranch, Component } from '../../../../types';
+import { click } from '../../../../../helpers/testUtils';
+
+const component = { key: 'component' } as Component;
+
+const shortBranch: ShortLivingBranch = {
+ isMain: false,
+ mergeBranch: 'master',
+ name: 'foo',
+ status: { bugs: 1, codeSmells: 2, vulnerabilities: 3 },
+ type: BranchType.SHORT
+};
+
+const mainBranch: MainBranch = { isMain: true, name: 'master' };
it('renders main branch', () => {
- const component = { key: 'component' } as Component;
- const mainBranch: MainBranch = { isMain: true, name: 'master' };
- expect(
- shallow(
- <ComponentNavBranchesMenuItem
- branch={mainBranch}
- component={component}
- onSelect={jest.fn()}
- selected={false}
- />
- )
- ).toMatchSnapshot();
+ expect(shallowRender({ branch: mainBranch })).toMatchSnapshot();
});
it('renders short-living branch', () => {
- const component = { key: 'component' } as Component;
- const shortBranch: ShortLivingBranch = {
- isMain: false,
- mergeBranch: 'master',
- name: 'foo',
- status: { bugs: 1, codeSmells: 2, vulnerabilities: 3 },
- type: BranchType.SHORT
- };
- expect(
- shallow(
- <ComponentNavBranchesMenuItem
- branch={shortBranch}
- component={component}
- onSelect={jest.fn()}
- selected={false}
- />
- )
- ).toMatchSnapshot();
+ expect(shallowRender()).toMatchSnapshot();
});
it('renders short-living orhpan branch', () => {
- const component = { key: 'component' } as Component;
- const shortBranch: ShortLivingBranch = {
- isMain: false,
- isOrphan: true,
- mergeBranch: 'master',
- name: 'foo',
- status: { bugs: 1, codeSmells: 2, vulnerabilities: 3 },
- type: BranchType.SHORT
- };
- expect(
- shallow(
- <ComponentNavBranchesMenuItem
- branch={shortBranch}
- component={component}
- onSelect={jest.fn()}
- selected={false}
- />
- )
- ).toMatchSnapshot();
+ expect(shallowRender({ branch: { ...shortBranch, isOrphan: true } })).toMatchSnapshot();
+});
+
+it('renames main branch', () => {
+ const onBranchesChange = jest.fn();
+ const wrapper = shallowRender({ branch: mainBranch, canAdmin: true, onBranchesChange });
+
+ click(wrapper.find('.js-rename'));
+ (wrapper.find('RenameBranchModal').prop('onRename') as Function)();
+ expect(onBranchesChange).toBeCalled();
});
+
+it('deletes short-living branch', () => {
+ const onBranchesChange = jest.fn();
+ const wrapper = shallowRender({ canAdmin: true, onBranchesChange });
+
+ click(wrapper.find('.js-delete'));
+ (wrapper.find('DeleteBranchModal').prop('onDelete') as Function)();
+ expect(onBranchesChange).toBeCalled();
+});
+
+function shallowRender(props?: { [P in keyof Props]?: Props[P] }) {
+ return shallow(
+ <ComponentNavBranchesMenuItem
+ branch={shortBranch}
+ component={component}
+ onBranchesChange={jest.fn()}
+ onSelect={jest.fn()}
+ selected={false}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMenu-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMenu-test.tsx
index 2e4af3fb2ee..8405e371da7 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMenu-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMenu-test.tsx
@@ -20,55 +20,47 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import ComponentNavMenu from '../ComponentNavMenu';
-import {
- Component,
- ShortLivingBranch,
- BranchType,
- LongLivingBranch,
- MainBranch
-} from '../../../../types';
+import { ShortLivingBranch, BranchType, LongLivingBranch, MainBranch } from '../../../../types';
-const mainBranch: MainBranch = {
- isMain: true,
- name: 'master'
+const mainBranch: MainBranch = { isMain: true, name: 'master' };
+
+const baseComponent = {
+ breadcrumbs: [],
+ key: 'foo',
+ name: 'foo',
+ organization: 'org',
+ qualifier: 'TRK'
};
it('should work with extensions', () => {
const component = {
- key: 'foo',
- qualifier: 'TRK',
+ ...baseComponent,
+ configuration: { showSettings: true, extensions: [{ key: 'foo', name: 'Foo' }] },
extensions: [{ key: 'component-foo', name: 'ComponentFoo' }]
};
- const conf = {
- showSettings: true,
- extensions: [{ key: 'foo', name: 'Foo' }]
- };
expect(
- shallow(
- <ComponentNavMenu branch={mainBranch} component={component as Component} conf={conf} />,
- { context: { branchesEnabled: true } }
- )
+ shallow(<ComponentNavMenu branch={mainBranch} component={component} />, {
+ context: { branchesEnabled: true }
+ })
).toMatchSnapshot();
});
it('should work with multiple extensions', () => {
const component = {
- key: 'foo',
- qualifier: 'TRK',
+ ...baseComponent,
+ configuration: {
+ showSettings: true,
+ extensions: [{ key: 'foo', name: 'Foo' }, { key: 'bar', name: 'Bar' }]
+ },
extensions: [
{ key: 'component-foo', name: 'ComponentFoo' },
{ key: 'component-bar', name: 'ComponentBar' }
]
};
- const conf = {
- showSettings: true,
- extensions: [{ key: 'foo', name: 'Foo' }, { key: 'bar', name: 'Bar' }]
- };
expect(
- shallow(
- <ComponentNavMenu branch={mainBranch} component={component as Component} conf={conf} />,
- { context: { branchesEnabled: true } }
- )
+ shallow(<ComponentNavMenu branch={mainBranch} component={component} />, {
+ context: { branchesEnabled: true }
+ })
).toMatchSnapshot();
});
@@ -79,10 +71,9 @@ it('should work for short-living branches', () => {
name: 'feature',
type: BranchType.SHORT
};
- const component = { key: 'foo', qualifier: 'TRK' } as Component;
- const conf = { showSettings: true };
+ const component = { ...baseComponent, configuration: { showSettings: true } };
expect(
- shallow(<ComponentNavMenu branch={branch} component={component} conf={conf} />, {
+ shallow(<ComponentNavMenu branch={branch} component={component} />, {
context: { branchesEnabled: true }
})
).toMatchSnapshot();
@@ -90,12 +81,15 @@ it('should work for short-living branches', () => {
it('should work for long-living branches', () => {
const branch: LongLivingBranch = { isMain: false, name: 'release', type: BranchType.LONG };
- const component = { key: 'foo', qualifier: 'TRK' } as Component;
[true, false].forEach(showSettings =>
expect(
- shallow(<ComponentNavMenu branch={branch} component={component} conf={{ showSettings }} />, {
- context: { branchesEnabled: true }
- })
+ shallow(
+ <ComponentNavMenu
+ branch={branch}
+ component={{ ...baseComponent, configuration: { showSettings } }}
+ />,
+ { context: { branchesEnabled: true } }
+ )
).toMatchSnapshot()
);
});
@@ -103,20 +97,12 @@ it('should work for long-living branches', () => {
it('should work for all qualifiers', () => {
['TRK', 'BRC', 'VW', 'SVW', 'APP'].forEach(checkWithQualifier);
expect.assertions(5);
-
function checkWithQualifier(qualifier: string) {
- const component = { key: 'foo', qualifier } as Component;
+ const component = { ...baseComponent, configuration: { showSettings: true }, qualifier };
expect(
- shallow(
- <ComponentNavMenu
- branch={mainBranch}
- component={component}
- conf={{ showSettings: true }}
- />,
- {
- context: { branchesEnabled: true }
- }
- )
+ shallow(<ComponentNavMenu branch={mainBranch} component={component} />, {
+ context: { branchesEnabled: true }
+ })
).toMatchSnapshot();
}
});
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMeta-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMeta-test.tsx
index 6949044c9ca..1e1d412852c 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMeta-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMeta-test.tsx
@@ -39,12 +39,7 @@ it('renders incremental badge', () => {
function check(incremental: boolean) {
expect(
shallow(
- <ComponentNavMeta
- branch={{} as Branch}
- component={component}
- conf={{}}
- incremental={incremental}
- />
+ <ComponentNavMeta branch={{} as Branch} component={component} incremental={incremental} />
).find('IncrementalBadge')
).toHaveLength(incremental ? 1 : 0);
}
@@ -58,9 +53,7 @@ it('renders status of short-living branch', () => {
status: { bugs: 0, codeSmells: 2, vulnerabilities: 3 },
type: BranchType.SHORT
};
- expect(
- shallow(<ComponentNavMeta branch={branch} component={component} conf={{}} />)
- ).toMatchSnapshot();
+ expect(shallow(<ComponentNavMeta branch={branch} component={component} />)).toMatchSnapshot();
});
it('renders meta for long-living branch', () => {
@@ -70,7 +63,5 @@ it('renders meta for long-living branch', () => {
status: { qualityGateStatus: 'OK' },
type: BranchType.LONG
};
- expect(
- shallow(<ComponentNavMeta branch={branch} component={component} conf={{}} />)
- ).toMatchSnapshot();
+ expect(shallow(<ComponentNavMeta branch={branch} component={component} />)).toMatchSnapshot();
});
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/DeleteBranchModal-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/DeleteBranchModal-test.tsx
index b2870587114..0df5a12fdbb 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/DeleteBranchModal-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/DeleteBranchModal-test.tsx
@@ -17,14 +17,14 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-jest.mock('../../../../api/branches', () => ({ deleteBranch: jest.fn() }));
+jest.mock('../../../../../api/branches', () => ({ deleteBranch: jest.fn() }));
import * as React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import DeleteBranchModal from '../DeleteBranchModal';
-import { ShortLivingBranch, BranchType } from '../../../../app/types';
-import { submit, doAsync, click } from '../../../../helpers/testUtils';
-import { deleteBranch } from '../../../../api/branches';
+import { ShortLivingBranch, BranchType } from '../../../../../app/types';
+import { submit, doAsync, click } from '../../../../../helpers/testUtils';
+import { deleteBranch } from '../../../../../api/branches';
beforeEach(() => {
(deleteBranch as jest.Mock<any>).mockClear();
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/NoBranchSupportPopup-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/NoBranchSupportPopup-test.tsx
deleted file mode 100644
index 4f5925156fc..00000000000
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/NoBranchSupportPopup-test.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import * as React from 'react';
-import { shallow } from 'enzyme';
-import NoBranchSupportPopup from '../NoBranchSupportPopup';
-
-it('renders', () => {
- expect(shallow(<NoBranchSupportPopup />)).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/RenameBranchModal-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/RenameBranchModal-test.tsx
index 3a1c962d68e..c1a0750e5bb 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/RenameBranchModal-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/RenameBranchModal-test.tsx
@@ -17,14 +17,14 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-jest.mock('../../../../api/branches', () => ({ renameBranch: jest.fn() }));
+jest.mock('../../../../../api/branches', () => ({ renameBranch: jest.fn() }));
import * as React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import RenameBranchModal from '../RenameBranchModal';
-import { MainBranch } from '../../../../app/types';
-import { submit, doAsync, click, change } from '../../../../helpers/testUtils';
-import { renameBranch } from '../../../../api/branches';
+import { MainBranch } from '../../../../../app/types';
+import { submit, doAsync, click, change } from '../../../../../helpers/testUtils';
+import { renameBranch } from '../../../../../api/branches';
beforeEach(() => {
(renameBranch as jest.Mock<any>).mockClear();
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 f84eeda97aa..a4317c09cec 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
@@ -50,7 +50,6 @@ exports[`renders 1`] = `
"qualifier": "TRK",
}
}
- conf={Object {}}
incremental={true}
isFailed={true}
isInProgress={true}
@@ -72,7 +71,6 @@ exports[`renders 1`] = `
"qualifier": "TRK",
}
}
- conf={Object {}}
location={Object {}}
/>
</ContextNavBar>
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranch-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranch-test.tsx.snap
index 38c20e78b97..1a1f5a3db79 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranch-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranch-test.tsx.snap
@@ -26,47 +26,6 @@ exports[`renders main branch 1`] = `
</div>
`;
-exports[`renders no branch support popup 1`] = `
-<div
- className="navbar-context-branches"
->
- <BranchIcon
- branch={
- Object {
- "isMain": true,
- "name": "master",
- }
- }
- className="little-spacer-right"
- color="#cdcdcd"
- />
- <span
- className="note"
- >
- master
- </span>
- <div
- className="display-inline-block spacer-left"
- >
- <a
- className="link-no-underline"
- href="#"
- onClick={[Function]}
- >
- <HelpIcon
- fill="#cdcdcd"
- />
- </a>
- <BubblePopupHelper
- isOpen={false}
- popup={<NoBranchSupportPopup />}
- position="bottomleft"
- togglePopup={[Function]}
- />
- </div>
-</div>
-`;
-
exports[`renders short-living branch 1`] = `
<div
className="navbar-context-branches dropdown"
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenu-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenu-test.tsx.snap
index 463fb48d493..eda0f3f1cb5 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenu-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenu-test.tsx.snap
@@ -39,6 +39,7 @@ exports[`renders list 1`] = `
"key": "component",
}
}
+ onBranchesChange={[Function]}
onSelect={[Function]}
selected={true}
/>
@@ -78,6 +79,7 @@ exports[`renders list 1`] = `
"key": "component",
}
}
+ onBranchesChange={[Function]}
onSelect={[Function]}
selected={false}
/>
@@ -101,6 +103,7 @@ exports[`renders list 1`] = `
"key": "component",
}
}
+ onBranchesChange={[Function]}
onSelect={[Function]}
selected={false}
/>
@@ -120,6 +123,7 @@ exports[`renders list 1`] = `
"key": "component",
}
}
+ onBranchesChange={[Function]}
onSelect={[Function]}
selected={false}
/>
@@ -159,6 +163,7 @@ exports[`renders list 1`] = `
"key": "component",
}
}
+ onBranchesChange={[Function]}
onSelect={[Function]}
selected={false}
/>
@@ -213,6 +218,7 @@ exports[`searches 1`] = `
"key": "component",
}
}
+ onBranchesChange={[Function]}
onSelect={[Function]}
selected={true}
/>
@@ -232,6 +238,7 @@ exports[`searches 1`] = `
"key": "component",
}
}
+ onBranchesChange={[Function]}
onSelect={[Function]}
selected={false}
/>
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenuItem-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenuItem-test.tsx.snap
index f4edb48406f..ee665153336 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenuItem-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenuItem-test.tsx.snap
@@ -17,7 +17,9 @@ exports[`renders main branch 1`] = `
}
}
>
- <div>
+ <div
+ className="navbar-context-meta-branch-menu-item-name"
+ >
<BranchIcon
branch={
Object {
@@ -70,7 +72,9 @@ exports[`renders short-living branch 1`] = `
}
}
>
- <div>
+ <div
+ className="navbar-context-meta-branch-menu-item-name"
+ >
<BranchIcon
branch={
Object {
@@ -132,7 +136,9 @@ exports[`renders short-living orhpan branch 1`] = `
}
}
>
- <div>
+ <div
+ className="navbar-context-meta-branch-menu-item-name"
+ >
<BranchIcon
branch={
Object {
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
index ea4cd792997..7b305c2b763 100644
--- 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
@@ -136,23 +136,6 @@ exports[`should work for all qualifiers 1`] = `
style={Object {}}
to={
Object {
- "pathname": "/project/branches",
- "query": Object {
- "id": "foo",
- },
- }
- }
- >
- project_branches.page
- </Link>
- </li>
- <li>
- <Link
- activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
"pathname": "/project/deletion",
"query": Object {
"id": "foo",
@@ -755,8 +738,7 @@ exports[`should work for long-living branches 1`] = `
}
}
>
- layout.settings
-  
+ branches.branch_settings
</Link>
</li>
</NavBarTabs>
@@ -1036,23 +1018,6 @@ exports[`should work with extensions 1`] = `
style={Object {}}
to={
Object {
- "pathname": "/project/branches",
- "query": Object {
- "id": "foo",
- },
- }
- }
- >
- project_branches.page
- </Link>
- </li>
- <li>
- <Link
- activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
"pathname": "/project/admin/extension/foo",
"query": Object {
"id": "foo",
@@ -1258,23 +1223,6 @@ exports[`should work with multiple extensions 1`] = `
style={Object {}}
to={
Object {
- "pathname": "/project/branches",
- "query": Object {
- "id": "foo",
- },
- }
- }
- >
- project_branches.page
- </Link>
- </li>
- <li>
- <Link
- activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
"pathname": "/project/admin/extension/foo",
"query": Object {
"id": "foo",
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/DeleteBranchModal-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/DeleteBranchModal-test.tsx.snap
index 934f8ed2d7d..934f8ed2d7d 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/DeleteBranchModal-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/DeleteBranchModal-test.tsx.snap
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/NoBranchSupportPopup-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/NoBranchSupportPopup-test.tsx.snap
deleted file mode 100644
index bcc5eadaede..00000000000
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/NoBranchSupportPopup-test.tsx.snap
+++ /dev/null
@@ -1,37 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<BubblePopup
- customClass="bubble-popup-bottom"
->
- <div
- className="abs-width-400"
- >
- <h6
- className="spacer-bottom"
- >
- branches.no_support.header
- </h6>
- <p
- className="big-spacer-bottom markdown"
- >
- branches.no_support.header.text
- </p>
- <p>
- <a
- href="https://redirect.sonarsource.com/doc/branches.html"
- target="_blank"
- >
- learn_more
- </a>
- <a
- className="button spacer-left"
- href="https://www.sonarsource.com/company/contact/"
- target="_blank"
- >
- buy_developer_pack
- </a>
- </p>
- </div>
-</BubblePopup>
-`;
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/RenameBranchModal-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/RenameBranchModal-test.tsx.snap
index 7867fa4785a..7867fa4785a 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/RenameBranchModal-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/RenameBranchModal-test.tsx.snap
diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts
index 1db17ec0ffd..cc8717235f3 100644
--- a/server/sonar-web/src/main/js/app/types.ts
+++ b/server/sonar-web/src/main/js/app/types.ts
@@ -79,7 +79,7 @@ export interface Component {
version?: string;
}
-export interface ComponentConfiguration {
+interface ComponentConfiguration {
extensions?: ComponentExtension[];
showBackgroundTasks?: boolean;
showLinks?: boolean;
diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.js b/server/sonar-web/src/main/js/app/utils/startReactApp.js
index b6577565efd..1b8f7f606c3 100644
--- a/server/sonar-web/src/main/js/app/utils/startReactApp.js
+++ b/server/sonar-web/src/main/js/app/utils/startReactApp.js
@@ -55,7 +55,6 @@ import permissionTemplatesRoutes from '../../apps/permission-templates/routes';
import portfolioRoutes from '../../apps/portfolio/routes';
import projectActivityRoutes from '../../apps/projectActivity/routes';
import projectAdminRoutes from '../../apps/project-admin/routes';
-import projectBranchesRoutes from '../../apps/projectBranches/routes';
import projectQualityGateRoutes from '../../apps/projectQualityGate/routes';
import projectQualityProfilesRoutes from '../../apps/projectQualityProfiles/routes';
import projectsRoutes from '../../apps/projects/routes';
@@ -207,7 +206,6 @@ const startReactApp = () => {
component={ProjectAdminPageExtension}
/>
<Route path="project/background_tasks" childRoutes={backgroundTasksRoutes} />
- <Route path="project/branches" childRoutes={projectBranchesRoutes} />
<Route path="project/settings" childRoutes={settingsRoutes} />
<Route path="project_roles" childRoutes={projectPermissionsRoutes} />
</Route>
diff --git a/server/sonar-web/src/main/js/apps/overview/components/App.js b/server/sonar-web/src/main/js/apps/overview/components/App.js
index 5c4b910f698..f7f3dbf4e86 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/App.js
+++ b/server/sonar-web/src/main/js/apps/overview/components/App.js
@@ -56,19 +56,23 @@ export default class App extends React.PureComponent {
query: { id: this.props.component.key }
});
}
- if (isShortLivingBranch(this.props.branch)) {
+ if (isShortLivingBranch(this.props.branch) && !this.isFile()) {
this.context.router.replace(getProjectBranchUrl(this.props.component.key, this.props.branch));
}
}
isPortfolio() {
- return this.props.component.qualifier === 'VW' || this.props.component.qualifier === 'SVW';
+ return ['VW', 'SVW'].includes(this.props.component.qualifier);
+ }
+
+ isFile() {
+ return ['FIL', 'UTS'].includes(this.props.component.qualifier);
}
render() {
const { branch, component } = this.props;
- if (this.isPortfolio() || isShortLivingBranch(branch)) {
+ if (this.isPortfolio() || (isShortLivingBranch(branch) && !this.isFile())) {
return null;
}
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/App.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/App.tsx
deleted file mode 100644
index 02a8e20365f..00000000000
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/App.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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 BranchRow from './BranchRow';
-import { Branch } from '../../../app/types';
-import { sortBranchesAsTree } from '../../../helpers/branches';
-import { translate } from '../../../helpers/l10n';
-
-interface Props {
- branches: Branch[];
- component: { key: string };
- onBranchesChange: () => void;
-}
-
-export default function App({ branches, component, onBranchesChange }: Props) {
- return (
- <div className="page page-limited">
- <header className="page-header">
- <h1 className="page-title">{translate('project_branches.page')}</h1>
- </header>
-
- <table className="data zebra zebra-hover">
- <thead>
- <tr>
- <th>{translate('branch')}</th>
- <th className="text-right">{translate('status')}</th>
- <th className="text-right">{translate('actions')}</th>
- </tr>
- </thead>
- <tbody>
- {sortBranchesAsTree(branches).map(branch => (
- <BranchRow
- branch={branch}
- component={component.key}
- key={branch.name}
- onChange={onBranchesChange}
- />
- ))}
- </tbody>
- </table>
- </div>
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/BranchRow.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/BranchRow.tsx
deleted file mode 100644
index e163481d759..00000000000
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/BranchRow.tsx
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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 { Branch } from '../../../app/types';
-import * as classNames from 'classnames';
-import DeleteBranchModal from './DeleteBranchModal';
-import BranchStatus from '../../../components/common/BranchStatus';
-import BranchIcon from '../../../components/icons-components/BranchIcon';
-import { isShortLivingBranch } from '../../../helpers/branches';
-import ChangeIcon from '../../../components/icons-components/ChangeIcon';
-import DeleteIcon from '../../../components/icons-components/DeleteIcon';
-import { translate } from '../../../helpers/l10n';
-import Tooltip from '../../../components/controls/Tooltip';
-import RenameBranchModal from './RenameBranchModal';
-
-interface Props {
- branch: Branch;
- component: string;
- onChange: () => void;
-}
-
-interface State {
- deleting: boolean;
- renaming: boolean;
-}
-
-export default class BranchRow extends React.PureComponent<Props, State> {
- mounted: boolean;
- state: State = { deleting: false, renaming: false };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- handleDeleteClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
- event.preventDefault();
- event.currentTarget.blur();
- this.setState({ deleting: true });
- };
-
- handleDeletingStop = () => {
- this.setState({ deleting: false });
- };
-
- handleRenameClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
- event.preventDefault();
- event.currentTarget.blur();
- this.setState({ renaming: true });
- };
-
- handleChange = () => {
- if (this.mounted) {
- this.setState({ deleting: false, renaming: false });
- this.props.onChange();
- }
- };
-
- handleRenamingStop = () => {
- this.setState({ renaming: false });
- };
-
- render() {
- const { branch, component } = this.props;
-
- return (
- <tr>
- <td>
- <BranchIcon
- branch={branch}
- className={classNames('little-spacer-right', {
- 'big-spacer-left': isShortLivingBranch(branch) && !branch.isOrphan
- })}
- />
- {branch.name}
- {branch.isMain && (
- <div className="outline-badge spacer-left">{translate('branches.main_branch')}</div>
- )}
- </td>
- <td className="thin nowrap text-right">
- <BranchStatus branch={branch} />
- </td>
- <td className="thin nowrap text-right">
- {branch.isMain ? (
- <Tooltip overlay={translate('branches.rename')}>
- <a className="js-rename link-no-underline" href="#" onClick={this.handleRenameClick}>
- <ChangeIcon />
- </a>
- </Tooltip>
- ) : (
- <Tooltip overlay={translate('branches.delete')}>
- <a className="js-delete link-no-underline" href="#" onClick={this.handleDeleteClick}>
- <DeleteIcon />
- </a>
- </Tooltip>
- )}
- </td>
-
- {this.state.deleting && (
- <DeleteBranchModal
- branch={branch}
- component={component}
- onClose={this.handleDeletingStop}
- onDelete={this.handleChange}
- />
- )}
-
- {this.state.renaming && (
- <RenameBranchModal
- branch={branch}
- component={component}
- onClose={this.handleRenamingStop}
- onRename={this.handleChange}
- />
- )}
- </tr>
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/App-test.tsx
deleted file mode 100644
index 4288105f79a..00000000000
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/App-test.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import * as React from 'react';
-import { shallow } from 'enzyme';
-import App from '../App';
-import { Branch, BranchType } from '../../../../app/types';
-
-it('renders sorted list of branches', () => {
- const branches: Branch[] = [
- { isMain: true, name: 'master' },
- { isMain: false, name: 'branch-1.0', type: BranchType.LONG },
- { isMain: false, name: 'branch-1.0', mergeBranch: 'master', type: BranchType.SHORT }
- ];
- expect(
- shallow(<App branches={branches} component={{ key: 'foo' }} onBranchesChange={jest.fn()} />)
- ).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/BranchRow-test.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/BranchRow-test.tsx
deleted file mode 100644
index 4edc3ce70d6..00000000000
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/BranchRow-test.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import * as React from 'react';
-import { shallow } from 'enzyme';
-import BranchRow from '../BranchRow';
-import { MainBranch, ShortLivingBranch, BranchType } from '../../../../app/types';
-import { click } from '../../../../helpers/testUtils';
-
-const mainBranch: MainBranch = { isMain: true, name: 'master' };
-
-const shortBranch: ShortLivingBranch = {
- isMain: false,
- name: 'feature',
- mergeBranch: 'foo',
- type: BranchType.SHORT
-};
-
-it('renders main branch', () => {
- expect(shallowRender(mainBranch)).toMatchSnapshot();
-});
-
-it('renders short-living branch', () => {
- expect(shallowRender(shortBranch)).toMatchSnapshot();
-});
-
-it('renames main branch', () => {
- const onChange = jest.fn();
- const wrapper = shallowRender(mainBranch, onChange);
-
- click(wrapper.find('.js-rename'));
- (wrapper.find('RenameBranchModal').prop('onRename') as Function)();
- expect(onChange).toBeCalled();
-});
-
-it('deletes short-living branch', () => {
- const onChange = jest.fn();
- const wrapper = shallowRender(shortBranch, onChange);
-
- click(wrapper.find('.js-delete'));
- (wrapper.find('DeleteBranchModal').prop('onDelete') as Function)();
- expect(onChange).toBeCalled();
-});
-
-function shallowRender(branch: MainBranch | ShortLivingBranch, onChange: () => void = jest.fn()) {
- const wrapper = shallow(<BranchRow branch={branch} component="foo" onChange={onChange} />);
- (wrapper.instance() as any).mounted = true;
- return wrapper;
-}
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap
deleted file mode 100644
index 6f983e33df8..00000000000
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap
+++ /dev/null
@@ -1,73 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders sorted list of branches 1`] = `
-<div
- className="page page-limited"
->
- <header
- className="page-header"
- >
- <h1
- className="page-title"
- >
- project_branches.page
- </h1>
- </header>
- <table
- className="data zebra zebra-hover"
- >
- <thead>
- <tr>
- <th>
- branch
- </th>
- <th
- className="text-right"
- >
- status
- </th>
- <th
- className="text-right"
- >
- actions
- </th>
- </tr>
- </thead>
- <tbody>
- <BranchRow
- branch={
- Object {
- "isMain": true,
- "name": "master",
- }
- }
- component="foo"
- onChange={[Function]}
- />
- <BranchRow
- branch={
- Object {
- "isMain": false,
- "mergeBranch": "master",
- "name": "branch-1.0",
- "type": "SHORT",
- }
- }
- component="foo"
- onChange={[Function]}
- />
- <BranchRow
- branch={
- Object {
- "isMain": false,
- "name": "branch-1.0",
- "type": "LONG",
- }
- }
- component="foo"
- onChange={[Function]}
- />
- </tbody>
- </table>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/BranchRow-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/BranchRow-test.tsx.snap
deleted file mode 100644
index ea135765ece..00000000000
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/BranchRow-test.tsx.snap
+++ /dev/null
@@ -1,100 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders main branch 1`] = `
-<tr>
- <td>
- <BranchIcon
- branch={
- Object {
- "isMain": true,
- "name": "master",
- }
- }
- className="little-spacer-right"
- />
- master
- <div
- className="outline-badge spacer-left"
- >
- branches.main_branch
- </div>
- </td>
- <td
- className="thin nowrap text-right"
- >
- <BranchStatus
- branch={
- Object {
- "isMain": true,
- "name": "master",
- }
- }
- />
- </td>
- <td
- className="thin nowrap text-right"
- >
- <Tooltip
- overlay="branches.rename"
- placement="bottom"
- >
- <a
- className="js-rename link-no-underline"
- href="#"
- onClick={[Function]}
- >
- <ChangeIcon />
- </a>
- </Tooltip>
- </td>
-</tr>
-`;
-
-exports[`renders short-living branch 1`] = `
-<tr>
- <td>
- <BranchIcon
- branch={
- Object {
- "isMain": false,
- "mergeBranch": "foo",
- "name": "feature",
- "type": "SHORT",
- }
- }
- className="little-spacer-right big-spacer-left"
- />
- feature
- </td>
- <td
- className="thin nowrap text-right"
- >
- <BranchStatus
- branch={
- Object {
- "isMain": false,
- "mergeBranch": "foo",
- "name": "feature",
- "type": "SHORT",
- }
- }
- />
- </td>
- <td
- className="thin nowrap text-right"
- >
- <Tooltip
- overlay="branches.delete"
- placement="bottom"
- >
- <a
- className="js-delete link-no-underline"
- href="#"
- onClick={[Function]}
- >
- <DeleteIcon />
- </a>
- </Tooltip>
- </td>
-</tr>
-`;
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/routes.ts b/server/sonar-web/src/main/js/apps/projectBranches/routes.ts
deleted file mode 100644
index 520805ebac5..00000000000
--- a/server/sonar-web/src/main/js/apps/projectBranches/routes.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact 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 { RouterState, IndexRouteProps } from 'react-router';
-
-const routes = [
- {
- getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) {
- import('./components/App').then(i => callback(null, { component: (i as any).default }));
- }
- }
-];
-
-export default routes;
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx
index 9c4d70c270e..c32b99adfcb 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/CreateProjectForm.tsx
@@ -34,6 +34,7 @@ interface Props {
}
interface State {
+ advanced: boolean;
branch: string;
createdProject?: { key: string; name: string };
key: string;
@@ -50,6 +51,7 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
constructor(props: Props) {
super(props);
this.state = {
+ advanced: false,
branch: '',
key: '',
loading: false,
@@ -71,6 +73,12 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
this.props.onClose();
};
+ handleAdvancedClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ this.setState(state => ({ advanced: !state.advanced }));
+ };
+
handleInputChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
const { name, value } = event.currentTarget;
this.setState({ [name]: value });
@@ -163,17 +171,6 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
/>
</div>
<div className="modal-field">
- <label htmlFor="create-project-branch">{translate('branch')}</label>
- <input
- id="create-project-branch"
- maxLength={200}
- name="branch"
- onChange={this.handleInputChange}
- type="text"
- value={this.state.branch}
- />
- </div>
- <div className="modal-field">
<label htmlFor="create-project-key">
{translate('key')}
<em className="mandatory">*</em>
@@ -202,6 +199,29 @@ export default class CreateProjectForm extends React.PureComponent<Props, State>
</div>
)}
</div>
+ {this.state.advanced ? (
+ <div className="modal-field big-spacer-top">
+ <label htmlFor="create-project-branch">{translate('branch')}</label>
+ <input
+ autoFocus={true}
+ id="create-project-branch"
+ maxLength={200}
+ name="branch"
+ onChange={this.handleInputChange}
+ type="text"
+ value={this.state.branch}
+ />
+ </div>
+ ) : (
+ <div className="modal-field big-spacer-top">
+ <a
+ className="js-more text-muted note"
+ href="#"
+ onClick={this.handleAdvancedClick}>
+ {translate('more')}
+ </a>
+ </div>
+ )}
</div>
<footer className="modal-foot">
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx
index 0212d094e8e..bd8c3114b4e 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx
@@ -26,7 +26,7 @@ jest.mock('../../../api/components', () => ({
import * as React from 'react';
import { shallow } from 'enzyme';
import CreateProjectForm from '../CreateProjectForm';
-import { change, submit } from '../../../helpers/testUtils';
+import { change, submit, click } from '../../../helpers/testUtils';
const createProject = require('../../../api/components').createProject as jest.Mock<any>;
@@ -46,9 +46,6 @@ it('creates project', async () => {
change(wrapper.find('input[name="name"]'), 'name', {
currentTarget: { name: 'name', value: 'name' }
});
- change(wrapper.find('input[name="branch"]'), 'branch', {
- currentTarget: { name: 'branch', value: 'branch' }
- });
change(wrapper.find('input[name="key"]'), 'key', {
currentTarget: { name: 'key', value: 'key' }
});
@@ -58,7 +55,7 @@ it('creates project', async () => {
submit(wrapper.find('form'));
expect(createProject).toBeCalledWith({
- branch: 'branch',
+ branch: '',
name: 'name',
organization: 'org',
project: 'key',
@@ -66,7 +63,21 @@ it('creates project', async () => {
});
expect(wrapper).toMatchSnapshot();
- await new Promise(resolve => setImmediate(resolve));
+ await new Promise(setImmediate);
wrapper.update();
expect(wrapper).toMatchSnapshot();
});
+
+it('shows more', () => {
+ const wrapper = shallow(
+ <CreateProjectForm
+ onClose={jest.fn()}
+ onProjectCreated={jest.fn()}
+ organization={organization}
+ />
+ );
+ expect(wrapper).toMatchSnapshot();
+
+ click(wrapper.find('.js-more'));
+ expect(wrapper).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap
index 036b78af957..332863ffafe 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap
@@ -56,23 +56,6 @@ exports[`creates project 1`] = `
className="modal-field"
>
<label
- htmlFor="create-project-branch"
- >
- branch
- </label>
- <input
- id="create-project-branch"
- maxLength={200}
- name="branch"
- onChange={[Function]}
- type="text"
- value=""
- />
- </div>
- <div
- className="modal-field"
- >
- <label
htmlFor="create-project-key"
>
key
@@ -111,6 +94,17 @@ exports[`creates project 1`] = `
/>
</div>
</div>
+ <div
+ className="modal-field big-spacer-top"
+ >
+ <a
+ className="js-more text-muted note"
+ href="#"
+ onClick={[Function]}
+ >
+ more
+ </a>
+ </div>
</div>
<footer
className="modal-foot"
@@ -190,23 +184,6 @@ exports[`creates project 2`] = `
className="modal-field"
>
<label
- htmlFor="create-project-branch"
- >
- branch
- </label>
- <input
- id="create-project-branch"
- maxLength={200}
- name="branch"
- onChange={[Function]}
- type="text"
- value="branch"
- />
- </div>
- <div
- className="modal-field"
- >
- <label
htmlFor="create-project-key"
>
key
@@ -245,6 +222,17 @@ exports[`creates project 2`] = `
/>
</div>
</div>
+ <div
+ className="modal-field big-spacer-top"
+ >
+ <a
+ className="js-more text-muted note"
+ href="#"
+ onClick={[Function]}
+ >
+ more
+ </a>
+ </div>
</div>
<footer
className="modal-foot"
@@ -324,23 +312,6 @@ exports[`creates project 3`] = `
className="modal-field"
>
<label
- htmlFor="create-project-branch"
- >
- branch
- </label>
- <input
- id="create-project-branch"
- maxLength={200}
- name="branch"
- onChange={[Function]}
- type="text"
- value="branch"
- />
- </div>
- <div
- className="modal-field"
- >
- <label
htmlFor="create-project-key"
>
key
@@ -379,6 +350,17 @@ exports[`creates project 3`] = `
/>
</div>
</div>
+ <div
+ className="modal-field big-spacer-top"
+ >
+ <a
+ className="js-more text-muted note"
+ href="#"
+ onClick={[Function]}
+ >
+ more
+ </a>
+ </div>
</div>
<footer
className="modal-foot"
@@ -467,3 +449,266 @@ exports[`creates project 4`] = `
</div>
</Modal>
`;
+
+exports[`shows more 1`] = `
+<Modal
+ ariaHideApp={true}
+ bodyOpenClassName="ReactModal__Body--open"
+ className="modal"
+ closeTimeoutMS={0}
+ contentLabel="modal form"
+ isOpen={true}
+ onRequestClose={[Function]}
+ overlayClassName="modal-overlay"
+ parentSelector={[Function]}
+ portalClassName="ReactModalPortal"
+ shouldCloseOnOverlayClick={true}
+>
+ <form
+ id="create-project-form"
+ onSubmit={[Function]}
+ >
+ <header
+ className="modal-head"
+ >
+ <h2>
+ qualifiers.create.TRK
+ </h2>
+ </header>
+ <div
+ className="modal-body"
+ >
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="create-project-name"
+ >
+ name
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ autoFocus={true}
+ id="create-project-name"
+ maxLength={2000}
+ name="name"
+ onChange={[Function]}
+ required={true}
+ type="text"
+ value=""
+ />
+ </div>
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="create-project-key"
+ >
+ key
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ id="create-project-key"
+ maxLength={400}
+ name="key"
+ onChange={[Function]}
+ required={true}
+ type="text"
+ value=""
+ />
+ </div>
+ <div
+ className="modal-field"
+ >
+ <label>
+ visibility
+ </label>
+ <VisibilitySelector
+ className="little-spacer-top"
+ onChange={[Function]}
+ visibility="public"
+ />
+ <div
+ className="spacer-top"
+ >
+ <UpgradeOrganizationBox
+ organization="org"
+ />
+ </div>
+ </div>
+ <div
+ className="modal-field big-spacer-top"
+ >
+ <a
+ className="js-more text-muted note"
+ href="#"
+ onClick={[Function]}
+ >
+ more
+ </a>
+ </div>
+ </div>
+ <footer
+ className="modal-foot"
+ >
+ <button
+ disabled={false}
+ id="create-project-submit"
+ type="submit"
+ >
+ create
+ </button>
+ <a
+ href="#"
+ id="create-project-cancel"
+ onClick={[Function]}
+ >
+ cancel
+ </a>
+ </footer>
+ </form>
+</Modal>
+`;
+
+exports[`shows more 2`] = `
+<Modal
+ ariaHideApp={true}
+ bodyOpenClassName="ReactModal__Body--open"
+ className="modal"
+ closeTimeoutMS={0}
+ contentLabel="modal form"
+ isOpen={true}
+ onRequestClose={[Function]}
+ overlayClassName="modal-overlay"
+ parentSelector={[Function]}
+ portalClassName="ReactModalPortal"
+ shouldCloseOnOverlayClick={true}
+>
+ <form
+ id="create-project-form"
+ onSubmit={[Function]}
+ >
+ <header
+ className="modal-head"
+ >
+ <h2>
+ qualifiers.create.TRK
+ </h2>
+ </header>
+ <div
+ className="modal-body"
+ >
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="create-project-name"
+ >
+ name
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ autoFocus={true}
+ id="create-project-name"
+ maxLength={2000}
+ name="name"
+ onChange={[Function]}
+ required={true}
+ type="text"
+ value=""
+ />
+ </div>
+ <div
+ className="modal-field"
+ >
+ <label
+ htmlFor="create-project-key"
+ >
+ key
+ <em
+ className="mandatory"
+ >
+ *
+ </em>
+ </label>
+ <input
+ id="create-project-key"
+ maxLength={400}
+ name="key"
+ onChange={[Function]}
+ required={true}
+ type="text"
+ value=""
+ />
+ </div>
+ <div
+ className="modal-field"
+ >
+ <label>
+ visibility
+ </label>
+ <VisibilitySelector
+ className="little-spacer-top"
+ onChange={[Function]}
+ visibility="public"
+ />
+ <div
+ className="spacer-top"
+ >
+ <UpgradeOrganizationBox
+ organization="org"
+ />
+ </div>
+ </div>
+ <div
+ className="modal-field big-spacer-top"
+ >
+ <label
+ htmlFor="create-project-branch"
+ >
+ branch
+ </label>
+ <input
+ autoFocus={true}
+ id="create-project-branch"
+ maxLength={200}
+ name="branch"
+ onChange={[Function]}
+ type="text"
+ value=""
+ />
+ </div>
+ </div>
+ <footer
+ className="modal-foot"
+ >
+ <button
+ disabled={false}
+ id="create-project-submit"
+ type="submit"
+ >
+ create
+ </button>
+ <a
+ href="#"
+ id="create-project-cancel"
+ onClick={[Function]}
+ >
+ cancel
+ </a>
+ </footer>
+ </form>
+</Modal>
+`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/App.js b/server/sonar-web/src/main/js/apps/settings/components/App.js
index 866830fbb69..dff1d0b74d5 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/App.js
+++ b/server/sonar-web/src/main/js/apps/settings/components/App.js
@@ -98,7 +98,7 @@ export default class App extends React.PureComponent {
link: (
<Link
to={{
- pathname: '/project/branches',
+ pathname: '/project/settings',
query: { id: this.props.component && this.props.component.key }
}}>
{translate('branches.settings_hint_tab')}
diff --git a/server/sonar-web/src/main/js/components/common/BranchStatus.tsx b/server/sonar-web/src/main/js/components/common/BranchStatus.tsx
index ea1604dfa5c..40688087cf0 100644
--- a/server/sonar-web/src/main/js/components/common/BranchStatus.tsx
+++ b/server/sonar-web/src/main/js/components/common/BranchStatus.tsx
@@ -41,35 +41,35 @@ export default function BranchStatus({ branch, concise = false }: Props) {
const totalIssues =
branch.status.bugs + branch.status.vulnerabilities + branch.status.codeSmells;
- return (
+ const indicatorClassName = classNames('branch-status-indicator', {
+ 'is-failed': totalIssues > 0,
+ 'is-passed': totalIssues === 0
+ });
+
+ return concise ? (
<ul className="list-inline branch-status">
+ <li>{totalIssues}</li>
+ <li className="spacer-left">
+ <i className={indicatorClassName} />
+ </li>
+ </ul>
+ ) : (
+ <ul className="list-inline branch-status">
+ <li className="spacer-right">
+ <i className={indicatorClassName} />
+ </li>
+ <li>
+ {branch.status.bugs}
+ <BugIcon />
+ </li>
+ <li>
+ {branch.status.vulnerabilities}
+ <VulnerabilityIcon />
+ </li>
<li>
- <i
- className={classNames('branch-status-indicator', {
- 'is-failed': totalIssues > 0,
- 'is-passed': totalIssues === 0
- })}
- />
+ {branch.status.codeSmells}
+ <CodeSmellIcon />
</li>
- {concise && <li>{totalIssues}</li>}
- {!concise && (
- <li>
- {branch.status.bugs}
- <BugIcon className="little-spacer-left" />
- </li>
- )}
- {!concise && (
- <li>
- {branch.status.vulnerabilities}
- <VulnerabilityIcon className="little-spacer-left" />
- </li>
- )}
- {!concise && (
- <li>
- {branch.status.codeSmells}
- <CodeSmellIcon className="little-spacer-left" />
- </li>
- )}
</ul>
);
} else {
diff --git a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/BranchStatus-test.tsx.snap b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/BranchStatus-test.tsx.snap
index 1f4ccfc4484..b106929fbe4 100644
--- a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/BranchStatus-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/BranchStatus-test.tsx.snap
@@ -20,28 +20,24 @@ exports[`renders status of short-living branches 1`] = `
<ul
className="list-inline branch-status"
>
- <li>
+ <li
+ className="spacer-right"
+ >
<i
className="branch-status-indicator is-passed"
/>
</li>
<li>
0
- <BugIcon
- className="little-spacer-left"
- />
+ <BugIcon />
</li>
<li>
0
- <VulnerabilityIcon
- className="little-spacer-left"
- />
+ <VulnerabilityIcon />
</li>
<li>
0
- <CodeSmellIcon
- className="little-spacer-left"
- />
+ <CodeSmellIcon />
</li>
</ul>
`;
@@ -50,28 +46,24 @@ exports[`renders status of short-living branches 2`] = `
<ul
className="list-inline branch-status"
>
- <li>
+ <li
+ className="spacer-right"
+ >
<i
className="branch-status-indicator is-failed"
/>
</li>
<li>
0
- <BugIcon
- className="little-spacer-left"
- />
+ <BugIcon />
</li>
<li>
0
- <VulnerabilityIcon
- className="little-spacer-left"
- />
+ <VulnerabilityIcon />
</li>
<li>
1
- <CodeSmellIcon
- className="little-spacer-left"
- />
+ <CodeSmellIcon />
</li>
</ul>
`;
@@ -80,28 +72,24 @@ exports[`renders status of short-living branches 3`] = `
<ul
className="list-inline branch-status"
>
- <li>
+ <li
+ className="spacer-right"
+ >
<i
className="branch-status-indicator is-failed"
/>
</li>
<li>
7
- <BugIcon
- className="little-spacer-left"
- />
+ <BugIcon />
</li>
<li>
6
- <VulnerabilityIcon
- className="little-spacer-left"
- />
+ <VulnerabilityIcon />
</li>
<li>
3
- <CodeSmellIcon
- className="little-spacer-left"
- />
+ <CodeSmellIcon />
</li>
</ul>
`;