* show last analysis date for short-living branches * put "main branch" badge * display hint on branch administration page * hide anything related to branches on sonarcloud without branch support * fixup! fix showing administration on branchtags/6.6-RC1
@@ -34,18 +34,20 @@ interface Props { | |||
interface State { | |||
branchesEnabled: boolean; | |||
loading: boolean; | |||
onSonarCloud: boolean; | |||
} | |||
class App extends React.PureComponent<Props, State> { | |||
mounted: boolean; | |||
state: State = { branchesEnabled: false, loading: true }; | |||
state: State = { branchesEnabled: false, loading: true, onSonarCloud: false }; | |||
static childContextTypes = { | |||
branchesEnabled: PropTypes.bool.isRequired | |||
branchesEnabled: PropTypes.bool.isRequired, | |||
onSonarCloud: PropTypes.bool | |||
}; | |||
getChildContext() { | |||
return { branchesEnabled: this.state.branchesEnabled }; | |||
return { branchesEnabled: this.state.branchesEnabled, onSonarCloud: this.state.onSonarCloud }; | |||
} | |||
componentDidMount() { | |||
@@ -64,7 +66,10 @@ class App extends React.PureComponent<Props, State> { | |||
fetchAppState = () => { | |||
return this.props.fetchAppState().then(appState => { | |||
if (this.mounted) { | |||
this.setState({ branchesEnabled: appState.branchesEnabled }); | |||
const onSonarCloud = | |||
appState.settings != undefined && | |||
appState.settings['sonar.lf.sonarqube.com.enabled'] === 'true'; | |||
this.setState({ branchesEnabled: appState.branchesEnabled, onSonarCloud }); | |||
} | |||
}); | |||
}; |
@@ -18,7 +18,7 @@ | |||
} | |||
.navbar-context-meta-branch { | |||
margin-top: 20px; | |||
margin-top: 3px; | |||
line-height: 16px; | |||
} | |||
@@ -53,7 +53,8 @@ export default class ComponentNavBranch extends React.PureComponent<Props, State | |||
}; | |||
static contextTypes = { | |||
branchesEnabled: PropTypes.bool.isRequired | |||
branchesEnabled: PropTypes.bool.isRequired, | |||
onSonarCloud: PropTypes.bool | |||
}; | |||
componentDidMount() { | |||
@@ -172,6 +173,10 @@ export default class ComponentNavBranch extends React.PureComponent<Props, State | |||
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"> |
@@ -24,6 +24,7 @@ import BranchStatus from '../../../../components/common/BranchStatus'; | |||
import { Branch, Component } from '../../../types'; | |||
import BranchIcon from '../../../../components/icons-components/BranchIcon'; | |||
import { isShortLivingBranch } from '../../../../helpers/branches'; | |||
import { translate } from '../../../../helpers/l10n'; | |||
import { getProjectBranchUrl } from '../../../../helpers/urls'; | |||
interface Props { | |||
@@ -53,6 +54,10 @@ export default function ComponentNavBranchesMenuItem({ branch, ...props }: Props | |||
})} | |||
/> | |||
{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} /> |
@@ -191,7 +191,7 @@ export default class ComponentNavMenu extends React.PureComponent<Props> { | |||
renderAdministration() { | |||
const { branch } = this.props; | |||
if (branch && isShortLivingBranch(branch)) { | |||
if (!this.props.conf.showSettings || (branch && isShortLivingBranch(branch))) { | |||
return null; | |||
} | |||
@@ -18,7 +18,6 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import * as classNames from 'classnames'; | |||
import IncrementalBadge from './IncrementalBadge'; | |||
import BranchStatus from '../../../../components/common/BranchStatus'; | |||
import { Branch, Component, ComponentConfiguration } from '../../../types'; | |||
@@ -56,7 +55,7 @@ export default function ComponentNavMeta(props: Props) { | |||
key="isInProgress" | |||
overlay={<div dangerouslySetInnerHTML={{ __html: tooltip }} />} | |||
mouseLeaveDelay={2}> | |||
<li className={classNames({ 'navbar-context-meta-branch': shortBranch })}> | |||
<li> | |||
<i className="spinner" style={{ marginTop: '-1px' }} />{' '} | |||
<span className="text-info">{translate('background_task.status.IN_PROGRESS')}</span> | |||
</li> | |||
@@ -71,7 +70,7 @@ export default function ComponentNavMeta(props: Props) { | |||
key="isPending" | |||
overlay={<div dangerouslySetInnerHTML={{ __html: tooltip }} />} | |||
mouseLeaveDelay={2}> | |||
<li className={classNames({ 'navbar-context-meta-branch': shortBranch })}> | |||
<li> | |||
<PendingIcon /> <span>{translate('background_task.status.PENDING')}</span> | |||
</li> | |||
</Tooltip> | |||
@@ -85,7 +84,7 @@ export default function ComponentNavMeta(props: Props) { | |||
key="isFailed" | |||
overlay={<div dangerouslySetInnerHTML={{ __html: tooltip }} />} | |||
mouseLeaveDelay={2}> | |||
<li className={classNames({ 'navbar-context-meta-branch': shortBranch })}> | |||
<li> | |||
<span className="badge badge-danger"> | |||
{translate('background_task.status.FAILED')} | |||
</span> | |||
@@ -94,7 +93,7 @@ export default function ComponentNavMeta(props: Props) { | |||
); | |||
} | |||
if (props.component.analysisDate && !shortBranch) { | |||
if (props.component.analysisDate) { | |||
metaList.push( | |||
<li key="analysisDate"> | |||
<DateTimeFormatter date={props.component.analysisDate} /> | |||
@@ -112,25 +111,21 @@ export default function ComponentNavMeta(props: Props) { | |||
if (props.incremental) { | |||
metaList.push( | |||
<li key="incremental" className={classNames({ 'navbar-context-meta-branch': shortBranch })}> | |||
<li key="incremental"> | |||
<IncrementalBadge /> | |||
</li> | |||
); | |||
} | |||
if (shortBranch) { | |||
metaList.push( | |||
<li className="navbar-context-meta-branch" key="branch-status"> | |||
<BranchStatus branch={props.branch!} /> | |||
</li> | |||
); | |||
} | |||
return ( | |||
<div className="navbar-context-meta"> | |||
<ul className="list-inline"> | |||
{metaList} | |||
</ul> | |||
{shortBranch && | |||
<div className="navbar-context-meta-branch"> | |||
<BranchStatus branch={props.branch!} /> | |||
</div>} | |||
</div> | |||
); | |||
} |
@@ -112,3 +112,13 @@ it('renders no branch support popup', () => { | |||
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(); | |||
}); |
@@ -91,10 +91,11 @@ 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; | |||
const conf = { showSettings: true }; | |||
expect( | |||
shallow(<ComponentNavMenu branch={branch} component={component} conf={conf} />, { | |||
context: { branchesEnabled: true } | |||
}) | |||
).toMatchSnapshot(); | |||
[true, false].forEach(showSettings => | |||
expect( | |||
shallow(<ComponentNavMenu branch={branch} component={component} conf={{ showSettings }} />, { | |||
context: { branchesEnabled: true } | |||
}) | |||
).toMatchSnapshot() | |||
); | |||
}); |
@@ -28,6 +28,11 @@ exports[`renders main branch 1`] = ` | |||
className="little-spacer-right" | |||
/> | |||
master | |||
<div | |||
className="outline-badge spacer-left" | |||
> | |||
branches.main_branch | |||
</div> | |||
</div> | |||
<div | |||
className="big-spacer-left note" |
@@ -116,6 +116,102 @@ exports[`should work for long-living branches 1`] = ` | |||
</NavBarTabs> | |||
`; | |||
exports[`should work for long-living branches 2`] = ` | |||
<NavBarTabs> | |||
<li> | |||
<Link | |||
activeClassName="active" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/dashboard", | |||
"query": Object { | |||
"branch": "release", | |||
"id": "foo", | |||
}, | |||
} | |||
} | |||
> | |||
overview.page | |||
</Link> | |||
</li> | |||
<li> | |||
<Link | |||
activeClassName="active" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/project/issues", | |||
"query": Object { | |||
"branch": "release", | |||
"id": "foo", | |||
"resolved": "false", | |||
}, | |||
} | |||
} | |||
> | |||
issues.page | |||
</Link> | |||
</li> | |||
<li> | |||
<Link | |||
activeClassName="active" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/component_measures", | |||
"query": Object { | |||
"branch": "release", | |||
"id": "foo", | |||
}, | |||
} | |||
} | |||
> | |||
layout.measures | |||
</Link> | |||
</li> | |||
<li> | |||
<Link | |||
activeClassName="active" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/code", | |||
"query": Object { | |||
"branch": "release", | |||
"id": "foo", | |||
}, | |||
} | |||
} | |||
> | |||
code.page | |||
</Link> | |||
</li> | |||
<li> | |||
<Link | |||
activeClassName="active" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/project/activity", | |||
"query": Object { | |||
"branch": "release", | |||
"id": "foo", | |||
}, | |||
} | |||
} | |||
> | |||
project_activity.page | |||
</Link> | |||
</li> | |||
</NavBarTabs> | |||
`; | |||
exports[`should work for short-living branches 1`] = ` | |||
<NavBarTabs> | |||
<li> |
@@ -27,25 +27,30 @@ exports[`renders status of short-living branch 1`] = ` | |||
<ul | |||
className="list-inline" | |||
> | |||
<li | |||
className="navbar-context-meta-branch" | |||
> | |||
<BranchStatus | |||
branch={ | |||
Object { | |||
"isMain": false, | |||
"mergeBranch": "master", | |||
"name": "feature", | |||
"status": Object { | |||
"bugs": 0, | |||
"codeSmells": 2, | |||
"vulnerabilities": 3, | |||
}, | |||
"type": "SHORT", | |||
} | |||
} | |||
<li> | |||
<DateTimeFormatter | |||
date="2017-01-02T00:00:00.000Z" | |||
/> | |||
</li> | |||
</ul> | |||
<div | |||
className="navbar-context-meta-branch" | |||
> | |||
<BranchStatus | |||
branch={ | |||
Object { | |||
"isMain": false, | |||
"mergeBranch": "master", | |||
"name": "feature", | |||
"status": Object { | |||
"bugs": 0, | |||
"codeSmells": 2, | |||
"vulnerabilities": 3, | |||
}, | |||
"type": "SHORT", | |||
} | |||
} | |||
/> | |||
</div> | |||
</div> | |||
`; |
@@ -93,6 +93,10 @@ export default class BranchRow extends React.PureComponent<Props, State> { | |||
})} | |||
/> | |||
{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} /> |
@@ -13,6 +13,11 @@ exports[`renders main branch 1`] = ` | |||
className="little-spacer-right" | |||
/> | |||
master | |||
<div | |||
className="outline-badge spacer-left" | |||
> | |||
branches.main_branch | |||
</div> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" |
@@ -20,6 +20,8 @@ | |||
// @flow | |||
import React from 'react'; | |||
import Helmet from 'react-helmet'; | |||
import { Link } from 'react-router'; | |||
import { FormattedMessage } from 'react-intl'; | |||
import PageHeader from './PageHeader'; | |||
import CategoryDefinitionsList from './CategoryDefinitionsList'; | |||
import AllCategoriesList from './AllCategoriesList'; | |||
@@ -87,7 +89,25 @@ export default class App extends React.PureComponent { | |||
<div id="settings-page" className="page page-limited"> | |||
<Helmet title={translate('settings.page')} /> | |||
{branchName == null && <PageHeader branch={branchName} component={this.props.component} />} | |||
{branchName | |||
? <div className="alert alert-info"> | |||
<FormattedMessage | |||
defaultMessage={translate('branches.settings_hint')} | |||
id="branches.settings_hint" | |||
values={{ | |||
link: ( | |||
<Link | |||
to={{ | |||
pathname: '/project/branches', | |||
query: { id: this.props.component && this.props.component.key } | |||
}}> | |||
{translate('branches.settings_hint_tab')} | |||
</Link> | |||
) | |||
}} | |||
/> | |||
</div> | |||
: <PageHeader branch={branchName} component={this.props.component} />} | |||
<div className="side-tabs-layout settings-layout"> | |||
{branchName == null && | |||
<div className="side-tabs-side"> |
@@ -3173,3 +3173,6 @@ branches.manage=Manage branches | |||
branches.orphan_branch=Orphan Branch | |||
branches.orphan_branches=Orphan Branches | |||
branches.orphan_branches.tooltip=When a target branch of a short-living branch was deleted, this short-living branch becomes orphan. | |||
branches.main_branch=Main Branch | |||
branches.settings_hint=To administrate your branches, you have to go to your main branch's {link} tab. | |||
branches.settings_hint_tab=Administration > Branches |