Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@layer components {
.header {
padding: var(--bui-space-4) var(--bui-space-4) var(--bui-space-4) var(--bui-space-5);
}

.action {
margin: 0;
}

.lastAnalyzed {
color: var(--bui-fg-secondary);
padding: var(--bui-space-2) 0;
}
}
Comment on lines +1 to +14
5 changes: 5 additions & 0 deletions workspaces/sonarqube/.changeset/eighty-baboons-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@backstage-community/plugin-sonarqube': major
---

migrated the @backstage-community/plugin-sonarqube plugin from Material-UI (MUI) v4 to Backstage UI (BUI) v1.7.0, eliminating deprecated Material-UI dependencies while improving theming consistency and reducing bundle size.
6 changes: 3 additions & 3 deletions workspaces/sonarqube/plugins/sonarqube/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@
"@backstage/frontend-app-api": "backstage:^",
"@backstage/frontend-plugin-api": "backstage:^",
"@backstage/plugin-catalog-react": "backstage:^",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/styles": "^4.10.0",
"@backstage/ui": "^1.7.0",
"@remixicon/react": "^4.3.0",
"react-aria-components": "^1.4.0",
"@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0",
"cross-fetch": "^4.0.0",
"luxon": "^3.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@layer components {
.badgeLabel {
color: var(--bui-fg-primary);
}

.badgeCompact {
margin: 0;
height: 28px;
}

.badgeError {
background-color: var(--bui-status-error);
}

.badgeSuccess {
background-color: var(--bui-status-ok);
}

