@@ -280,7 +280,7 @@ class ComponentMeasuresApp extends React.PureComponent<Props, State> { | |||
<LargeCenteredLayout id="component-measures" className="sw-pt-8"> | |||
<Suggestions suggestions="component_measures" /> | |||
<Helmet defer={false} title={translate('layout.measures')} /> | |||
<PageContentFontWrapper> | |||
<PageContentFontWrapper className="sw-body-sm"> | |||
<Spinner className="my-10 sw-flex sw-content-center" loading={this.state.loading} /> | |||
{measures.length > 0 ? ( |
@@ -18,7 +18,7 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import classNames from 'classnames'; | |||
import { Highlight, Link, MetricsLabel, MetricsRatingBadge } from 'design-system'; | |||
import { Link, MetricsLabel, MetricsRatingBadge } from 'design-system'; | |||
import * as React from 'react'; | |||
import LanguageDistribution from '../../../components/charts/LanguageDistribution'; | |||
import Tooltip from '../../../components/controls/Tooltip'; | |||
@@ -58,36 +58,39 @@ export default function MeasureHeader(props: Props) { | |||
<div className="sw-flex sw-items-center sw-justify-between sw-gap-4"> | |||
<div className="it__measure-details-metric sw-flex sw-items-center sw-gap-1"> | |||
<strong className="sw-body-md-highlight">{getLocalizedMetricName(metric)}</strong> | |||
<Measure | |||
className={classNames('it__measure-details-value sw-body-md')} | |||
metricKey={metric.key} | |||
metricType={metric.type} | |||
value={measureValue} | |||
ratingComponent={ | |||
<MetricsRatingBadge | |||
label={ | |||
measureValue | |||
? translateWithParameters( | |||
'metric.has_rating_X', | |||
formatMeasure(measureValue, MetricType.Rating), | |||
) | |||
: translate('metric.no_rating') | |||
} | |||
rating={formatMeasure(measureValue, MetricType.Rating) as MetricsLabel} | |||
/> | |||
} | |||
/> | |||
<div className="sw-flex sw-items-center sw-ml-2"> | |||
<Measure | |||
className={classNames('it__measure-details-value sw-body-md')} | |||
metricKey={metric.key} | |||
metricType={metric.type} | |||
value={measureValue} | |||
ratingComponent={ | |||
<MetricsRatingBadge | |||
label={ | |||
measureValue | |||
? translateWithParameters( | |||
'metric.has_rating_X', | |||
formatMeasure(measureValue, MetricType.Rating), | |||
) | |||
: translate('metric.no_rating') | |||
} | |||
rating={formatMeasure(measureValue, MetricType.Rating) as MetricsLabel} | |||
/> | |||
} | |||
/> | |||
</div> | |||
{!isDiff && hasHistory && ( | |||
<Tooltip overlay={translate('component_measures.show_metric_history')}> | |||
<Highlight> | |||
<span className="sw-ml-4"> | |||
<Link | |||
className="it__show-history-link sw-font-semibold sw-ml-4" | |||
className="it__show-history-link sw-font-semibold" | |||
to={getMeasureHistoryUrl(component.key, metric.key, branchLike)} | |||
> | |||
{translate('component_measures.see_metric_history')} | |||
</Link> | |||
</Highlight> | |||
</span> | |||
</Tooltip> | |||
)} | |||
</div> |
@@ -53,6 +53,7 @@ export default function SubnavigationMeasureValue({ measure }: Props) { | |||
} | |||
metricKey={measure.metric.key} | |||
metricType={measure.metric.type} | |||
small | |||
value={value} | |||
/> | |||
</Note> |
@@ -17,12 +17,14 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { QualityGateIndicator } from 'design-system'; | |||
import * as React from 'react'; | |||
import Tooltip from '../../components/controls/Tooltip'; | |||
import Level from '../../components/ui/Level'; | |||
import Rating from '../../components/ui/Rating'; | |||
import { translateWithParameters } from '../../helpers/l10n'; | |||
import { formatMeasure } from '../../helpers/measures'; | |||
import { MetricType } from '../../types/metrics'; | |||
import { Status } from '../../types/types'; | |||
import RatingTooltipContent from './RatingTooltipContent'; | |||
interface Props { | |||
@@ -49,7 +51,20 @@ export default function Measure({ | |||
} | |||
if (metricType === MetricType.Level) { | |||
return <Level className={className} level={value?.toString()} small={small} />; | |||
const formatted = formatMeasure(value, MetricType.Level); | |||
const ariaLabel = translateWithParameters('overview.quality_gate_x', formatted); | |||
return ( | |||
<> | |||
<QualityGateIndicator | |||
status={(value as Status) ?? 'NONE'} | |||
className="sw-mr-2" | |||
ariaLabel={ariaLabel} | |||
size={small ? 'sm' : 'md'} | |||
/> | |||
<span className={small ? '' : 'sw-body-md'}>{formatted}</span> | |||
</> | |||
); | |||
} | |||
if (metricType !== MetricType.Rating) { |
@@ -19,6 +19,7 @@ | |||
*/ | |||
import { render, screen } from '@testing-library/react'; | |||
import * as React from 'react'; | |||
import { Status } from '../../../apps/overview/utils'; | |||
import { MetricKey, MetricType } from '../../../types/metrics'; | |||
import MeasureIndicator from '../MeasureIndicator'; | |||
@@ -32,3 +33,26 @@ it('renders correctly for coverage', () => { | |||
); | |||
expect(screen.getByRole('img')).toMatchSnapshot(); | |||
}); | |||
it('renders correctly for failed quality gate', () => { | |||
const wrapper = render( | |||
<MeasureIndicator | |||
metricKey={MetricKey.alert_status} | |||
metricType={MetricType.Level} | |||
small | |||
value={Status.ERROR} | |||
/>, | |||
); | |||
expect(wrapper.baseElement).toMatchSnapshot(); | |||
}); | |||
it('renders correctly for passed quality gate', () => { | |||
const wrapper = render( | |||
<MeasureIndicator | |||
metricKey={MetricKey.alert_status} | |||
metricType={MetricType.Level} | |||
value={Status.OK} | |||
/>, | |||
); | |||
expect(wrapper.baseElement).toMatchSnapshot(); | |||
}); |
@@ -25,3 +25,83 @@ exports[`renders correctly for coverage 1`] = ` | |||
</g> | |||
</svg> | |||
`; | |||
exports[`renders correctly for failed quality gate 1`] = ` | |||
<body> | |||
<div> | |||
<div | |||
aria-label="overview.quality_gate_x.ERROR" | |||
class="sw-flex sw-justify-center sw-items-center" | |||
> | |||
<svg | |||
class="sw-mr-2" | |||
fill="none" | |||
height="1rem" | |||
role="status" | |||
width="1rem" | |||
xmlns="http://www.w3.org/2000/svg" | |||
> | |||
<rect | |||
fill="rgb(254,205,202)" | |||
height="1rem" | |||
rx="2" | |||
width="1rem" | |||
/> | |||
<path | |||
d="m10.227 5 .871.871-5.227 5.227L5 10.227z" | |||
fill="rgb(128,27,20)" | |||
/> | |||
<path | |||
d="m11.098 10.227-.871.87L5 5.872 5.87 5z" | |||
fill="rgb(128,27,20)" | |||
/> | |||
</svg> | |||
</div> | |||
<span | |||
class="" | |||
> | |||
ERROR | |||
</span> | |||
</div> | |||
</body> | |||
`; | |||
exports[`renders correctly for passed quality gate 1`] = ` | |||
<body> | |||
<div> | |||
<div | |||
aria-label="overview.quality_gate_x.OK" | |||
class="sw-flex sw-justify-center sw-items-center" | |||
> | |||
<svg | |||
class="sw-mr-2" | |||
fill="none" | |||
height="1.5rem" | |||
role="status" | |||
width="1.5rem" | |||
xmlns="http://www.w3.org/2000/svg" | |||
> | |||
<rect | |||
fill="rgb(209,250,223)" | |||
height="1.5rem" | |||
rx="2" | |||
width="1.5rem" | |||
/> | |||
<path | |||
d="m16.95 7.5 1.308 1.307-7.84 7.84-1.308-1.306z" | |||
fill="rgb(5,96,58)" | |||
/> | |||
<path | |||
d="m11.79 15.34-1.307 1.307-4.484-4.483 1.307-1.306z" | |||
fill="rgb(5,96,58)" | |||
/> | |||
</svg> | |||
</div> | |||
<span | |||
class="sw-body-md" | |||
> | |||
OK | |||
</span> | |||
</div> | |||
</body> | |||
`; |