]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20197 Adjust tooltips and add deprecated fields
authorstanislavh <stanislav.honcharov@sonarsource.com>
Wed, 23 Aug 2023 13:40:43 +0000 (15:40 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 24 Aug 2023 20:03:07 +0000 (20:03 +0000)
server/sonar-web/design-system/src/components/Pill.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx
server/sonar-web/src/main/js/apps/coding-rules/styles.css
server/sonar-web/src/main/js/components/icons/SeverityIcon.tsx
server/sonar-web/src/main/js/components/shared/CleanCodeAttributePill.tsx
server/sonar-web/src/main/js/components/shared/SeverityHelper.tsx
server/sonar-web/src/main/js/types/types.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 6f576d55226c70bf3c9dfd3d3f8a68f9a39bceec..59a0f54287ecffe872a4f47add902d2e4c2369da 100644 (file)
@@ -50,7 +50,6 @@ export function Pill({ children, variant, ...rest }: PillProps) {
 const StyledPill = styled.span<{
   color: ThemeColors;
 }>`
-  ${tw`sw-cursor-pointer`};
   ${tw`sw-body-sm`};
   ${tw`sw-w-fit`};
   ${tw`sw-inline-block`};
index e0a6f151548984b9fbf829756d6469c4b70875df..5aec7b822a514b997c1abb4aabe117647aaf1cfe 100644 (file)
  */
 
 import * as React from 'react';
+import { colors } from '../../../app/theme';
+import DocumentationTooltip from '../../../components/common/DocumentationTooltip';
 import Link from '../../../components/common/Link';
 import Dropdown from '../../../components/controls/Dropdown';
 import HelpTooltip from '../../../components/controls/HelpTooltip';
 import Tooltip from '../../../components/controls/Tooltip';
 import { ButtonLink } from '../../../components/controls/buttons';
+import IssueTypeIcon from '../../../components/icons/IssueTypeIcon';
 import LinkIcon from '../../../components/icons/LinkIcon';
 import DateFormatter from '../../../components/intl/DateFormatter';
 import { CleanCodeAttributePill } from '../../../components/shared/CleanCodeAttributePill';
+import SeverityHelper from '../../../components/shared/SeverityHelper';
 import SoftwareImpactPill from '../../../components/shared/SoftwareImpactPill';
 import TagsList from '../../../components/tags/TagsList';
 import { PopupPlacement } from '../../../components/ui/popups';
@@ -45,6 +49,56 @@ interface Props {
 const EXTERNAL_RULE_REPO_PREFIX = 'external_';
 
 export default class RuleDetailsMeta extends React.PureComponent<Props> {
+  renderType = () => {
+    const { ruleDetails } = this.props;
+    return (
+      <li className="coding-rules-detail-property muted" data-meta="type">
+        <DocumentationTooltip
+          content={
+            <>
+              <p className="sw-mb-4">{translate('coding_rules.type.deprecation.title')}</p>
+              <p>{translate('coding_rules.type.deprecation.filter_by')}</p>
+            </>
+          }
+          links={[
+            {
+              href: '/user-guide/rules',
+              label: translate('learn_more'),
+            },
+          ]}
+        >
+          <IssueTypeIcon className="little-spacer-right" query={ruleDetails.type} />
+          {translate('issue.type', ruleDetails.type)}
+        </DocumentationTooltip>
+      </li>
+    );
+  };
+
+  renderSeverity = () => (
+    <li className="coding-rules-detail-property muted" data-meta="severity">
+      <DocumentationTooltip
+        content={
+          <>
+            <p className="sw-mb-4">{translate('coding_rules.severity.deprecation.title')}</p>
+            <p>{translate('coding_rules.severity.deprecation.filter_by')}</p>
+          </>
+        }
+        links={[
+          {
+            href: '/user-guide/rules',
+            label: translate('learn_more'),
+          },
+        ]}
+      >
+        <SeverityHelper
+          fill={colors.neutral200}
+          className="display-inline-flex-center"
+          severity={this.props.ruleDetails.severity}
+        />
+      </DocumentationTooltip>
+    </li>
+  );
+
   renderStatus = () => {
     const { ruleDetails } = this.props;
     if (ruleDetails.status === 'READY') {
@@ -65,7 +119,7 @@ export default class RuleDetailsMeta extends React.PureComponent<Props> {
     const allTags = [...sysTags, ...tags];
 
     return (
-      <li className="coding-rules-detail-property" data-meta="tags">
+      <div className="coding-rules-detail-property null-spacer-bottom" data-meta="tags">
         {this.props.canWrite ? (
           <Dropdown
             closeOnClick={false}
@@ -77,7 +131,7 @@ export default class RuleDetailsMeta extends React.PureComponent<Props> {
                 tags={tags}
               />
             }
-            overlayPlacement={PopupPlacement.BottomLeft}
+            overlayPlacement={PopupPlacement.BottomRight}
           >
             <ButtonLink>
               <TagsList
@@ -93,7 +147,7 @@ export default class RuleDetailsMeta extends React.PureComponent<Props> {
             tags={allTags.length > 0 ? allTags : [translate('coding_rules.no_tags')]}
           />
         )}
-      </li>
+      </div>
     );
   };
 
@@ -201,17 +255,15 @@ export default class RuleDetailsMeta extends React.PureComponent<Props> {
     const hasTypeData = !ruleDetails.isExternal || ruleDetails.type !== 'UNKNOWN';
     return (
       <div className="js-rule-meta">
-        {ruleDetails.cleanCodeAttributeCategory !== undefined && (
-          <CleanCodeAttributePill
-            className="big-spacer-bottom"
-            cleanCodeAttributeCategory={ruleDetails.cleanCodeAttributeCategory}
-            cleanCodeAttribute={ruleDetails.cleanCodeAttribute}
-            type="rule"
-          />
-        )}
-
-        <div className="page-header">
-          <div className="pull-right">
+        <div className="display-flex-space-between spacer-bottom">
+          {ruleDetails.cleanCodeAttributeCategory !== undefined && (
+            <CleanCodeAttributePill
+              cleanCodeAttributeCategory={ruleDetails.cleanCodeAttributeCategory}
+              cleanCodeAttribute={ruleDetails.cleanCodeAttribute}
+              type="rule"
+            />
+          )}
+          <div className="pull-right display-flex-center spacer-right">
             {this.renderKey()}
             {!ruleDetails.isExternal && (
               <Link
@@ -223,11 +275,15 @@ export default class RuleDetailsMeta extends React.PureComponent<Props> {
               </Link>
             )}
           </div>
+        </div>
+
+        <div className="display-flex-space-between big-spacer-bottom">
           <h1 className="page-title coding-rules-detail-header">{ruleDetails.name}</h1>
+          {this.renderTags()}
         </div>
 
         <div className="display-flex-center">
-          {ruleDetails.impacts !== undefined && (
+          {!!ruleDetails.impacts.length && (
             <div className="sw-flex sw-items-center flex-1">
               <span>{translate('issue.software_qualities.label')}</span>
               <ul className="sw-flex sw-gap-2">
@@ -247,8 +303,9 @@ export default class RuleDetailsMeta extends React.PureComponent<Props> {
 
           {hasTypeData && (
             <ul className="coding-rules-detail-properties">
+              {this.renderType()}
+              {this.renderSeverity()}
               {!ruleDetails.isExternal && this.renderStatus()}
-              {this.renderTags()}
               {!ruleDetails.isExternal && this.renderCreationDate()}
               {this.renderRepository()}
               {!ruleDetails.isExternal && (
index 152976ad5cc437dac18eb1d62f1c3b7f84c6138a..31281f1a830e0c6964988f7bed018fdcc83970b0 100644 (file)
@@ -247,16 +247,15 @@ export default class RuleListItem extends React.PureComponent<Props> {
                   <span className="display-inline-flex-center spacer-left note">
                     {rule.langName}
                   </span>
-                  {rule.impacts !== undefined &&
-                    rule.impacts.map(({ severity, softwareQuality }) => (
-                      <SoftwareImpactPill
-                        className="spacer-left"
-                        key={softwareQuality}
-                        severity={severity}
-                        quality={softwareQuality}
-                        type="rule"
-                      />
-                    ))}
+                  {rule.impacts.map(({ severity, softwareQuality }) => (
+                    <SoftwareImpactPill
+                      className="spacer-left"
+                      key={softwareQuality}
+                      severity={severity}
+                      quality={softwareQuality}
+                      type="rule"
+                    />
+                  ))}
                   {allTags.length > 0 && (
                     <TagsList
                       allowUpdate={false}
index bb7548b2a1692dc755092980b932f0eec9f0588b..45f50e22938e4da9c6687c93d0a1468c56c96604 100644 (file)
   font-size: var(--smallFontSize);
 }
 
+.coding-rules-detail-property.muted {
+  color: var(--neutral200);
+}
+
 .coding-rules-detail-tag + .coding-rules-detail-tag {
   margin-left: 10px;
 }
index ef6cb2ce9f27033c948c378a5812376c02e1cd66..6151ff9e5fb459acc659d2dd8ebc73805512f691 100644 (file)
@@ -51,7 +51,7 @@ function BlockerSeverityIcon(iconProps: IconProps) {
     <Icon {...iconProps}>
       <path
         d="M8 14c-3.311 0-6-2.689-6-6s2.689-6 6-6 6 2.689 6 6-2.689 6-6 6zM7 9h2V4H7v5zm0 3h2v-2H7v2z"
-        style={{ fill: colors.red, fillRule: 'nonzero' }}
+        style={{ fill: iconProps.fill ?? colors.red, fillRule: 'nonzero' }}
       />
     </Icon>
   );
@@ -62,7 +62,7 @@ function CriticalSeverityIcon(iconProps: IconProps) {
     <Icon {...iconProps}>
       <path
         d="M8 2c3.311 0 6 2.689 6 6s-2.689 6-6 6-6-2.689-6-6 2.689-6 6-6zm1 10V7.414l1.893 1.893c.13.124.282.216.457.261a1.006 1.006 0 0 0 1.176-.591 1.01 1.01 0 0 0 .01-.729 1.052 1.052 0 0 0-.229-.355c-1.212-1.212-2.394-2.456-3.638-3.636a1.073 1.073 0 0 0-.169-.123 1.05 1.05 0 0 0-.448-.133h-.104a1.053 1.053 0 0 0-.493.16 1.212 1.212 0 0 0-.162.132C6.08 5.505 4.836 6.687 3.656 7.932a.994.994 0 0 0-.051 1.275c.208.271.548.42.888.389.198-.019.378-.098.535-.218.041-.035.04-.034.079-.071L7 7.414V12h2z"
-        style={{ fill: colors.red, fillRule: 'nonzero' }}
+        style={{ fill: iconProps.fill ?? colors.red, fillRule: 'nonzero' }}
       />
     </Icon>
   );
@@ -73,7 +73,7 @@ function MajorSeverityIcon(iconProps: IconProps) {
     <Icon {...iconProps}>
       <path
         d="M8 2c3.311 0 6 2.689 6 6s-2.689 6-6 6-6-2.689-6-6 2.689-6 6-6zm.08 2.903c.071.008.14.019.208.039.138.042.26.114.37.205 1.244 1.146 2.426 2.357 3.639 3.536.1.103.181.218.234.352a1.01 1.01 0 0 1 .001.728 1.002 1.002 0 0 1-1.169.609 1.042 1.042 0 0 1-.46-.255L8 7.295l-2.903 2.822c-.039.036-.039.036-.08.07a1.002 1.002 0 0 1-1.604-.947c.032-.196.122-.37.253-.519C4.847 7.51 6.09 6.362 7.303 5.183c.052-.048.106-.093.167-.131a1.041 1.041 0 0 1 .61-.149z"
-        style={{ fill: colors.red }}
+        style={{ fill: iconProps.fill ?? colors.red }}
       />
     </Icon>
   );
@@ -84,7 +84,7 @@ function MinorSeverityIcon(iconProps: IconProps) {
     <Icon {...iconProps}>
       <path
         d="M8 2c3.311 0 6 2.689 6 6s-2.689 6-6 6-6-2.689-6-6 2.689-6 6-6zm1 6.586V4H7v4.586L5.107 6.693a1.178 1.178 0 0 0-.165-.134 1.041 1.041 0 0 0-.662-.152 1 1 0 0 0-.587 1.7c1.212 1.212 2.394 2.456 3.638 3.636.094.08.195.146.311.191a1.008 1.008 0 0 0 1.065-.227c1.213-1.212 2.457-2.394 3.637-3.639a.994.994 0 0 0 .051-1.275 1.012 1.012 0 0 0-.888-.389 1.041 1.041 0 0 0-.535.218c-.04.034-.04.034-.079.071L9 8.586z"
-        style={{ fill: colors.green }}
+        style={{ fill: iconProps.fill ?? colors.green }}
       />
     </Icon>
   );
@@ -95,7 +95,7 @@ function InfoSeverityIcon(iconProps: IconProps) {
     <Icon {...iconProps}>
       <path
         d="M8 2c3.311 0 6 2.689 6 6s-2.689 6-6 6-6-2.689-6-6 2.689-6 6-6zm1 5H7v5h2V7zm0-3H7v2h2V4z"
-        style={{ fill: colors.darkBlue }}
+        style={{ fill: iconProps.fill ?? colors.darkBlue }}
       />
     </Icon>
   );
index 43bcfcf23ea5c4097565e2ced08a5d4fdbeafd1b..56f0f6d2fb9a80e1e5cead2874c9a64e25b08bd0 100644 (file)
@@ -33,7 +33,6 @@ export interface Props {
 
 export function CleanCodeAttributePill(props: Props) {
   const { className, cleanCodeAttributeCategory, cleanCodeAttribute, type = 'issue' } = props;
-  const showAdvice = type === 'issue';
 
   return (
     <DocumentationTooltip
@@ -47,16 +46,14 @@ export function CleanCodeAttributePill(props: Props) {
               'title'
             )}
           </p>
-          {showAdvice && (
-            <p>
-              {translate(
-                type,
-                cleanCodeAttribute ? 'clean_code_attribute' : 'clean_code_attribute_category',
-                cleanCodeAttribute ?? cleanCodeAttributeCategory,
-                'advice'
-              )}
-            </p>
-          )}
+          <p>
+            {translate(
+              'issue',
+              cleanCodeAttribute ? 'clean_code_attribute' : 'clean_code_attribute_category',
+              cleanCodeAttribute ?? cleanCodeAttributeCategory,
+              'advice'
+            )}
+          </p>
         </>
       }
       links={[
index 1d3e2a145b5b2b5fc2670cb65e7edc2400abe45b..c88881d28ba329ff4e09329aeb080d75ff9ba0d3 100644 (file)
@@ -23,16 +23,17 @@ import { translate } from '../../helpers/l10n';
 
 interface Props {
   className?: string;
+  fill?: string;
   severity: string;
 }
 
-export default function SeverityHelper({ className, severity }: Props) {
+export default function SeverityHelper({ className, severity, fill }: Props) {
   if (!severity) {
     return null;
   }
   return (
     <span className={className}>
-      <SeverityIcon className="little-spacer-right" severity={severity} aria-hidden />
+      <SeverityIcon className="little-spacer-right" fill={fill} severity={severity} aria-hidden />
       {translate('severity', severity)}
     </span>
   );
index aa26d7d3192f6a52dac6bfa4bb2c911a52674df0..27dc1a9542c5445a841d79e01bb626324dee2525 100644 (file)
@@ -535,7 +535,7 @@ export type RawQuery = Dict<any>;
 export interface Rule {
   cleanCodeAttributeCategory?: CleanCodeAttributeCategory;
   cleanCodeAttribute?: CleanCodeAttribute;
-  impacts?: Array<{
+  impacts: Array<{
     softwareQuality: SoftwareQuality;
     severity: SoftwareImpactSeverity;
   }>;
index d2ab93e2495c5ef8227b08cd98d8a624c3583677..fa2dd3db8eb2b2c61b3d6a4cf318ea135a58fe8a 100644 (file)
@@ -2273,6 +2273,11 @@ coding_rules.type.tooltip.CODE_SMELL=Code Smell Detection Rule
 coding_rules.type.tooltip.BUG=Bug Detection Rule
 coding_rules.type.tooltip.VULNERABILITY=Vulnerability Detection Rule
 coding_rules.type.tooltip.SECURITY_HOTSPOT=Security Hotspot Detection Rule
+coding_rules.type.deprecation.title=Types of detection rules are deprecated.
+coding_rules.type.deprecation.filter_by=You can now filter rules by Clean Code Attribute and Software Quality.
+coding_rules.severity.deprecation.title=Severities are now directly tied to the software quality impacted. This old severity is deprecated and can no longer be modified.
+coding_rules.severity.deprecation.filter_by=You can now filter rules by Software Quality and new Severity.
+
 coding_rules.update_custom_rule=Update Custom Rule
 
 coding_rules.filters.activation=Activation