.badgeUnknown {
background-color: var(--bui-bg-surface-3);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@
* limitations under the License.
*/
import { useMemo } from 'react';
import Chip from '@material-ui/core/Chip';
import { makeStyles } from '@material-ui/core/styles';
import { Tag, Text } from '@backstage/ui';
import { TooltipTrigger, Tooltip } from 'react-aria-components';
import {
RiBugLine,
RiLockLine,
RiLockUnlockLine,
RiCheckLine,
RiAlertLine,
RiShieldLine,
RiExternalLinkLine,
} from '@remixicon/react';
import { defaultDuplicationRatings } from '../SonarQubeTable/types';
import BugReport from '@material-ui/icons/BugReport';
import Lock from '@material-ui/icons/Lock';
import Typography from '@material-ui/core/Typography';
import LockOpen from '@material-ui/icons/LockOpen';
import SentimentVeryDissatisfied from '@material-ui/icons/SentimentVeryDissatisfied';
import SentimentVerySatisfied from '@material-ui/icons/SentimentVerySatisfied';
import Security from '@material-ui/icons/Security';
import { DateTime } from 'luxon';
import { Percentage } from './Percentage';
import { Rating } from './Rating';
Expand All @@ -32,8 +34,7 @@ import { Value } from './Value';
import { FindingSummary } from '@backstage-community/plugin-sonarqube-react';
import { useTranslationRef } from '@backstage/frontend-plugin-api';
import { sonarqubeTranslationRef } from '../../translation';
import Tooltip from '@material-ui/core/Tooltip';
import LinkIcon from '@material-ui/icons/Link';
import styles from './MetricInsights.module.css';

type MetricInsightsProps = {
value: FindingSummary | any;
Expand All @@ -42,71 +43,55 @@ type MetricInsightsProps = {
sonarQubeComponentKey?: string;
};

const useStyles = makeStyles(theme => ({
badgeLabel: {
color: theme.palette.common.white,
},
badgeCompact: {
margin: 0,
height: 28,
},
badgeError: {
backgroundColor: theme.palette.error.main,
},
badgeSuccess: {
backgroundColor: theme.palette.success.main,
},
badgeUnknown: {
backgroundColor: theme.palette.grey[500],
},
}));

export const QualityBadge = (props: MetricInsightsProps) => {
const classes = useStyles();
const { t } = useTranslationRef(sonarqubeTranslationRef);

const { value } = props;
let gateLabel: string = t('sonarQubeCard.qualityBadgeLabel.notComputed');
let gateColor = classes.badgeUnknown;
let badgeClass = styles.badgeUnknown;
let gateLinkToolTip = '';

if (value?.metrics.alert_status) {
const gatePassed = value?.metrics.alert_status === 'OK';
gateLabel = gatePassed
? t('sonarQubeCard.qualityBadgeLabel.gatePassed')
: t('sonarQubeCard.qualityBadgeLabel.gateFailed');
gateColor = gatePassed ? classes.badgeSuccess : classes.badgeError;
badgeClass = gatePassed ? styles.badgeSuccess : styles.badgeError;
}
let clickableAttrs = {};

if (value.projectUrl) {
gateLinkToolTip = t('sonarQubeCard.qualityBadgeTooltip');
clickableAttrs = {
component: 'a',
href: value.projectUrl,
target: '_blank',
rel: 'noopener noreferrer',
clickable: true,
};
}

const qualityBadge = (
<Tooltip title={gateLinkToolTip}>
<Chip
label={gateLabel}
{...clickableAttrs}
className={props.compact ? classes.badgeCompact : ''}
classes={{ root: gateColor, label: classes.badgeLabel }}
icon={value.projectUrl ? <LinkIcon /> : undefined}
/>
</Tooltip>
<Tag
className={`${badgeClass} ${props.compact ? styles.badgeCompact : ''}`}
>
{value.projectUrl ? <RiExternalLinkLine size={16} /> : null}
{gateLabel}
</Tag>
);

if (!value.projectUrl) {
return qualityBadge;
}

return (
<TooltipTrigger>
<a href={value.projectUrl} target="_blank" rel="noopener noreferrer">
{qualityBadge}
</a>
<Tooltip>{gateLinkToolTip}</Tooltip>
</TooltipTrigger>
);
Comment on lines 50 to 86
return qualityBadge;
};

export const BugReportRatingCard = (props: MetricInsightsProps) => {
const { value, title } = props;
return (
<RatingCard
compact={props.compact}
titleIcon={<BugReport />}
titleIcon={<RiBugLine size={20} />}
title={title}
link={value.getIssuesUrl('BUG')}
leftSlot={<Value value={value.metrics.bugs} compact={props.compact} />}
Expand All @@ -121,7 +106,11 @@ export const VulnerabilitiesRatingCard = (props: MetricInsightsProps) => {
<RatingCard
compact={props.compact}
titleIcon={
value.metrics.vulnerabilities === '0' ? <Lock /> : <LockOpen />
value.metrics.vulnerabilities === '0' ? (
<RiLockLine size={20} />
) : (
<RiLockUnlockLine size={20} />
)
}
title={title}
link={value.getIssuesUrl('VULNERABILITY')}
Expand All @@ -140,9 +129,9 @@ export const CodeSmellsRatingCard = (props: MetricInsightsProps) => {
compact={props.compact}
titleIcon={
value.metrics.code_smells === '0' ? (
<SentimentVerySatisfied />
<RiCheckLine size={20} />
) : (
<SentimentVeryDissatisfied />
<RiAlertLine size={20} />
)
}
title={title}
Expand All @@ -161,7 +150,7 @@ export const HotspotsReviewed = (props: MetricInsightsProps) => {
value.metrics.security_review_rating && (
<RatingCard
compact={props.compact}
titleIcon={<Security />}
titleIcon={<RiShieldLine size={20} />}
title={title}
link={value.getSecurityHotspotsUrl()}
leftSlot={
Expand Down Expand Up @@ -249,13 +238,13 @@ export const NoSonarQubeCard = (props: MetricInsightsProps) => {
const { value, sonarQubeComponentKey } = props;
const { t } = useTranslationRef(sonarqubeTranslationRef);
return (
<Typography color="textSecondary" variant="subtitle2">
<Text color="secondary" variant="body-small">
{value?.isSonarQubeAnnotationEnabled &&
t('sonarQubeCard.noSonarQubeError.hasAnnotation', {
project: sonarQubeComponentKey || '',
})}
{!value?.isSonarQubeAnnotationEnabled &&
t('sonarQubeCard.noSonarQubeError.noAnnotation')}
</Typography>
</Text>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@layer components {
:root {
--sonarqube-percentage-ok: var(--bui-status-ok, #1db679);
--sonarqube-percentage-error: var(--bui-status-error, #e82c3c);
}

.root {
height: var(--bui-space-6);
width: var(--bui-space-6);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,32 @@
* limitations under the License.
*/

import { makeStyles } from '@material-ui/core/styles';
import { useTheme } from '@material-ui/core/styles';
import { Circle } from 'rc-progress';

const useStyles = makeStyles(theme => ({
root: {
height: theme.spacing(3),
width: theme.spacing(3),
},
}));
import styles from './Percentage.module.css';

export const Percentage = ({ value }: { value?: string }) => {
const classes = useStyles();
const theme = useTheme();
// Use theme tokens from CSS variables, fallback to SonarQube default colors
const getStyleValue = (propertyName: string, fallback: string): string => {
if (typeof window === 'undefined') return fallback;
const colorValue = window
.getComputedStyle(document.documentElement)
.getPropertyValue(propertyName)
.trim();
return colorValue || fallback;
};

const okColor = getStyleValue('--sonarqube-percentage-ok', '#1db679');
const errorColor = getStyleValue('--sonarqube-percentage-error', '#e82c3c');

return (
<Circle
strokeLinecap="butt"
percent={+(value || 0)}
strokeWidth={16}
strokeColor={theme.palette.status.ok}
trailColor={theme.palette.status.error}
strokeColor={okColor}
trailColor={errorColor}
Comment on lines +39 to +40
trailWidth={16}
className={classes.root}
className={styles.root}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
@layer components {
:root {
--sonarqube-rating-a: var(--bui-status-ok, #1db679);
--sonarqube-rating-b: #a3d566;
--sonarqube-rating-c: var(--bui-status-pending, #f2cc0f);
--sonarqube-rating-d: var(--bui-status-warning, #f88d3d);
--sonarqube-rating-e: var(--bui-status-error, #e82c3c);
}

.ratingDefault {
height: var(--bui-space-6);
width: var(--bui-space-6);
color: var(--bui-fg-primary);
background-color: var(--bui-bg-surface-3);
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--bui-radius-2);
font-weight: 600;
}

.ratingA {
height: var(--bui-space-6);
width: var(--bui-space-6);
color: var(--bui-white);
background-color: var(--sonarqube-rating-a);
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--bui-radius-2);
font-weight: 600;
}
Comment on lines +22 to +32

.ratingB {
height: var(--bui-space-6);
width: var(--bui-space-6);
color: var(--bui-white);
background-color: var(--sonarqube-rating-b);
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--bui-radius-2);
font-weight: 600;
}

.ratingC {
height: var(--bui-space-6);
width: var(--bui-space-6);
color: var(--bui-white);
background-color: var(--sonarqube-rating-c);
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--bui-radius-2);
font-weight: 600;
}

.ratingD {
height: var(--bui-space-6);
width: var(--bui-space-6);
color: var(--bui-white);
background-color: var(--sonarqube-rating-d);
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--bui-radius-2);
font-weight: 600;
}

.ratingE {
height: var(--bui-space-6);
width: var(--bui-space-6);
color: var(--bui-white);
background-color: var(--sonarqube-rating-e);
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--bui-radius-2);
font-weight: 600;
}
}
Loading
Loading