aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/issues
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-web/src/main/js/apps/issues')
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx52
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.css26
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx125
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.css2
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/IssueSourceViewerHeader-test.tsx50
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/IssueSourceViewerHeader-test.tsx.snap261
-rw-r--r--server/sonar-web/src/main/js/apps/issues/styles.css4
9 files changed, 495 insertions, 31 deletions
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx
index 00a82856a42..08a9d12d6aa 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx
@@ -23,7 +23,6 @@ import Issue from '../../../components/issue/Issue';
import SecondaryIssue from '../../../components/issue/SecondaryIssue';
import getCoverageStatus from '../../../components/SourceViewer/helpers/getCoverageStatus';
import { locationsByLine } from '../../../components/SourceViewer/helpers/indexing';
-import SourceViewerHeaderSlim from '../../../components/SourceViewer/SourceViewerHeaderSlim';
import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { BranchLike } from '../../../types/branch-like';
import { isFile } from '../../../types/component';
@@ -40,6 +39,7 @@ import {
SourceLine,
SourceViewerFile
} from '../../../types/types';
+import IssueSourceViewerHeader from './IssueSourceViewerHeader';
import SnippetViewer from './SnippetViewer';
import {
createSnippets,
@@ -422,7 +422,7 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone
return (
<div className="component-source-container" ref={this.rootNodeRef}>
- <SourceViewerHeaderSlim
+ <IssueSourceViewerHeader
branchLike={branchLike}
expandable={!fullyShown && isFile(snippetGroup.component.q)}
loading={loading}
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx
index b3b93d314f3..0457ce290df 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx
@@ -268,29 +268,35 @@ export default class CrossComponentSourceViewer extends React.PureComponent<Prop
})}
{locationsByComponent.length === 0 && (
- <ComponentSourceSnippetGroupViewer
- branchLike={this.props.branchLike}
- duplications={duplications}
- duplicationsByLine={duplicationsByLine}
- highlightedLocationMessage={this.props.highlightedLocationMessage}
- issue={issue}
- issuePopup={this.state.issuePopup}
- issuesByLine={issuesByComponent[issue.component] || {}}
- isLastOccurenceOfPrimaryComponent={true}
- lastSnippetGroup={true}
- loadDuplications={this.fetchDuplications}
- locations={[]}
- onIssueChange={this.props.onIssueChange}
- onIssueSelect={this.props.onIssueSelect}
- onIssuePopupToggle={this.handleIssuePopupToggle}
- onLocationSelect={this.props.onLocationSelect}
- renderDuplicationPopup={this.renderDuplicationPopup}
- scroll={this.props.scroll}
- snippetGroup={{
- locations: [getPrimaryLocation(issue)],
- ...components[issue.component]
- }}
- />
+ <SourceViewerContext.Provider
+ value={{
+ branchLike: this.props.branchLike,
+ file: components[issue.component].component
+ }}>
+ <ComponentSourceSnippetGroupViewer
+ branchLike={this.props.branchLike}
+ duplications={duplications}
+ duplicationsByLine={duplicationsByLine}
+ highlightedLocationMessage={this.props.highlightedLocationMessage}
+ issue={issue}
+ issuePopup={this.state.issuePopup}
+ issuesByLine={issuesByComponent[issue.component] || {}}
+ isLastOccurenceOfPrimaryComponent={true}
+ lastSnippetGroup={true}
+ loadDuplications={this.fetchDuplications}
+ locations={[]}
+ onIssueChange={this.props.onIssueChange}
+ onIssueSelect={this.props.onIssueSelect}
+ onIssuePopupToggle={this.handleIssuePopupToggle}
+ onLocationSelect={this.props.onLocationSelect}
+ renderDuplicationPopup={this.renderDuplicationPopup}
+ scroll={this.props.scroll}
+ snippetGroup={{
+ locations: [getPrimaryLocation(issue)],
+ ...components[issue.component]
+ }}
+ />
+ </SourceViewerContext.Provider>
)}
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.css b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.css
new file mode 100644
index 00000000000..4f70cddf426
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.css
@@ -0,0 +1,26 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+.source-viewer-header-slim {
+ padding: 4px 10px;
+ border: 1px solid var(--gray80);
+ background-color: var(--barBackgroundColor);
+ align-items: center;
+ min-height: 25px;
+}
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx
new file mode 100644
index 00000000000..69b4465cbe1
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx
@@ -0,0 +1,125 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import classNames from 'classnames';
+import * as React from 'react';
+import { Link } from 'react-router-dom';
+import { ButtonIcon } from '../../../components/controls/buttons';
+import { ClipboardIconButton } from '../../../components/controls/clipboard';
+import ExpandSnippetIcon from '../../../components/icons/ExpandSnippetIcon';
+import QualifierIcon from '../../../components/icons/QualifierIcon';
+import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import { getBranchLikeQuery } from '../../../helpers/branch-like';
+import { translate } from '../../../helpers/l10n';
+import { collapsedDirFromPath, fileFromPath } from '../../../helpers/path';
+import { getBranchLikeUrl, getComponentIssuesUrl, getPathUrlAsString } from '../../../helpers/urls';
+import { BranchLike } from '../../../types/branch-like';
+import { ComponentQualifier } from '../../../types/component';
+import { SourceViewerFile } from '../../../types/types';
+import './IssueSourceViewerHeader.css';
+
+export interface Props {
+ branchLike: BranchLike | undefined;
+ expandable?: boolean;
+ displayProjectName?: boolean;
+ linkToProject?: boolean;
+ loading?: boolean;
+ onExpand?: () => void;
+ sourceViewerFile: SourceViewerFile;
+}
+
+export default function IssueSourceViewerHeader(props: Props) {
+ const {
+ branchLike,
+ expandable,
+ displayProjectName = true,
+ linkToProject = true,
+ loading,
+ onExpand,
+ sourceViewerFile
+ } = props;
+ const { measures, path, project, projectName, q } = sourceViewerFile;
+
+ const projectNameLabel = (
+ <>
+ <QualifierIcon qualifier={ComponentQualifier.Project} /> <span>{projectName}</span>
+ </>
+ );
+
+ const isProjectRoot = q === ComponentQualifier.Project;
+
+ return (
+ <div className="source-viewer-header-slim display-flex-row display-flex-space-between">
+ <div className="display-flex-center flex-1">
+ {displayProjectName && (
+ <div className="spacer-right">
+ {linkToProject ? (
+ <a
+ className="link-with-icon"
+ href={getPathUrlAsString(getBranchLikeUrl(project, branchLike))}>
+ {projectNameLabel}
+ </a>
+ ) : (
+ projectNameLabel
+ )}
+ </div>
+ )}
+
+ {!isProjectRoot && (
+ <>
+ <div className="spacer-right">
+ <QualifierIcon qualifier={q} /> <span>{collapsedDirFromPath(path)}</span>
+ <span className="component-name-file">{fileFromPath(path)}</span>
+ </div>
+
+ <div className="spacer-right">
+ <ClipboardIconButton className="button-link link-no-underline" copyValue={path} />
+ </div>
+ </>
+ )}
+ </div>
+
+ {!isProjectRoot && measures.issues !== undefined && (
+ <div
+ className={classNames('flex-0 big-spacer-left', {
+ 'little-spacer-right': !expandable || loading
+ })}>
+ <Link
+ to={getComponentIssuesUrl(project, {
+ ...getBranchLikeQuery(branchLike),
+ files: path,
+ resolved: 'false'
+ })}>
+ {translate('source_viewer.view_all_issues')}
+ </Link>
+ </div>
+ )}
+
+ {expandable && (
+ <DeferredSpinner className="little-spacer-right" loading={loading}>
+ <div className="flex-0 big-spacer-left">
+ <ButtonIcon className="js-actions" onClick={onExpand}>
+ <ExpandSnippetIcon />
+ </ButtonIcon>
+ </div>
+ </DeferredSpinner>
+ )}
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.css b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.css
index 3f49652df25..b3b151ce897 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.css
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.css
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
.snippet {
- margin: var(--gridSize);
+ margin: var(--gridSize) 0;
border: 1px solid var(--gray80);
overflow-x: auto;
overflow-y: hidden;
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/IssueSourceViewerHeader-test.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/IssueSourceViewerHeader-test.tsx
new file mode 100644
index 00000000000..06655111a37
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/IssueSourceViewerHeader-test.tsx
@@ -0,0 +1,50 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockMainBranch } from '../../../../helpers/mocks/branch-like';
+import { mockSourceViewerFile } from '../../../../helpers/mocks/sources';
+import { ComponentQualifier } from '../../../../types/component';
+import IssueSourceViewerHeader, { Props } from '../IssueSourceViewerHeader';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+ expect(shallowRender({ linkToProject: false })).toMatchSnapshot('no link to project');
+ expect(shallowRender({ displayProjectName: false })).toMatchSnapshot('no project name');
+ expect(
+ shallowRender({
+ sourceViewerFile: mockSourceViewerFile('foo/bar.ts', 'my-project', {
+ q: ComponentQualifier.Project
+ })
+ })
+ ).toMatchSnapshot('project root');
+});
+
+function shallowRender(props: Partial<Props> = {}) {
+ return shallow(
+ <IssueSourceViewerHeader
+ branchLike={mockMainBranch()}
+ expandable={true}
+ onExpand={jest.fn()}
+ sourceViewerFile={mockSourceViewerFile('foo/bar.ts', 'my-project')}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap
index 58f647c8012..9825cb22d5c 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap
@@ -4,7 +4,7 @@ exports[`should render correctly 1`] = `
<div
className="component-source-container"
>
- <SourceViewerHeaderSlim
+ <IssueSourceViewerHeader
branchLike={
Object {
"analysisDate": "2018-01-01",
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/IssueSourceViewerHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/IssueSourceViewerHeader-test.tsx.snap
new file mode 100644
index 00000000000..0b6e42a0fb3
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/IssueSourceViewerHeader-test.tsx.snap
@@ -0,0 +1,261 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+ className="source-viewer-header-slim display-flex-row display-flex-space-between"
+>
+ <div
+ className="display-flex-center flex-1"
+ >
+ <div
+ className="spacer-right"
+ >
+ <a
+ className="link-with-icon"
+ href="/dashboard?id=my-project"
+ >
+ <QualifierIcon
+ qualifier="TRK"
+ />
+
+ <span>
+ MyProject
+ </span>
+ </a>
+ </div>
+ <div
+ className="spacer-right"
+ >
+ <QualifierIcon
+ qualifier="FIL"
+ />
+
+ <span>
+ foo/
+ </span>
+ <span
+ className="component-name-file"
+ >
+ bar.ts
+ </span>
+ </div>
+ <div
+ className="spacer-right"
+ >
+ <ClipboardIconButton
+ className="button-link link-no-underline"
+ copyValue="foo/bar.ts"
+ />
+ </div>
+ </div>
+ <div
+ className="flex-0 big-spacer-left"
+ >
+ <Link
+ to={
+ Object {
+ "hash": "",
+ "pathname": "/project/issues",
+ "search": "?files=foo%2Fbar.ts&resolved=false&id=my-project",
+ }
+ }
+ >
+ source_viewer.view_all_issues
+ </Link>
+ </div>
+ <DeferredSpinner
+ className="little-spacer-right"
+ >
+ <div
+ className="flex-0 big-spacer-left"
+ >
+ <ButtonIcon
+ className="js-actions"
+ onClick={[MockFunction]}
+ >
+ <ExpandSnippetIcon />
+ </ButtonIcon>
+ </div>
+ </DeferredSpinner>
+</div>
+`;
+
+exports[`should render correctly: no link to project 1`] = `
+<div
+ className="source-viewer-header-slim display-flex-row display-flex-space-between"
+>
+ <div
+ className="display-flex-center flex-1"
+ >
+ <div
+ className="spacer-right"
+ >
+ <QualifierIcon
+ qualifier="TRK"
+ />
+
+ <span>
+ MyProject
+ </span>
+ </div>
+ <div
+ className="spacer-right"
+ >
+ <QualifierIcon
+ qualifier="FIL"
+ />
+
+ <span>
+ foo/
+ </span>
+ <span
+ className="component-name-file"
+ >
+ bar.ts
+ </span>
+ </div>
+ <div
+ className="spacer-right"
+ >
+ <ClipboardIconButton
+ className="button-link link-no-underline"
+ copyValue="foo/bar.ts"
+ />
+ </div>
+ </div>
+ <div
+ className="flex-0 big-spacer-left"
+ >
+ <Link
+ to={
+ Object {
+ "hash": "",
+ "pathname": "/project/issues",
+ "search": "?files=foo%2Fbar.ts&resolved=false&id=my-project",
+ }
+ }
+ >
+ source_viewer.view_all_issues
+ </Link>
+ </div>
+ <DeferredSpinner
+ className="little-spacer-right"
+ >
+ <div
+ className="flex-0 big-spacer-left"
+ >
+ <ButtonIcon
+ className="js-actions"
+ onClick={[MockFunction]}
+ >
+ <ExpandSnippetIcon />
+ </ButtonIcon>
+ </div>
+ </DeferredSpinner>
+</div>
+`;
+
+exports[`should render correctly: no project name 1`] = `
+<div
+ className="source-viewer-header-slim display-flex-row display-flex-space-between"
+>
+ <div
+ className="display-flex-center flex-1"
+ >
+ <div
+ className="spacer-right"
+ >
+ <QualifierIcon
+ qualifier="FIL"
+ />
+
+ <span>
+ foo/
+ </span>
+ <span
+ className="component-name-file"
+ >
+ bar.ts
+ </span>
+ </div>
+ <div
+ className="spacer-right"
+ >
+ <ClipboardIconButton
+ className="button-link link-no-underline"
+ copyValue="foo/bar.ts"
+ />
+ </div>
+ </div>
+ <div
+ className="flex-0 big-spacer-left"
+ >
+ <Link
+ to={
+ Object {
+ "hash": "",
+ "pathname": "/project/issues",
+ "search": "?files=foo%2Fbar.ts&resolved=false&id=my-project",
+ }
+ }
+ >
+ source_viewer.view_all_issues
+ </Link>
+ </div>
+ <DeferredSpinner
+ className="little-spacer-right"
+ >
+ <div
+ className="flex-0 big-spacer-left"
+ >
+ <ButtonIcon
+ className="js-actions"
+ onClick={[MockFunction]}
+ >
+ <ExpandSnippetIcon />
+ </ButtonIcon>
+ </div>
+ </DeferredSpinner>
+</div>
+`;
+
+exports[`should render correctly: project root 1`] = `
+<div
+ className="source-viewer-header-slim display-flex-row display-flex-space-between"
+>
+ <div
+ className="display-flex-center flex-1"
+ >
+ <div
+ className="spacer-right"
+ >
+ <a
+ className="link-with-icon"
+ href="/dashboard?id=my-project"
+ >
+ <QualifierIcon
+ qualifier="TRK"
+ />
+
+ <span>
+ MyProject
+ </span>
+ </a>
+ </div>
+ </div>
+ <DeferredSpinner
+ className="little-spacer-right"
+ >
+ <div
+ className="flex-0 big-spacer-left"
+ >
+ <ButtonIcon
+ className="js-actions"
+ onClick={[MockFunction]}
+ >
+ <ExpandSnippetIcon />
+ </ButtonIcon>
+ </div>
+ </DeferredSpinner>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/issues/styles.css b/server/sonar-web/src/main/js/apps/issues/styles.css
index 0e5328c913b..44e4a054e81 100644
--- a/server/sonar-web/src/main/js/apps/issues/styles.css
+++ b/server/sonar-web/src/main/js/apps/issues/styles.css
@@ -137,10 +137,6 @@
color: white;
}
-.component-source-container {
- border: 1px solid var(--gray80);
-}
-
.component-source-container + .component-source-container {
margin-top: var(--gridSize);
}