]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19069 Disable the option to change an issue type in issues list and issue view
authorstanislavh <stanislav.honcharov@sonarsource.com>
Tue, 18 Apr 2023 12:29:18 +0000 (14:29 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 25 Apr 2023 20:03:00 +0000 (20:03 +0000)
server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts
server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx
server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.tsx
server/sonar-web/src/main/js/apps/issues/components/__tests__/BulkChangeModal-it.tsx
server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewer-it.tsx
server/sonar-web/src/main/js/components/issue/Issue.css
server/sonar-web/src/main/js/components/issue/__tests__/Issue-it.tsx
server/sonar-web/src/main/js/components/issue/components/IssueActionsBar.tsx
server/sonar-web/src/main/js/components/issue/components/IssueType.tsx [deleted file]
server/sonar-web/src/main/js/types/issues.ts

index 46bedfce13d94b402b7fb09bf41265b6069a2ead..73e33083b283b800b623393bb429562dbd4ddb99 100644 (file)
@@ -62,8 +62,8 @@ import {
   editIssueComment,
   getIssueChangelog,
   getIssueFlowSnippets,
-  searchIssueTags,
   searchIssues,
+  searchIssueTags,
   setIssueAssignee,
   setIssueSeverity,
   setIssueTags,
@@ -528,11 +528,11 @@ export default class IssuesServiceMock {
   }
 
   handleBulkChangeIssues = (issueKeys: string[], query: RequestData) => {
-    //For now we only check for issue type change.
+    //For now we only check for issue severity change.
     this.list
       .filter((i) => issueKeys.includes(i.issue.key))
       .forEach((data) => {
-        data.issue.type = query.set_type;
+        data.issue.severity = query.set_severity;
       });
     return this.reply({});
   };
index e90b41b0a4e5f3fd73d7f54ffc2d9a73fe98b179..d90c6b6f4f8a3aaa236b5c499d1c15f97a203abd 100644 (file)
@@ -246,12 +246,6 @@ describe('issues app', () => {
       // Check that we bulk change the selected issue
       const issueBoxFixThat = within(screen.getByRole('region', { name: 'Fix that' }));
 
-      expect(
-        issueBoxFixThat.getByRole('button', {
-          name: 'issue.type.type_x_click_to_change.issue.type.CODE_SMELL',
-        })
-      ).toBeInTheDocument();
-
       await user.click(
         screen.getByRole('checkbox', { name: 'issues.action_select.label.Fix that' })
       );
@@ -261,14 +255,14 @@ describe('issues app', () => {
       await user.keyboard('New Comment');
       expect(screen.getByRole('button', { name: 'apply' })).toBeDisabled();
 
-      await selectEvent.select(screen.getByRole('combobox', { name: 'issue.set_type' }), [
-        'issue.type.BUG',
+      await selectEvent.select(screen.getByRole('combobox', { name: 'issue.set_severity' }), [
+        'severity.BLOCKER',
       ]);
       await user.click(screen.getByRole('button', { name: 'apply' }));
 
       expect(
         issueBoxFixThat.getByRole('button', {
-          name: 'issue.type.type_x_click_to_change.issue.type.BUG',
+          name: 'issue.severity.severity_x_click_to_change.severity.BLOCKER',
         })
       ).toBeInTheDocument();
     });
@@ -613,22 +607,6 @@ describe('issues item', () => {
     // Get a specific issue list item
     const listItem = within(await screen.findByRole('region', { name: 'Fix that' }));
 
-    // Change issue type
-    await user.click(
-      listItem.getByRole('button', {
-        name: `issue.type.type_x_click_to_change.issue.type.CODE_SMELL`,
-      })
-    );
-    expect(listItem.getByText('issue.type.BUG')).toBeInTheDocument();
-    expect(listItem.getByText('issue.type.VULNERABILITY')).toBeInTheDocument();
-
-    await user.click(listItem.getByText('issue.type.VULNERABILITY'));
-    expect(
-      listItem.getByRole('button', {
-        name: `issue.type.type_x_click_to_change.issue.type.VULNERABILITY`,
-      })
-    ).toBeInTheDocument();
-
     // Change issue severity
     expect(listItem.getByText('severity.MAJOR')).toBeInTheDocument();
 
index 1a31ccfb7fffd2cb93f0e0112f042d45b2c87ec9..fb3e941c7bd51161de2be215c0c21376580a7b03 100644 (file)
@@ -33,14 +33,13 @@ import Select, {
   LabelValueSelectOption,
   SearchSelect,
 } from '../../../components/controls/Select';
-import IssueTypeIcon from '../../../components/icons/IssueTypeIcon';
 import SeverityHelper from '../../../components/shared/SeverityHelper';
 import { Alert } from '../../../components/ui/Alert';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
 import { SEVERITIES } from '../../../helpers/constants';
 import { throwGlobalError } from '../../../helpers/error';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { Component, Dict, Issue, IssueType, Paging } from '../../../types/types';
+import { Component, Dict, Issue, Paging } from '../../../types/types';
 import { CurrentUser } from '../../../types/users';
 import AssigneeSelect, { AssigneeOption } from './AssigneeSelect';
 
@@ -67,7 +66,6 @@ interface FormFields {
   removeTags?: Array<{ label: string; value: string }>;
   severity?: string;
   transition?: string;
-  type?: string;
 }
 
 interface State extends FormFields {
@@ -85,30 +83,10 @@ enum InputField {
   assignee = 'assignee',
   removeTags = 'removeTags',
   severity = 'severity',
-  type = 'type',
 }
 
 export const MAX_PAGE_SIZE = 500;
 
-function typeFieldTypeRenderer(option: LabelValueSelectOption) {
-  return (
-    <div className="display-flex-center">
-      <IssueTypeIcon query={option.value} />
-      <span className="little-spacer-left">{option.label}</span>
-    </div>
-  );
-}
-
-function TypeFieldOptionComponent(props: OptionProps<LabelValueSelectOption, false>) {
-  return <components.Option {...props}>{typeFieldTypeRenderer(props.data)}</components.Option>;
-}
-
-function TypeFieldSingleValueComponent(props: SingleValueProps<LabelValueSelectOption, false>) {
-  return (
-    <components.SingleValue {...props}>{typeFieldTypeRenderer(props.data)}</components.SingleValue>
-  );
-}
-
 function SeverityFieldOptionComponent(props: OptionProps<LabelValueSelectOption, false>) {
   return (
     <components.Option {...props}>
@@ -218,7 +196,6 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
         remove_tags: this.state.removeTags && this.state.removeTags.map((t) => t.value).join(),
         sendNotifications: this.state.notifications,
         set_severity: this.state.severity,
-        set_type: this.state.type,
       },
       (x) => x !== undefined
     );
@@ -258,15 +235,14 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
   }
 
   canSubmit = () => {
-    const { addTags, assignee, removeTags, severity, transition, type } = this.state;
+    const { addTags, assignee, removeTags, severity, transition } = this.state;
 
     return Boolean(
       (addTags && addTags.length > 0) ||
         (removeTags && removeTags.length > 0) ||
         assignee ||
         severity ||
-        transition ||
-        type
+        transition
     );
   };
 
@@ -331,38 +307,6 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
     return this.renderField(field, 'issue.assign.formlink', affected, input);
   };
 
-  renderTypeField = () => {
-    const affected = this.state.issues.filter(hasAction('set_type')).length;
-    const field = InputField.type;
-
-    if (affected === 0) {
-      return null;
-    }
-
-    const types: IssueType[] = ['BUG', 'VULNERABILITY', 'CODE_SMELL'];
-    const options: LabelValueSelectOption[] = types.map((type) => ({
-      label: translate('issue.type', type),
-      value: type,
-    }));
-
-    const input = (
-      <Select
-        className="input-super-large"
-        inputId={`issues-bulk-change-${field}`}
-        isClearable={true}
-        isSearchable={false}
-        components={{
-          Option: TypeFieldOptionComponent,
-          SingleValue: TypeFieldSingleValueComponent,
-        }}
-        onChange={this.handleSelectFieldChange('type')}
-        options={options}
-      />
-    );
-
-    return this.renderField(field, 'issue.set_type', affected, input);
-  };
-
   renderSeverityField = () => {
     const affected = this.state.issues.filter(hasAction('set_severity')).length;
     const field = InputField.severity;
@@ -519,7 +463,6 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
           )}
 
           {this.renderAssigneeField()}
-          {this.renderTypeField()}
           {this.renderSeverityField()}
           {this.renderTagsField(InputField.addTags, 'issue.add_tags', true)}
           {this.renderTagsField(InputField.removeTags, 'issue.remove_tags', false)}
index d629ed13c86ea3ed23ea12dc12c320cca4673b06..bfa2336519017777ffe277903f0df7dffc50bfc3 100644 (file)
@@ -25,7 +25,6 @@ import { bulkChangeIssues } from '../../../../api/issues';
 import { SEVERITIES } from '../../../../helpers/constants';
 import { mockIssue, mockLoggedInUser } from '../../../../helpers/testMocks';
 import { renderComponent } from '../../../../helpers/testReactTestingUtils';
-import { IssueType } from '../../../../types/issues';
 import { Issue } from '../../../../types/types';
 import BulkChangeModal, { MAX_PAGE_SIZE } from '../BulkChangeModal';
 
@@ -57,10 +56,7 @@ it('should display warning when too many issues are passed', async () => {
   expect(await screen.findByText('issue_bulk_change.max_issues_reached')).toBeInTheDocument();
 });
 
-it.each([
-  ['type', 'set_type'],
-  ['severity', 'set_severity'],
-])('should render select for %s', async (_field, action) => {
+it.each([['severity', 'set_severity']])('should render select for %s', async (_field, action) => {
   renderBulkChangeModal([mockIssue(false, { actions: [action] })]);
 
   expect(await screen.findByText('issue.' + action)).toBeInTheDocument();
@@ -143,11 +139,6 @@ it('should properly submit', async () => {
     'tag2',
   ]);
 
-  // Select a type
-  await selectEvent.select(screen.getByRole('combobox', { name: 'issue.set_type' }), [
-    `issue.type.CODE_SMELL`,
-  ]);
-
   // Select a severity
   await selectEvent.select(screen.getByRole('combobox', { name: 'issue.set_severity' }), [
     `severity.${SEVERITIES[0]}`,
@@ -175,7 +166,6 @@ it('should properly submit', async () => {
     set_severity: 'BLOCKER',
     add_tags: 'tag1,tag2',
     do_transition: 'Transition2',
-    set_type: IssueType.CodeSmell,
     sendNotifications: true,
   });
 });
index 1439f083790ed4c96196c2b9d89d7cccc5440dbb..60f2ca41ac27da2f687586b7d53a9afa8a31c097 100644 (file)
@@ -145,12 +145,6 @@ it('should be able to interact with issue action', async () => {
   const user = userEvent.setup();
   renderSourceViewer();
 
-  //Open Issue type
-  await user.click(
-    await screen.findByRole('button', { name: 'issue.type.type_x_click_to_change.issue.type.BUG' })
-  );
-  expect(ui.codeSmellTypeButton.get()).toBeInTheDocument();
-
   // Open severity
   await user.click(
     await screen.findByRole('button', {
index 5be5920c71f4c237cc86295bc2e94e9a801447f5..a5c12dfd9de7bd282fa7c8318416d8dd0a614585 100644 (file)
   display: flex;
 }
 
+.issue-meta.disabled {
+  color: var(--gray60);
+}
+
 .issue-meta + .issue-meta {
   margin-left: var(--gridSize);
 }
index 0f6d97998983f075f918cbdb61af3d46ef4f0bb3..8d6b59b898184b96eedd1a85b01109c387fdc660 100644 (file)
@@ -125,19 +125,6 @@ describe('rendering', () => {
 });
 
 describe('updating', () => {
-  it('should allow updating the type', async () => {
-    const { ui } = getPageObject();
-    const issue = mockRawIssue(false, {
-      type: IssueType.Bug,
-      actions: [IssueActions.SetType],
-    });
-    issuesHandler.setIssueList([{ issue, snippets: {} }]);
-    renderIssue({ issue: mockIssue(false, { ...pick(issue, 'actions', 'key', 'type') }) });
-
-    await ui.updateType(IssueType.Bug, IssueType.CodeSmell);
-    expect(ui.updateTypeBtn(IssueType.CodeSmell).get()).toBeInTheDocument();
-  });
-
   it('should allow updating the severity', async () => {
     const { ui } = getPageObject();
     const issue = mockRawIssue(false, {
index 1153e95b6c891b78e37769be4b5c8f0518fe6dda..fbf9d08f5c43ceb74149d7066a892e6e1e4caa97 100644 (file)
@@ -27,13 +27,13 @@ import {
   IssueType as IssueTypeEnum,
 } from '../../../types/issues';
 import { Issue, RawQuery } from '../../../types/types';
+import IssueTypeIcon from '../../icons/IssueTypeIcon';
 import { updateIssue } from '../actions';
 import IssueAssign from './IssueAssign';
 import IssueCommentAction from './IssueCommentAction';
 import IssueSeverity from './IssueSeverity';
 import IssueTags from './IssueTags';
 import IssueTransition from './IssueTransition';
-import IssueType from './IssueType';
 
 interface Props {
   issue: Issue;
@@ -98,21 +98,15 @@ export default class IssueActionsBar extends React.PureComponent<Props, State> {
     const canAssign = issue.actions.includes(IssueActions.Assign);
     const canComment = issue.actions.includes(IssueActions.Comment);
     const canSetSeverity = issue.actions.includes(IssueActions.SetSeverity);
-    const canSetType = issue.actions.includes(IssueActions.SetType);
     const canSetTags = issue.actions.includes(IssueActions.SetTags);
     const hasTransitions = issue.transitions.length > 0;
 
     return (
       <div className={classNames(className, 'issue-actions')}>
         <div className="issue-meta-list">
-          <div className="issue-meta">
-            <IssueType
-              canSetType={canSetType}
-              isOpen={this.props.currentPopup === 'set-type' && canSetType}
-              issue={issue}
-              setIssueProperty={this.setIssueProperty}
-              togglePopup={this.props.togglePopup}
-            />
+          <div className="issue-meta display-flex-center disabled">
+            <IssueTypeIcon className="little-spacer-right" query={issue.type} />
+            {translate('issue.type', issue.type)}
           </div>
           <div className="issue-meta">
             <IssueSeverity
diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueType.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueType.tsx
deleted file mode 100644 (file)
index 048b1d3..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 { setIssueType } from '../../../api/issues';
-import { colors } from '../../../app/theme';
-import { ButtonLink } from '../../../components/controls/buttons';
-import Toggler from '../../../components/controls/Toggler';
-import DropdownIcon from '../../../components/icons/DropdownIcon';
-import IssueTypeIcon from '../../../components/icons/IssueTypeIcon';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { IssueResponse } from '../../../types/issues';
-import { Issue, RawQuery } from '../../../types/types';
-import SetTypePopup from '../popups/SetTypePopup';
-
-interface Props {
-  canSetType: boolean;
-  isOpen: boolean;
-  issue: Pick<Issue, 'type'>;
-  setIssueProperty: (
-    property: keyof Issue,
-    popup: string,
-    apiCall: (query: RawQuery) => Promise<IssueResponse>,
-    value: string
-  ) => void;
-  togglePopup: (popup: string, show?: boolean) => void;
-}
-
-export default class IssueType extends React.PureComponent<Props> {
-  toggleSetType = (open?: boolean) => {
-    this.props.togglePopup('set-type', open);
-  };
-
-  setType = (type: string) => {
-    this.props.setIssueProperty('type', 'set-type', setIssueType, type);
-  };
-
-  handleClose = () => {
-    this.toggleSetType(false);
-  };
-
-  render() {
-    const { issue } = this.props;
-    if (this.props.canSetType) {
-      return (
-        <div className="dropdown">
-          <Toggler
-            onRequestClose={this.handleClose}
-            open={this.props.isOpen && this.props.canSetType}
-            overlay={<SetTypePopup issue={issue} onSelect={this.setType} />}
-          >
-            <ButtonLink
-              aria-label={translateWithParameters(
-                'issue.type.type_x_click_to_change',
-                translate('issue.type', issue.type)
-              )}
-              aria-expanded={this.props.isOpen}
-              className="issue-action issue-action-with-options js-issue-set-type"
-              onClick={this.toggleSetType}
-            >
-              <IssueTypeIcon
-                className="little-spacer-right"
-                fill={colors.baseFontColor}
-                query={issue.type}
-              />
-              {translate('issue.type', issue.type)}
-              <DropdownIcon className="little-spacer-left" />
-            </ButtonLink>
-          </Toggler>
-        </div>
-      );
-    }
-
-    return (
-      <span>
-        <IssueTypeIcon className="little-spacer-right" query={issue.type} />
-        {translate('issue.type', issue.type)}
-      </span>
-    );
-  }
-}
index 04ec365eaf133f88b0c8847a9c316b666296906c..f812988ce803f8bec9099ea14c073d51b969e3c6 100644 (file)
@@ -94,7 +94,6 @@ export enum IssueStatus {
 }
 
 export enum IssueActions {
-  SetType = 'set_type',
   SetTags = 'set_tags',
   SetSeverity = 'set_severity',
   Comment = 'comment',