]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-16385 Make rule information stick at top of issue page
authorMathieu Suen <mathieu.suen@sonarsource.com>
Wed, 1 Jun 2022 09:42:53 +0000 (11:42 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 1 Jun 2022 20:03:02 +0000 (20:03 +0000)
server/sonar-web/src/main/js/apps/issues/components/IssueRuleHeader.tsx [deleted file]
server/sonar-web/src/main/js/apps/issues/components/IssueTabViewer.tsx
server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx
server/sonar-web/src/main/js/apps/issues/styles.css

diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssueRuleHeader.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssueRuleHeader.tsx
deleted file mode 100644 (file)
index 74584d2..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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 * as React from 'react';
-import { Link } from 'react-router';
-import { getRuleUrl } from '../../../helpers/urls';
-import { Issue, RuleDetails } from '../../../types/types';
-
-interface IssueRuleHeaderProps {
-  ruleDetails: RuleDetails;
-  issue: Issue;
-}
-
-export default function IssueRuleHeader(props: IssueRuleHeaderProps) {
-  const {
-    ruleDetails: { name, key },
-    issue: { message }
-  } = props;
-
-  return (
-    <>
-      <h1 className="text-bold">{message}</h1>
-      <div className="spacer-top big-spacer-bottom">
-        <span className="note padded-right">{name}</span>
-        <Link className="small" to={getRuleUrl(key)} target="_blank">
-          {key}
-        </Link>
-      </div>
-    </>
-  );
-}
index a9a43464f854b7194127f3d80812b9778311e0c4..94e5cdc6d1776c58754ebf3033dddfba060e2b2f 100644 (file)
  */
 import classNames from 'classnames';
 import * as React from 'react';
+import { Link } from 'react-router';
 import BoxedTabs from '../../../components/controls/BoxedTabs';
 import { translate } from '../../../helpers/l10n';
 import { sanitizeString } from '../../../helpers/sanitize';
-import { RuleDescriptionSections, RuleDetails } from '../../../types/types';
+import { getRuleUrl } from '../../../helpers/urls';
+import { Component, Issue, RuleDescriptionSections, RuleDetails } from '../../../types/types';
 
 interface Props {
+  component?: Component;
+  issue: Issue;
   codeTabContent: React.ReactNode;
   ruleDetails: RuleDetails;
 }
@@ -113,13 +117,35 @@ export default class IssueViewerTabs extends React.PureComponent<Props, State> {
   }
 
   render() {
-    const { codeTabContent } = this.props;
+    const {
+      component,
+      codeTabContent,
+      ruleDetails: { name, key },
+      issue: { message }
+    } = this.props;
     const { tabs, currentTabKey } = this.state;
 
     return (
       <>
-        <BoxedTabs onSelect={this.handleSelectTabs} selected={currentTabKey} tabs={tabs} />
-        <div className="bordered huge-spacer-bottom">
+        <div
+          className={classNames('issue-header', {
+            'issue-project-level': component !== undefined
+          })}>
+          <h1 className="text-bold">{message}</h1>
+          <div className="spacer-top big-spacer-bottom">
+            <span className="note padded-right">{name}</span>
+            <Link className="small" to={getRuleUrl(key)} target="_blank">
+              {key}
+            </Link>
+          </div>
+          <BoxedTabs
+            className="bordered-bottom"
+            onSelect={this.handleSelectTabs}
+            selected={currentTabKey}
+            tabs={tabs}
+          />
+        </div>
+        <div className="bordered-right bordered-left bordered-bottom huge-spacer-bottom">
           <div
             className={classNames('padded', {
               hidden: currentTabKey !== TabKeys.Code
index 911b422183f259dacc9c65b0b8665a6045f61a8c..b5f8d364f32631e5db8ceb044a33e86c992764eb 100644 (file)
@@ -18,6 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import styled from '@emotion/styled';
+import classNames from 'classnames';
 import { debounce, keyBy, omit, without } from 'lodash';
 import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
@@ -89,7 +90,6 @@ import {
   STANDARDS
 } from '../utils';
 import BulkChangeModal, { MAX_PAGE_SIZE } from './BulkChangeModal';
-import IssueRuleHeader from './IssueRuleHeader';
 import IssuesList from './IssuesList';
 import IssuesSourceViewer from './IssuesSourceViewer';
 import IssueTabViewer from './IssueTabViewer';
@@ -1078,29 +1078,29 @@ export default class App extends React.PureComponent<Props, State> {
       paging,
       loadingRule
     } = this.state;
+    const { component } = this.props;
     return (
       <div className="layout-page-main-inner">
         <DeferredSpinner loading={loadingRule}>
           {openIssue && openRuleDetails ? (
-            <>
-              <IssueRuleHeader ruleDetails={openRuleDetails} issue={openIssue} />
-              <IssueTabViewer
-                codeTabContent={
-                  <IssuesSourceViewer
-                    branchLike={fillBranchLike(openIssue.branch, openIssue.pullRequest)}
-                    issues={issues}
-                    locationsNavigator={this.state.locationsNavigator}
-                    onIssueChange={this.handleIssueChange}
-                    onIssueSelect={this.openIssue}
-                    onLocationSelect={this.selectLocation}
-                    openIssue={openIssue}
-                    selectedFlowIndex={this.state.selectedFlowIndex}
-                    selectedLocationIndex={this.state.selectedLocationIndex}
-                  />
-                }
-                ruleDetails={openRuleDetails}
-              />
-            </>
+            <IssueTabViewer
+              codeTabContent={
+                <IssuesSourceViewer
+                  branchLike={fillBranchLike(openIssue.branch, openIssue.pullRequest)}
+                  issues={issues}
+                  locationsNavigator={this.state.locationsNavigator}
+                  onIssueChange={this.handleIssueChange}
+                  onIssueSelect={this.openIssue}
+                  onLocationSelect={this.selectLocation}
+                  openIssue={openIssue}
+                  selectedFlowIndex={this.state.selectedFlowIndex}
+                  selectedLocationIndex={this.state.selectedLocationIndex}
+                />
+              }
+              issue={openIssue}
+              component={component}
+              ruleDetails={openRuleDetails}
+            />
           ) : (
             <DeferredSpinner loading={loading}>
               {checkAll && paging && paging.total > MAX_PAGE_SIZE && (
@@ -1140,7 +1140,7 @@ export default class App extends React.PureComponent<Props, State> {
 
         {this.renderSide(openIssue)}
 
-        <div role="main" className="layout-page-main">
+        <div role="main" className={classNames('layout-page-main', { 'open-issue': !!openIssue })}>
           {this.renderHeader({ openIssue, paging, selectedIndex })}
 
           {this.renderPage()}
index 16f0981c472047f5581b748d3aae3d5b57352eb1..99fcc9ad4730d2c577976aa12f20de6554a1078c 100644 (file)
 .bulk-change-radio-button:hover {
   background-color: var(--barBackgroundColor);
 }
+
+.issue-header {
+  z-index: 100;
+  position: sticky;
+  top: 48px;
+  background-color: white;
+  padding-top: 20px;
+}
+
+.issue-project-level.issue-header {
+  top: 120px;
+}
+
+.layout-page-main.open-issue {
+  padding-top: 0;
+}