Browse Source

SONAR-20023 Modify Issues List to reflect CCT

tags/10.2.0.77647
7PH 10 months ago
parent
commit
1bc1663e58
28 changed files with 418 additions and 36 deletions
  1. 1
    1
      server/sonar-web/design-system/src/components/Breadcrumbs.tsx
  2. 1
    1
      server/sonar-web/design-system/src/components/BubbleChart.tsx
  3. 1
    1
      server/sonar-web/design-system/src/components/ColorsLegend.tsx
  4. 1
    1
      server/sonar-web/design-system/src/components/DropdownMenu.tsx
  5. 1
    1
      server/sonar-web/design-system/src/components/FacetBox.tsx
  6. 1
    1
      server/sonar-web/design-system/src/components/Histogram.tsx
  7. 2
    2
      server/sonar-web/design-system/src/components/NavBarTabs.tsx
  8. 72
    0
      server/sonar-web/design-system/src/components/Pill.tsx
  9. 1
    1
      server/sonar-web/design-system/src/components/Tags.tsx
  10. 5
    0
      server/sonar-web/design-system/src/components/Text.tsx
  11. 1
    1
      server/sonar-web/design-system/src/components/Tooltip.tsx
  12. 1
    1
      server/sonar-web/design-system/src/components/TreeMapRect.tsx
  13. 1
    1
      server/sonar-web/design-system/src/components/__tests__/DropdownMenu-test.tsx
  14. 37
    0
      server/sonar-web/design-system/src/components/__tests__/Pill-test.tsx
  15. 1
    1
      server/sonar-web/design-system/src/components/__tests__/Tooltip-test.tsx
  16. 1
    1
      server/sonar-web/design-system/src/components/clipboard.tsx
  17. 1
    1
      server/sonar-web/design-system/src/components/code-line/LineCoverage.tsx
  18. 2
    0
      server/sonar-web/design-system/src/components/index.ts
  19. 16
    0
      server/sonar-web/design-system/src/theme/light.ts
  20. 54
    1
      server/sonar-web/src/main/js/apps/issues/__tests__/utils-test.ts
  21. 27
    4
      server/sonar-web/src/main/js/apps/issues/utils.ts
  22. 2
    2
      server/sonar-web/src/main/js/components/issue/__tests__/Issue-it.tsx
  23. 20
    5
      server/sonar-web/src/main/js/components/issue/components/IssueActionsBar.tsx
  24. 13
    6
      server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx
  25. 1
    1
      server/sonar-web/src/main/js/components/issue/components/IssueView.tsx
  26. 52
    0
      server/sonar-web/src/main/js/components/shared/CleanCodeAttributePill.tsx
  27. 82
    0
      server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx
  28. 20
    2
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 1
- 1
server/sonar-web/design-system/src/components/Breadcrumbs.tsx View File

