diff options
author | Dejan Milisavljevic <130993898+dejan-milisavljevic-sonarsource@users.noreply.github.com> | 2024-09-18 14:03:50 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-09-18 20:02:59 +0000 |
commit | e55e29f6e2632c1eef4db2d659e685a50caa10a6 (patch) | |
tree | c178331b8d79e523a9c3bdbbc82f611781413dbc /server/sonar-web/src/main | |
parent | 7fe4eae27f3b725ea08c79d5a0373596200a1627 (diff) | |
download | sonarqube-e55e29f6e2632c1eef4db2d659e685a50caa10a6.tar.gz sonarqube-e55e29f6e2632c1eef4db2d659e685a50caa10a6.zip |
SONAR-22951 Use 5 levels severities for Software Impact
Co-authored-by: Léo Geoffroy <leo.geoffroy@sonarsource.com>
Co-authored-by: Stanislav <31501873+stanislavhh@users.noreply.github.com>
Co-authored-by: Viktor Vorona <viktor.vorona@sonarsource.com>
Co-authored-by: OrlovAlexander <35396155+OrlovAlexander85@users.noreply.github.com>
Co-authored-by: stanislavh <stanislav.honcharov@sonarsource.com>
Diffstat (limited to 'server/sonar-web/src/main')
58 files changed, 289 insertions, 1089 deletions
diff --git a/server/sonar-web/src/main/js/app/components/ChangeInCalculationPill.tsx b/server/sonar-web/src/main/js/app/components/ChangeInCalculationPill.tsx index bdf31ea2a25..af82e478ded 100644 --- a/server/sonar-web/src/main/js/app/components/ChangeInCalculationPill.tsx +++ b/server/sonar-web/src/main/js/app/components/ChangeInCalculationPill.tsx @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { Popover } from '@sonarsource/echoes-react'; -import { Pill } from 'design-system'; +import { Pill, PillVariant } from 'design-system'; import * as React from 'react'; import DocumentationLink from '../../components/common/DocumentationLink'; import { DocLink } from '../../helpers/doc-links'; @@ -49,7 +49,11 @@ export default function ChangeInCalculation({ qualifier }: Readonly<Props>) { </DocumentationLink> } > - <Pill variant="info" className="sw-ml-2" onClick={() => setIsPopoverOpen(!isPopoverOpen)}> + <Pill + variant={PillVariant.Accent} + className="sw-ml-2" + onClick={() => setIsPopoverOpen(!isPopoverOpen)} + > {translate('projects.awaiting_scan')} </Pill> </Popover> diff --git a/server/sonar-web/src/main/js/app/components/metrics/RatingComponent.tsx b/server/sonar-web/src/main/js/app/components/metrics/RatingComponent.tsx index ce59c6e03ce..f63301ef1f3 100644 --- a/server/sonar-web/src/main/js/app/components/metrics/RatingComponent.tsx +++ b/server/sonar-web/src/main/js/app/components/metrics/RatingComponent.tsx @@ -61,6 +61,8 @@ function isNewRatingMetric(metricKey: MetricKey) { const useGetMetricKeyForRating = (ratingMetric: RatingMetricKeys): MetricKey | null => { const { data: isLegacy, isLoading } = useIsLegacyCCTMode(); + const hasSoftwareQualityRating = !!SOFTWARE_QUALITY_RATING_METRICS_MAP[ratingMetric]; + if (isNewRatingMetric(ratingMetric)) { return ratingMetric; } @@ -68,7 +70,9 @@ const useGetMetricKeyForRating = (ratingMetric: RatingMetricKeys): MetricKey | n if (isLoading) { return null; } - return isLegacy ? ratingMetric : SOFTWARE_QUALITY_RATING_METRICS_MAP[ratingMetric]; + return isLegacy || !hasSoftwareQualityRating + ? ratingMetric + : SOFTWARE_QUALITY_RATING_METRICS_MAP[ratingMetric]; }; export default function RatingComponent(props: Readonly<Props>) { @@ -108,7 +112,6 @@ export default function RatingComponent(props: Readonly<Props>) { const badge = ( <MetricsRatingBadge label={getLabel ? getLabel(rating) : (value ?? '—')} - isLegacy={measure?.metric ? !isNewRatingMetric(measure.metric as MetricKey) : false} rating={rating} size={size} className={className} diff --git a/server/sonar-web/src/main/js/apps/code/__tests__/__snapshots__/utils-test.tsx.snap b/server/sonar-web/src/main/js/apps/code/__tests__/__snapshots__/utils-test.tsx.snap index bd05e186171..2ba88be1e1d 100644 --- a/server/sonar-web/src/main/js/apps/code/__tests__/__snapshots__/utils-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/code/__tests__/__snapshots__/utils-test.tsx.snap @@ -19,7 +19,6 @@ exports[`getCodeMetrics should return the right metrics for apps 1`] = ` exports[`getCodeMetrics should return the right metrics for portfolios 1`] = ` [ "releasability_rating", - "software_quality_releasability_rating", "new_security_rating", "new_software_quality_security_rating", "new_reliability_rating", @@ -27,10 +26,8 @@ exports[`getCodeMetrics should return the right metrics for portfolios 1`] = ` "new_maintainability_rating", "new_software_quality_maintainability_rating", "new_security_review_rating", - "new_software_quality_security_review_rating", "new_lines", "releasability_rating", - "software_quality_releasability_rating", "security_rating", "software_quality_security_rating", "reliability_rating", @@ -38,7 +35,6 @@ exports[`getCodeMetrics should return the right metrics for portfolios 1`] = ` "sqale_rating", "software_quality_maintainability_rating", "security_review_rating", - "software_quality_security_review_rating", "ncloc", ] `; @@ -46,7 +42,6 @@ exports[`getCodeMetrics should return the right metrics for portfolios 1`] = ` exports[`getCodeMetrics should return the right metrics for portfolios 2`] = ` [ "releasability_rating", - "software_quality_releasability_rating", "new_security_rating", "new_software_quality_security_rating", "new_reliability_rating", @@ -54,10 +49,8 @@ exports[`getCodeMetrics should return the right metrics for portfolios 2`] = ` "new_maintainability_rating", "new_software_quality_maintainability_rating", "new_security_review_rating", - "new_software_quality_security_review_rating", "new_lines", "releasability_rating", - "software_quality_releasability_rating", "security_rating", "software_quality_security_rating", "reliability_rating", @@ -65,7 +58,6 @@ exports[`getCodeMetrics should return the right metrics for portfolios 2`] = ` "sqale_rating", "software_quality_maintainability_rating", "security_review_rating", - "software_quality_security_review_rating", "ncloc", "alert_status", ] @@ -74,7 +66,6 @@ exports[`getCodeMetrics should return the right metrics for portfolios 2`] = ` exports[`getCodeMetrics should return the right metrics for portfolios 3`] = ` [ "releasability_rating", - "software_quality_releasability_rating", "new_security_rating", "new_software_quality_security_rating", "new_reliability_rating", @@ -82,7 +73,6 @@ exports[`getCodeMetrics should return the right metrics for portfolios 3`] = ` "new_maintainability_rating", "new_software_quality_maintainability_rating", "new_security_review_rating", - "new_software_quality_security_review_rating", "new_lines", "alert_status", ] @@ -91,7 +81,6 @@ exports[`getCodeMetrics should return the right metrics for portfolios 3`] = ` exports[`getCodeMetrics should return the right metrics for portfolios 4`] = ` [ "releasability_rating", - "software_quality_releasability_rating", "security_rating", "software_quality_security_rating", "reliability_rating", @@ -99,7 +88,6 @@ exports[`getCodeMetrics should return the right metrics for portfolios 4`] = ` "sqale_rating", "software_quality_maintainability_rating", "security_review_rating", - "software_quality_security_review_rating", "ncloc", "alert_status", ] diff --git a/server/sonar-web/src/main/js/apps/code/components/CodeAppRenderer.tsx b/server/sonar-web/src/main/js/apps/code/components/CodeAppRenderer.tsx index 021f688d401..173311aee9a 100644 --- a/server/sonar-web/src/main/js/apps/code/components/CodeAppRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/CodeAppRenderer.tsx @@ -49,6 +49,7 @@ import { areCCTMeasuresComputed, areSoftwareQualityRatingsComputed, } from '../../../helpers/measures'; +import { useIsLegacyCCTMode } from '../../../queries/settings'; import { BranchLike } from '../../../types/branch-like'; import { isApplication } from '../../../types/component'; import { Component, ComponentMeasure, Dict, Metric } from '../../../types/types'; @@ -108,6 +109,8 @@ export default function CodeAppRenderer(props: Readonly<Props>) { const showComponentList = sourceViewer === undefined && components.length > 0 && !showSearch; + const { data: isLegacy, isLoading: isLoadingLegacy } = useIsLegacyCCTMode(); + const metricKeys = intersection( getCodeMetrics(component.qualifier, branchLike, { newCode: newCodeSelected }), Object.keys(metrics), @@ -121,10 +124,10 @@ export default function CodeAppRenderer(props: Readonly<Props>) { ); const filteredMetrics = difference(metricKeys, [ - ...(allComponentsHaveSoftwareQualityMeasures + ...(allComponentsHaveSoftwareQualityMeasures && !isLegacy ? OLD_TAXONOMY_METRICS : CCT_SOFTWARE_QUALITY_METRICS), - ...(allComponentsHaveRatings + ...(allComponentsHaveRatings && !isLegacy ? [...OLD_TAXONOMY_RATINGS, ...LEAK_OLD_TAXONOMY_RATINGS] : SOFTWARE_QUALITY_RATING_METRICS), ]).map((key) => metrics[key]); @@ -156,7 +159,7 @@ export default function CodeAppRenderer(props: Readonly<Props>) { </FlagMessage> )} - <Spinner isLoading={loading}> + <Spinner isLoading={loading || isLoadingLegacy}> {!allComponentsHaveSoftwareQualityMeasures && ( <AnalysisMissingInfoMessage qualifier={component.qualifier} diff --git a/server/sonar-web/src/main/js/apps/code/utils.ts b/server/sonar-web/src/main/js/apps/code/utils.ts index a7056a18b26..caddcc41f85 100644 --- a/server/sonar-web/src/main/js/apps/code/utils.ts +++ b/server/sonar-web/src/main/js/apps/code/utils.ts @@ -37,7 +37,6 @@ const APPLICATION_METRICS = [MetricKey.alert_status, ...METRICS]; const PORTFOLIO_METRICS = [ MetricKey.releasability_rating, - MetricKey.software_quality_releasability_rating, MetricKey.security_rating, MetricKey.software_quality_security_rating, MetricKey.reliability_rating, @@ -45,13 +44,11 @@ const PORTFOLIO_METRICS = [ MetricKey.sqale_rating, MetricKey.software_quality_maintainability_rating, MetricKey.security_review_rating, - MetricKey.software_quality_security_review_rating, MetricKey.ncloc, ]; const NEW_PORTFOLIO_METRICS = [ MetricKey.releasability_rating, - MetricKey.software_quality_releasability_rating, MetricKey.new_security_rating, MetricKey.new_software_quality_security_rating, MetricKey.new_reliability_rating, @@ -59,7 +56,6 @@ const NEW_PORTFOLIO_METRICS = [ MetricKey.new_maintainability_rating, MetricKey.new_software_quality_maintainability_rating, MetricKey.new_security_review_rating, - MetricKey.new_software_quality_security_review_rating, MetricKey.new_lines, ]; diff --git a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts index 7e57a1be36d..fb96c419291 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts +++ b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts @@ -188,7 +188,7 @@ describe('Rules app list', () => { // Filter by severity await user.click(ui.severetiesFacet.get()); - await user.click(ui.facetItem(/severity.HIGH/).get()); + await user.click(ui.facetItem(/severity_impact.HIGH/).get()); expect(ui.getAllRuleListItems()).toHaveLength(8); }); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CustomRule-it.ts b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CustomRule-it.ts index 208cc1a0b98..cc8644c433a 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CustomRule-it.ts +++ b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CustomRule-it.ts @@ -86,9 +86,11 @@ describe('custom rule', () => { await user.click(ui.cleanCodeQualityCheckbox(SoftwareQuality.Reliability).get()); await user.click(ui.cleanCodeSeveritySelect(SoftwareQuality.Reliability).get()); - await user.click(byRole('option', { name: 'severity.MEDIUM severity.MEDIUM' }).get()); + await user.click( + byRole('option', { name: 'severity_impact.MEDIUM severity_impact.MEDIUM' }).get(), + ); - expect(ui.createCustomRuleDialog.byText('severity.MEDIUM').get()).toBeInTheDocument(); + expect(ui.createCustomRuleDialog.byText('severity_impact.MEDIUM').get()).toBeInTheDocument(); await user.click(ui.statusSelect.get()); await user.click(byRole('option', { name: 'rules.status.BETA' }).get()); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/AttributeCategoryFacet.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/AttributeCategoryFacet.tsx index c314a1150cf..76f99ceeb88 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/AttributeCategoryFacet.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/AttributeCategoryFacet.tsx @@ -18,9 +18,9 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import Facet, { BasicProps } from '../../../components/facets/Facet'; import { CLEAN_CODE_CATEGORIES } from '../../../helpers/constants'; import { translate } from '../../../helpers/l10n'; -import Facet, { BasicProps } from './Facet'; export default function AttributeCategoryFacet(props: BasicProps) { const renderName = React.useCallback( diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormFieldsCCT.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormFieldsCCT.tsx index 94e0d1f4447..5482b9a4147 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormFieldsCCT.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormFieldsCCT.tsx @@ -133,7 +133,7 @@ export function SoftwareQualitiesFields( () => IMPACT_SEVERITIES.map((severity) => ({ value: severity, - label: intl.formatMessage({ id: `severity.${severity}` }), + label: intl.formatMessage({ id: `severity_impact.${severity}` }), Icon: <SoftwareImpactSeverityIcon severity={severity} />, })), [intl], diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/FacetsList.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/FacetsList.tsx index e937feb4806..a1dbda02d91 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/FacetsList.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/FacetsList.tsx @@ -21,6 +21,7 @@ import { BasicSeparator } from 'design-system'; import * as React from 'react'; import { Profile } from '../../../api/quality-profiles'; import { useAvailableFeatures } from '../../../app/components/available-features/withAvailableFeatures'; +import SeverityFacet from '../../../components/facets/SeverityFacet'; import { translate } from '../../../helpers/l10n'; import { Feature } from '../../../types/features'; import { Dict } from '../../../types/types'; @@ -33,7 +34,6 @@ import InheritanceFacet from './InheritanceFacet'; import PrioritizedRulesFacet from './PrioritizedRulesFacet'; import ProfileFacet from './ProfileFacet'; import RepositoryFacet from './RepositoryFacet'; -import SeverityFacet from './SeverityFacet'; import SoftwareQualityFacet from './SoftwareQualityFacet'; import StatusFacet from './StatusFacet'; import TagFacet from './TagFacet'; diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/InheritanceFacet.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/InheritanceFacet.tsx index 49a5fb2eae4..92080f9afef 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/InheritanceFacet.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/InheritanceFacet.tsx @@ -18,9 +18,9 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import Facet, { BasicProps } from '../../../components/facets/Facet'; import { translate } from '../../../helpers/l10n'; import { RuleInheritance } from '../../../types/types'; -import Facet, { BasicProps } from './Facet'; interface Props extends Omit<BasicProps, 'values'> { disabled: boolean; diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/PrioritizedRulesFacet.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/PrioritizedRulesFacet.tsx index 66f1cc3b697..f739939fd89 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/PrioritizedRulesFacet.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/PrioritizedRulesFacet.tsx @@ -18,8 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import Facet, { BasicProps } from '../../../components/facets/Facet'; import { translate } from '../../../helpers/l10n'; -import Facet, { BasicProps } from './Facet'; interface Props extends Omit<BasicProps, 'onChange' | 'values'> { disabled: boolean; diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RepositoryFacet.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RepositoryFacet.tsx index f53ba7c4ae4..997f2f4bed1 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/RepositoryFacet.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RepositoryFacet.tsx @@ -21,12 +21,12 @@ import { Note } from 'design-system'; import * as React from 'react'; import { getRuleRepositories } from '../../../api/rules'; import withLanguagesContext from '../../../app/components/languages/withLanguagesContext'; +import { BasicProps } from '../../../components/facets/Facet'; import { translate } from '../../../helpers/l10n'; import { highlightTerm } from '../../../helpers/search'; import { Languages } from '../../../types/languages'; import { Dict } from '../../../types/types'; import { ListStyleFacet } from '../../issues/sidebar/ListStyleFacet'; -import { BasicProps } from './Facet'; interface StateProps { languages: Languages; diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/SoftwareQualityFacet.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/SoftwareQualityFacet.tsx index 1097682a5aa..26151da5286 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/SoftwareQualityFacet.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/SoftwareQualityFacet.tsx @@ -18,9 +18,9 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import Facet, { BasicProps } from '../../../components/facets/Facet'; import { SOFTWARE_QUALITIES } from '../../../helpers/constants'; import { translate } from '../../../helpers/l10n'; -import Facet, { BasicProps } from './Facet'; export default function SoftwareQualityFacet(props: BasicProps) { const renderName = React.useCallback( diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/StatusFacet.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/StatusFacet.tsx index 8fcd7092240..e7d03dc66d2 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/StatusFacet.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/StatusFacet.tsx @@ -18,9 +18,9 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import Facet, { BasicProps } from '../../../components/facets/Facet'; import { RULE_STATUSES } from '../../../helpers/constants'; import { translate } from '../../../helpers/l10n'; -import Facet, { BasicProps } from './Facet'; export default class StatusFacet extends React.PureComponent<BasicProps> { renderName = (status: string) => translate('rules.status', status.toLowerCase()); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/TagFacet.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/TagFacet.tsx index 78c3fd4a660..ae3b0b11a13 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/TagFacet.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/TagFacet.tsx @@ -20,10 +20,10 @@ import { uniq } from 'lodash'; import * as React from 'react'; import { getRuleTags } from '../../../api/rules'; +import { BasicProps } from '../../../components/facets/Facet'; import { translate } from '../../../helpers/l10n'; import { highlightTerm } from '../../../helpers/search'; import { ListStyleFacet } from '../../issues/sidebar/ListStyleFacet'; -import { BasicProps } from './Facet'; export default class TagFacet extends React.PureComponent<BasicProps> { handleSearch = (query: string) => { diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/TemplateFacet.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/TemplateFacet.tsx index 87fdc3d87cf..3d49c68bcb9 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/TemplateFacet.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/TemplateFacet.tsx @@ -20,8 +20,8 @@ import { HelperHintIcon } from 'design-system'; import * as React from 'react'; import HelpTooltip from '~sonar-aligned/components/controls/HelpTooltip'; +import Facet, { BasicProps } from '../../../components/facets/Facet'; import { translate } from '../../../helpers/l10n'; -import Facet, { BasicProps } from './Facet'; interface Props extends Omit<BasicProps, 'onChange' | 'values'> { onChange: (changes: { template: boolean | undefined }) => void; diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/TypeFacet.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/TypeFacet.tsx index c92a3c11697..09ba4b0635d 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/TypeFacet.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/TypeFacet.tsx @@ -18,10 +18,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import Facet, { BasicProps } from '../../../components/facets/Facet'; import IssueTypeIcon from '../../../components/icon-mappers/IssueTypeIcon'; import { RULE_TYPES } from '../../../helpers/constants'; import { translate } from '../../../helpers/l10n'; -import Facet, { BasicProps } from './Facet'; export default class TypeFacet extends React.PureComponent<BasicProps> { renderName = (type: string) => ( diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx index 95165e1448a..332801e1d32 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx @@ -36,7 +36,6 @@ import { isDefined } from '../../../helpers/types'; import { getProjectUrl } from '../../../helpers/urls'; import { useCurrentBranchQuery } from '../../../queries/branch'; import { useComponentTreeQuery, useMeasuresComponentQuery } from '../../../queries/measures'; -import { useIsLegacyCCTMode } from '../../../queries/settings'; import { useLocation, useRouter } from '../../../sonar-aligned/components/hoc/withRouter'; import { BranchLike } from '../../../types/branch-like'; import { isApplication, isFile, isView } from '../../../types/component'; @@ -71,7 +70,6 @@ export default function MeasureContent(props: Readonly<Props>) { const { data: branchLike } = useCurrentBranchQuery(rootComponent); const router = useRouter(); const query = parseQuery(rawQuery); - const { data: isLegacy } = useIsLegacyCCTMode(); const { selected, asc, view } = query; const containerRef = React.useRef<HTMLDivElement>(null); @@ -209,14 +207,7 @@ export default function MeasureContent(props: Readonly<Props>) { ); } - return ( - <TreeMapView - isLegacyMode={Boolean(isLegacy)} - components={components} - handleSelect={onOpenComponent} - metric={metric} - /> - ); + return <TreeMapView components={components} handleSelect={onOpenComponent} metric={metric} />; }; return ( diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChartView.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChartView.tsx index 86cdf3e2be9..67f507873ab 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChartView.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChartView.tsx @@ -32,7 +32,6 @@ import * as React from 'react'; import HelpTooltip from '~sonar-aligned/components/controls/HelpTooltip'; import { formatMeasure } from '~sonar-aligned/helpers/measures'; import { MetricKey } from '~sonar-aligned/types/metrics'; -import { SOFTWARE_QUALITY_RATING_METRICS } from '../../../helpers/constants'; import { getLocalizedMetricDomain, getLocalizedMetricName, @@ -42,7 +41,6 @@ import { import { getCCTMeasureValue, isDiffMetric } from '../../../helpers/measures'; import { isDefined } from '../../../helpers/types'; import { getComponentDrilldownUrl } from '../../../helpers/urls'; -import { useIsLegacyCCTMode } from '../../../queries/settings'; import { BranchLike } from '../../../types/branch-like'; import { isProject, isView } from '../../../types/component'; import { @@ -88,7 +86,6 @@ export default function BubbleChartView(props: Readonly<Props>) { bubblesByDomain, } = props; const theme = useTheme(); - const { data: isLegacy } = useIsLegacyCCTMode(); const bubbleMetrics = getBubbleMetrics(bubblesByDomain, domain, metrics); const [ratingFilters, setRatingFilters] = React.useState<{ [rating: number]: boolean }>({}); @@ -116,8 +113,7 @@ export default function BubbleChartView(props: Readonly<Props>) { return undefined; } - const bubbleColor = - `bubble.${isLegacy ? 'legacy.' : ''}${(colorRating ?? 1) as BubbleColorVal}` as const; + const bubbleColor = `bubble.${(colorRating ?? 1) as BubbleColorVal}` as const; return { x, @@ -220,12 +216,6 @@ export default function BubbleChartView(props: Readonly<Props>) { </div> {bubbleMetrics.colors && ( <ColorRatingsLegend - isLegacy={ - isLegacy || - bubbleMetrics.colors.every( - (m) => !SOFTWARE_QUALITY_RATING_METRICS.includes(m.key as MetricKey), - ) - } className="sw-mt-2" filters={ratingFilters} onRatingClick={handleRatingFilterClick} diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ColorRatingsLegend.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ColorRatingsLegend.tsx index 80acadf8b66..b765f2e4a25 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ColorRatingsLegend.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ColorRatingsLegend.tsx @@ -33,14 +33,13 @@ import { translateWithParameters } from '../../../helpers/l10n'; export interface ColorRatingsLegendProps { className?: string; filters: { [rating: number]: boolean }; - isLegacy?: boolean; onRatingClick: (selection: number) => void; } export default function ColorRatingsLegend(props: ColorRatingsLegendProps) { - const { className, filters, isLegacy } = props; + const { className, filters } = props; const theme = useTheme(); - const RATINGS = isLegacy ? [1, 2, 3, 4, 5] : [1, 2, 3, 4]; + const RATINGS = [1, 2, 3, 4, 5]; const ratingsColors = RATINGS.map((rating: BubbleColorVal) => { const formattedMeasure = formatMeasure(rating, MetricType.Rating); @@ -50,10 +49,10 @@ export default function ColorRatingsLegend(props: ColorRatingsLegendProps) { label: formattedMeasure, value: rating, selected: !filters[rating], - backgroundColor: themeColor(isLegacy ? `bubble.legacy.${rating}` : `bubble.${rating}`)({ + backgroundColor: themeColor(`bubble.${rating}`)({ theme, }), - borderColor: themeContrast(isLegacy ? `bubble.legacy.${rating}` : `bubble.${rating}`)({ + borderColor: themeContrast(`bubble.${rating}`)({ theme, }), }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.tsx index 49cf36bb1f1..27b3dd2832e 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.tsx @@ -46,7 +46,6 @@ import EmptyResult from './EmptyResult'; interface TreeMapViewProps { components: ComponentMeasureEnhanced[]; handleSelect: (component: ComponentMeasureIntern) => void; - isLegacyMode: boolean; metric: Metric; } @@ -57,8 +56,7 @@ interface State { } const PERCENT_SCALE_DOMAIN = [0, 25, 50, 75, 100]; -const RATING_SCALE_DOMAIN = [1, 2, 3, 4]; -const LEGACY_RATING_SCALE_DOMAIN = [1, 2, 3, 4, 5]; +const RATING_SCALE_DOMAIN = [1, 2, 3, 4, 5]; const HEIGHT = 500; const NA_COLORS: [ThemeColors, ThemeColors] = ['treeMap.NA1', 'treeMap.NA2']; @@ -69,13 +67,6 @@ const TREEMAP_COLORS: ThemeColors[] = [ 'treeMap.D', 'treeMap.E', ]; -const TREEMAP_LEGACY_COLORS: ThemeColors[] = [ - 'treeMap.legacy.A', - 'treeMap.legacy.B', - 'treeMap.legacy.C', - 'treeMap.legacy.D', - 'treeMap.legacy.E', -]; export class TreeMapView extends React.PureComponent<Props, State> { state: State; @@ -149,10 +140,8 @@ export class TreeMapView extends React.PureComponent<Props, State> { }; getMappedThemeColors = (): string[] => { - const { theme, isLegacyMode } = this.props; - return (isLegacyMode ? TREEMAP_LEGACY_COLORS : TREEMAP_COLORS).map((c) => - themeColor(c)({ theme }), - ); + const { theme } = this.props; + return TREEMAP_COLORS.map((c) => themeColor(c)({ theme })); }; getLevelColorScale = () => @@ -171,9 +160,8 @@ export class TreeMapView extends React.PureComponent<Props, State> { }; getRatingColorScale = () => { - const { isLegacyMode } = this.props; return scaleLinear<string, string>() - .domain(isLegacyMode ? LEGACY_RATING_SCALE_DOMAIN : RATING_SCALE_DOMAIN) + .domain(RATING_SCALE_DOMAIN) .range(this.getMappedThemeColors()); }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/utils.ts b/server/sonar-web/src/main/js/apps/component-measures/utils.ts index 5424ba9d481..e47f720209e 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/utils.ts +++ b/server/sonar-web/src/main/js/apps/component-measures/utils.ts @@ -240,11 +240,7 @@ export function banQualityGateMeasure({ measures = [], qualifier }: ComponentMea bannedMetrics.push(MetricKey.alert_status); } if (qualifier === ComponentQualifier.Application) { - bannedMetrics.push( - MetricKey.releasability_rating, - MetricKey.releasability_effort, - MetricKey.software_quality_releasability_rating, - ); + bannedMetrics.push(MetricKey.releasability_rating, MetricKey.releasability_effort); } return measures.filter((measure) => !bannedMetrics.includes(measure.metric)); } diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/IssueHeader-it.tsx b/server/sonar-web/src/main/js/apps/issues/__tests__/IssueHeader-it.tsx index b1edb27376b..f249c8f48e4 100644 --- a/server/sonar-web/src/main/js/apps/issues/__tests__/IssueHeader-it.tsx +++ b/server/sonar-web/src/main/js/apps/issues/__tests__/IssueHeader-it.tsx @@ -52,14 +52,14 @@ it('renders correctly', async () => { `issue.clean_code_attribute_category.${issue.cleanCodeAttributeCategory}`, ).get(); expect(cctBadge).toBeInTheDocument(); - await expect(cctBadge).toHaveATooltipWithContent( + await expect(cctBadge).toHaveAPopoverWithContent( `issue.clean_code_attribute.${issue.cleanCodeAttribute}`, ); // Software Qualities const qualityBadge = byText(`software_quality.${issue.impacts[0].softwareQuality}`).get(); expect(qualityBadge).toBeInTheDocument(); - await expect(qualityBadge).toHaveATooltipWithContent('software_quality'); + await expect(qualityBadge).toHaveAPopoverWithContent('software_quality'); // Deprecated type const type = byText(`issue.type.${issue.type}`).get(); diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/SeverityFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/SeverityFacet.tsx deleted file mode 100644 index d4be7b73af1..00000000000 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/SeverityFacet.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 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 { HelperHintIcon } from 'design-system'; -import * as React from 'react'; -import DocHelpTooltip from '~sonar-aligned/components/controls/DocHelpTooltip'; -import SoftwareImpactSeverityIcon from '../../../components/icon-mappers/SoftwareImpactSeverityIcon'; -import { IMPACT_SEVERITIES } from '../../../helpers/constants'; -import { DocLink } from '../../../helpers/doc-links'; -import { translate } from '../../../helpers/l10n'; -import { SoftwareImpactSeverity } from '../../../types/clean-code-taxonomy'; -import { CommonProps, SimpleListStyleFacet } from './SimpleListStyleFacet'; - -interface Props extends CommonProps { - severities: SoftwareImpactSeverity[]; -} - -export function SeverityFacet(props: Props) { - const { severities = [], ...rest } = props; - - return ( - <SimpleListStyleFacet - property="impactSeverities" - itemNamePrefix="severity" - listItems={IMPACT_SEVERITIES} - selectedItems={severities} - renderIcon={(severity: string, disabled: boolean) => ( - <SoftwareImpactSeverityIcon severity={severity} disabled={disabled} /> - )} - help={ - <DocHelpTooltip - placement="right" - content={ - <> - <p>{translate('issues.facet.impactSeverities.help.line1')}</p> - <p className="sw-mt-2">{translate('issues.facet.impactSeverities.help.line2')}</p> - </> - } - links={[ - { - href: DocLink.CleanCodeIntroduction, - label: translate('learn_more'), - }, - ]} - > - <HelperHintIcon /> - </DocHelpTooltip> - } - {...rest} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx index 749603a72d7..1f643e26f92 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx @@ -25,6 +25,7 @@ import { isPortfolioLike } from '~sonar-aligned/helpers/component'; import { ComponentQualifier } from '~sonar-aligned/types/component'; import { useAppState } from '../../../app/components/app-state/withAppStateContext'; import { useAvailableFeatures } from '../../../app/components/available-features/withAvailableFeatures'; +import SeverityFacet from '../../../components/facets/SeverityFacet'; import { translate } from '../../../helpers/l10n'; import { BranchLike } from '../../../types/branch-like'; import { isApplication, isProject, isView } from '../../../types/component'; @@ -52,7 +53,6 @@ import { PrioritizedRuleFacet } from './PrioritizedRuleFacet'; import { ProjectFacet } from './ProjectFacet'; import { RuleFacet } from './RuleFacet'; import { ScopeFacet } from './ScopeFacet'; -import { SeverityFacet } from './SeverityFacet'; import { SoftwareQualityFacet } from './SoftwareQualityFacet'; import { StandardFacet } from './StandardFacet'; import { TagFacet } from './TagFacet'; @@ -204,8 +204,8 @@ export function Sidebar(props: Readonly<Props>) { onChange={props.onFilterChange} onToggle={props.onFacetToggle} open={!!openFacets.impactSeverities} - severities={query.impactSeverities} stats={facets.impactSeverities} + values={query.impactSeverities} /> <BasicSeparator className="sw-my-4" /> diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-it.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-it.tsx index abd6403a955..e878ce08907 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-it.tsx +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-it.tsx @@ -48,7 +48,9 @@ it('should render correct facets for Projects with PrioritizedRules feature', () expect(screen.getAllByRole('button').map((button) => button.textContent)).toStrictEqual([ 'issues.facet.cleanCodeAttributeCategories', 'issues.facet.impactSoftwareQualities', - 'issues.facet.impactSeverities', + 'coding_rules.facet.impactSeverities', + // help icon + '', 'issues.facet.types', 'issues.facet.scopes', 'issues.facet.issueStatuses', @@ -72,7 +74,9 @@ it('should render correct facets for Application', () => { expect(screen.getAllByRole('button').map((button) => button.textContent)).toStrictEqual([ 'issues.facet.cleanCodeAttributeCategories', 'issues.facet.impactSoftwareQualities', - 'issues.facet.impactSeverities', + 'coding_rules.facet.impactSeverities', + // help icon + '', 'issues.facet.types', 'issues.facet.scopes', 'issues.facet.issueStatuses', @@ -94,7 +98,9 @@ it('should render correct facets for Portfolio', () => { expect(screen.getAllByRole('button').map((button) => button.textContent)).toStrictEqual([ 'issues.facet.cleanCodeAttributeCategories', 'issues.facet.impactSoftwareQualities', - 'issues.facet.impactSeverities', + 'coding_rules.facet.impactSeverities', + // help icon + '', 'issues.facet.types', 'issues.facet.scopes', 'issues.facet.issueStatuses', @@ -116,7 +122,9 @@ it('should render correct facets for SubPortfolio', () => { expect(screen.getAllByRole('button').map((button) => button.textContent)).toStrictEqual([ 'issues.facet.cleanCodeAttributeCategories', 'issues.facet.impactSoftwareQualities', - 'issues.facet.impactSeverities', + 'coding_rules.facet.impactSeverities', + // help icon + '', 'issues.facet.types', 'issues.facet.scopes', 'issues.facet.issueStatuses', diff --git a/server/sonar-web/src/main/js/apps/issues/test-utils.tsx b/server/sonar-web/src/main/js/apps/issues/test-utils.tsx index 3918108f48b..aca69041755 100644 --- a/server/sonar-web/src/main/js/apps/issues/test-utils.tsx +++ b/server/sonar-web/src/main/js/apps/issues/test-utils.tsx @@ -107,7 +107,7 @@ export const ui = { softwareQualityFacet: byRole('button', { name: 'issues.facet.impactSoftwareQualities', }), - severityFacet: byRole('button', { name: 'issues.facet.impactSeverities' }), + severityFacet: byRole('button', { name: 'coding_rules.facet.impactSeverities' }), prioritizedRuleFacet: byRole('button', { name: 'issues.facet.prioritized_rule.category' }), clearCodeCategoryFacet: byTestId('clear-issues.facet.cleanCodeAttributeCategories'), @@ -121,7 +121,7 @@ export const ui = { clearResolutionFacet: byTestId('clear-issues.facet.resolutions'), clearRuleFacet: byTestId('clear-issues.facet.rules'), clearScopeFacet: byTestId('clear-issues.facet.scopes'), - clearSeverityFacet: byTestId('clear-issues.facet.impactSeverities'), + clearSeverityFacet: byTestId('clear-coding_rules.facet.impactSeverities'), clearIssueStatusFacet: byTestId('clear-issues.facet.issueStatuses'), clearTagFacet: byTestId('clear-issues.facet.tags'), clearPrioritizedRuleFacet: byTestId('clear-issues.facet.prioritized_rule.category'), @@ -139,7 +139,9 @@ export const ui = { confirmedStatusFilter: byRole('checkbox', { name: 'issue.issue_status.CONFIRMED' }), fixedResolutionFilter: byRole('checkbox', { name: 'issue.resolution.FIXED' }), mainScopeFilter: byRole('checkbox', { name: 'issue.scope.MAIN' }), - mediumSeverityFilter: byRole('checkbox', { name: `severity.${SoftwareImpactSeverity.Medium}` }), + mediumSeverityFilter: byRole('checkbox', { + name: `severity_impact.${SoftwareImpactSeverity.Medium}`, + }), openStatusFilter: byRole('checkbox', { name: 'issue.issue_status.OPEN' }), vulnerabilityIssueTypeFilter: byRole('checkbox', { name: 'issue.type.VULNERABILITY' }), prioritizedRuleFilter: byRole('checkbox', { name: 'issues.facet.prioritized_rule' }), diff --git a/server/sonar-web/src/main/js/apps/overview/branches/OverallCodeMeasuresPanel.tsx b/server/sonar-web/src/main/js/apps/overview/branches/OverallCodeMeasuresPanel.tsx index dcf7161a700..12ba4763629 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/OverallCodeMeasuresPanel.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/OverallCodeMeasuresPanel.tsx @@ -197,6 +197,12 @@ export default function OverallCodeMeasuresPanel(props: Readonly<OverallCodeMeas <RatingComponent branchLike={branch} componentKey={component.key} + getTooltip={(rating) => + intl.formatMessage({ id: `metric.security_review_rating.tooltip.${rating}` }) + } + getLabel={(rating) => + intl.formatMessage({ id: 'metric.has_rating_X' }, { 0: rating }) + } ratingMetric={MetricKey.security_review_rating} size="md" /> diff --git a/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureBreakdownCard.tsx b/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureBreakdownCard.tsx deleted file mode 100644 index 463c20ea9f7..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureBreakdownCard.tsx +++ /dev/null @@ -1,131 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2024 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 { Tooltip } from '@sonarsource/echoes-react'; -import classNames from 'classnames'; -import { DiscreetLinkBox, themeColor, themeContrast } from 'design-system'; -import * as React from 'react'; -import { useIntl } from 'react-intl'; -import { formatMeasure } from '~sonar-aligned/helpers/measures'; -import { getComponentIssuesUrl } from '~sonar-aligned/helpers/urls'; -import { MetricType } from '~sonar-aligned/types/metrics'; -import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils'; -import { Branch } from '../../../types/branch-like'; -import { SoftwareImpactSeverity, SoftwareQuality } from '../../../types/clean-code-taxonomy'; -import { Component } from '../../../types/types'; - -export interface SoftwareImpactMeasureBreakdownCardProps { - active?: boolean; - branch?: Branch; - component: Component; - severity: SoftwareImpactSeverity; - softwareQuality: SoftwareQuality; - value?: string; -} - -export function SoftwareImpactMeasureBreakdownCard( - props: Readonly<SoftwareImpactMeasureBreakdownCardProps>, -) { - const { softwareQuality, component, value, severity, active, branch } = props; - - const intl = useIntl(); - - const url = getComponentIssuesUrl(component.key, { - ...DEFAULT_ISSUES_QUERY, - impactSoftwareQualities: softwareQuality, - impactSeverities: severity, - branch: branch?.name, - }); - - const testId = `overview__software-impact-${softwareQuality}-severity-${severity}`; - const cardClasses = - 'sw-w-1/3 sw-px-2 sw-py-1 sw-rounded-1 sw-text-xs sw-font-semibold sw-select-none sw-flex sw-gap-1 sw-justify-center sw-items-center'; - - if (!value) { - return ( - <StyledBreakdownCard - data-testid={testId} - className={classNames(cardClasses, severity, { - active, - })} - > - - - </StyledBreakdownCard> - ); - } - - return ( - <Tooltip - content={intl.formatMessage({ - id: `overview.measures.software_impact.severity.${severity}.tooltip`, - })} - > - <StyledBreakdownLinkCard - data-testid={testId} - className={classNames(cardClasses, severity, { - active, - })} - aria-label={intl.formatMessage( - { - id: 'overview.measures.software_impact.severity.see_x_open_issues', - }, - { - count: formatMeasure(value, MetricType.ShortInteger), - softwareQuality: intl.formatMessage({ - id: `software_quality.${softwareQuality}`, - }), - severity: intl.formatMessage({ - id: `overview.measures.software_impact.severity.${severity}.tooltip`, - }), - }, - )} - disabled={component.needIssueSync} - to={url} - > - <span>{formatMeasure(value, MetricType.ShortInteger)}</span> - <span> - {intl.formatMessage({ - id: `overview.measures.software_impact.severity.${severity}`, - })} - </span> - </StyledBreakdownLinkCard> - </Tooltip> - ); -} - -const StyledBreakdownCard = styled.div` - background-color: ${themeColor('overviewSoftwareImpactSeverityNeutral')}; - - &.active.HIGH { - background-color: ${themeColor('overviewSoftwareImpactSeverityHigh')}; - color: ${themeContrast('overviewSoftwareImpactSeverityHigh')}; - } - &.active.MEDIUM { - background-color: ${themeColor('overviewSoftwareImpactSeverityMedium')}; - color: ${themeContrast('overviewSoftwareImpactSeverityMedium')}; - } - &.active.LOW { - background-color: ${themeColor('overviewSoftwareImpactSeverityLow')}; - color: ${themeContrast('overviewSoftwareImpactSeverityLow')}; - } -`; -const StyledBreakdownLinkCard = StyledBreakdownCard.withComponent(DiscreetLinkBox); - -export default SoftwareImpactMeasureBreakdownCard; diff --git a/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureCard.tsx b/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureCard.tsx index 37c5e3248a8..96cd155eeca 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureCard.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureCard.tsx @@ -33,15 +33,10 @@ import { import { isDefined } from '../../../helpers/types'; import { useIsLegacyCCTMode } from '../../../queries/settings'; import { Branch } from '../../../types/branch-like'; -import { - SoftwareImpactMeasureData, - SoftwareImpactSeverity, - SoftwareQuality, -} from '../../../types/clean-code-taxonomy'; +import { SoftwareImpactMeasureData, SoftwareQuality } from '../../../types/clean-code-taxonomy'; import { QualityGateStatusConditionEnhanced } from '../../../types/quality-gates'; import { Component, MeasureEnhanced } from '../../../types/types'; import { Status, softwareQualityToMeasure } from '../utils'; -import SoftwareImpactMeasureBreakdownCard from './SoftwareImpactMeasureBreakdownCard'; import SoftwareImpactMeasureRating from './SoftwareImpactMeasureRating'; export interface SoftwareImpactBreakdownCardProps { @@ -79,13 +74,6 @@ export function SoftwareImpactMeasureCard(props: Readonly<SoftwareImpactBreakdow branch: branch?.name, }); - // We highlight the highest severity breakdown card with non-zero count - const highlightedSeverity = - measure && - [SoftwareImpactSeverity.High, SoftwareImpactSeverity.Medium, SoftwareImpactSeverity.Low].find( - (severity) => measure[severity] > 0, - ); - const countTooltipOverlay = intl.formatMessage({ id: 'overview.measures.software_impact.count_tooltip', }); @@ -149,25 +137,6 @@ export function SoftwareImpactMeasureCard(props: Readonly<SoftwareImpactBreakdow /> </div> </div> - {measure && ( - <div className="sw-flex sw-gap-2"> - {[ - SoftwareImpactSeverity.High, - SoftwareImpactSeverity.Medium, - SoftwareImpactSeverity.Low, - ].map((severity) => ( - <SoftwareImpactMeasureBreakdownCard - branch={branch} - key={severity} - component={component} - softwareQuality={softwareQuality} - value={measure?.[severity]?.toString()} - severity={severity} - active={highlightedSeverity === severity} - /> - ))} - </div> - )} </div> </div> ); diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx index 4a99bdfa447..0bc93912b37 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx @@ -44,7 +44,7 @@ import { mockQualityGateProjectStatus } from '../../../../helpers/mocks/quality- import { mockLoggedInUser, mockMeasure, mockPaging } from '../../../../helpers/testMocks'; import { renderComponent } from '../../../../helpers/testReactTestingUtils'; import { ComponentPropsType } from '../../../../helpers/testUtils'; -import { SoftwareImpactSeverity, SoftwareQuality } from '../../../../types/clean-code-taxonomy'; +import { SoftwareQuality } from '../../../../types/clean-code-taxonomy'; import { ProjectAnalysisEventCategory } from '../../../../types/project-activity'; import { SettingsKey } from '../../../../types/settings'; import { CaycStatus } from '../../../../types/types'; @@ -299,53 +299,21 @@ describe('project overview', () => { await user.click(await ui.overallCodeButton.find()); - ui.expectSoftwareImpactMeasureCard( - SoftwareQuality.Security, - 'B', - { - total: 1, - [SoftwareImpactSeverity.High]: 0, - [SoftwareImpactSeverity.Medium]: 1, - [SoftwareImpactSeverity.Low]: 0, - }, - [false, true, false], - ); + ui.expectSoftwareImpactMeasureCard(SoftwareQuality.Security, 'B', 1); await ui.expectSoftwareImpactMeasureCardRatingTooltip( SoftwareQuality.Security, 'B', 'overview.measures.software_impact.improve_rating_tooltip.software_quality.SECURITY.software_quality.security.B.overview.measures.software_impact.severity.LOW.improve_tooltip', ); - ui.expectSoftwareImpactMeasureCard( - SoftwareQuality.Reliability, - 'A', - { - total: 3, - [SoftwareImpactSeverity.High]: 0, - [SoftwareImpactSeverity.Medium]: 2, - [SoftwareImpactSeverity.Low]: 1, - }, - [false, true, false], - undefined, - true, - ); + ui.expectSoftwareImpactMeasureCard(SoftwareQuality.Reliability, 'A', 3, undefined, true); await ui.expectSoftwareImpactMeasureCardRatingTooltip( SoftwareQuality.Reliability, 'A', 'overview.measures.software_impact.improve_rating_tooltip.A.software_quality.RELIABILITY.software_quality.reliability.A.overview.measures.software_impact.severity.LOW.improve_tooltip', ); - ui.expectSoftwareImpactMeasureCard( - SoftwareQuality.Maintainability, - 'D', - { - total: 2, - [SoftwareImpactSeverity.High]: 0, - [SoftwareImpactSeverity.Medium]: 0, - [SoftwareImpactSeverity.Low]: 1, - }, - [false, false, true], - ); + ui.expectSoftwareImpactMeasureCard(SoftwareQuality.Maintainability, 'D', 2); await ui.expectSoftwareImpactMeasureCardRatingTooltip( SoftwareQuality.Maintainability, 'D', @@ -360,18 +328,7 @@ describe('project overview', () => { await user.click(await ui.overallCodeButton.find()); - ui.expectSoftwareImpactMeasureCard( - SoftwareQuality.Maintainability, - 'D', - { - total: 2, - [SoftwareImpactSeverity.High]: 0, - [SoftwareImpactSeverity.Medium]: 0, - [SoftwareImpactSeverity.Low]: 1, - }, - [false, false, true], - '', - ); + ui.expectSoftwareImpactMeasureCard(SoftwareQuality.Maintainability, 'D', 2, ''); }); it('should render old measures if software impact are missing', async () => { diff --git a/server/sonar-web/src/main/js/apps/overview/branches/test-utils.ts b/server/sonar-web/src/main/js/apps/overview/branches/test-utils.ts index d9832064480..9367882d9a1 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/test-utils.ts +++ b/server/sonar-web/src/main/js/apps/overview/branches/test-utils.ts @@ -19,11 +19,7 @@ */ import userEvent from '@testing-library/user-event'; import { byLabelText, byRole, byTestId, byText } from '~sonar-aligned/helpers/testSelector'; -import { - SoftwareImpactMeasureData, - SoftwareImpactSeverity, - SoftwareQuality, -} from '../../../types/clean-code-taxonomy'; +import { SoftwareImpactSeverity, SoftwareQuality } from '../../../types/clean-code-taxonomy'; export const getPageObjects = () => { const user = userEvent.setup(); @@ -41,8 +37,7 @@ export const getPageObjects = () => { expectSoftwareImpactMeasureCard: ( softwareQuality: SoftwareQuality, rating?: string, - data?: SoftwareImpactMeasureData, - severitiesActiveState?: boolean[], + total?: number, branch = 'master', failed = false, ) => { @@ -59,58 +54,17 @@ export const getPageObjects = () => { byText(rating, { exact: true }).get(ui.softwareImpactMeasureCard(softwareQuality).get()), ).toBeInTheDocument(); } - if (data) { + if (total !== undefined) { const branchQuery = branch ? `&branch=${branch}` : ''; expect( byRole('link', { - name: `overview.measures.software_impact.see_list_of_x_open_issues.${data.total}.software_quality.${softwareQuality}`, + name: `overview.measures.software_impact.see_list_of_x_open_issues.${total}.software_quality.${softwareQuality}`, }).get(), ).toHaveAttribute( 'href', `/project/issues?issueStatuses=OPEN%2CCONFIRMED&impactSoftwareQualities=${softwareQuality}${branchQuery}&id=foo`, ); - expect( - byRole('link', { - name: `overview.measures.software_impact.severity.see_x_open_issues.${ - data[SoftwareImpactSeverity.High] - }.software_quality.${softwareQuality}.overview.measures.software_impact.severity.HIGH.tooltip`, - }).get(), - ).toHaveAttribute( - 'href', - `/project/issues?issueStatuses=OPEN%2CCONFIRMED&impactSoftwareQualities=${softwareQuality}&impactSeverities=${SoftwareImpactSeverity.High}${branchQuery}&id=foo`, - ); - expect( - byRole('link', { - name: `overview.measures.software_impact.severity.see_x_open_issues.${ - data[SoftwareImpactSeverity.Medium] - }.software_quality.${softwareQuality}.overview.measures.software_impact.severity.MEDIUM.tooltip`, - }).get(), - ).toBeInTheDocument(); - expect( - byRole('link', { - name: `overview.measures.software_impact.severity.see_x_open_issues.${ - data[SoftwareImpactSeverity.Low] - }.software_quality.${softwareQuality}.overview.measures.software_impact.severity.LOW.tooltip`, - }).get(), - ).toBeInTheDocument(); - } - if (severitiesActiveState) { - ui.expectSoftwareImpactMeasureBreakdownCard( - softwareQuality, - SoftwareImpactSeverity.High, - severitiesActiveState[0], - ); - ui.expectSoftwareImpactMeasureBreakdownCard( - softwareQuality, - SoftwareImpactSeverity.Medium, - severitiesActiveState[1], - ); - ui.expectSoftwareImpactMeasureBreakdownCard( - softwareQuality, - SoftwareImpactSeverity.Low, - severitiesActiveState[2], - ); } }, expectSoftwareImpactMeasureCardToHaveOldMeasures: ( diff --git a/server/sonar-web/src/main/js/apps/overview/utils.tsx b/server/sonar-web/src/main/js/apps/overview/utils.tsx index fa7e2786ea5..0d94c172778 100644 --- a/server/sonar-web/src/main/js/apps/overview/utils.tsx +++ b/server/sonar-web/src/main/js/apps/overview/utils.tsx @@ -68,9 +68,7 @@ export const BRANCH_OVERVIEW_METRICS: string[] = [ MetricKey.security_hotspots_reviewed, MetricKey.new_security_hotspots_reviewed, MetricKey.security_review_rating, - MetricKey.software_quality_security_review_rating, MetricKey.new_security_review_rating, - MetricKey.new_software_quality_security_review_rating, // code smells MetricKey.code_smells, diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx index 80b1d177e92..f059e203627 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx @@ -253,8 +253,7 @@ export default class ProjectActivityGraphs extends React.PureComponent<Props, St }; render() { - const { analyses, leakPeriodDate, loading, measuresHistory, metrics, query, isLegacy } = - this.props; + const { analyses, leakPeriodDate, loading, measuresHistory, metrics, query } = this.props; const { graphEndDate, graphStartDate, series } = this.state; return ( @@ -278,7 +277,6 @@ export default class ProjectActivityGraphs extends React.PureComponent<Props, St graphs={this.state.graphs} leakPeriodDate={leakPeriodDate} loading={loading} - isLegacy={isLegacy} measuresHistory={measuresHistory} removeCustomMetric={this.handleRemoveCustomMetric} selectedDate={query.selectedDate} diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-it.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-it.tsx index 1e29192a079..e8132d76bef 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-it.tsx @@ -609,7 +609,6 @@ describe('ratings', () => { expect(await ui.graphs.findAll()).toHaveLength(1); expect(ui.metricChangedInfoBtn.get()).toBeInTheDocument(); expect(ui.gapInfoMessage.get()).toBeInTheDocument(); - expect(byText('E').query()).not.toBeInTheDocument(); }); it('should not show old rating if new one was always there', async () => { @@ -652,10 +651,9 @@ describe('ratings', () => { expect(await ui.graphs.findAll()).toHaveLength(1); expect(ui.metricChangedInfoBtn.query()).not.toBeInTheDocument(); expect(ui.gapInfoMessage.query()).not.toBeInTheDocument(); - expect(byText('E').query()).not.toBeInTheDocument(); }); - it('should show E if no new metrics', async () => { + it('should not show change info button if no new metrics', async () => { timeMachineHandler.setMeasureHistory([ mockMeasureHistory({ metric: MetricKey.reliability_rating, @@ -686,10 +684,9 @@ describe('ratings', () => { expect(await ui.graphs.findAll()).toHaveLength(1); expect(ui.metricChangedInfoBtn.query()).not.toBeInTheDocument(); expect(ui.gapInfoMessage.query()).not.toBeInTheDocument(); - expect(byText('E').get()).toBeInTheDocument(); }); - it('should not show gaps message and metric change button, but should show E in legacy mode', async () => { + it('should not show gaps message and metric change button in legacy mode', async () => { settingsHandler.set(SettingsKey.LegacyMode, 'true'); timeMachineHandler.setMeasureHistory([ mockMeasureHistory({ @@ -746,7 +743,6 @@ describe('ratings', () => { expect(await ui.graphs.findAll()).toHaveLength(1); expect(ui.metricChangedInfoBtn.query()).not.toBeInTheDocument(); expect(ui.gapInfoMessage.query()).not.toBeInTheDocument(); - expect(byText('E').get()).toBeInTheDocument(); }); }); diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.tsx index 63fe3f135e9..927df18e7aa 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.tsx @@ -88,20 +88,20 @@ it('should show legacy filters', async () => { settingsHandler.set(SettingsKey.LegacyMode, 'true'); renderPageSidebar(); - expect(await screen.findAllByText('E')).toHaveLength(4); - expect(screen.queryByText(/projects.facets.rating_option/)).not.toBeInTheDocument(); - expect(screen.queryByText('projects.facets.maintainability.description')).not.toBeInTheDocument(); - expect(screen.queryByText('projects.facets.security_review.description')).not.toBeInTheDocument(); + expect(await screen.findAllByText(/projects.facets.rating_option/)).toHaveLength(20); + expect(screen.getByText('projects.facets.rating_option.security.legacy.1')).toBeInTheDocument(); + expect( + screen.getByText('projects.facets.rating_option.reliability.legacy.1'), + ).toBeInTheDocument(); }); it('should show non legacy filters', async () => { settingsHandler.set(SettingsKey.LegacyMode, 'false'); renderPageSidebar(); - expect(await screen.findAllByText(/projects.facets.rating_option/)).toHaveLength(16); - expect(screen.queryAllByText('E')).toHaveLength(0); - expect(screen.getByText('projects.facets.maintainability.description')).toBeInTheDocument(); - expect(screen.getByText('projects.facets.security_review.description')).toBeInTheDocument(); + expect(await screen.findAllByText(/projects.facets.rating_option/)).toHaveLength(20); + expect(screen.getByText('projects.facets.rating_option.security.1')).toBeInTheDocument(); + expect(screen.getByText('projects.facets.rating_option.reliability.1')).toBeInTheDocument(); }); function renderPageSidebar(overrides: Partial<PageSidebarProps> = {}, currentUser?: CurrentUser) { diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx index e062e396102..998ac41cc8c 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx @@ -38,7 +38,7 @@ const MEASURES = { [MetricKey.reliability_rating]: '1.0', [MetricKey.security_rating]: '1.0', [MetricKey.sqale_rating]: '1.0', - [MetricKey.security_review_rating]: '1.0', + [MetricKey.security_review_rating]: '3.0', [MetricKey.new_bugs]: '12', }; @@ -123,7 +123,7 @@ describe('upgrade scenario (awaiting scan)', () => { }), [MetricKey.security_review_rating]: mockMeasure({ metric: MetricKey.security_review_rating, - value: '1', + value: '3', }), }; @@ -140,9 +140,9 @@ describe('upgrade scenario (awaiting scan)', () => { metric: MetricKey.software_quality_security_rating, value: '2', }), - [MetricKey.software_quality_security_review_rating]: mockMeasure({ - metric: MetricKey.software_quality_security_review_rating, - value: '2', + [MetricKey.security_review_rating]: mockMeasure({ + metric: MetricKey.security_review_rating, + value: '3', }), }; beforeEach(() => { @@ -174,7 +174,6 @@ describe('upgrade scenario (awaiting scan)', () => { [MetricKey.software_quality_maintainability_rating]: '2', [MetricKey.software_quality_reliability_rating]: '2', [MetricKey.software_quality_security_rating]: '2', - [MetricKey.software_quality_security_review_rating]: '2', [MetricKey.code_smells]: '4', [MetricKey.bugs]: '5', [MetricKey.vulnerabilities]: '6', @@ -183,7 +182,8 @@ describe('upgrade scenario (awaiting scan)', () => { expect(screen.getByText('1')).toBeInTheDocument(); expect(screen.getByText('2')).toBeInTheDocument(); expect(screen.getByText('3')).toBeInTheDocument(); - await waitFor(() => expect(screen.getAllByText('B')).toHaveLength(4)); + await waitFor(() => expect(screen.getAllByText('B')).toHaveLength(3)); + await waitFor(() => expect(screen.getAllByText('C')).toHaveLength(1)); expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); expect(screen.queryByText('4')).not.toBeInTheDocument(); expect(screen.queryByText('5')).not.toBeInTheDocument(); @@ -208,7 +208,8 @@ describe('upgrade scenario (awaiting scan)', () => { expect(screen.getByText('4')).toBeInTheDocument(); expect(screen.getByText('5')).toBeInTheDocument(); expect(screen.getByText('6')).toBeInTheDocument(); - expect(screen.getAllByText('A')).toHaveLength(4); + expect(screen.getAllByText('A')).toHaveLength(3); + expect(screen.getAllByText('C')).toHaveLength(1); }); it('should display awaiting analysis badge, show new software qualities, but old ratings', async () => { @@ -236,7 +237,8 @@ describe('upgrade scenario (awaiting scan)', () => { expect(screen.queryByText('4')).not.toBeInTheDocument(); expect(screen.queryByText('5')).not.toBeInTheDocument(); expect(screen.queryByText('6')).not.toBeInTheDocument(); - await waitFor(() => expect(screen.getAllByText('A')).toHaveLength(4)); + await waitFor(() => expect(screen.getAllByText('A')).toHaveLength(3)); + expect(screen.getAllByText('C')).toHaveLength(1); }); it('should display awaiting analysis badge and show the old measures for Application', async () => { @@ -296,7 +298,8 @@ describe('upgrade scenario (awaiting scan)', () => { expect(screen.getByText('4')).toBeInTheDocument(); expect(screen.getByText('5')).toBeInTheDocument(); expect(screen.getByText('6')).toBeInTheDocument(); - await waitFor(() => expect(screen.getAllByText('A')).toHaveLength(4)); + await waitFor(() => expect(screen.getAllByText('A')).toHaveLength(3)); + expect(screen.getAllByText('C')).toHaveLength(1); expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); }); @@ -323,7 +326,6 @@ describe('upgrade scenario (awaiting scan)', () => { [MetricKey.software_quality_maintainability_rating]: '2', [MetricKey.software_quality_reliability_rating]: '2', [MetricKey.software_quality_security_rating]: '2', - [MetricKey.software_quality_security_review_rating]: '2', [MetricKey.code_smells]: '4', [MetricKey.bugs]: '5', [MetricKey.vulnerabilities]: '6', @@ -335,7 +337,8 @@ describe('upgrade scenario (awaiting scan)', () => { expect(screen.queryByText('1')).not.toBeInTheDocument(); expect(screen.queryByText('2')).not.toBeInTheDocument(); expect(screen.queryByText('3')).not.toBeInTheDocument(); - await waitFor(() => expect(screen.getAllByText('A')).toHaveLength(4)); + await waitFor(() => expect(screen.getAllByText('A')).toHaveLength(3)); + expect(screen.getAllByText('C')).toHaveLength(1); expect(screen.queryByText('B')).not.toBeInTheDocument(); expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); }); diff --git a/server/sonar-web/src/main/js/apps/projects/filters/RatingFacet.tsx b/server/sonar-web/src/main/js/apps/projects/filters/RatingFacet.tsx index 5dbbfc52654..cd9aad48051 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/RatingFacet.tsx +++ b/server/sonar-web/src/main/js/apps/projects/filters/RatingFacet.tsx @@ -17,6 +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 { Spinner } from '@sonarsource/echoes-react'; import { MetricsRatingBadge, RatingEnum } from 'design-system'; import * as React from 'react'; import { useIntl } from 'react-intl'; @@ -37,9 +38,8 @@ interface Props { value?: any; } -export default function RatingFacet(props: Props) { +export default function RatingFacet(props: Readonly<Props>) { const { facet, maxFacetValue, name, property, value } = props; - const { data: isLegacy } = useIsLegacyCCTMode(); const renderAccessibleLabel = React.useCallback( (option: number) => { @@ -65,14 +65,14 @@ export default function RatingFacet(props: Props) { facet={facet} header={translate('metric_domain', name)} description={ - !isLegacy && hasDescription(property) + hasDescription(property) ? translate(`projects.facets.${property.replace('new_', '')}.description`) : undefined } highlightUnder={1} maxFacetValue={maxFacetValue} onQueryChange={props.onQueryChange} - options={isLegacy ? [1, 2, 3, 4, 5] : [1, 2, 3, 4]} + options={[1, 2, 3, 4, 5]} property={property} renderAccessibleLabel={renderAccessibleLabel} renderOption={(option) => renderOption(option, property)} @@ -93,25 +93,24 @@ function RatingOption({ option, property, }: Readonly<{ option: string | number; property: string }>) { - const { data: isLegacy } = useIsLegacyCCTMode(); + const { data: isLegacy, isLoading } = useIsLegacyCCTMode(); const intl = useIntl(); const ratingFormatted = formatMeasure(option, MetricType.Rating); + const propertyWithoutPrefix = property.replace('new_', ''); + const isSecurityOrReliability = ['security', 'reliability'].includes(propertyWithoutPrefix); return ( - <> + <Spinner isLoading={isLoading}> <MetricsRatingBadge label={ratingFormatted} rating={ratingFormatted as RatingEnum} - isLegacy={isLegacy} size="xs" /> - {!isLegacy && ( - <span className="sw-ml-2"> - {intl.formatMessage({ - id: `projects.facets.rating_option.${property.replace('new_', '')}.${option}`, - })} - </span> - )} - </> + <span className="sw-ml-2"> + {intl.formatMessage({ + id: `projects.facets.rating_option.${propertyWithoutPrefix}${isLegacy && isSecurityOrReliability ? '.legacy' : ''}.${option}`, + })} + </span> + </Spinner> ); } diff --git a/server/sonar-web/src/main/js/apps/projects/utils.ts b/server/sonar-web/src/main/js/apps/projects/utils.ts index 21ec392bfbe..709e2626f52 100644 --- a/server/sonar-web/src/main/js/apps/projects/utils.ts +++ b/server/sonar-web/src/main/js/apps/projects/utils.ts @@ -102,7 +102,6 @@ export const METRICS = [ MetricKey.software_quality_maintainability_rating, MetricKey.security_hotspots_reviewed, MetricKey.security_review_rating, - MetricKey.software_quality_security_review_rating, MetricKey.duplicated_lines_density, MetricKey.coverage, MetricKey.ncloc, @@ -138,8 +137,8 @@ export const LEGACY_FACETS = [ export const FACETS = [ MetricKey.software_quality_reliability_rating, MetricKey.software_quality_security_rating, - MetricKey.software_quality_security_review_rating, MetricKey.software_quality_maintainability_rating, + MetricKey.security_review_rating, MetricKey.coverage, MetricKey.duplicated_lines_density, MetricKey.ncloc, @@ -166,8 +165,8 @@ export const LEGACY_LEAK_FACETS = [ export const LEAK_FACETS = [ MetricKey.new_software_quality_reliability_rating, MetricKey.new_software_quality_security_rating, - MetricKey.new_software_quality_security_review_rating, MetricKey.new_software_quality_maintainability_rating, + MetricKey.new_security_review_rating, MetricKey.new_coverage, MetricKey.new_duplicated_lines_density, MetricKey.new_lines, @@ -318,8 +317,6 @@ export const propertyToMetricMap: Dict<string | undefined> = { new_reliability: 'new_software_quality_reliability_rating', security: 'software_quality_security_rating', new_security: 'new_software_quality_security_rating', - security_review: 'software_quality_security_review_rating', - new_security_review: 'new_software_quality_security_review_rating', maintainability: 'software_quality_maintainability_rating', new_maintainability: 'new_software_quality_maintainability_rating', }; diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/AddConditionModal.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/AddConditionModal.tsx index 4f0bfadf919..f97e31fa685 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/AddConditionModal.tsx +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/AddConditionModal.tsx @@ -50,8 +50,6 @@ const FORBIDDEN_METRICS: string[] = [ MetricKey.new_software_quality_reliability_rating, MetricKey.software_quality_security_rating, MetricKey.new_software_quality_security_rating, - MetricKey.software_quality_security_review_rating, - MetricKey.new_software_quality_security_review_rating, MetricKey.effort_to_reach_software_quality_maintainability_rating_a, MetricKey.software_quality_maintainability_remediation_effort, MetricKey.new_software_quality_maintainability_remediation_effort, diff --git a/server/sonar-web/src/main/js/components/activity-graph/GraphHistory.tsx b/server/sonar-web/src/main/js/components/activity-graph/GraphHistory.tsx index 028c1ccd46e..1317281641b 100644 --- a/server/sonar-web/src/main/js/components/activity-graph/GraphHistory.tsx +++ b/server/sonar-web/src/main/js/components/activity-graph/GraphHistory.tsx @@ -41,7 +41,6 @@ interface Props { graphEndDate?: Date; graphStartDate?: Date; isCustom?: boolean; - isLegacy?: boolean; leakPeriodDate?: Date; measuresHistory: MeasureHistory[]; metricsType: string; @@ -69,7 +68,6 @@ export default function GraphHistory(props: Readonly<Props>) { series, showAreas, graphDescription, - isLegacy, } = props; const [tooltipIdx, setTooltipIdx] = React.useState<number | undefined>(undefined); const [tooltipXPos, setTooltipXPos] = React.useState<number | undefined>(undefined); @@ -124,7 +122,6 @@ export default function GraphHistory(props: Readonly<Props>) { splitPointDate={measuresHistory.find((m) => m.splitPointDate)?.splitPointDate} metricType={metricsType} selectedDate={selectedDate} - isLegacy={isLegacy} series={series} showAreas={showAreas} startDate={graphStartDate} diff --git a/server/sonar-web/src/main/js/components/activity-graph/GraphsHistory.tsx b/server/sonar-web/src/main/js/components/activity-graph/GraphsHistory.tsx index 9be16362121..5bf2137e1d8 100644 --- a/server/sonar-web/src/main/js/components/activity-graph/GraphsHistory.tsx +++ b/server/sonar-web/src/main/js/components/activity-graph/GraphsHistory.tsx @@ -33,7 +33,6 @@ interface Props { graphEndDate?: Date; graphStartDate?: Date; graphs: Serie[][]; - isLegacy?: boolean; leakPeriodDate?: Date; loading: boolean; measuresHistory: MeasureHistory[]; @@ -67,8 +66,7 @@ export default class GraphsHistory extends React.PureComponent<Props, State> { }; render() { - const { analyses, graph, loading, series, ariaLabel, canShowDataAsTable, isLegacy } = - this.props; + const { analyses, graph, loading, series, ariaLabel, canShowDataAsTable } = this.props; const isCustom = isCustomGraph(graph); if (loading) { @@ -107,7 +105,6 @@ export default class GraphsHistory extends React.PureComponent<Props, State> { graphStartDate={this.props.graphStartDate} isCustom={isCustom} key={idx} - isLegacy={isLegacy} leakPeriodDate={this.props.leakPeriodDate} measuresHistory={this.props.measuresHistory} metricsType={getSeriesMetricType(graphSeries)} diff --git a/server/sonar-web/src/main/js/components/charts/AdvancedTimeline.tsx b/server/sonar-web/src/main/js/components/charts/AdvancedTimeline.tsx index 632410a2370..2f86e9e75ed 100644 --- a/server/sonar-web/src/main/js/components/charts/AdvancedTimeline.tsx +++ b/server/sonar-web/src/main/js/components/charts/AdvancedTimeline.tsx @@ -51,7 +51,6 @@ export interface PropsWithoutTheme { height: number; hideGrid?: boolean; hideXAxis?: boolean; - isLegacy?: boolean; leakPeriodDate?: Date; // used to avoid same y ticks labels maxYTicksCount?: number; @@ -145,10 +144,7 @@ export class AdvancedTimelineClass extends React.PureComponent<Props, State> { } getRatingScale = (availableHeight: number) => { - const { isLegacy } = this.props; - return scalePoint<number>() - .domain(isLegacy ? [5, 4, 3, 2, 1] : [4, 3, 2, 1]) - .range([availableHeight, 0]); + return scalePoint<number>().domain([5, 4, 3, 2, 1]).range([availableHeight, 0]); }; getLevelScale = (availableHeight: number) => { diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/AdvancedTimeline-test.tsx b/server/sonar-web/src/main/js/components/charts/__tests__/AdvancedTimeline-test.tsx index fbf0cad0c04..ed7dfb4d301 100644 --- a/server/sonar-web/src/main/js/components/charts/__tests__/AdvancedTimeline-test.tsx +++ b/server/sonar-web/src/main/js/components/charts/__tests__/AdvancedTimeline-test.tsx @@ -61,18 +61,9 @@ it('should render correctly', () => { checkSnapShot({ zoomSpeed: 2 }, 'zoomSpeed'); checkSnapShot({ leakPeriodDate: new Date('2019-10-02T00:00:00.000Z') }, 'leakPeriodDate'); checkSnapShot({ basisCurve: true }, 'basisCurve'); - checkSnapShot({ isLegacy: false }, 'not legacy'); checkSnapShot( - { isLegacy: false, splitPointDate: new Date('2019-10-02T00:00:00.000Z') }, - 'not legacy + split point, but not Rating', - ); - checkSnapShot( - { - isLegacy: false, - splitPointDate: new Date('2019-10-02T00:00:00.000Z'), - metricType: MetricType.Rating, - }, - 'not legacy + split point', + { splitPointDate: new Date('2019-10-02T00:00:00.000Z') }, + 'split point, but not Rating', ); }); @@ -113,7 +104,6 @@ function renderComponent(props?: Partial<PropsWithoutTheme>) { ]} width={100} zoomSpeed={1} - isLegacy {...props} /> </TooltipProvider>, diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/AdvancedTimeline-test.tsx.snap b/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/AdvancedTimeline-test.tsx.snap index 93c34d14707..b44f642ade9 100644 --- a/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/AdvancedTimeline-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/AdvancedTimeline-test.tsx.snap @@ -1913,171 +1913,7 @@ exports[`should render correctly: no height 1`] = `null`; exports[`should render correctly: no width 1`] = `null`; -exports[`should render correctly: not legacy + split point 1`] = ` -<svg - class="line-chart" - height="100" - width="100" -> - <g - transform="translate(50, 26)" - > - <g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="24" - y2="24" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="16" - y2="16" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="8" - y2="8" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="0" - y2="0" - /> - </g> - </g> - <g - transform="translate(0, 20)" - > - <text - class="line-chart-tick sw-body-sm" - text-anchor="end" - transform="rotate(-35, 15, 24)" - x="15" - y="24" - > - October - </text> - <text - class="line-chart-tick sw-body-sm" - text-anchor="end" - transform="rotate(-35, 20, 24)" - x="20" - y="24" - > - 06 AM - </text> - <text - class="line-chart-tick sw-body-sm" - text-anchor="end" - transform="rotate(-35, 25, 24)" - x="25" - y="24" - > - 12 PM - </text> - <text - class="line-chart-tick sw-body-sm" - text-anchor="end" - transform="rotate(-35, 30, 24)" - x="30" - y="24" - > - 06 PM - </text> - <text - class="line-chart-tick sw-body-sm" - text-anchor="end" - transform="rotate(-35, 35, 24)" - x="35" - y="24" - > - Wed 02 - </text> - <text - class="line-chart-tick sw-body-sm" - text-anchor="end" - transform="rotate(-35, 40, 24)" - x="40" - y="24" - > - 06 AM - </text> - <text - class="line-chart-tick sw-body-sm" - text-anchor="end" - transform="rotate(-35, 45, 24)" - x="45" - y="24" - > - 12 PM - </text> - <text - class="line-chart-tick sw-body-sm" - text-anchor="end" - transform="rotate(-35, 50, 24)" - x="50" - y="24" - > - 06 PM - </text> - </g> - <g> - <path - class="line-chart-path line-chart-path-0" - d="M0,0L20,8" - stroke="rgb(85,170,223)" - stroke-dasharray="0" - /> - <path - class="line-chart-path line-chart-path-1" - d="M40,16Z" - stroke="rgb(58,127,173)" - stroke-dasharray="3" - /> - </g> - <g> - <circle - cx="40" - cy="16" - fill="rgb(58,127,173)" - r="2" - stroke="white" - stroke-width="1" - /> - </g> - <rect - class="chart-mouse-events-overlay" - height="24" - width="40" - /> - <line - class="line-tooltip" - stroke-dasharray="2" - x1="20" - x2="20" - y1="24" - y2="-10" - /> - </g> -</svg> -`; - -exports[`should render correctly: not legacy + split point, but not Rating 1`] = ` +exports[`should render correctly: rating metric 1`] = ` <svg class="line-chart" height="100" @@ -2101,107 +1937,8 @@ exports[`should render correctly: not legacy + split point, but not Rating 1`] = class="line-chart-grid" x1="0" x2="40" - y1="22.4" - y2="22.4" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="20.8" - y2="20.8" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="19.200000000000003" - y2="19.200000000000003" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="17.6" - y2="17.6" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="16" - y2="16" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="14.400000000000002" - y2="14.400000000000002" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="12.800000000000002" - y2="12.800000000000002" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="11.2" - y2="11.2" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="9.600000000000001" - y2="9.600000000000001" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="8" - y2="8" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="6.399999999999999" - y2="6.399999999999999" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="4.800000000000002" - y2="4.800000000000002" + y1="18" + y2="18" /> </g> <g> @@ -2209,8 +1946,8 @@ exports[`should render correctly: not legacy + split point, but not Rating 1`] = class="line-chart-grid" x1="0" x2="40" - y1="3.1999999999999993" - y2="3.1999999999999993" + y1="12" + y2="12" /> </g> <g> @@ -2218,8 +1955,8 @@ exports[`should render correctly: not legacy + split point, but not Rating 1`] = class="line-chart-grid" x1="0" x2="40" - y1="1.6000000000000023" - y2="1.6000000000000023" + y1="6" + y2="6" /> </g> <g> @@ -2311,13 +2048,13 @@ exports[`should render correctly: not legacy + split point, but not Rating 1`] = <g> <path class="line-chart-path line-chart-path-0" - d="M0,16L20,8" + d="M0,0L20,6" stroke="rgb(85,170,223)" stroke-dasharray="0" /> <path class="line-chart-path line-chart-path-1" - d="M40,0Z" + d="M40,12Z" stroke="rgb(58,127,173)" stroke-dasharray="3" /> @@ -2325,7 +2062,7 @@ exports[`should render correctly: not legacy + split point, but not Rating 1`] = <g> <circle cx="40" - cy="0" + cy="12" fill="rgb(58,127,173)" r="2" stroke="white" @@ -2337,19 +2074,11 @@ exports[`should render correctly: not legacy + split point, but not Rating 1`] = height="24" width="40" /> - <line - class="line-tooltip" - stroke-dasharray="2" - x1="20" - x2="20" - y1="24" - y2="-10" - /> </g> </svg> `; -exports[`should render correctly: not legacy 1`] = ` +exports[`should render correctly: selected date 1`] = ` <svg class="line-chart" height="100" @@ -2604,167 +2333,27 @@ exports[`should render correctly: not legacy 1`] = ` stroke-width="1" /> </g> - <rect - class="chart-mouse-events-overlay" - height="24" - width="40" - /> - </g> -</svg> -`; - -exports[`should render correctly: rating metric 1`] = ` -<svg - class="line-chart" - height="100" - width="100" -> - <g - transform="translate(50, 26)" - > <g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="24" - y2="24" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="18" - y2="18" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="12" - y2="12" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="6" - y2="6" - /> - </g> - <g> - <line - class="line-chart-grid" - x1="0" - x2="40" - y1="0" - y2="0" - /> - </g> - </g> - <g - transform="translate(0, 20)" - > - <text - class="line-chart-tick sw-body-sm" - text-anchor="end" - transform="rotate(-35, 15, 24)" - x="15" - y="24" - > - October - </text> - <text - class="line-chart-tick sw-body-sm" - text-anchor="end" - transform="rotate(-35, 20, 24)" - x="20" - y="24" - > - 06 AM - </text> - <text - class="line-chart-tick sw-body-sm" - text-anchor="end" - transform="rotate(-35, 25, 24)" - x="25" - y="24" - > - 12 PM - </text> - <text - class="line-chart-tick sw-body-sm" - text-anchor="end" - transform="rotate(-35, 30, 24)" - x="30" - y="24" - > - 06 PM - </text> - <text - class="line-chart-tick sw-body-sm" - text-anchor="end" - transform="rotate(-35, 35, 24)" - x="35" - y="24" - > - Wed 02 - </text> - <text - class="line-chart-tick sw-body-sm" - text-anchor="end" - transform="rotate(-35, 40, 24)" - x="40" - y="24" - > - 06 AM - </text> - <text - class="line-chart-tick sw-body-sm" - text-anchor="end" - transform="rotate(-35, 45, 24)" - x="45" - y="24" - > - 12 PM - </text> - <text - class="line-chart-tick sw-body-sm" - text-anchor="end" - transform="rotate(-35, 50, 24)" - x="50" - y="24" - > - 06 PM - </text> - </g> - <g> - <path - class="line-chart-path line-chart-path-0" - d="M0,0L20,6" - stroke="rgb(85,170,223)" - stroke-dasharray="0" + <line + class="line-tooltip" + x1="0" + x2="0" + y1="24" + y2="0" /> - <path - class="line-chart-path line-chart-path-1" - d="M40,12Z" - stroke="rgb(58,127,173)" - stroke-dasharray="3" + <circle + cx="0" + cy="16" + fill="rgb(85,170,223)" + r="4" + stroke="white" + stroke-width="1" /> - </g> - <g> <circle - cx="40" - cy="12" + cx="0" + cy="0" fill="rgb(58,127,173)" - r="2" + r="4" stroke="white" stroke-width="1" /> @@ -2778,7 +2367,7 @@ exports[`should render correctly: rating metric 1`] = ` </svg> `; -exports[`should render correctly: selected date 1`] = ` +exports[`should render correctly: split point, but not Rating 1`] = ` <svg class="line-chart" height="100" @@ -3033,36 +2622,19 @@ exports[`should render correctly: selected date 1`] = ` stroke-width="1" /> </g> - <g> - <line - class="line-tooltip" - x1="0" - x2="0" - y1="24" - y2="0" - /> - <circle - cx="0" - cy="16" - fill="rgb(85,170,223)" - r="4" - stroke="white" - stroke-width="1" - /> - <circle - cx="0" - cy="0" - fill="rgb(58,127,173)" - r="4" - stroke="white" - stroke-width="1" - /> - </g> <rect class="chart-mouse-events-overlay" height="24" width="40" /> + <line + class="line-tooltip" + stroke-dasharray="2" + x1="20" + x2="20" + y1="24" + y2="-10" + /> </g> </svg> `; diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/Facet.tsx b/server/sonar-web/src/main/js/components/facets/Facet.tsx index 2f594df563f..0273f0f4739 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/Facet.tsx +++ b/server/sonar-web/src/main/js/components/facets/Facet.tsx @@ -23,14 +23,15 @@ import { orderBy, sortBy, without } from 'lodash'; import * as React from 'react'; import { formatMeasure } from '~sonar-aligned/helpers/measures'; import { MetricType } from '~sonar-aligned/types/metrics'; -import Tooltip from '../../../components/controls/Tooltip'; -import { translate } from '../../../helpers/l10n'; -import { Dict } from '../../../types/types'; -import { FacetItemsList } from '../../issues/sidebar/FacetItemsList'; -import { MultipleSelectionHint } from '../../issues/sidebar/MultipleSelectionHint'; -import { FacetKey } from '../query'; +import { FacetKey } from '../../apps/coding-rules/query'; +import { FacetItemsList } from '../../apps/issues/sidebar/FacetItemsList'; +import { MultipleSelectionHint } from '../../apps/issues/sidebar/MultipleSelectionHint'; +import { translate } from '../../helpers/l10n'; +import { Dict } from '../../types/types'; +import Tooltip from '../controls/Tooltip'; export interface BasicProps { + fetching?: boolean; help?: React.ReactNode; onChange: (changes: Dict<string | string[] | undefined>) => void; onToggle: (facet: FacetKey) => void; @@ -103,6 +104,7 @@ export default class Facet extends React.PureComponent<Props> { stats, help, values, + fetching, } = this.props; const items = this.props.options || @@ -119,10 +121,11 @@ export default class Facet extends React.PureComponent<Props> { return ( <FacetBox - className={classNames('it__search-navigator-facet-box', { + className={classNames('it__search-navigator-facet-box it__search-navigator-facet-header', { 'it__search-navigator-facet-box-forbidden': disabled, })} data-property={property} + loading={fetching} clearIconLabel={translate('clear')} count={values.length} id={headerId} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/SeverityFacet.tsx b/server/sonar-web/src/main/js/components/facets/SeverityFacet.tsx index 6acfbcc3871..3f7cae9eca6 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/SeverityFacet.tsx +++ b/server/sonar-web/src/main/js/components/facets/SeverityFacet.tsx @@ -18,28 +18,31 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { HelperHintIcon } from 'design-system'; +import { Popover } from '@sonarsource/echoes-react'; +import { BareButton, HelperHintIcon } from 'design-system'; import * as React from 'react'; -import DocHelpTooltip from '~sonar-aligned/components/controls/DocHelpTooltip'; -import SoftwareImpactSeverityIcon from '../../../components/icon-mappers/SoftwareImpactSeverityIcon'; -import { IMPACT_SEVERITIES } from '../../../helpers/constants'; -import { DocLink } from '../../../helpers/doc-links'; -import { translate } from '../../../helpers/l10n'; +import { useIntl } from 'react-intl'; +import { IMPACT_SEVERITIES } from '../../helpers/constants'; +import { DocLink } from '../../helpers/doc-links'; +import { translate } from '../../helpers/l10n'; +import DocumentationLink from '../common/DocumentationLink'; +import SoftwareImpactSeverityIcon from '../icon-mappers/SoftwareImpactSeverityIcon'; import Facet, { BasicProps } from './Facet'; -export default function SeverityFacet(props: BasicProps) { +export default function SeverityFacet(props: Readonly<BasicProps>) { + const intl = useIntl(); const renderName = React.useCallback( (severity: string, disabled: boolean) => ( <div className="sw-flex sw-items-center"> <SoftwareImpactSeverityIcon severity={severity} disabled={disabled} /> - <span className="sw-ml-1">{translate('severity', severity)}</span> + <span className="sw-ml-1">{translate('severity_impact', severity)}</span> </div> ), [], ); const renderTextName = React.useCallback( - (severity: string) => translate('severity', severity), + (severity: string) => translate('severity_impact', severity), [], ); @@ -51,23 +54,24 @@ export default function SeverityFacet(props: BasicProps) { renderName={renderName} renderTextName={renderTextName} help={ - <DocHelpTooltip - placement="right" - content={ + <Popover + title={intl.formatMessage({ id: 'severity_impact.levels' })} + description={ <> - <p>{translate('issues.facet.impactSeverities.help.line1')}</p> - <p className="sw-mt-2">{translate('issues.facet.impactSeverities.help.line2')}</p> + <p>{intl.formatMessage({ id: 'severity_impact.help.line1' })}</p> + <p className="sw-mt-2">{intl.formatMessage({ id: 'severity_impact.help.line2' })}</p> </> } - links={[ - { - href: DocLink.CleanCodeIntroduction, - label: translate('learn_more'), - }, - ]} + footer={ + <DocumentationLink to={DocLink.CleanCodeIntroduction}> + {intl.formatMessage({ id: 'learn_more' })} + </DocumentationLink> + } > - <HelperHintIcon /> - </DocHelpTooltip> + <BareButton aria-label={intl.formatMessage({ id: 'more_information' })}> + <HelperHintIcon /> + </BareButton> + </Popover> } /> ); diff --git a/server/sonar-web/src/main/js/components/icon-mappers/SoftwareImpactSeverityIcon.tsx b/server/sonar-web/src/main/js/components/icon-mappers/SoftwareImpactSeverityIcon.tsx index 61251a5db8c..9d1cebd666b 100644 --- a/server/sonar-web/src/main/js/components/icon-mappers/SoftwareImpactSeverityIcon.tsx +++ b/server/sonar-web/src/main/js/components/icon-mappers/SoftwareImpactSeverityIcon.tsx @@ -19,7 +19,9 @@ */ import { IconProps, + SoftwareImpactSeverityBlockerIcon, SoftwareImpactSeverityHighIcon, + SoftwareImpactSeverityInfoIcon, SoftwareImpactSeverityLowIcon, SoftwareImpactSeverityMediumIcon, } from 'design-system'; @@ -33,10 +35,14 @@ interface Props extends IconProps { severity: string | null | undefined; } +const defaultIconSize = 14; + const severityIcons: Dict<(props: IconProps) => React.ReactElement> = { + [SoftwareImpactSeverity.Blocker]: SoftwareImpactSeverityBlockerIcon, [SoftwareImpactSeverity.High]: SoftwareImpactSeverityHighIcon, [SoftwareImpactSeverity.Medium]: SoftwareImpactSeverityMediumIcon, [SoftwareImpactSeverity.Low]: SoftwareImpactSeverityLowIcon, + [SoftwareImpactSeverity.Info]: SoftwareImpactSeverityInfoIcon, }; export default function SoftwareImpactSeverityIcon({ severity, ...iconProps }: Readonly<Props>) { @@ -45,5 +51,12 @@ export default function SoftwareImpactSeverityIcon({ severity, ...iconProps }: R } const DesiredIcon = severityIcons[severity]; - return <DesiredIcon {...iconProps} aria-label={translate('severity', severity)} />; + return ( + <DesiredIcon + {...iconProps} + width={iconProps?.width ?? defaultIconSize} + height={iconProps?.height ?? defaultIconSize} + aria-label={translate('severity_impact', severity)} + /> + ); } diff --git a/server/sonar-web/src/main/js/components/measure/utils.ts b/server/sonar-web/src/main/js/components/measure/utils.ts index 60e1437baf7..f37449d3e98 100644 --- a/server/sonar-web/src/main/js/components/measure/utils.ts +++ b/server/sonar-web/src/main/js/components/measure/utils.ts @@ -28,7 +28,6 @@ export const KNOWN_RATINGS = [ MetricKey.software_quality_maintainability_rating, MetricKey.software_quality_reliability_rating, MetricKey.software_quality_security_rating, - MetricKey.software_quality_security_review_rating, 'maintainability_rating', // Needed to provide the label for "new_maintainability_rating" ]; diff --git a/server/sonar-web/src/main/js/components/shared/CleanCodeAttributePill.tsx b/server/sonar-web/src/main/js/components/shared/CleanCodeAttributePill.tsx index 20ec0c2b7df..9e386598774 100644 --- a/server/sonar-web/src/main/js/components/shared/CleanCodeAttributePill.tsx +++ b/server/sonar-web/src/main/js/components/shared/CleanCodeAttributePill.tsx @@ -18,12 +18,13 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { Pill } from 'design-system'; +import { Popover } from '@sonarsource/echoes-react'; +import { Pill, PillVariant } from 'design-system'; import React from 'react'; -import DocHelpTooltip from '~sonar-aligned/components/controls/DocHelpTooltip'; import { DocLink } from '../../helpers/doc-links'; import { translate } from '../../helpers/l10n'; import { CleanCodeAttribute, CleanCodeAttributeCategory } from '../../types/clean-code-taxonomy'; +import DocumentationLink from '../common/DocumentationLink'; export interface Props { className?: string; @@ -36,35 +37,26 @@ export function CleanCodeAttributePill(props: Readonly<Props>) { const { className, cleanCodeAttributeCategory, cleanCodeAttribute, type = 'issue' } = props; return ( - <DocHelpTooltip - content={ - <> - <p className="sw-mb-4"> - {translate( - type, - cleanCodeAttribute ? 'clean_code_attribute' : 'clean_code_attribute_category', - cleanCodeAttribute ?? cleanCodeAttributeCategory, - 'title', - )} - </p> - <p> - {translate( - 'issue', - cleanCodeAttribute ? 'clean_code_attribute' : 'clean_code_attribute_category', - cleanCodeAttribute ?? cleanCodeAttributeCategory, - 'advice', - )} - </p> - </> + <Popover + title={translate( + type, + cleanCodeAttribute ? 'clean_code_attribute' : 'clean_code_attribute_category', + cleanCodeAttribute ?? cleanCodeAttributeCategory, + 'title', + )} + description={translate( + 'issue', + cleanCodeAttribute ? 'clean_code_attribute' : 'clean_code_attribute_category', + cleanCodeAttribute ?? cleanCodeAttributeCategory, + 'advice', + )} + footer={ + <DocumentationLink to={DocLink.CleanCodeIntroduction}> + {translate('learn_more')} + </DocumentationLink> } - links={[ - { - href: DocLink.CleanCodeIntroduction, - label: translate('learn_more'), - }, - ]} > - <Pill variant="accent" data-guiding-id="issue-1" className={className}> + <Pill variant={PillVariant.Accent} data-guiding-id="issue-1" className={className}> <span className="sw-font-semibold"> {translate(type, 'clean_code_attribute_category', cleanCodeAttributeCategory)} </span> @@ -72,6 +64,6 @@ export function CleanCodeAttributePill(props: Readonly<Props>) { <span> | {translate(type, 'clean_code_attribute', cleanCodeAttribute)}</span> )} </Pill> - </DocHelpTooltip> + </Popover> ); } diff --git a/server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx b/server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx index f0ae5848ac2..88b531e6232 100644 --- a/server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx +++ b/server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx @@ -18,14 +18,16 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { Popover } from '@sonarsource/echoes-react'; import classNames from 'classnames'; -import { Pill } from 'design-system'; +import { Pill, PillVariant } from 'design-system'; +import { noop } from 'lodash'; import React from 'react'; import { FormattedMessage } from 'react-intl'; -import DocHelpTooltip from '~sonar-aligned/components/controls/DocHelpTooltip'; import { DocLink } from '../../helpers/doc-links'; import { translate } from '../../helpers/l10n'; import { SoftwareImpactSeverity } from '../../types/clean-code-taxonomy'; +import DocumentationLink from '../common/DocumentationLink'; import SoftwareImpactSeverityIcon from '../icon-mappers/SoftwareImpactSeverityIcon'; export interface Props { @@ -39,34 +41,50 @@ export default function SoftwareImpactPill(props: Props) { const { className, severity, quality, type = 'issue' } = props; const variant = { - [SoftwareImpactSeverity.High]: 'danger', - [SoftwareImpactSeverity.Medium]: 'warning', - [SoftwareImpactSeverity.Low]: 'info', - }[severity] as 'danger' | 'warning' | 'info'; + [SoftwareImpactSeverity.Blocker]: PillVariant.Critical, + [SoftwareImpactSeverity.High]: PillVariant.Danger, + [SoftwareImpactSeverity.Medium]: PillVariant.Warning, + [SoftwareImpactSeverity.Low]: PillVariant.Caution, + [SoftwareImpactSeverity.Info]: PillVariant.Info, + }[severity]; return ( - <DocHelpTooltip - content={ - <FormattedMessage - id={`${type}.impact.severity.tooltip`} - defaultMessage={translate(`${type}.impact.severity.tooltip`)} - values={{ - severity: translate('severity', severity).toLowerCase(), - quality: quality.toLowerCase(), - }} - /> + <Popover + title={translate('severity_impact.title')} + description={ + <> + <FormattedMessage + id={`${type}.impact.severity.tooltip`} + values={{ + severity: translate('severity_impact', severity).toLowerCase(), + quality: quality.toLowerCase(), + }} + /> + <p className="sw-mt-2"> + <span className="sw-mr-1">{translate('severity_impact.help.line1')}</span> + {translate('severity_impact.help.line2')} + </p> + </> + } + footer={ + <DocumentationLink to={DocLink.CleanCodeIntroduction}> + {translate('learn_more')} + </DocumentationLink> } - links={[ - { - href: DocLink.CleanCodeIntroduction, - label: translate('learn_more'), - }, - ]} > - <Pill className={classNames('sw-flex sw-gap-1 sw-items-center', className)} variant={variant}> + <Pill + className={classNames('sw-flex sw-gap-1 sw-items-center', className)} + onClick={noop} + variant={variant} + > {quality} - <SoftwareImpactSeverityIcon severity={severity} data-guiding-id="issue-3" /> + <SoftwareImpactSeverityIcon + width={14} + height={14} + severity={severity} + data-guiding-id="issue-3" + /> </Pill> - </DocHelpTooltip> + </Popover> ); } diff --git a/server/sonar-web/src/main/js/components/shared/SoftwareImpactPillList.tsx b/server/sonar-web/src/main/js/components/shared/SoftwareImpactPillList.tsx index 3bc09b6b130..476e9cd98bf 100644 --- a/server/sonar-web/src/main/js/components/shared/SoftwareImpactPillList.tsx +++ b/server/sonar-web/src/main/js/components/shared/SoftwareImpactPillList.tsx @@ -34,9 +34,11 @@ interface SoftwareImpactPillListProps extends React.HTMLAttributes<HTMLUListElem } const severityMap = { - [SoftwareImpactSeverity.High]: 2, - [SoftwareImpactSeverity.Medium]: 1, - [SoftwareImpactSeverity.Low]: 0, + [SoftwareImpactSeverity.Blocker]: 4, + [SoftwareImpactSeverity.High]: 3, + [SoftwareImpactSeverity.Medium]: 2, + [SoftwareImpactSeverity.Low]: 1, + [SoftwareImpactSeverity.Info]: 0, }; export default function SoftwareImpactPillList({ diff --git a/server/sonar-web/src/main/js/helpers/activity-graph.ts b/server/sonar-web/src/main/js/helpers/activity-graph.ts index 6d7ebd1b895..62c3e9d46c4 100644 --- a/server/sonar-web/src/main/js/helpers/activity-graph.ts +++ b/server/sonar-web/src/main/js/helpers/activity-graph.ts @@ -68,10 +68,7 @@ export const mergeRatingMeasureHistory = ( const historyMapper = (historyItem: { date: string; value?: string }) => ({ date: parseDateFn(historyItem.date), - value: - softwareQualityMeasuresMap.size > 0 && historyItem.value === '5.0' - ? '4.0' - : historyItem.value, + value: historyItem.value, }); return historyDataFiltered.map((measure) => { diff --git a/server/sonar-web/src/main/js/helpers/constants.ts b/server/sonar-web/src/main/js/helpers/constants.ts index ce344a72320..2e70556dfaa 100644 --- a/server/sonar-web/src/main/js/helpers/constants.ts +++ b/server/sonar-web/src/main/js/helpers/constants.ts @@ -112,11 +112,9 @@ export const LEAK_OLD_TAXONOMY_METRICS = [ ]; export const OLD_TAXONOMY_RATINGS = [ - MetricKey.releasability_rating, MetricKey.sqale_rating, MetricKey.security_rating, MetricKey.reliability_rating, - MetricKey.security_review_rating, MetricKey.sqale_index, MetricKey.reliability_remediation_effort, MetricKey.security_remediation_effort, @@ -128,7 +126,6 @@ export const LEAK_OLD_TAXONOMY_RATINGS = [ MetricKey.new_maintainability_rating, MetricKey.new_security_rating, MetricKey.new_reliability_rating, - MetricKey.new_security_review_rating, MetricKey.new_technical_debt, MetricKey.new_security_remediation_effort, MetricKey.new_reliability_remediation_effort, @@ -191,9 +188,6 @@ export const DEPRECATED_ACTIVITY_METRICS = [ ]; export const SOFTWARE_QUALITY_RATING_METRICS_MAP: Record<string, MetricKey> = { - [MetricKey.releasability_rating]: MetricKey.software_quality_releasability_rating, - [MetricKey.releasability_rating_distribution]: - MetricKey.software_quality_releasability_rating_distribution, [MetricKey.sqale_rating]: MetricKey.software_quality_maintainability_rating, [MetricKey.maintainability_rating_distribution]: MetricKey.software_quality_maintainability_rating_distribution, @@ -202,9 +196,6 @@ export const SOFTWARE_QUALITY_RATING_METRICS_MAP: Record<string, MetricKey> = { [MetricKey.reliability_rating]: MetricKey.software_quality_reliability_rating, [MetricKey.reliability_rating_distribution]: MetricKey.software_quality_reliability_rating_distribution, - [MetricKey.security_review_rating]: MetricKey.software_quality_security_review_rating, - [MetricKey.security_review_rating_distribution]: - MetricKey.software_quality_security_review_rating_distribution, [MetricKey.reliability_remediation_effort]: MetricKey.software_quality_reliability_remediation_effort, [MetricKey.security_remediation_effort]: MetricKey.software_quality_security_remediation_effort, @@ -214,20 +205,14 @@ export const SOFTWARE_QUALITY_RATING_METRICS_MAP: Record<string, MetricKey> = { MetricKey.effort_to_reach_software_quality_maintainability_rating_a, [MetricKey.last_change_on_maintainability_rating]: MetricKey.last_change_on_software_quality_maintainability_rating, - [MetricKey.last_change_on_releasability_rating]: - MetricKey.last_change_on_software_quality_releasability_rating, [MetricKey.last_change_on_reliability_rating]: MetricKey.last_change_on_software_quality_reliability_rating, [MetricKey.last_change_on_security_rating]: MetricKey.last_change_on_software_quality_security_rating, - [MetricKey.last_change_on_security_review_rating]: - MetricKey.last_change_on_software_quality_security_review_rating, [MetricKey.maintainability_rating_effort]: MetricKey.software_quality_maintainability_rating_effort, [MetricKey.reliability_rating_effort]: MetricKey.software_quality_reliability_rating_effort, [MetricKey.security_rating_effort]: MetricKey.software_quality_security_rating_effort, - [MetricKey.security_review_rating_effort]: - MetricKey.software_quality_security_review_rating_effort, [MetricKey.new_maintainability_rating]: MetricKey.new_software_quality_maintainability_rating, [MetricKey.new_maintainability_rating_distribution]: MetricKey.new_software_quality_maintainability_rating_distribution, @@ -237,9 +222,6 @@ export const SOFTWARE_QUALITY_RATING_METRICS_MAP: Record<string, MetricKey> = { [MetricKey.new_reliability_rating]: MetricKey.new_software_quality_reliability_rating, [MetricKey.new_reliability_rating_distribution]: MetricKey.new_software_quality_reliability_rating_distribution, - [MetricKey.new_security_review_rating]: MetricKey.new_software_quality_security_review_rating, - [MetricKey.new_security_review_rating_distribution]: - MetricKey.new_software_quality_security_review_rating_distribution, [MetricKey.new_technical_debt]: MetricKey.new_software_quality_maintainability_remediation_effort, [MetricKey.new_reliability_remediation_effort]: MetricKey.new_software_quality_reliability_remediation_effort, @@ -249,11 +231,9 @@ export const SOFTWARE_QUALITY_RATING_METRICS_MAP: Record<string, MetricKey> = { }; export const SOFTWARE_QUALITY_RATING_METRICS = [ - MetricKey.software_quality_releasability_rating, MetricKey.software_quality_maintainability_rating, MetricKey.software_quality_security_rating, MetricKey.software_quality_reliability_rating, - MetricKey.software_quality_security_review_rating, MetricKey.software_quality_maintainability_remediation_effort, MetricKey.software_quality_reliability_remediation_effort, MetricKey.software_quality_security_remediation_effort, @@ -262,7 +242,6 @@ export const SOFTWARE_QUALITY_RATING_METRICS = [ MetricKey.new_software_quality_maintainability_rating, MetricKey.new_software_quality_security_rating, MetricKey.new_software_quality_reliability_rating, - MetricKey.new_software_quality_security_review_rating, MetricKey.new_software_quality_maintainability_remediation_effort, MetricKey.new_software_quality_reliability_remediation_effort, MetricKey.new_software_quality_security_remediation_effort, diff --git a/server/sonar-web/src/main/js/sonar-aligned/types/metrics.ts b/server/sonar-web/src/main/js/sonar-aligned/types/metrics.ts index d88e3fcf552..7ce4c1604ed 100644 --- a/server/sonar-web/src/main/js/sonar-aligned/types/metrics.ts +++ b/server/sonar-web/src/main/js/sonar-aligned/types/metrics.ts @@ -69,10 +69,8 @@ export enum MetricKey { last_change_on_security_rating = 'last_change_on_security_rating', last_change_on_security_review_rating = 'last_change_on_security_review_rating', last_change_on_software_quality_maintainability_rating = 'last_change_on_software_quality_maintainability_rating', - last_change_on_software_quality_releasability_rating = 'last_change_on_software_quality_releasability_rating', last_change_on_software_quality_reliability_rating = 'last_change_on_software_quality_reliability_rating', last_change_on_software_quality_security_rating = 'last_change_on_software_quality_security_rating', - last_change_on_software_quality_security_review_rating = 'last_change_on_software_quality_security_review_rating', last_commit_date = 'last_commit_date', leak_projects = 'leak_projects', line_coverage = 'line_coverage', @@ -129,9 +127,7 @@ export enum MetricKey { new_security_remediation_effort = 'new_security_remediation_effort', new_software_quality_security_remediation_effort = 'new_software_quality_security_remediation_effort', new_security_review_rating = 'new_security_review_rating', - new_software_quality_security_review_rating = 'new_software_quality_security_review_rating', new_security_review_rating_distribution = 'new_security_review_rating_distribution', - new_software_quality_security_review_rating_distribution = 'new_software_quality_security_review_rating_distribution', new_sqale_debt_ratio = 'new_sqale_debt_ratio', new_software_quality_maintainability_debt_ratio = 'new_software_quality_maintainability_debt_ratio', new_technical_debt = 'new_technical_debt', @@ -152,9 +148,7 @@ export enum MetricKey { quality_profiles = 'quality_profiles', releasability_effort = 'releasability_effort', releasability_rating = 'releasability_rating', - software_quality_releasability_rating = 'software_quality_releasability_rating', releasability_rating_distribution = 'releasability_rating_distribution', - software_quality_releasability_rating_distribution = 'software_quality_releasability_rating_distribution', reliability_issues = 'reliability_issues', reliability_rating = 'reliability_rating', software_quality_reliability_rating = 'software_quality_reliability_rating', @@ -177,11 +171,8 @@ export enum MetricKey { security_remediation_effort = 'security_remediation_effort', software_quality_security_remediation_effort = 'software_quality_security_remediation_effort', security_review_rating = 'security_review_rating', - software_quality_security_review_rating = 'software_quality_security_review_rating', security_review_rating_distribution = 'security_review_rating_distribution', - software_quality_security_review_rating_distribution = 'software_quality_security_review_rating_distribution', security_review_rating_effort = 'security_review_rating_effort', - software_quality_security_review_rating_effort = 'software_quality_security_review_rating_effort', skipped_tests = 'skipped_tests', sonarjava_feedback = 'sonarjava_feedback', sqale_debt_ratio = 'sqale_debt_ratio', diff --git a/server/sonar-web/src/main/js/types/clean-code-taxonomy.ts b/server/sonar-web/src/main/js/types/clean-code-taxonomy.ts index 13bea9be601..d5a80809f20 100644 --- a/server/sonar-web/src/main/js/types/clean-code-taxonomy.ts +++ b/server/sonar-web/src/main/js/types/clean-code-taxonomy.ts @@ -18,9 +18,11 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ export enum SoftwareImpactSeverity { + Blocker = 'BLOCKER', High = 'HIGH', Medium = 'MEDIUM', Low = 'LOW', + Info = 'INFO', } export enum CleanCodeAttributeCategory { @@ -60,7 +62,9 @@ export interface SoftwareImpact { export interface SoftwareImpactMeasureData { total: number; + [SoftwareImpactSeverity.Blocker]: number; [SoftwareImpactSeverity.High]: number; [SoftwareImpactSeverity.Medium]: number; [SoftwareImpactSeverity.Low]: number; + [SoftwareImpactSeverity.Info]: number; } diff --git a/server/sonar-web/src/main/js/types/jest.d.ts b/server/sonar-web/src/main/js/types/jest.d.ts index 7f43b94e89a..8db287d8628 100644 --- a/server/sonar-web/src/main/js/types/jest.d.ts +++ b/server/sonar-web/src/main/js/types/jest.d.ts @@ -19,6 +19,7 @@ */ declare namespace jest { interface Matchers<R> { + toHaveAPopoverWithContent(content: string): Promise<CustomMatcherResult>; toHaveATooltipWithContent(content: string): Promise<CustomMatcherResult>; toHaveNoA11yViolations(): Promise<CustomMatcherResult>; } |