From: stanislavh Date: Fri, 2 Jun 2023 12:43:14 +0000 (+0200) Subject: SONAR-19391 Measure Header uses new design X-Git-Tag: 10.1.0.73491~143 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=b2e7f33ced83a025961195ba5065986e94aae9f0;p=sonarqube.git SONAR-19391 Measure Header uses new design --- diff --git a/server/sonar-web/design-system/src/components/InputSelect.tsx b/server/sonar-web/design-system/src/components/InputSelect.tsx index 59bb6d728d9..3b51e2a9a4f 100644 --- a/server/sonar-web/design-system/src/components/InputSelect.tsx +++ b/server/sonar-web/design-system/src/components/InputSelect.tsx @@ -120,18 +120,19 @@ export function InputSelect< classNames={{ container: () => 'sw-relative sw-inline-block sw-align-middle', placeholder: () => 'sw-truncate sw-leading-4', - menu: () => 'sw-w-auto', menuList: () => 'sw-overflow-y-auto sw-py-2 sw-max-h-[12.25rem]', control: ({ isDisabled }) => classNames( - 'sw-absolut sw-box-border sw-rounded-2 sw-overflow-hidden sw-z-dropdown-menu', + 'sw-absolut sw-box-border sw-rounded-2 sw-overflow-hidden', isDisabled && 'sw-pointer-events-none sw-cursor-not-allowed' ), + menu: () => 'sw-z-dropdown-menu', option: ({ isDisabled }) => classNames( 'sw-py-2 sw-px-3 sw-cursor-pointer', isDisabled && 'sw-pointer-events-none sw-cursor-not-allowed' ), + ...props.classNames, }} components={{ ...props.components, @@ -178,7 +179,6 @@ export function selectStyle< menu: (base) => ({ ...base, width: INPUT_SIZES[size], - zIndex: 101, }), option: (base, { isFocused, isSelected }) => ({ ...base, diff --git a/server/sonar-web/design-system/src/components/KeyboardHint.tsx b/server/sonar-web/design-system/src/components/KeyboardHint.tsx index cbfa57434c3..845ab2f5c91 100644 --- a/server/sonar-web/design-system/src/components/KeyboardHint.tsx +++ b/server/sonar-web/design-system/src/components/KeyboardHint.tsx @@ -24,17 +24,18 @@ import { Key } from '../helpers/keyboard'; import { KeyboardHintKeys } from './KeyboardHintKeys'; interface Props { + className?: string; command: string; title?: string; } -export function KeyboardHint({ title, command }: Props) { +export function KeyboardHint({ title, command, className }: Props) { const normalizedCommand = command .replace(Key.Control, isMacOS() ? 'Command' : 'Control') .replace(Key.Alt, isMacOS() ? 'Option' : 'Alt'); return ( - + {title && {title}} diff --git a/server/sonar-web/src/main/js/apps/component-measures/__tests__/ComponentMeasures-it.tsx b/server/sonar-web/src/main/js/apps/component-measures/__tests__/ComponentMeasures-it.tsx index 6d8679e4a7b..53ef62d652a 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/__tests__/ComponentMeasures-it.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/__tests__/ComponentMeasures-it.tsx @@ -420,9 +420,7 @@ function getPageObject() { // Overview seeDataAsListLink: byRole('link', { name: 'component_measures.overview.see_data_as_list' }), bubbleChart: byTestId('bubble-chart'), - newCodePeriodTxt: byText( - 'overview.new_code_period_x.overview.period.previous_version_only_date' - ), + newCodePeriodTxt: byText('component_measures.leak_legend.new_code'), // Navigation overviewDomainBtn: byRole('button', { @@ -482,7 +480,7 @@ function getPageObject() { showAllBtn: byRole('button', { name: 'component_measures.hidden_best_score_metrics_show_label', }), - goToActivityLink: byRole('link', { name: 'component_measures.show_metric_history' }), + goToActivityLink: byRole('link', { name: 'component_measures.see_metric_history' }), }; const ui = { diff --git a/server/sonar-web/src/main/js/apps/component-measures/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/component-measures/__tests__/utils-test.ts index 49988516fd2..1ba9141ce06 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/__tests__/utils-test.ts +++ b/server/sonar-web/src/main/js/apps/component-measures/__tests__/utils-test.ts @@ -18,6 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { ComponentQualifier } from '../../../types/component'; +import { MeasurePageView } from '../../../types/measures'; import { MetricKey } from '../../../types/metrics'; import { ComponentMeasure } from '../../../types/types'; import * as utils from '../utils'; @@ -142,17 +143,19 @@ describe('parseQuery', () => { describe('serializeQuery', () => { it('should correctly serialize the query', () => { - expect(utils.serializeQuery({ metric: '', selected: '', view: 'list' })).toEqual({ + expect(utils.serializeQuery({ metric: '', selected: '', view: MeasurePageView.list })).toEqual({ view: 'list', }); - expect(utils.serializeQuery({ metric: 'foo', selected: 'bar', view: 'tree' })).toEqual({ + expect( + utils.serializeQuery({ metric: 'foo', selected: 'bar', view: MeasurePageView.tree }) + ).toEqual({ metric: 'foo', selected: 'bar', }); }); it('should be memoized', () => { - const query: utils.Query = { metric: 'foo', selected: 'bar', view: 'tree' }; + const query: utils.Query = { metric: 'foo', selected: 'bar', view: MeasurePageView.tree }; expect(utils.serializeQuery(query)).toBe(utils.serializeQuery(query)); }); }); diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx index 7a90f5734fd..13aaf746887 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx @@ -42,6 +42,7 @@ import { getBranchLikeQuery, isPullRequest, isSameBranchLike } from '../../../he import { translate } from '../../../helpers/l10n'; import { BranchLike } from '../../../types/branch-like'; import { ComponentQualifier } from '../../../types/component'; +import { MeasurePageView } from '../../../types/measures'; import { MetricKey } from '../../../types/metrics'; import { ComponentMeasure, @@ -188,10 +189,10 @@ class ComponentMeasuresApp extends React.PureComponent { const metric = this.getSelectedMetric(query, false); if (metric) { - if (query.view === 'treemap' && !hasTreemap(metric.key, metric.type)) { - query.view = 'tree'; - } else if (query.view === 'tree' && !hasTree(metric.key)) { - query.view = 'list'; + if (query.view === MeasurePageView.treemap && !hasTreemap(metric.key, metric.type)) { + query.view = MeasurePageView.tree; + } else if (query.view === MeasurePageView.tree && !hasTree(metric.key)) { + query.view = MeasurePageView.list; } } @@ -255,7 +256,7 @@ class ComponentMeasuresApp extends React.PureComponent { } return ( - + { +class LeakPeriodLegend extends React.PureComponent { formatDate = (date: string) => { return this.props.intl.formatDate(date, longFormatterOption); }; @@ -45,24 +46,26 @@ export class LeakPeriodLegend extends React.PureComponent{translate('issues.new_code_period')}; + const { component, period } = this.props; + + if (component.qualifier === ComponentQualifier.Application) { + return ( + + {translate('issues.new_code_period')} + + ); } const leakPeriodLabel = getPeriodLabel( period, period.mode === 'manual_baseline' ? this.formatDateTime : this.formatDate ); - if (!leakPeriodLabel) { - return null; - } const label = ( -
- {translateWithParameters('overview.new_code_period_x', leakPeriodLabel)} -
+ + {translateWithParameters('component_measures.leak_legend.new_code')}{' '} + {leakPeriodLabel} + ); if (period.mode === 'days' || period.mode === NewCodePeriodSettingType.NUMBER_OF_DAYS) { @@ -87,3 +90,8 @@ export class LeakPeriodLegend extends React.PureComponent { const componentKey = selected || rootComponent.key; const baseComponentMetrics = [requestedMetric.key]; if (requestedMetric.key === MetricKey.ncloc) { - baseComponentMetrics.push('ncloc_language_distribution'); + baseComponentMetrics.push(MetricKey.ncloc_language_distribution); } Promise.all([ getComponentTree(strategy, componentKey, metricKeys, opts), @@ -205,7 +208,7 @@ export default class MeasureContent extends React.PureComponent { metric: Pick, options: Object = {} ) { - const strategy = view === 'list' ? 'leaves' : 'children'; + const strategy = view === MeasurePageView.list ? 'leaves' : 'children'; const metricKeys = [metric.key]; const opts: RequestData = { ...getBranchLikeQuery(this.props.branchLike), @@ -223,17 +226,17 @@ export default class MeasureContent extends React.PureComponent { }; const isDiff = isDiffMetric(metric.key); - if (view === 'tree') { + if (view === MeasurePageView.tree) { metricKeys.push(...(complementary[metric.key] || [])); opts.asc = true; opts.s = 'qualifier,name'; - } else if (view === 'list') { + } else if (view === MeasurePageView.list) { metricKeys.push(...(complementary[metric.key] || [])); opts.asc = metric.direction === 1; opts.metricSort = metric.key; setMetricSort(); - } else if (view === 'treemap') { - const sizeMetric = isDiff ? 'new_lines' : 'ncloc'; + } else if (view === MeasurePageView.treemap) { + const sizeMetric = isDiff ? MetricKey.new_lines : MetricKey.ncloc; metricKeys.push(sizeMetric); opts.asc = false; opts.metricSort = sizeMetric; @@ -291,7 +294,7 @@ export default class MeasureContent extends React.PureComponent { getDefaultShowBestMeasures() { const { asc, view } = this.props; - if ((asc !== undefined && view === 'list') || view === 'tree') { + if ((asc !== undefined && view === MeasurePageView.list) || view === MeasurePageView.tree) { return true; } return false; @@ -303,7 +306,7 @@ export default class MeasureContent extends React.PureComponent { if (!metric) { return null; } - if (view === 'tree' || view === 'list') { + if (view === MeasurePageView.list || view === MeasurePageView.tree) { const selectedIdx = this.getSelectedIndex(); return ( { { /> } right={ -
+
{!isFileComponent && metric && ( <> -
+ {translate('component_measures.view_as')} -
+ - + + + + {paging && paging.total > 0 && ( + + )} )}
} /> - - {isFileComponent ? ( -
- -
- ) : ( - this.renderMeasure() - )} +
+ + {isFileComponent ? ( +
+ +
+ ) : ( + this.renderMeasure() + )} +
); } diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentHeader.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentHeader.tsx index 21af06f9157..e97a6ca889e 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentHeader.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentHeader.tsx @@ -29,7 +29,7 @@ interface Props { export default function MeasureContentHeader({ left, right }: Props) { return ( -
{left}
+
{left}
{right}
); diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.tsx index 5a0fbaf137d..e26f7d7d2b6 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.tsx @@ -17,18 +17,19 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import classNames from 'classnames'; +import { Highlight, Link, MetricsLabel, MetricsRatingBadge } from 'design-system'; import * as React from 'react'; import LanguageDistribution from '../../../components/charts/LanguageDistribution'; -import Link from '../../../components/common/Link'; import Tooltip from '../../../components/controls/Tooltip'; -import HistoryIcon from '../../../components/icons/HistoryIcon'; -import IssueTypeIcon from '../../../components/icons/IssueTypeIcon'; import Measure from '../../../components/measure/Measure'; -import { getLocalizedMetricName, translate } from '../../../helpers/l10n'; -import { isDiffMetric } from '../../../helpers/measures'; +import { getLocalizedMetricName, translate, translateWithParameters } from '../../../helpers/l10n'; +import { formatMeasure, isDiffMetric } from '../../../helpers/measures'; import { getMeasureHistoryUrl } from '../../../helpers/urls'; import { BranchLike } from '../../../types/branch-like'; -import { ComponentMeasure, Measure as TypeMeasure, Metric, Period } from '../../../types/types'; +import { ComponentQualifier } from '../../../types/component'; +import { MetricKey, MetricType } from '../../../types/metrics'; +import { ComponentMeasure, Metric, Period, Measure as TypeMeasure } from '../../../types/types'; import { hasFullMeasures } from '../utils'; import LeakPeriodLegend from './LeakPeriodLegend'; @@ -45,46 +46,59 @@ export default function MeasureHeader(props: Props) { const { branchLike, component, leakPeriod, measureValue, metric, secondaryMeasure } = props; const isDiff = isDiffMetric(metric.key); const hasHistory = - ['VW', 'SVW', 'APP', 'TRK'].includes(component.qualifier) && hasFullMeasures(branchLike); + [ + ComponentQualifier.Portfolio, + ComponentQualifier.SubPortfolio, + ComponentQualifier.Application, + ComponentQualifier.Project, + ].includes(component.qualifier as ComponentQualifier) && hasFullMeasures(branchLike); const displayLeak = hasFullMeasures(branchLike); return ( -
-
-
- - {getLocalizedMetricName(metric)} - - - +
+
+ {getLocalizedMetricName(metric)} + - - + } + /> + {!isDiff && hasHistory && ( - - - + + + {translate('component_measures.see_metric_history')} + + )}
-
- {displayLeak && leakPeriod && ( - - )} -
+ {displayLeak && leakPeriod && ( + + )}
{secondaryMeasure && - secondaryMeasure.metric === 'ncloc_language_distribution' && + secondaryMeasure.metric === MetricKey.ncloc_language_distribution && secondaryMeasure.value !== undefined && ( -
+
)} diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx index ff9f686eafb..f6ef3bf6236 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx @@ -177,11 +177,7 @@ export default class MeasureOverview extends React.PureComponent { current={this.state.components.length} /> {leakPeriod && displayLeak && ( - + )} } diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx index d4eda508832..2b490fedaf9 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx @@ -17,18 +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 { InputSelect } from 'design-system'; import * as React from 'react'; -import { components, OptionProps, SingleValueProps } from 'react-select'; -import Select from '../../../components/controls/Select'; -import ListIcon from '../../../components/icons/ListIcon'; -import TreeIcon from '../../../components/icons/TreeIcon'; -import TreemapIcon from '../../../components/icons/TreemapIcon'; import { translate } from '../../../helpers/l10n'; import { MeasurePageView } from '../../../types/measures'; import { Metric } from '../../../types/types'; import { hasList, hasTree, hasTreemap } from '../utils'; -interface Props { +export interface MeasureViewSelectProps { className?: string; metric: Metric; handleViewChange: (view: MeasurePageView) => void; @@ -36,75 +32,46 @@ interface Props { } interface ViewOption { - icon: JSX.Element; label: string; - value: string; + value: MeasurePageView; } -export default class MeasureViewSelect extends React.PureComponent { - getOptions = () => { - const { metric } = this.props; - const options: ViewOption[] = []; - if (hasTree(metric.key)) { - options.push({ - icon: , - label: translate('component_measures.tab.tree'), - value: 'tree', - }); - } - if (hasList(metric.key)) { - options.push({ - icon: , - label: translate('component_measures.tab.list'), - value: 'list', - }); - } - if (hasTreemap(metric.key, metric.type)) { - options.push({ - icon: , - label: translate('component_measures.tab.treemap'), - value: 'treemap', - }); - } - return options; - }; +export default function MeasureViewSelect(props: MeasureViewSelectProps) { + const { metric, view, className } = props; + const options = []; + if (hasTree(metric.key)) { + options.push({ + label: translate('component_measures.tab.tree'), + value: MeasurePageView.tree, + }); + } + if (hasList(metric.key)) { + options.push({ + label: translate('component_measures.tab.list'), + value: MeasurePageView.list, + }); + } + if (hasTreemap(metric.key, metric.type)) { + options.push({ + label: translate('component_measures.tab.treemap'), + value: MeasurePageView.treemap, + }); + } - handleChange = (option: ViewOption) => { - return this.props.handleViewChange(option.value as MeasurePageView); + const handleChange = (option: ViewOption) => { + return props.handleViewChange(option.value); }; - renderOption = (props: OptionProps) => ( - - {props.data.icon} - {props.data.label} - + return ( + o.value === view)} + /> ); - - renderValue = (props: SingleValueProps) => ( - - {props.data.icon} - {props.data.label} - - ); - - render() { - const { className, view } = this.props; - const options = this.getOptions(); - - return ( -