aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorWouter Admiraal <wouter.admiraal@sonarsource.com>2020-04-28 15:47:44 +0200
committersonartech <sonartech@sonarsource.com>2020-05-04 20:03:53 +0000
commitb994ce6100ae6664b1b08aafbaf763cefdf582cb (patch)
tree9839e70aa1ae423c43e5ec156203f0a9067d59e4 /server
parent6099cf43753ba9de110a7921ac9dc269ee4d9a96 (diff)
downloadsonarqube-b994ce6100ae6664b1b08aafbaf763cefdf582cb.tar.gz
sonarqube-b994ce6100ae6664b1b08aafbaf763cefdf582cb.zip
SONAR-13339 Disable component menu links when no analysis has been run yet
Diffstat (limited to 'server')
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx3
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/Menu.css7
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx346
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/Menu-test.tsx25
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap3
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Menu-test.tsx.snap477
6 files changed, 485 insertions, 376 deletions
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 29f8c596785..ceeae0dbbf6 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
@@ -104,7 +104,10 @@ export default function ComponentNav(props: Props) {
</div>
<Menu
branchLike={currentBranchLike}
+ branchLikes={branchLikes}
component={component}
+ isInProgress={isInProgress}
+ isPending={isPending}
onToggleProjectInfo={() => setDisplayProjectInfo(!displayProjectInfo)}
/>
<InfoDrawer
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/Menu.css b/server/sonar-web/src/main/js/app/components/nav/component/Menu.css
index 3a62e30ca89..83418b1b3f5 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/Menu.css
+++ b/server/sonar-web/src/main/js/app/components/nav/component/Menu.css
@@ -25,3 +25,10 @@
color: var(--blue);
border-bottom-color: transparent;
}
+
+.navbar-tabs > li > a.disabled-link,
+.navbar-tabs > li > a.disabled-link:hover {
+ color: var(--gray71);
+ cursor: default;
+ border-bottom-color: transparent;
+}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx
index c1aea4a7bfa..b217d44e23c 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx
@@ -19,8 +19,9 @@
*/
import * as classNames from 'classnames';
import * as React from 'react';
-import { Link } from 'react-router';
+import { Link, LinkProps } from 'react-router';
import Dropdown from 'sonar-ui-common/components/controls/Dropdown';
+import Tooltip from 'sonar-ui-common/components/controls/Tooltip';
import BulletListIcon from 'sonar-ui-common/components/icons/BulletListIcon';
import DropdownIcon from 'sonar-ui-common/components/icons/DropdownIcon';
import NavBarTabs from 'sonar-ui-common/components/ui/NavBarTabs';
@@ -28,7 +29,7 @@ import { hasMessage, translate } from 'sonar-ui-common/helpers/l10n';
import { withAppState } from '../../../../components/hoc/withAppState';
import { getBranchLikeQuery, isMainBranch, isPullRequest } from '../../../../helpers/branch-like';
import { isSonarCloud } from '../../../../helpers/system';
-import { BranchLike } from '../../../../types/branch-like';
+import { BranchLike, BranchParameters } from '../../../../types/branch-like';
import { ComponentQualifier } from '../../../../types/component';
import './Menu.css';
@@ -52,66 +53,101 @@ const SETTINGS_URLS = [
interface Props {
appState: Pick<T.AppState, 'branchesEnabled'>;
branchLike: BranchLike | undefined;
+ branchLikes: BranchLike[] | undefined;
component: T.Component;
+ isInProgress?: boolean;
+ isPending?: boolean;
onToggleProjectInfo: () => void;
}
+type Query = BranchParameters & { id: string };
+
+function MenuLink({
+ hasAnalysis,
+ label,
+ ...props
+}: LinkProps & { hasAnalysis: boolean; label: React.ReactNode }) {
+ return hasAnalysis ? (
+ <Link {...props}>{label}</Link>
+ ) : (
+ <Tooltip overlay={translate('layout.must_be_configured')}>
+ <a aria-disabled="true" className="disabled-link">
+ {label}
+ </a>
+ </Tooltip>
+ );
+}
+
export class Menu extends React.PureComponent<Props> {
- isProject() {
+ hasAnalysis = () => {
+ const { branchLikes = [], component, isInProgress, isPending } = this.props;
+ const hasBranches = branchLikes.length > 1;
+ return hasBranches || isInProgress || isPending || component.analysisDate !== undefined;
+ };
+
+ isProject = () => {
return this.props.component.qualifier === ComponentQualifier.Project;
- }
+ };
- isDeveloper() {
+ isDeveloper = () => {
return this.props.component.qualifier === ComponentQualifier.Developper;
- }
+ };
- isPortfolio() {
+ isPortfolio = () => {
const { qualifier } = this.props.component;
return (
qualifier === ComponentQualifier.Portfolio || qualifier === ComponentQualifier.SubPortfolio
);
- }
+ };
- isApplication() {
+ isApplication = () => {
return this.props.component.qualifier === ComponentQualifier.Application;
- }
+ };
- getConfiguration() {
+ getConfiguration = () => {
return this.props.component.configuration || {};
- }
+ };
- getQuery = () => {
+ getQuery = (): Query => {
return { id: this.props.component.key, ...getBranchLikeQuery(this.props.branchLike) };
};
- renderDashboardLink() {
- const pathname = this.isPortfolio() ? '/portfolio' : '/dashboard';
+ renderDashboardLink = (query: Query, isPortfolio: boolean) => {
+ const pathname = isPortfolio ? '/portfolio' : '/dashboard';
return (
<li>
- <Link activeClassName="active" to={{ pathname, query: this.getQuery() }}>
+ <Link activeClassName="active" to={{ pathname, query }}>
{translate('overview.page')}
</Link>
</li>
);
- }
+ };
- renderCodeLink() {
+ renderCodeLink = (
+ hasAnalysis: boolean,
+ query: Query,
+ isApplication: boolean,
+ isPortfolio: boolean
+ ) => {
if (this.isDeveloper()) {
return null;
}
return (
<li>
- <Link activeClassName="active" to={{ pathname: '/code', query: this.getQuery() }}>
- {this.isPortfolio() || this.isApplication()
- ? translate('view_projects.page')
- : translate('code.page')}
- </Link>
+ <MenuLink
+ activeClassName="active"
+ hasAnalysis={hasAnalysis}
+ label={
+ isPortfolio || isApplication ? translate('view_projects.page') : translate('code.page')
+ }
+ to={{ pathname: '/code', query }}
+ />
</li>
);
- }
+ };
- renderActivityLink() {
+ renderActivityLink = (hasAnalysis: boolean, query: Query) => {
const { branchLike } = this.props;
if (isPullRequest(branchLike)) {
@@ -120,54 +156,58 @@ export class Menu extends React.PureComponent<Props> {
return (
<li>
- <Link
+ <MenuLink
activeClassName="active"
- to={{ pathname: '/project/activity', query: this.getQuery() }}>
- {translate('project_activity.page')}
- </Link>
+ hasAnalysis={hasAnalysis}
+ label={translate('project_activity.page')}
+ to={{ pathname: '/project/activity', query }}
+ />
</li>
);
- }
+ };
- renderIssuesLink() {
+ renderIssuesLink = (hasAnalysis: boolean, query: Query) => {
return (
<li>
- <Link
+ <MenuLink
activeClassName="active"
- to={{ pathname: '/project/issues', query: { ...this.getQuery(), resolved: 'false' } }}>
- {translate('issues.page')}
- </Link>
+ hasAnalysis={hasAnalysis}
+ label={translate('issues.page')}
+ to={{ pathname: '/project/issues', query: { ...query, resolved: 'false' } }}
+ />
</li>
);
- }
+ };
- renderComponentMeasuresLink() {
+ renderComponentMeasuresLink = (hasAnalysis: boolean, query: Query) => {
return (
<li>
- <Link
+ <MenuLink
activeClassName="active"
- to={{ pathname: '/component_measures', query: this.getQuery() }}>
- {translate('layout.measures')}
- </Link>
+ hasAnalysis={hasAnalysis}
+ label={translate('layout.measures')}
+ to={{ pathname: '/component_measures', query }}
+ />
</li>
);
- }
+ };
- renderSecurityHotspotsLink() {
+ renderSecurityHotspotsLink = (hasAnalysis: boolean, query: Query, isPortfolio: boolean) => {
return (
- !this.isPortfolio() && (
+ !isPortfolio && (
<li>
- <Link
+ <MenuLink
activeClassName="active"
- to={{ pathname: '/security_hotspots', query: this.getQuery() }}>
- {translate('layout.security_hotspots')}
- </Link>
+ hasAnalysis={hasAnalysis}
+ label={translate('layout.security_hotspots')}
+ to={{ pathname: '/security_hotspots', query }}
+ />
</li>
)
);
- }
+ };
- renderSecurityReports() {
+ renderSecurityReports = (hasAnalysis: boolean, query: Query) => {
const { branchLike, component } = this.props;
const { extensions = [] } = component;
@@ -185,19 +225,25 @@ export class Menu extends React.PureComponent<Props> {
return (
<li>
- <Link
+ <MenuLink
activeClassName="active"
+ hasAnalysis={hasAnalysis}
+ label={translate('layout.security_reports')}
to={{
pathname: '/project/extension/securityreport/securityreport',
- query: this.getQuery()
- }}>
- {translate('layout.security_reports')}
- </Link>
+ query
+ }}
+ />
</li>
);
- }
+ };
- renderAdministration() {
+ renderAdministration = (
+ query: Query,
+ isProject: boolean,
+ isApplication: boolean,
+ isPortfolio: boolean
+ ) => {
const { branchLike, component } = this.props;
if (!this.getConfiguration().showSettings || isPullRequest(branchLike)) {
@@ -206,7 +252,7 @@ export class Menu extends React.PureComponent<Props> {
const isSettingsActive = SETTINGS_URLS.some(url => window.location.href.indexOf(url) !== -1);
- const adminLinks = this.renderAdministrationLinks();
+ const adminLinks = this.renderAdministrationLinks(query, isProject, isApplication, isPortfolio);
if (!adminLinks.some(link => link != null)) {
return null;
}
@@ -232,33 +278,38 @@ export class Menu extends React.PureComponent<Props> {
)}
</Dropdown>
);
- }
+ };
- renderAdministrationLinks() {
+ renderAdministrationLinks = (
+ query: Query,
+ isProject: boolean,
+ isApplication: boolean,
+ isPortfolio: boolean
+ ) => {
return [
- this.renderSettingsLink(),
- this.renderBranchesLink(),
- this.renderBaselineLink(),
- this.renderProfilesLink(),
- this.renderQualityGateLink(),
- this.renderCustomMeasuresLink(),
- this.renderLinksLink(),
- this.renderPermissionsLink(),
- this.renderBackgroundTasksLink(),
- this.renderUpdateKeyLink(),
- this.renderWebhooksLink(),
- ...this.renderAdminExtensions(),
- this.renderDeletionLink()
+ this.renderSettingsLink(query, isApplication, isPortfolio),
+ this.renderBranchesLink(query, isProject),
+ this.renderBaselineLink(query, isApplication, isPortfolio),
+ this.renderProfilesLink(query),
+ this.renderQualityGateLink(query),
+ this.renderCustomMeasuresLink(query),
+ this.renderLinksLink(query),
+ this.renderPermissionsLink(query),
+ this.renderBackgroundTasksLink(query),
+ this.renderUpdateKeyLink(query),
+ this.renderWebhooksLink(query, isProject),
+ ...this.renderAdminExtensions(query),
+ this.renderDeletionLink(query)
];
- }
+ };
- renderProjectInformationButton() {
+ renderProjectInformationButton = (isProject: boolean, isApplication: boolean) => {
if (isPullRequest(this.props.branchLike)) {
return null;
}
return (
- (this.isProject() || this.isApplication()) && (
+ (isProject || isApplication) && (
<li>
<a
className="menu-button"
@@ -270,32 +321,30 @@ export class Menu extends React.PureComponent<Props> {
role="button"
tabIndex={0}>
<BulletListIcon className="little-spacer-right" />
- {translate(this.isProject() ? 'project' : 'application', 'info.title')}
+ {translate(isProject ? 'project' : 'application', 'info.title')}
</a>
</li>
)
);
- }
+ };
- renderSettingsLink() {
- if (!this.getConfiguration().showSettings || this.isApplication() || this.isPortfolio()) {
+ renderSettingsLink = (query: Query, isApplication: boolean, isPortfolio: boolean) => {
+ if (!this.getConfiguration().showSettings || isApplication || isPortfolio) {
return null;
}
return (
<li key="settings">
- <Link
- activeClassName="active"
- to={{ pathname: '/project/settings', query: this.getQuery() }}>
+ <Link activeClassName="active" to={{ pathname: '/project/settings', query }}>
{translate('project_settings.page')}
</Link>
</li>
);
- }
+ };
- renderBranchesLink() {
+ renderBranchesLink = (query: Query, isProject: boolean) => {
if (
!this.props.appState.branchesEnabled ||
- !this.isProject() ||
+ !isProject ||
!this.getConfiguration().showSettings
) {
return null;
@@ -303,145 +352,131 @@ export class Menu extends React.PureComponent<Props> {
return (
<li key="branches">
- <Link
- activeClassName="active"
- to={{ pathname: '/project/branches', query: this.getQuery() }}>
+ <Link activeClassName="active" to={{ pathname: '/project/branches', query }}>
{translate('project_branch_pull_request.page')}
</Link>
</li>
);
- }
+ };
- renderBaselineLink() {
- if (!this.getConfiguration().showSettings || this.isApplication() || this.isPortfolio()) {
+ renderBaselineLink = (query: Query, isApplication: boolean, isPortfolio: boolean) => {
+ if (!this.getConfiguration().showSettings || isApplication || isPortfolio) {
return null;
}
return (
<li key="baseline">
- <Link
- activeClassName="active"
- to={{ pathname: '/project/baseline', query: this.getQuery() }}>
+ <Link activeClassName="active" to={{ pathname: '/project/baseline', query }}>
{translate('project_baseline.page')}
</Link>
</li>
);
- }
+ };
- renderProfilesLink() {
+ renderProfilesLink = (query: Query) => {
if (!this.getConfiguration().showQualityProfiles) {
return null;
}
return (
<li key="profiles">
- <Link
- activeClassName="active"
- to={{ pathname: '/project/quality_profiles', query: this.getQuery() }}>
+ <Link activeClassName="active" to={{ pathname: '/project/quality_profiles', query }}>
{translate('project_quality_profiles.page')}
</Link>
</li>
);
- }
+ };
- renderQualityGateLink() {
+ renderQualityGateLink = (query: Query) => {
if (!this.getConfiguration().showQualityGates) {
return null;
}
return (
<li key="quality_gate">
- <Link
- activeClassName="active"
- to={{ pathname: '/project/quality_gate', query: this.getQuery() }}>
+ <Link activeClassName="active" to={{ pathname: '/project/quality_gate', query }}>
{translate('project_quality_gate.page')}
</Link>
</li>
);
- }
+ };
- renderCustomMeasuresLink() {
+ renderCustomMeasuresLink = (query: Query) => {
if (isSonarCloud() || !this.getConfiguration().showManualMeasures) {
return null;
}
return (
<li key="custom_measures">
- <Link
- activeClassName="active"
- to={{ pathname: '/custom_measures', query: this.getQuery() }}>
+ <Link activeClassName="active" to={{ pathname: '/custom_measures', query }}>
{translate('custom_measures.page')}
</Link>
</li>
);
- }
+ };
- renderLinksLink() {
+ renderLinksLink = (query: Query) => {
if (!this.getConfiguration().showLinks) {
return null;
}
return (
<li key="links">
- <Link activeClassName="active" to={{ pathname: '/project/links', query: this.getQuery() }}>
+ <Link activeClassName="active" to={{ pathname: '/project/links', query }}>
{translate('project_links.page')}
</Link>
</li>
);
- }
+ };
- renderPermissionsLink() {
+ renderPermissionsLink = (query: Query) => {
if (!this.getConfiguration().showPermissions) {
return null;
}
return (
<li key="permissions">
- <Link activeClassName="active" to={{ pathname: '/project_roles', query: this.getQuery() }}>
+ <Link activeClassName="active" to={{ pathname: '/project_roles', query }}>
{translate('permissions.page')}
</Link>
</li>
);
- }
+ };
- renderBackgroundTasksLink() {
+ renderBackgroundTasksLink = (query: Query) => {
if (!this.getConfiguration().showBackgroundTasks) {
return null;
}
return (
<li key="background_tasks">
- <Link
- activeClassName="active"
- to={{ pathname: '/project/background_tasks', query: this.getQuery() }}>
+ <Link activeClassName="active" to={{ pathname: '/project/background_tasks', query }}>
{translate('background_tasks.page')}
</Link>
</li>
);
- }
+ };
- renderUpdateKeyLink() {
+ renderUpdateKeyLink = (query: Query) => {
if (!this.getConfiguration().showUpdateKey) {
return null;
}
return (
<li key="update_key">
- <Link activeClassName="active" to={{ pathname: '/project/key', query: this.getQuery() }}>
+ <Link activeClassName="active" to={{ pathname: '/project/key', query }}>
{translate('update_key.page')}
</Link>
</li>
);
- }
+ };
- renderWebhooksLink() {
- if (!this.getConfiguration().showSettings || !this.isProject()) {
+ renderWebhooksLink = (query: Query, isProject: boolean) => {
+ if (!this.getConfiguration().showSettings || !isProject) {
return null;
}
return (
<li key="webhooks">
- <Link
- activeClassName="active"
- to={{ pathname: '/project/webhooks', query: this.getQuery() }}>
+ <Link activeClassName="active" to={{ pathname: '/project/webhooks', query }}>
{translate('webhooks.page')}
</Link>
</li>
);
- }
+ };
- renderDeletionLink() {
+ renderDeletionLink = (query: Query) => {
const { qualifier } = this.props.component;
if (!this.getConfiguration().showSettings) {
@@ -460,18 +495,16 @@ export class Menu extends React.PureComponent<Props> {
return (
<li key="project_delete">
- <Link
- activeClassName="active"
- to={{ pathname: '/project/deletion', query: this.getQuery() }}>
+ <Link activeClassName="active" to={{ pathname: '/project/deletion', query }}>
{translate('deletion.page')}
</Link>
</li>
);
- }
+ };
- renderExtension = ({ key, name }: T.Extension, isAdmin: boolean) => {
+ renderExtension = ({ key, name }: T.Extension, isAdmin: boolean, baseQuery: Query) => {
const pathname = isAdmin ? `/project/admin/extension/${key}` : `/project/extension/${key}`;
- const query = { ...this.getQuery(), qualifier: this.props.component.qualifier };
+ const query = { ...baseQuery, qualifier: this.props.component.qualifier };
return (
<li key={key}>
<Link activeClassName="active" to={{ pathname, query }}>
@@ -481,15 +514,15 @@ export class Menu extends React.PureComponent<Props> {
);
};
- renderAdminExtensions() {
+ renderAdminExtensions = (query: Query) => {
if (this.props.branchLike && !isMainBranch(this.props.branchLike)) {
return [];
}
const extensions = this.getConfiguration().extensions || [];
- return extensions.map(e => this.renderExtension(e, true));
- }
+ return extensions.map(e => this.renderExtension(e, true, query));
+ };
- renderExtensions() {
+ renderExtensions = (query: Query) => {
const extensions = this.props.component.extensions || [];
const withoutSecurityExtension = extensions.filter(
extension => !extension.key.startsWith('securityreport/')
@@ -506,7 +539,7 @@ export class Menu extends React.PureComponent<Props> {
data-test="extensions"
overlay={
<ul className="menu">
- {withoutSecurityExtension.map(e => this.renderExtension(e, false))}
+ {withoutSecurityExtension.map(e => this.renderExtension(e, false, query))}
</ul>
}
tagName="li">
@@ -524,24 +557,29 @@ export class Menu extends React.PureComponent<Props> {
)}
</Dropdown>
);
- }
+ };
render() {
+ const isProject = this.isProject();
+ const isApplication = this.isApplication();
+ const isPortfolio = this.isPortfolio();
+ const hasAnalysis = this.hasAnalysis();
+ const query = this.getQuery();
return (
<div className="display-flex-center display-flex-space-between">
<NavBarTabs>
- {this.renderDashboardLink()}
- {this.renderIssuesLink()}
- {this.renderSecurityHotspotsLink()}
- {this.renderSecurityReports()}
- {this.renderComponentMeasuresLink()}
- {this.renderCodeLink()}
- {this.renderActivityLink()}
- {this.renderExtensions()}
+ {this.renderDashboardLink(query, isPortfolio)}
+ {this.renderIssuesLink(hasAnalysis, query)}
+ {this.renderSecurityHotspotsLink(hasAnalysis, query, isPortfolio)}
+ {this.renderSecurityReports(hasAnalysis, query)}
+ {this.renderComponentMeasuresLink(hasAnalysis, query)}
+ {this.renderCodeLink(hasAnalysis, query, isApplication, isPortfolio)}
+ {this.renderActivityLink(hasAnalysis, query)}
+ {this.renderExtensions(query)}
</NavBarTabs>
<NavBarTabs>
- {this.renderAdministration()}
- {this.renderProjectInformationButton()}
+ {this.renderAdministration(query, isProject, isApplication, isPortfolio)}
+ {this.renderProjectInformationButton(isProject, isApplication)}
</NavBarTabs>
</div>
);
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/Menu-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/Menu-test.tsx
index 7f825a0c9ef..041f3c3d8fa 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/Menu-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/Menu-test.tsx
@@ -25,18 +25,17 @@ import {
mockMainBranch,
mockPullRequest
} from '../../../../../helpers/mocks/branch-like';
+import { mockComponent } from '../../../../../helpers/testMocks';
import { ComponentQualifier } from '../../../../../types/component';
import { Menu } from '../Menu';
const mainBranch = mockMainBranch();
-const baseComponent = {
- breadcrumbs: [],
+const baseComponent = mockComponent({
+ analysisDate: '2019-12-01',
key: 'foo',
- name: 'foo',
- organization: 'org',
- qualifier: 'TRK'
-};
+ name: 'foo'
+});
it('should work with extensions', () => {
const component = {
@@ -137,12 +136,26 @@ it('should work for all qualifiers', () => {
}
});
+it('should disable links if no analysis has been done', () => {
+ expect(
+ shallowRender({
+ component: {
+ ...baseComponent,
+ analysisDate: undefined
+ }
+ })
+ ).toMatchSnapshot();
+});
+
function shallowRender(props: Partial<Menu['props']>) {
return shallow<Menu>(
<Menu
appState={{ branchesEnabled: true }}
branchLike={mainBranch}
+ branchLikes={[mainBranch]}
component={baseComponent}
+ isInProgress={false}
+ isPending={false}
onToggleProjectInfo={jest.fn()}
{...props}
/>
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 e4bd6ee6c20..e311796c90b 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
@@ -67,6 +67,7 @@ exports[`renders 1`] = `
/>
</div>
<Connect(withAppState(Menu))
+ branchLikes={Array []}
component={
Object {
"breadcrumbs": Array [
@@ -82,6 +83,8 @@ exports[`renders 1`] = `
"qualifier": "TRK",
}
}
+ isInProgress={true}
+ isPending={true}
onToggleProjectInfo={[Function]}
/>
<InfoDrawer
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Menu-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Menu-test.tsx.snap
index ddb2ec883eb..e57b7935e4b 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Menu-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Menu-test.tsx.snap
@@ -1,5 +1,122 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`should disable links if no analysis has been done 1`] = `
+<div
+ className="display-flex-center display-flex-space-between"
+>
+ <NavBarTabs>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/dashboard",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ >
+ overview.page
+ </Link>
+ </li>
+ <li>
+ <MenuLink
+ activeClassName="active"
+ hasAnalysis={false}
+ label="issues.page"
+ to={
+ Object {
+ "pathname": "/project/issues",
+ "query": Object {
+ "id": "foo",
+ "resolved": "false",
+ },
+ }
+ }
+ />
+ </li>
+ <li>
+ <MenuLink
+ activeClassName="active"
+ hasAnalysis={false}
+ label="layout.security_hotspots"
+ to={
+ Object {
+ "pathname": "/security_hotspots",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ />
+ </li>
+ <li>
+ <MenuLink
+ activeClassName="active"
+ hasAnalysis={false}
+ label="layout.measures"
+ to={
+ Object {
+ "pathname": "/component_measures",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ />
+ </li>
+ <li>
+ <MenuLink
+ activeClassName="active"
+ hasAnalysis={false}
+ label="code.page"
+ to={
+ Object {
+ "pathname": "/code",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ />
+ </li>
+ <li>
+ <MenuLink
+ activeClassName="active"
+ hasAnalysis={false}
+ label="project_activity.page"
+ to={
+ Object {
+ "pathname": "/project/activity",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+ />
+ </li>
+ </NavBarTabs>
+ <NavBarTabs>
+ <li>
+ <a
+ className="menu-button"
+ onClick={[Function]}
+ role="button"
+ tabIndex={0}
+ >
+ <BulletListIcon
+ className="little-spacer-right"
+ />
+ project.info.title
+ </a>
+ </li>
+ </NavBarTabs>
+</div>
+`;
+
exports[`should render correctly for security extensions 1`] = `
<Dropdown
data-test="extensions"
@@ -59,10 +176,10 @@ exports[`should work for a branch 1`] = `
</Link>
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="issues.page"
to={
Object {
"pathname": "/project/issues",
@@ -73,15 +190,13 @@ exports[`should work for a branch 1`] = `
},
}
}
- >
- issues.page
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="layout.security_hotspots"
to={
Object {
"pathname": "/security_hotspots",
@@ -91,15 +206,13 @@ exports[`should work for a branch 1`] = `
},
}
}
- >
- layout.security_hotspots
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="layout.measures"
to={
Object {
"pathname": "/component_measures",
@@ -109,15 +222,13 @@ exports[`should work for a branch 1`] = `
},
}
}
- >
- layout.measures
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="code.page"
to={
Object {
"pathname": "/code",
@@ -127,15 +238,13 @@ exports[`should work for a branch 1`] = `
},
}
}
- >
- code.page
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="project_activity.page"
to={
Object {
"pathname": "/project/activity",
@@ -145,9 +254,7 @@ exports[`should work for a branch 1`] = `
},
}
}
- >
- project_activity.page
- </Link>
+ />
</li>
</NavBarTabs>
<NavBarTabs>
@@ -294,10 +401,10 @@ exports[`should work for a branch 2`] = `
</Link>
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="issues.page"
to={
Object {
"pathname": "/project/issues",
@@ -308,15 +415,13 @@ exports[`should work for a branch 2`] = `
},
}
}
- >
- issues.page
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="layout.security_hotspots"
to={
Object {
"pathname": "/security_hotspots",
@@ -326,15 +431,13 @@ exports[`should work for a branch 2`] = `
},
}
}
- >
- layout.security_hotspots
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="layout.measures"
to={
Object {
"pathname": "/component_measures",
@@ -344,15 +447,13 @@ exports[`should work for a branch 2`] = `
},
}
}
- >
- layout.measures
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="code.page"
to={
Object {
"pathname": "/code",
@@ -362,15 +463,13 @@ exports[`should work for a branch 2`] = `
},
}
}
- >
- code.page
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="project_activity.page"
to={
Object {
"pathname": "/project/activity",
@@ -380,9 +479,7 @@ exports[`should work for a branch 2`] = `
},
}
}
- >
- project_activity.page
- </Link>
+ />
</li>
</NavBarTabs>
<NavBarTabs>
@@ -426,10 +523,10 @@ exports[`should work for all qualifiers 1`] = `
</Link>
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="issues.page"
to={
Object {
"pathname": "/project/issues",
@@ -439,15 +536,13 @@ exports[`should work for all qualifiers 1`] = `
},
}
}
- >
- issues.page
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="layout.security_hotspots"
to={
Object {
"pathname": "/security_hotspots",
@@ -456,15 +551,13 @@ exports[`should work for all qualifiers 1`] = `
},
}
}
- >
- layout.security_hotspots
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="layout.measures"
to={
Object {
"pathname": "/component_measures",
@@ -473,15 +566,13 @@ exports[`should work for all qualifiers 1`] = `
},
}
}
- >
- layout.measures
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="code.page"
to={
Object {
"pathname": "/code",
@@ -490,15 +581,13 @@ exports[`should work for all qualifiers 1`] = `
},
}
}
- >
- code.page
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="project_activity.page"
to={
Object {
"pathname": "/project/activity",
@@ -507,9 +596,7 @@ exports[`should work for all qualifiers 1`] = `
},
}
}
- >
- project_activity.page
- </Link>
+ />
</li>
</NavBarTabs>
<NavBarTabs>
@@ -650,10 +737,10 @@ exports[`should work for all qualifiers 2`] = `
</Link>
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="issues.page"
to={
Object {
"pathname": "/project/issues",
@@ -663,15 +750,13 @@ exports[`should work for all qualifiers 2`] = `
},
}
}
- >
- issues.page
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="layout.measures"
to={
Object {
"pathname": "/component_measures",
@@ -680,15 +765,13 @@ exports[`should work for all qualifiers 2`] = `
},
}
}
- >
- layout.measures
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="view_projects.page"
to={
Object {
"pathname": "/code",
@@ -697,15 +780,13 @@ exports[`should work for all qualifiers 2`] = `
},
}
}
- >
- view_projects.page
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="project_activity.page"
to={
Object {
"pathname": "/project/activity",
@@ -714,9 +795,7 @@ exports[`should work for all qualifiers 2`] = `
},
}
}
- >
- project_activity.page
- </Link>
+ />
</li>
</NavBarTabs>
<NavBarTabs>
@@ -776,10 +855,10 @@ exports[`should work for all qualifiers 3`] = `
</Link>
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="issues.page"
to={
Object {
"pathname": "/project/issues",
@@ -789,15 +868,13 @@ exports[`should work for all qualifiers 3`] = `
},
}
}
- >
- issues.page
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="layout.measures"
to={
Object {
"pathname": "/component_measures",
@@ -806,15 +883,13 @@ exports[`should work for all qualifiers 3`] = `
},
}
}
- >
- layout.measures
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="view_projects.page"
to={
Object {
"pathname": "/code",
@@ -823,15 +898,13 @@ exports[`should work for all qualifiers 3`] = `
},
}
}
- >
- view_projects.page
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="project_activity.page"
to={
Object {
"pathname": "/project/activity",
@@ -840,9 +913,7 @@ exports[`should work for all qualifiers 3`] = `
},
}
}
- >
- project_activity.page
- </Link>
+ />
</li>
</NavBarTabs>
<NavBarTabs />
@@ -872,10 +943,10 @@ exports[`should work for all qualifiers 4`] = `
</Link>
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="issues.page"
to={
Object {
"pathname": "/project/issues",
@@ -885,15 +956,13 @@ exports[`should work for all qualifiers 4`] = `
},
}
}
- >
- issues.page
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="layout.security_hotspots"
to={
Object {
"pathname": "/security_hotspots",
@@ -902,15 +971,13 @@ exports[`should work for all qualifiers 4`] = `
},
}
}
- >
- layout.security_hotspots
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="layout.measures"
to={
Object {
"pathname": "/component_measures",
@@ -919,15 +986,13 @@ exports[`should work for all qualifiers 4`] = `
},
}
}
- >
- layout.measures
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="view_projects.page"
to={
Object {
"pathname": "/code",
@@ -936,15 +1001,13 @@ exports[`should work for all qualifiers 4`] = `
},
}
}
- >
- view_projects.page
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="project_activity.page"
to={
Object {
"pathname": "/project/activity",
@@ -953,9 +1016,7 @@ exports[`should work for all qualifiers 4`] = `
},
}
}
- >
- project_activity.page
- </Link>
+ />
</li>
</NavBarTabs>
<NavBarTabs>
@@ -1029,10 +1090,10 @@ exports[`should work for pull requests 1`] = `
</Link>
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="issues.page"
to={
Object {
"pathname": "/project/issues",
@@ -1043,15 +1104,13 @@ exports[`should work for pull requests 1`] = `
},
}
}
- >
- issues.page
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="layout.security_hotspots"
to={
Object {
"pathname": "/security_hotspots",
@@ -1061,15 +1120,13 @@ exports[`should work for pull requests 1`] = `
},
}
}
- >
- layout.security_hotspots
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="layout.measures"
to={
Object {
"pathname": "/component_measures",
@@ -1079,15 +1136,13 @@ exports[`should work for pull requests 1`] = `
},
}
}
- >
- layout.measures
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="code.page"
to={
Object {
"pathname": "/code",
@@ -1097,9 +1152,7 @@ exports[`should work for pull requests 1`] = `
},
}
}
- >
- code.page
- </Link>
+ />
</li>
</NavBarTabs>
<NavBarTabs />
@@ -1130,10 +1183,10 @@ exports[`should work for pull requests 2`] = `
</Link>
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="issues.page"
to={
Object {
"pathname": "/project/issues",
@@ -1144,15 +1197,13 @@ exports[`should work for pull requests 2`] = `
},
}
}
- >
- issues.page
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="layout.security_hotspots"
to={
Object {
"pathname": "/security_hotspots",
@@ -1162,15 +1213,13 @@ exports[`should work for pull requests 2`] = `
},
}
}
- >
- layout.security_hotspots
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="layout.measures"
to={
Object {
"pathname": "/component_measures",
@@ -1180,15 +1229,13 @@ exports[`should work for pull requests 2`] = `
},
}
}
- >
- layout.measures
- </Link>
+ />
</li>
<li>
- <Link
+ <MenuLink
activeClassName="active"
- onlyActiveOnIndex={false}
- style={Object {}}
+ hasAnalysis={true}
+ label="code.page"
to={
Object {
"pathname": "/code",
@@ -1198,9 +1245,7 @@ exports[`should work for pull requests 2`] = `
},
}
}
- >
- code.page
- </Link>
+ />
</li>
</NavBarTabs>
<NavBarTabs />