}
handleBulkChangeIssues = (issueKeys: string[], query: RequestData) => {
- //For now we only check for issue type change.
+ // For now we only check for issue type and status change.
this.list
.filter((i) => issueKeys.includes(i.issue.key))
.forEach((data) => {
- data.issue.type = query.set_type;
+ data.issue.type = query.set_type ?? data.issue.type;
+ data.issue.status = query.do_transition ?? data.issue.status;
});
return this.reply(undefined);
};
import { act, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
-import selectEvent from 'react-select-event';
import { TabKeys } from '../../../components/rules/RuleTabViewer';
import { mockLoggedInUser } from '../../../helpers/testMocks';
import { byRole } from '../../../helpers/testSelector';
expect(screen.getByRole('button', { name: 'issues.bulk_change_X_issues.10' })).toHaveFocus();
await user.click(screen.getByRole('checkbox', { name: 'issues.select_all_issues' }));
- expect(
- within(screen.getByRole('region', { name: 'Fix that' })).getByLabelText(
- 'issue.type.type_x_click_to_change.issue.type.CODE_SMELL'
- )
- ).toBeInTheDocument();
+ // Check that we bulk change the selected issue
+ const issueBoxFixThat = within(screen.getByRole('region', { name: 'Fix that' }));
await user.click(
screen.getByRole('checkbox', { name: 'issues.action_select.label.Fix that' })
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 user.click(screen.getByRole('radio', { name: 'issue.transition.falsepositive' }));
await user.click(screen.getByRole('button', { name: 'apply' }));
expect(
- await within(screen.getByRole('region', { name: 'Fix that' })).findByLabelText(
- 'issue.type.type_x_click_to_change.issue.type.BUG'
+ issueBoxFixThat.queryByLabelText(
+ 'issue.transition.status_x_click_to_change.issue.status.falsepositive'
)
- ).toBeInTheDocument();
+ ).not.toBeInTheDocument();
});
});
});
// Get a specific issue list item
const listItem = within(await screen.findByRole('region', { name: 'Fix that' }));
- // Change issue type
- await act(async () => {
- await user.click(
- listItem.getByLabelText('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 act(async () => {
- await user.click(listItem.getByText('issue.type.VULNERABILITY'));
- });
- expect(
- listItem.getByLabelText('issue.type.type_x_click_to_change.issue.type.VULNERABILITY')
- ).toBeInTheDocument();
-
- // Change issue severity
- expect(listItem.getByText('severity.MAJOR')).toBeInTheDocument();
-
- await act(async () => {
- await user.click(
- listItem.getByLabelText('issue.severity.severity_x_click_to_change.severity.MAJOR')
- );
- });
- expect(listItem.getByText('severity.MINOR')).toBeInTheDocument();
- expect(listItem.getByText('severity.INFO')).toBeInTheDocument();
- await act(async () => {
- await user.click(listItem.getByText('severity.MINOR'));
- });
- expect(
- listItem.getByLabelText('issue.severity.severity_x_click_to_change.severity.MINOR')
- ).toBeInTheDocument();
-
// Change issue status
expect(listItem.getByText('issue.status.OPEN')).toBeInTheDocument();
).not.toBeInTheDocument();
// Assign issue to a different user
-
await act(async () => {
await user.click(
listItem.getByRole('combobox', { name: 'issue.assign.unassigned_click_to_assign' })
await act(async () => {
await user.click(await ui.issueItemAction5.find());
- // open severity popup on key press 'i'
-
- await user.keyboard('i');
- });
- expect(screen.getByText('severity.MINOR')).toBeInTheDocument();
- expect(screen.getByText('severity.INFO')).toBeInTheDocument();
-
- // open status popup on key press 'f'
- await act(async () => {
+ // Open status popup on key press 'f'
await user.keyboard('f');
});
expect(screen.getByText('issue.transition.confirm')).toBeInTheDocument();
expect(screen.getByText('issue.transition.resolve')).toBeInTheDocument();
- // open comment popup on key press 'c'
+ // Open comment popup on key press 'c'
await act(async () => {
await user.keyboard('c');
});
await user.keyboard('{Escape}');
});
- // open tags popup on key press 't'
-
+ // Open tags popup on key press 't'
await act(async () => {
await user.keyboard('t');
});
expect(screen.getByRole('searchbox', { name: 'search.search_for_tags' })).toBeInTheDocument();
expect(screen.getByText('android')).toBeInTheDocument();
expect(screen.getByText('accessibility')).toBeInTheDocument();
- // closing tags popup
+
+ // Close tags popup
await act(async () => {
await user.click(screen.getByText('issue.no_tag'));
- // open assign popup on key press 'a'
+ // Open assign popup on key press 'a'
await user.keyboard('a');
});
expect(screen.getByRole('searchbox', { name: 'search.search_for_tags' })).toBeInTheDocument();
expect(screen.getByText('/security_hotspots?assignedToMe=false')).toBeInTheDocument();
});
-
- // it('should filter out hotspots', () => {
- // renderProjectIssuesApp(
- // `project/issues?types=${IssueType.SecurityHotspot},${IssueType.CodeSmell}`
- // );
-
- // expect(ui.clearIssueTypeFacet.get()).toBeInTheDocument();
- // });
});
describe('Activity', () => {
FormField,
HelperHintIcon,
Highlight,
- InputSelect,
LabelValueSelectOption,
LightLabel,
Modal,
import { SingleValue } from 'react-select';
import { bulkChangeIssues, searchIssueTags } from '../../../api/issues';
import FormattingTips from '../../../components/common/FormattingTips';
-import IssueSeverityIcon from '../../../components/icon-mappers/IssueSeverityIcon';
-import IssueTypeIcon from '../../../components/icon-mappers/IssueTypeIcon';
-import { SEVERITIES } from '../../../helpers/constants';
import { throwGlobalError } from '../../../helpers/error';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { withBranchStatusRefresh } from '../../../queries/branch';
-import { IssueSeverity } from '../../../types/issues';
-import { Dict, Issue, IssueType, Paging } from '../../../types/types';
+import { Dict, Issue, Paging } from '../../../types/types';
import AssigneeSelect from './AssigneeSelect';
import TagsSelect from './TagsSelect';
this.setState({ comment: event.currentTarget.value });
};
- handleSelectFieldChange =
- (field: 'severity' | 'type') => (data: LabelValueSelectOption<string> | null) => {
- if (data) {
- this.setState<keyof FormFields>({ [field]: data.value });
- } else {
- this.setState<keyof FormFields>({ [field]: undefined });
- }
- };
-
handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
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<IssueType>[] = types.map((type) => ({
- label: translate('issue.type', type),
- value: type,
- Icon: <IssueTypeIcon type={type} />,
- }));
-
- const input = (
- <InputSelect
- className="sw-w-abs-300"
- inputId={`issues-bulk-change-${field}`}
- isClearable
- isSearchable={false}
- onChange={this.handleSelectFieldChange('type')}
- options={options}
- size="full"
- />
- );
-
- return this.renderField(field, 'issue.set_type', affected, input);
- };
-
- renderSeverityField = () => {
- const affected = this.state.issues.filter(hasAction('set_severity')).length;
- const field = InputField.severity;
-
- if (affected === 0) {
- return null;
- }
-
- const options: LabelValueSelectOption<IssueSeverity>[] = SEVERITIES.map((severity) => ({
- label: translate('severity', severity),
- value: severity,
- Icon: <IssueSeverityIcon severity={severity} />,
- }));
-
- const input = (
- <InputSelect
- className="sw-w-abs-300"
- inputId={`issues-bulk-change-${field}`}
- isClearable
- isSearchable={false}
- onChange={this.handleSelectFieldChange('severity')}
- options={options}
- size="full"
- />
- );
-
- return this.renderField(field, 'issue.set_severity', affected, input);
- };
-
renderTagsField = (
field: InputField.addTags | InputField.removeTags,
label: string,
)}
{this.renderAssigneeField()}
- {this.renderTypeField()}
- {this.renderSeverityField()}
{!needIssueSync && this.renderTagsField(InputField.addTags, 'issue.add_tags', true)}
{!needIssueSync &&
import { act, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import * as React from 'react';
-import selectEvent from 'react-select-event';
import { bulkChangeIssues } from '../../../../api/issues';
import CurrentUserContextProvider from '../../../../app/components/current-user/CurrentUserContextProvider';
-import { SEVERITIES } from '../../../../helpers/constants';
import { mockIssue, mockLoggedInUser } from '../../../../helpers/testMocks';
import { renderComponent } from '../../../../helpers/testReactTestingUtils';
import { ComponentPropsType } from '../../../../helpers/testUtils';
-import { IssueType } from '../../../../types/issues';
import { Issue } from '../../../../types/types';
import { CurrentUser } from '../../../../types/users';
import BulkChangeModal, { MAX_PAGE_SIZE } from '../BulkChangeModal';
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) => {
- renderBulkChangeModal([mockIssue(false, { actions: [action] })]);
-
- expect(await screen.findByText('issue.' + action)).toBeInTheDocument();
-});
-
it('should render tags correctly', async () => {
renderBulkChangeModal([mockIssue(false, { actions: ['set_tags'] })]);
it('should disable the submit button unless some change is configured', async () => {
const user = userEvent.setup();
- renderBulkChangeModal([mockIssue(false, { actions: ['set_severity', 'comment'] })]);
+ renderBulkChangeModal([mockIssue(false, { actions: ['set_tags', 'comment'] })]);
// Apply button should be disabled
expect(await screen.findByRole('button', { name: 'apply' })).toBeDisabled();
await user.type(screen.getByRole('textbox', { name: /issue.comment.formlink/ }), 'some comment');
expect(screen.getByRole('button', { name: 'apply' })).toBeDisabled();
- // Select a severity
- await selectEvent.select(screen.getByRole('combobox', { name: 'issue.set_severity' }), [
- `severity.${SEVERITIES[0]}`,
- ]);
+ // Add a tag
+ await act(async () => {
+ await user.click(screen.getByRole('combobox', { name: 'issue.add_tags' }));
+ await user.click(screen.getByText('tag1'));
+ await user.click(screen.getByText('tag2'));
+ });
// Apply button should be enabled now
expect(screen.getByRole('button', { name: 'apply' })).toBeEnabled();
await user.click(screen.getByText('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]}`,
- ]);
-
- // Severity
- await selectEvent.select(screen.getByRole('combobox', { name: 'issue.set_severity' }), [
- `severity.${SEVERITIES[0]}`,
- ]);
-
// Comment
await user.type(screen.getByRole('textbox', { name: /issue.comment.formlink/ }), 'some comment');
comment: 'some comment',
do_transition: 'Transition2',
sendNotifications: true,
- set_severity: 'BLOCKER',
- set_type: IssueType.CodeSmell,
});
});
import { HttpStatus } from '../../../helpers/request';
import { mockIssue } from '../../../helpers/testMocks';
import { renderComponent } from '../../../helpers/testReactTestingUtils';
-import { byText } from '../../../helpers/testSelector';
import SourceViewer, { Props } from '../SourceViewer';
import loadIssues from '../helpers/loadIssues';
};
});
-const ui = {
- codeSmellTypeButton: byText('issue.type.CODE_SMELL'),
- minorSeverityButton: byText(/severity.MINOR/),
-};
-
const componentsHandler = new ComponentsServiceMock();
const issuesHandler = new IssuesServiceMock();
const message = 'First Issue';
it('should be able to interact with issue action', async () => {
jest.mocked(loadIssues).mockResolvedValueOnce([
mockIssue(false, {
- actions: ['set_type', 'set_tags', 'comment', 'set_severity', 'assign'],
+ actions: ['set_tags', 'comment', 'assign'],
key: 'issue1',
message,
line: 1,
const user = userEvent.setup();
renderSourceViewer();
- //Open Issue type
- await user.click(
- await screen.findByLabelText('issue.type.type_x_click_to_change.issue.type.BUG')
- );
-
- expect(ui.codeSmellTypeButton.get()).toBeInTheDocument();
-
- // Open severity
- await user.click(
- await screen.findByLabelText('issue.severity.severity_x_click_to_change.severity.MAJOR')
- );
-
- expect(ui.minorSeverityButton.get()).toBeInTheDocument();
-
- // Close
- await user.keyboard('{Escape}');
- expect(ui.minorSeverityButton.query()).not.toBeInTheDocument();
-
- // Change the severity
- await user.click(
- await screen.findByLabelText('issue.severity.severity_x_click_to_change.severity.MAJOR')
- );
-
- expect(ui.minorSeverityButton.get()).toBeInTheDocument();
- await user.click(ui.minorSeverityButton.get());
-
- expect(
- screen.getByLabelText('issue.severity.severity_x_click_to_change.severity.MINOR')
- ).toBeInTheDocument();
+ // Assign issue to a different user
+ await act(async () => {
+ await user.click(
+ await screen.findByRole('combobox', { name: 'issue.assign.unassigned_click_to_assign' })
+ );
+ await user.click(screen.getByLabelText('search.search_for_users'));
+ await user.keyboard('luke');
+ });
+ expect(screen.getByText('Skywalker')).toBeInTheDocument();
});
it('should load line when looking around unloaded line', async () => {
});
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, {
- severity: IssueSeverity.Blocker,
- actions: [IssueActions.SetSeverity],
- });
- issuesHandler.setIssueList([{ issue, snippets: {} }]);
- renderIssue({ issue: mockIssue(false, { ...pick(issue, 'actions', 'key', 'severity') }) });
-
- await ui.updateSeverity(IssueSeverity.Blocker, IssueSeverity.Minor);
- expect(ui.updateSeverityBtn(IssueSeverity.Minor).get()).toBeInTheDocument();
- });
-
it('should allow updating the status', async () => {
const { ui } = getPageObject();
const issue = mockRawIssue(false, {
expect(ui.setAssigneeBtn(/Organa/).get()).toBeInTheDocument();
await ui.pressDismissShortcut();
- await ui.pressSeverityShortcut();
- expect(ui.setSeverityBtn(IssueSeverity.Minor).get()).toBeInTheDocument();
- await ui.pressDismissShortcut();
-
await ui.pressCommentShortcut();
expect(ui.commentTextInput.get()).toBeInTheDocument();
await ui.pressDismissShortcut();
--- /dev/null
+/*
+ * 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 { Link } from 'design-system';
+import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import { translate } from '../../../helpers/l10n';
+
+export interface DeprecatedTooltipProps {
+ docUrl: string;
+ field: 'type' | 'severity';
+}
+
+export function DeprecatedFieldTooltip({ field, docUrl }: DeprecatedTooltipProps) {
+ return (
+ <>
+ <p className="sw-mb-4">{translate('issue', field, 'deprecation.title')}</p>
+ <p>{translate('issue', field, 'deprecation.filter_by')}</p>
+ <ul className="sw-list-disc sw-ml-6">
+ <li>{translate('issue.clean_code_attributes')}</li>
+ <li>{translate('issue.software_qualities')}</li>
+ </ul>
+ <hr className="sw-w-full sw-mx-0 sw-my-4" />
+ <FormattedMessage
+ defaultMessage={translate('learn_more_x')}
+ id="learn_more_x"
+ values={{
+ link: (
+ <Link isExternal to={docUrl}>
+ {translate('issue', field, 'deprecation.documentation')}
+ </Link>
+ ),
+ }}
+ />
+ </>
+ );
+}
import * as React from 'react';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { isDefined } from '../../../helpers/types';
-import {
- IssueActions,
- IssueResolution,
- IssueResponse,
- IssueType as IssueTypeEnum,
-} from '../../../types/issues';
+import { IssueActions, IssueResolution, IssueType as IssueTypeEnum } from '../../../types/issues';
import { RuleStatus } from '../../../types/rules';
-import { Issue, RawQuery } from '../../../types/types';
+import { Issue } from '../../../types/types';
import Tooltip from '../../controls/Tooltip';
import DateFromNow from '../../intl/DateFromNow';
import SoftwareImpactPill from '../../shared/SoftwareImpactPill';
import { WorkspaceContext } from '../../workspace/context';
-import { updateIssue } from '../actions';
import IssueAssign from './IssueAssign';
import IssueBadges from './IssueBadges';
import IssueCommentAction from './IssueCommentAction';
commentPlaceholder: '',
});
- const setIssueProperty = (
- property: keyof Issue,
- popup: string,
- apiCall: (query: RawQuery) => Promise<IssueResponse>,
- value: string
- ) => {
- if (issue[property] !== value) {
- const newIssue = { ...issue, [property]: value };
- updateIssue(onChange, apiCall({ issue: issue.key, [property]: value }), issue, newIssue);
- }
-
- togglePopup(popup, false);
- };
-
const toggleComment = (open: boolean, placeholder = '', autoTriggered = false) => {
setCommentState({
commentPlaceholder: placeholder,
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 hasTransitions = issue.transitions.length > 0;
const hasComments = !!issue.comments?.length;
/>
</li>
+ <li>
+ <IssueAssign
+ isOpen={currentPopup === 'assign'}
+ togglePopup={togglePopup}
+ canAssign={canAssign}
+ issue={issue}
+ onAssign={onAssign}
+ />
+ </li>
+
<li className="sw-flex sw-gap-3">
{issue.impacts.map(({ severity, softwareQuality }, index) => (
<SoftwareImpactPill
</li>
<li>
- <IssueType canSetType={canSetType} issue={issue} setIssueProperty={setIssueProperty} />
+ <IssueType issue={issue} />
</li>
<li>
- <IssueSeverity
- isOpen={currentPopup === 'set-severity'}
- togglePopup={togglePopup}
- canSetSeverity={canSetSeverity}
- issue={issue}
- setIssueProperty={setIssueProperty}
- />
- </li>
-
- <li>
- <IssueAssign
- isOpen={currentPopup === 'assign'}
- togglePopup={togglePopup}
- canAssign={canAssign}
- issue={issue}
- onAssign={onAssign}
- />
+ <IssueSeverity issue={issue} />
</li>
</ul>
{canComment && (
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { DiscreetSelect } from 'design-system';
+import { DisabledText, Tooltip } from 'design-system';
import * as React from 'react';
-import { setIssueSeverity } from '../../../api/issues';
-import { SEVERITIES } from '../../../helpers/constants';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { IssueResponse, IssueSeverity as IssueSeverityType } from '../../../types/issues';
-import { Issue, RawQuery } from '../../../types/types';
+import { useDocUrl } from '../../../helpers/docs';
+import { translate } from '../../../helpers/l10n';
+import { IssueSeverity as IssueSeverityType } from '../../../types/issues';
+import { Issue } from '../../../types/types';
import IssueSeverityIcon from '../../icon-mappers/IssueSeverityIcon';
+import { DeprecatedFieldTooltip } from './DeprecatedFieldTooltip';
interface Props {
- canSetSeverity: boolean;
- isOpen: boolean;
issue: Pick<Issue, 'severity'>;
- togglePopup: (popup: string, show?: boolean) => void;
- setIssueProperty: (
- property: keyof Issue,
- popup: string,
- apiCall: (query: RawQuery) => Promise<IssueResponse>,
- value: string
- ) => void;
}
-export default class IssueSeverity extends React.PureComponent<Props> {
- setSeverity = ({ value }: { value: string }) => {
- this.props.setIssueProperty('severity', 'set-severity', setIssueSeverity, value);
- this.toggleSetSeverity(false);
- };
+export default function IssueSeverity({ issue }: Props) {
+ const docUrl = useDocUrl('/');
- toggleSetSeverity = (open: boolean) => {
- this.props.togglePopup('set-severity', open);
- };
-
- handleClose = () => {
- this.toggleSetSeverity(false);
- };
-
- render() {
- const { issue } = this.props;
-
- const typesOptions = SEVERITIES.map((severity) => ({
- label: translate('severity', severity),
- value: severity,
- Icon: <IssueSeverityIcon severity={severity} aria-hidden />,
- }));
-
- if (this.props.canSetSeverity) {
- return (
- <DiscreetSelect
- aria-label={translateWithParameters(
- 'issue.severity.severity_x_click_to_change',
- translate('severity', issue.severity)
- )}
- menuIsOpen={this.props.isOpen && this.props.canSetSeverity}
- className="it__issue-severity"
- options={typesOptions}
- onMenuClose={this.handleClose}
- onMenuOpen={() => this.toggleSetSeverity(true)}
- setValue={this.setSeverity}
- value={issue.severity}
+ return (
+ <Tooltip overlay={<DeprecatedFieldTooltip field="type" docUrl={docUrl} />}>
+ <DisabledText className="sw-flex sw-items-center sw-gap-1 sw-cursor-not-allowed">
+ <IssueSeverityIcon
+ fill="iconSeverityDisabled"
+ severity={issue.severity as IssueSeverityType}
+ aria-hidden
/>
- );
- }
-
- return (
- <span className="sw-flex sw-items-center sw-gap-1">
- <IssueSeverityIcon severity={issue.severity as IssueSeverityType} aria-hidden />
-
{translate('severity', issue.severity)}
- </span>
- );
- }
+ </DisabledText>
+ </Tooltip>
+ );
}
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { DiscreetSelect } from 'design-system';
+import { DisabledText, Tooltip } from 'design-system';
import * as React from 'react';
-import { setIssueType } from '../../../api/issues';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { IssueResponse } from '../../../types/issues';
-import { Issue, RawQuery } from '../../../types/types';
+import { useDocUrl } from '../../../helpers/docs';
+import { translate } from '../../../helpers/l10n';
+import { Issue } from '../../../types/types';
import IssueTypeIcon from '../../icon-mappers/IssueTypeIcon';
+import { DeprecatedFieldTooltip } from './DeprecatedFieldTooltip';
interface Props {
- canSetType: boolean;
issue: Pick<Issue, 'type'>;
- setIssueProperty: (
- property: keyof Issue,
- popup: string,
- apiCall: (query: RawQuery) => Promise<IssueResponse>,
- value: string
- ) => void;
}
-export default class IssueType extends React.PureComponent<Props> {
- setType = ({ value }: { value: string }) => {
- this.props.setIssueProperty('type', 'set-type', setIssueType, value);
- };
+export default function IssueType({ issue }: Props) {
+ const docUrl = useDocUrl('/');
- render() {
- const { issue } = this.props;
- const TYPES = ['BUG', 'VULNERABILITY', 'CODE_SMELL'];
-
- const typesOptions = TYPES.map((type) => ({
- label: translate('issue.type', type),
- value: type,
- Icon: <IssueTypeIcon type={type} />,
- }));
-
- if (this.props.canSetType) {
- return (
- <DiscreetSelect
- aria-label={translateWithParameters(
- 'issue.type.type_x_click_to_change',
- translate('issue.type', issue.type)
- )}
- className="it__issue-type"
- options={typesOptions}
- setValue={this.setType}
- value={issue.type}
- />
- );
- }
-
- return (
- <span className="sw-flex sw-items-center sw-gap-1">
- <IssueTypeIcon type={issue.type} />
+ return (
+ <Tooltip overlay={<DeprecatedFieldTooltip field="type" docUrl={docUrl} />}>
+ <DisabledText className="sw-flex sw-items-center sw-gap-1 sw-cursor-not-allowed">
+ <IssueTypeIcon fill="iconTypeDisabled" type={issue.type} aria-hidden />
{translate('issue.type', issue.type)}
- </span>
- );
- }
+ </DisabledText>
+ </Tooltip>
+ );
}
+/*
+ * 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 classNames from 'classnames';
import { Link, Pill } from 'design-system';
import React from 'react';
+/*
+ * 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 classNames from 'classnames';
import { Link, Pill } from 'design-system';
import React from 'react';