@@ -32,7 +32,7 @@ import {
import { useResizeObserver } from '../hooks/useResizeObserver';
import { Dropdown } from './Dropdown';
import { InteractiveIcon } from './InteractiveIcon';
import Tooltip from './Tooltip';
import { Tooltip } from './Tooltip';
import { ChevronDownIcon, ChevronRightIcon } from './icons';

const WIDTH_OF_BREADCRUMB_DROPDOWN = 32;

+ 1
- 1
server/sonar-web/design-system/src/components/BubbleChart.tsx View File

@@ -31,7 +31,7 @@ import tw from 'twin.macro';
import { themeColor, themeContrast } from '../helpers';
import { BubbleColorVal } from '../types/charts';
import { Note } from './Text';
import Tooltip from './Tooltip';
import { Tooltip } from './Tooltip';
import { ButtonSecondary } from './buttons';

const TICKS_COUNT = 5;

+ 1
- 1
server/sonar-web/design-system/src/components/ColorsLegend.tsx View File

@@ -22,7 +22,7 @@ import styled from '@emotion/styled';
import tw from 'twin.macro';
import { themeBorder, themeColor, themeContrast } from '../helpers';
import { BubbleColorVal } from '../types/charts';
import Tooltip from './Tooltip';
import { Tooltip } from './Tooltip';
import { Checkbox } from './input/Checkbox';

export interface ColorFilterOption {

+ 1
- 1
server/sonar-web/design-system/src/components/DropdownMenu.tsx View File

@@ -27,7 +27,7 @@ import { themeBorder, themeColor, themeContrast } from '../helpers/theme';
import { InputSizeKeys, ThemedProps } from '../types/theme';
import { BaseLink, LinkProps } from './Link';
import NavLink from './NavLink';
import Tooltip from './Tooltip';
import { Tooltip } from './Tooltip';
import { ClipboardBase } from './clipboard';
import { Checkbox } from './input/Checkbox';
import { RadioButton } from './input/RadioButton';

+ 1
- 1
server/sonar-web/design-system/src/components/FacetBox.tsx View File

@@ -27,7 +27,7 @@ import { themeColor } from '../helpers';
import { Badge } from './Badge';
import { DestructiveIcon } from './InteractiveIcon';
import { Spinner } from './Spinner';
import Tooltip from './Tooltip';
import { Tooltip } from './Tooltip';
import { BareButton } from './buttons';
import { OpenCloseIndicator } from './icons';
import { CloseIcon } from './icons/CloseIcon';

+ 1
- 1
server/sonar-web/design-system/src/components/Histogram.tsx View File

@@ -24,7 +24,7 @@ import { scaleBand, ScaleBand, scaleLinear, ScaleLinear } from 'd3-scale';
import React from 'react';
import tw from 'twin.macro';
import { themeColor, themeContrast } from '../helpers';
import Tooltip, { TooltipWrapper } from './Tooltip';
import { Tooltip, TooltipWrapper } from './Tooltip';

interface Props {
bars: number[];

+ 2
- 2
server/sonar-web/design-system/src/components/NavBarTabs.tsx View File

@@ -24,9 +24,9 @@ import React from 'react';
import tw, { theme } from 'twin.macro';
import { themeBorder, themeColor, themeContrast } from '../helpers/theme';
import { isDefined } from '../helpers/types';
import { ChevronDownIcon } from './icons/ChevronDownIcon';
import NavLink, { NavLinkProps } from './NavLink';
import Tooltip from './Tooltip';
import { Tooltip } from './Tooltip';
import { ChevronDownIcon } from './icons/ChevronDownIcon';

interface Props extends React.HTMLAttributes<HTMLUListElement> {
children?: React.ReactNode;

+ 72
- 0
server/sonar-web/design-system/src/components/Pill.tsx View File

@@ -0,0 +1,72 @@
/*
* 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 styled from '@emotion/styled';
import { ReactNode } from 'react';
import tw from 'twin.macro';
import { themeColor, themeContrast } from '../helpers/theme';
import { ThemeColors } from '../types/theme';

type PillVariant = 'danger' | 'warning' | 'info' | 'neutral';

const variantThemeColors: Record<PillVariant, ThemeColors> = {
danger: 'pillDanger',
warning: 'pillWarning',
info: 'pillInfo',
neutral: 'pillNeutral',
};

interface PillProps {
children: ReactNode;
className?: string;
title?: string;
variant: PillVariant;
}

export function Pill({ className, children, title, variant }: PillProps) {
const commonProps = {
'aria-label': title ?? children?.toString(),
className,
role: 'status',
title,
};
return (
<StyledPill color={variantThemeColors[variant]} {...commonProps}>
{children}
</StyledPill>
);
}

const StyledPill = styled.span<{
color: ThemeColors;
}>`
${tw`sw-cursor-pointer`};
${tw`sw-w-fit`};
${tw`sw-inline-block`};
${tw`sw-whitespace-nowrap`};
${tw`sw-px-[8px] sw-py-[2px]`};
${tw`sw-rounded-pill`};

color: ${({ color }) => themeContrast(color)};
background-color: ${({ color }) => themeColor(color)};

&:empty {
${tw`sw-hidden`}
}
`;

+ 1
- 1
server/sonar-web/design-system/src/components/Tags.tsx View File

@@ -95,7 +95,7 @@ export function Tags({
>
{({ a11yAttrs, onToggleClick, open }) => (
<WrapperButton
className="sw-flex sw-items-center sw-gap-1 sw-p-1 sw-h-auto sw-rounded-0"
className="sw-flex sw-items-center sw-gap-1 sw-p-0 sw-h-auto sw-rounded-0"
onClick={onToggleClick}
{...a11yAttrs}
>

+ 5
- 0
server/sonar-web/design-system/src/components/Text.tsx View File

@@ -104,6 +104,11 @@ const StyledTextError = styled(StyledText)`
color: ${themeColor('danger')};
`;

export const DisabledText = styled.span`
${tw`sw-font-regular`};
color: ${themeColor('pageContentLight')};
`;

export const LightLabel = styled.span`
color: ${themeColor('pageContentLight')};
`;

+ 1
- 1
server/sonar-web/design-system/src/components/Tooltip.tsx View File

@@ -67,7 +67,7 @@ function isMeasured(state: State): state is OwnState & Measurements {
return state.height !== undefined;
}

export default function Tooltip(props: TooltipProps) {
export function Tooltip(props: TooltipProps) {
// overlay is a ReactNode, so it can be a boolean, `undefined` or `null`
// this allows to easily render a tooltip conditionally
// more generaly we avoid rendering empty tooltips

+ 1
- 1
server/sonar-web/design-system/src/components/TreeMapRect.tsx View File

@@ -23,7 +23,7 @@ import React from 'react';
import tw from 'twin.macro';
import { themeColor } from '../helpers';
import { BasePlacement, PopupPlacement } from '../helpers/positioning';
import Tooltip from './Tooltip';
import { Tooltip } from './Tooltip';

const SIZE_SCALE = scaleLinear().domain([3, 15]).range([11, 18]).clamp(true);
const TEXT_VISIBLE_AT_WIDTH = 40;

+ 1
- 1
server/sonar-web/design-system/src/components/__tests__/DropdownMenu-test.tsx View File

@@ -32,7 +32,7 @@ import {
ItemNavLink,
ItemRadioButton,
} from '../DropdownMenu';
import Tooltip from '../Tooltip';
import { Tooltip } from '../Tooltip';
import { MenuIcon } from '../icons/MenuIcon';

beforeEach(() => {

+ 37
- 0
server/sonar-web/design-system/src/components/__tests__/Pill-test.tsx View File

@@ -0,0 +1,37 @@
/*
* 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 { screen } from '@testing-library/react';
import { render } from '../../helpers/testUtils';
import { Pill } from '../Pill';

it('should render correctly', () => {
render(<Pill variant="neutral">23</Pill>);
expect(screen.getByRole('status')).toBeInTheDocument();
expect(screen.getByRole('status')).toHaveAttribute('aria-label', '23');
});

it('should accept overriding label', () => {
render(
<Pill title="23 foo in bucket" variant="danger">
23
</Pill>
);
expect(screen.getByRole('status')).toHaveAttribute('aria-label', '23 foo in bucket');
});

+ 1
- 1
server/sonar-web/design-system/src/components/__tests__/Tooltip-test.tsx View File

@@ -20,7 +20,7 @@
import { screen } from '@testing-library/react';
import { render } from '../../helpers/testUtils';
import { FCProps } from '../../types/misc';
import Tooltip, { TooltipInner } from '../Tooltip';
import { Tooltip, TooltipInner } from '../Tooltip';

jest.mock('react-dom', () => {
const reactDom = jest.requireActual('react-dom') as object;

+ 1
- 1
server/sonar-web/design-system/src/components/clipboard.tsx View File

@@ -23,7 +23,7 @@ import Clipboard from 'clipboard';
import React from 'react';
import { INTERACTIVE_TOOLTIP_DELAY } from '../helpers/constants';
import { DiscreetInteractiveIcon, InteractiveIcon, InteractiveIconSize } from './InteractiveIcon';
import Tooltip from './Tooltip';
import { Tooltip } from './Tooltip';
import { ButtonSecondary } from './buttons';
import { CopyIcon } from './icons/CopyIcon';
import { IconProps } from './icons/Icon';

+ 1
- 1
server/sonar-web/design-system/src/components/code-line/LineCoverage.tsx View File

@@ -23,7 +23,7 @@ import React, { memo } from 'react';
import tw from 'twin.macro';
import { PopupPlacement } from '../../helpers/positioning';
import { themeColor } from '../../helpers/theme';
import Tooltip from '../Tooltip';
import { Tooltip } from '../Tooltip';
import { LineMeta } from './LineStyles';

interface Props {

+ 2
- 0
server/sonar-web/design-system/src/components/index.ts View File

@@ -61,6 +61,7 @@ export * from './MetricsRatingBadge';
export * from './NavBarTabs';
export * from './NewCodeLegend';
export * from './OutsideClickHandler';
export { Pill } from './Pill';
export { QualityGateIndicator } from './QualityGateIndicator';
export * from './SearchHighlighter';
export * from './SelectionCard';
@@ -75,6 +76,7 @@ export * from './TagsSelector';
export * from './Text';
export * from './Title';
export { ToggleButton } from './ToggleButton';
export { Tooltip } from './Tooltip';
export { TopBar } from './TopBar';
export * from './TreeMap';
export * from './TreeMapRect';

+ 16
- 0
server/sonar-web/design-system/src/theme/light.ts View File

@@ -276,6 +276,12 @@ export const lightTheme = {
badgeDeleted: COLORS.red[100],
badgeCounter: COLORS.blueGrey[100],

// pills
pillDanger: COLORS.red[100],
pillWarning: COLORS.yellowGreen[500],
pillInfo: COLORS.indigo[100],
pillNeutral: COLORS.blueGrey[50],

// input select
selectOptionSelected: secondary.light,

@@ -323,6 +329,8 @@ export const lightTheme = {
iconSeverityMajor: danger.light,
iconSeverityMinor: COLORS.yellowGreen[400],
iconSeverityInfo: COLORS.blue[400],
iconSeverityDisabled: COLORS.blueGrey[300],
iconTypeDisabled: COLORS.blueGrey[300],
iconDirectory: COLORS.orange[300],
iconFile: COLORS.blueGrey[300],
iconProject: COLORS.blueGrey[300],
@@ -637,6 +645,12 @@ export const lightTheme = {
badgeDeleted: COLORS.red[900],
badgeCounter: secondary.darker,

// pills
pillDanger: COLORS.red[800],
pillWarning: COLORS.yellowGreen[900],
pillInfo: COLORS.indigo[900],
pillNeutral: COLORS.blueGrey[500],

// breadcrumbs
breadcrumb: secondary.dark,

@@ -732,6 +746,8 @@ export const lightTheme = {

// issue box
issueTypeIcon: COLORS.red[900],
iconSeverityDisabled: COLORS.white,
iconTypeDisabled: COLORS.white,

// selection card
selectionCardDisabled: secondary.dark,

+ 54
- 1
server/sonar-web/src/main/js/apps/issues/__tests__/utils-test.ts View File

@@ -24,6 +24,7 @@ import {
} from '../../../types/issues';
import { SecurityStandard } from '../../../types/security';
import {
parseQuery,
serializeQuery,
shouldOpenSonarSourceSecurityFacet,
shouldOpenStandardsChildFacet,
@@ -100,7 +101,6 @@ describe('serialize/deserialize', () => {
rules: 'a,b',
s: 'rules',
scopes: 'a,b',
severities: 'a,b',
inNewCodePeriod: 'true',
sonarsourceSecurity: 'a,b',
statuses: 'a,b',
@@ -108,6 +108,59 @@ describe('serialize/deserialize', () => {
types: 'a,b',
});
});

it('should deserialize correctly', () => {
expect(
parseQuery({
assigned: 'true',
assignees: 'first,second',
author: ['author'],
cleanCodeAttributeCategory: 'CONSISTENT',
impactSeverity: 'LOW',
severities: 'CRITICAL,MAJOR',
impactSoftwareQuality: 'MAINTAINABILITY',
})
).toStrictEqual({
assigned: true,
assignees: ['first', 'second'],
author: ['author'],
cleanCodeAttributeCategory: [CleanCodeAttributeCategory.Consistent],
codeVariants: [],
createdAfter: undefined,
createdAt: '',
createdBefore: undefined,
createdInLast: '',
cwe: [],
directories: [],
files: [],
impactSeverity: [
SoftwareImpactSeverity.Low,
SoftwareImpactSeverity.High,
SoftwareImpactSeverity.Medium,
],
impactSoftwareQuality: [SoftwareQuality.Maintainability],
inNewCodePeriod: false,
issues: [],
languages: [],
'owaspAsvs-4.0': [],
owaspAsvsLevel: '',
owaspTop10: [],
'owaspTop10-2021': [],
'pciDss-3.2': [],
'pciDss-4.0': [],
projects: [],
resolutions: [],
resolved: true,
rules: [],
scopes: [],
severities: [],
sonarsourceSecurity: [],
sort: '',
statuses: [],
tags: [],
types: [],
});
});
});

describe('shouldOpenStandardsFacet', () => {

+ 27
- 4
server/sonar-web/src/main/js/apps/issues/utils.ts View File

@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { isArray } from 'lodash';
import { compact, isArray, uniq } from 'lodash';
import { getUsers } from '../../api/users';
import { formatMeasure } from '../../helpers/measures';
import {
@@ -106,7 +106,7 @@ export function parseQuery(query: RawQuery): Query {
cwe: parseAsArray(query.cwe, parseAsString),
directories: parseAsArray(query.directories, parseAsString),
files: parseAsArray(query.files, parseAsString),
impactSeverity: parseAsArray<SoftwareImpactSeverity>(query.impactSeverity, parseAsString),
impactSeverity: parseImpactSeverityQuery(query.impactSeverity, query.severities),
impactSoftwareQuality: parseAsArray<SoftwareQuality>(
query.impactSoftwareQuality,
parseAsString
@@ -125,7 +125,7 @@ export function parseQuery(query: RawQuery): Query {
resolved: parseAsBoolean(query.resolved),
rules: parseAsArray(query.rules, parseAsString),
scopes: parseAsArray(query.scopes, parseAsString),
severities: parseAsArray(query.severities, parseAsString),
severities: [],
sonarsourceSecurity: parseAsArray(query.sonarsourceSecurity, parseAsString),
sort: parseAsSort(query.s),
statuses: parseAsArray(query.statuses, parseAsString),
@@ -135,6 +135,29 @@ export function parseQuery(query: RawQuery): Query {
};
}

function parseImpactSeverityQuery(
newSeverities: string,
oldSeverities?: string
): SoftwareImpactSeverity[] {
const OLD_TO_NEW_MAPPER = {
BLOCKER: SoftwareImpactSeverity.High,
CRITICAL: SoftwareImpactSeverity.High,
MAJOR: SoftwareImpactSeverity.Medium,
MINOR: SoftwareImpactSeverity.Low,
INFO: SoftwareImpactSeverity.Low,
};

// Merging new and old severities includes mapping for old to new
return compact(
uniq([
...parseAsArray<SoftwareImpactSeverity>(newSeverities, parseAsString),
...parseAsArray(oldSeverities, parseAsString).map(
(oldSeverity: string) => OLD_TO_NEW_MAPPER[oldSeverity as keyof typeof OLD_TO_NEW_MAPPER]
),
])
);
}

export function getOpen(query: RawQuery): string | undefined {
return query.open;
}
@@ -173,7 +196,7 @@ export function serializeQuery(query: Query): RawQuery {
rules: serializeStringArray(query.rules),
s: serializeString(query.sort),
scopes: serializeStringArray(query.scopes),
severities: serializeStringArray(query.severities),
severities: undefined,
impactSeverity: serializeStringArray(query.impactSeverity),
impactSoftwareQuality: serializeStringArray(query.impactSoftwareQuality),
inNewCodePeriod: query.inNewCodePeriod ? 'true' : undefined,

+ 2
- 2
server/sonar-web/src/main/js/components/issue/__tests__/Issue-it.tsx View File

@@ -59,7 +59,7 @@ describe('rendering', () => {
expect(ui.effort('2 days').get()).toBeInTheDocument();
expect(ui.issueMessageLink.get()).toHaveAttribute(
'href',
'/issues?scopes=MAIN&severities=MINOR&types=VULNERABILITY&open=AVsae-CQS-9G3txfbFN2'
'/issues?scopes=MAIN&impactSeverities=MINOR&types=VULNERABILITY&open=AVsae-CQS-9G3txfbFN2'
);

await ui.clickIssueMessage();
@@ -441,7 +441,7 @@ function renderIssue(
}

return renderAppRoutes(
'issues?scopes=MAIN&severities=MINOR&types=VULNERABILITY',
'issues?scopes=MAIN&impactSeverity=LOW&types=VULNERABILITY',
() => (
<Route
path="issues"

+ 20
- 5
server/sonar-web/src/main/js/components/issue/components/IssueActionsBar.tsx View File

@@ -34,6 +34,7 @@ import { RuleStatus } from '../../../types/rules';
import { Issue, RawQuery } 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';
@@ -130,13 +131,12 @@ export default function IssueActionsBar(props: Props) {

return (
<div
className={classNames(className, 'sw-flex sw-flex-wrap sw-items-center sw-justify-between')}
className={classNames(
className,
'sw-flex sw-gap-2 sw-flex-wrap sw-items-center sw-justify-between'
)}
>
<ul className="it__issue-header-actions sw-flex sw-items-center sw-gap-3 sw-body-sm">
<li>
<IssueType canSetType={canSetType} issue={issue} setIssueProperty={setIssueProperty} />
</li>

<li>
<IssueTransition
isOpen={currentPopup === 'transition'}
@@ -147,6 +147,21 @@ export default function IssueActionsBar(props: Props) {
/>
</li>

<li className="sw-flex sw-gap-3">
{issue.impacts.map(({ severity, softwareQuality }, index) => (
<SoftwareImpactPill
key={index}
cleanCodeAttributeCategory={issue.cleanCodeAttributeCategory}
severity={severity}
quality={softwareQuality}
/>
))}
</li>

<li>
<IssueType canSetType={canSetType} issue={issue} setIssueProperty={setIssueProperty} />
</li>

<li>
<IssueSeverity
isOpen={currentPopup === 'set-severity'}

+ 13
- 6
server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx View File

@@ -22,6 +22,7 @@ import * as React from 'react';
import { BranchLike } from '../../../types/branch-like';
import { IssueActions } from '../../../types/issues';
import { Issue } from '../../../types/types';
import { CleanCodeAttributePill } from '../../shared/CleanCodeAttributePill';
import IssueMessage from './IssueMessage';
import IssueTags from './IssueTags';

@@ -39,13 +40,19 @@ export default function IssueTitleBar(props: IssueTitleBarProps) {
const canSetTags = issue.actions.includes(IssueActions.SetTags);

return (
<div className="sw-flex sw-items-center">
<div className="sw-w-full">
<IssueMessage
issue={issue}
branchLike={props.branchLike}
displayWhyIsThisAnIssue={displayWhyIsThisAnIssue}
<div className="sw-flex sw-items-end">
<div className="sw-w-full sw-flex sw-flex-col">
<CleanCodeAttributePill
className="sw-mb-1"
cleanCodeAttributeCategory={issue.cleanCodeAttributeCategory}
/>
<div className="sw-w-fit">
<IssueMessage
issue={issue}
branchLike={props.branchLike}
displayWhyIsThisAnIssue={displayWhyIsThisAnIssue}
/>
</div>
</div>
<div className="js-issue-tags sw-body-sm sw-grow-0 sw-whitespace-nowrap">
<IssueTags

+ 1
- 1
server/sonar-web/src/main/js/components/issue/components/IssueView.tsx View File

@@ -99,7 +99,7 @@ export default class IssueView extends React.PureComponent<Props> {
>
<div className="sw-flex sw-w-full sw-px-2 sw-gap-4">
{hasCheckbox && (
<span className="sw-mt-1/2 sw-self-start">
<span className="sw-mt-6 sw-self-start">
<Checkbox
checked={checked ?? false}
onCheck={this.handleCheck}

+ 52
- 0
server/sonar-web/src/main/js/components/shared/CleanCodeAttributePill.tsx View File

@@ -0,0 +1,52 @@
import classNames from 'classnames';
import { Link, Pill } from 'design-system';
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { useDocUrl } from '../../helpers/docs';
import { translate } from '../../helpers/l10n';
import { CleanCodeAttributeCategory } from '../../types/issues';
import Tooltip from '../controls/Tooltip';

export interface Props {
className?: string;
cleanCodeAttributeCategory: CleanCodeAttributeCategory;
}

export function CleanCodeAttributePill(props: Props) {
const { className, cleanCodeAttributeCategory } = props;

const docUrl = useDocUrl('/');

return (
<Tooltip
overlay={
<>
<p className="sw-mb-4">
{translate('issue.clean_code_attribute_category', cleanCodeAttributeCategory, 'title')}
</p>
<p>
{translate('issue.clean_code_attribute_category', cleanCodeAttributeCategory, 'advice')}
</p>
<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.type.deprecation.documentation')}
</Link>
),
}}
/>
</>
}
>
<span className="sw-w-fit">
<Pill variant="neutral" className={classNames('sw-mr-2', className)}>
{translate('issue.clean_code_attribute_category', cleanCodeAttributeCategory, 'issue')}
</Pill>
</span>
</Tooltip>
);
}

+ 82
- 0
server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx View File

@@ -0,0 +1,82 @@
import classNames from 'classnames';
import { Link, Pill } from 'design-system';
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { useDocUrl } from '../../helpers/docs';
import { translate } from '../../helpers/l10n';
import {
CleanCodeAttributeCategory,
SoftwareImpactSeverity,
SoftwareQuality,
} from '../../types/issues';
import Tooltip from '../controls/Tooltip';
import SoftwareImpactSeverityIcon from '../icons/SoftwareImpactSeverityIcon';

export interface Props {
className?: string;
cleanCodeAttributeCategory: CleanCodeAttributeCategory;
severity: SoftwareImpactSeverity;
quality: SoftwareQuality;
}

export default function SoftwareImpactPill(props: Props) {
const { cleanCodeAttributeCategory, className, severity, quality } = props;

const docUrl = useDocUrl('/');

const variant = {
[SoftwareImpactSeverity.High]: 'danger',
[SoftwareImpactSeverity.Medium]: 'warning',
[SoftwareImpactSeverity.Low]: 'info',
}[severity] as 'danger' | 'warning' | 'info';

return (
<div>
<Tooltip
overlay={
<>
<p className="sw-mb-4">
{translate(
'issue.clean_code_attribute_category',
cleanCodeAttributeCategory,
'title'
)}
</p>
<p>
<FormattedMessage
id="issue.impact.severity.tooltip"
defaultMessage={translate('issue.impact.severity.tooltip')}
values={{
severity: translate('severity', severity).toLowerCase(),
quality: translate('issue.software_quality', quality).toLowerCase(),
}}
/>
</p>
<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.type.deprecation.documentation')}
</Link>
),
}}
/>
</>
}
>
<span>
<Pill
className={classNames('sw-flex sw-gap-1 sw-items-center', className)}
variant={variant}
>
{translate('issue.software_quality', quality)}
<SoftwareImpactSeverityIcon severity={severity} />
</Pill>
</span>
</Tooltip>
</div>
);
}

+ 20
- 2
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -109,6 +109,7 @@ key=Key
language=Language
last_analysis=Last Analysis
learn_more=Learn More
learn_more_x=Learn More: {link}
library=Library
line_number=Line Number
links=Links
@@ -964,22 +965,39 @@ issue.type.BUG.plural=Bugs
issue.type.VULNERABILITY.plural=Vulnerabilities
issue.type.SECURITY_HOTSPOT.plural=Security Hotspots

issue.type.deprecation.title=Issue types are deprecated and can no longer be modified.
issue.type.deprecation.filter_by=You can now filter issues by:
issue.type.deprecation.documentation=Documentation

issue.severity.deprecation.title=Severities are now directly tied to the software quality impacted. This old severity is deprecated and can no longer be modified.
issue.severity.deprecation.filter_by=You can now filter issues by:
issue.severity.deprecation.documentation=Documentation

issue.software_qualities=Software qualities
issue.software_quality.SECURITY=Security
issue.software_quality.RELIABILITY=Reliability
issue.software_quality.MAINTAINABILITY=Maintainability

issue.impact.severity.tooltip=This issue has a {severity} impact on the {quality} of your software.

issue.clean_code_attribute_category.CONSISTENT=Consistency
issue.clean_code_attribute_category.CONSISTENT.title=This is a consistency issue.
issue.clean_code_attribute_category.CONSISTENT.advice=To be consistent, the code needs to be written in a uniform and conventional way.
issue.clean_code_attribute_category.CONSISTENT.issue=Consistency issue
issue.clean_code_attribute_category.INTENTIONAL=Intentionality
issue.clean_code_attribute_category.INTENTIONAL.title=This is an intentionality issue.
issue.clean_code_attribute_category.INTENTIONAL.advice=To be intentional, the code content needs to be precise and purposeful.
issue.clean_code_attribute_category.INTENTIONAL.issue=Intentionality issue
issue.clean_code_attribute_category.ADAPTABLE=Adaptability
issue.clean_code_attribute_category.ADAPTABLE.title=This is an adaptability issue.
issue.clean_code_attribute_category.ADAPTABLE.advice=To be adaptable, code needs to be be structured to be easy to evolve with confidence.
issue.clean_code_attribute_category.ADAPTABLE.advice=To be adaptable, code needs to be structured to be easy to evolve with confidence.
issue.clean_code_attribute_category.ADAPTABLE.issue=Adaptability issue
issue.clean_code_attribute_category.RESPONSIBLE=Responsibility
issue.clean_code_attribute_category.RESPONSIBLE.title=This is a responsibility issue.
issue.clean_code_attribute_category.RESPONSIBLE.advice=To be responsible, the code must take into account its ethical obligations on data and potential impact of societal norms.
issue.clean_code_attribute_category.RESPONSIBLE.issue=Responsability issue
issue.clean_code_attribute_category.RESPONSIBLE.issue=Responsibility issue

issue.clean_code_attributes=Clean Code attributes
issue.clean_code_attribute.CLEAR=Clear
issue.clean_code_attribute.COMPLETE=Complete
issue.clean_code_attribute.CONVENTIONAL=Conventional

Loading…
Cancel
Save