From f0347a3cf3e278c3d7ddb263a5dd0062b9421bff Mon Sep 17 00:00:00 2001 From: Jeremy Davis Date: Tue, 6 Jun 2023 16:11:38 +0200 Subject: [PATCH] SONAR-19472 Migrate the component table to the new UI --- .../src/components/MetricsRatingBadge.tsx | 25 +++- .../design-system/src/components/Table.tsx | 8 +- .../src/components/icons/PinIcon.tsx | 23 ++++ .../src/components/icons/index.ts | 1 + .../main/js/apps/code/__tests__/Code-it.ts | 2 +- .../apps/code/components/CodeAppRenderer.tsx | 95 +++++++------ .../js/apps/code/components/Component.tsx | 129 ++++++++---------- .../apps/code/components/ComponentMeasure.tsx | 72 +++++++--- .../js/apps/code/components/ComponentName.tsx | 35 ++--- .../js/apps/code/components/ComponentPin.tsx | 42 +++--- .../js/apps/code/components/Components.tsx | 121 ++++++++-------- .../apps/code/components/ComponentsEmpty.tsx | 18 +-- .../apps/code/components/ComponentsHeader.tsx | 38 +++--- .../js/apps/code/components/SearchResults.tsx | 67 --------- .../main/js/components/measure/Measure.tsx | 6 +- .../__snapshots__/Measure-test.tsx.snap | 2 +- 16 files changed, 341 insertions(+), 343 deletions(-) create mode 100644 server/sonar-web/design-system/src/components/icons/PinIcon.tsx delete mode 100644 server/sonar-web/src/main/js/apps/code/components/SearchResults.tsx diff --git a/server/sonar-web/design-system/src/components/MetricsRatingBadge.tsx b/server/sonar-web/design-system/src/components/MetricsRatingBadge.tsx index f91758932d7..2bdf0ca7f9f 100644 --- a/server/sonar-web/design-system/src/components/MetricsRatingBadge.tsx +++ b/server/sonar-web/design-system/src/components/MetricsRatingBadge.tsx @@ -38,9 +38,14 @@ const SIZE_MAPPING = { export function MetricsRatingBadge({ className, size = 'sm', label, rating, ...ariaAttrs }: Props) { if (!rating) { return ( - - – - + + — + ); } return ( @@ -56,6 +61,15 @@ export function MetricsRatingBadge({ className, size = 'sm', label, rating, ...a ); } +const StyledNoRatingBadge = styled.div<{ size: string }>` + display: inline-flex; + align-items: center; + justify-content: center; + + width: ${getProp('size')}; + height: ${getProp('size')}; +`; + const MetricsRatingBadgeStyled = styled.div<{ rating: MetricsLabel; size: string }>` width: ${getProp('size')}; height: ${getProp('size')}; @@ -63,7 +77,10 @@ const MetricsRatingBadgeStyled = styled.div<{ rating: MetricsLabel; size: string font-size: ${({ size }) => (size === '2rem' ? '0.875rem' : '0.75rem')}; background-color: ${({ rating }) => themeColor(`rating.${rating}`)}; - ${tw`sw-inline-flex sw-items-center sw-justify-center`}; + display: inline-flex; + align-items: center; + justify-content: center; + ${tw`sw-rounded-pill`}; ${tw`sw-font-semibold`}; `; diff --git a/server/sonar-web/design-system/src/components/Table.tsx b/server/sonar-web/design-system/src/components/Table.tsx index ac3a8060b54..8b8e75a240c 100644 --- a/server/sonar-web/design-system/src/components/Table.tsx +++ b/server/sonar-web/design-system/src/components/Table.tsx @@ -152,13 +152,13 @@ export const TableRowInteractive = styled(TableRowInteractiveBase)` `; export const ContentCell = styled(CellComponent)` - ${tw`sw-text-left`} + ${tw`sw-text-left sw-justify-start`} `; export const NumericalCell = styled(CellComponent)` - ${tw`sw-text-right`} + ${tw`sw-text-right sw-justify-end`} `; export const RatingCell = styled(CellComponent)` - ${tw`sw-text-right`} + ${tw`sw-text-right sw-justify-end`} `; export const CheckboxCell = styled(CellComponent)` ${tw`sw-text-center`} @@ -181,7 +181,7 @@ const StyledTable = styled.table` const CellComponentStyled = styled.td` color: ${themeColor('pageContent')}; - + ${tw`sw-flex sw-items-center`} ${tw`sw-body-sm`} ${tw`sw-py-4 sw-px-2`} ${tw`sw-align-top`} diff --git a/server/sonar-web/design-system/src/components/icons/PinIcon.tsx b/server/sonar-web/design-system/src/components/icons/PinIcon.tsx new file mode 100644 index 00000000000..e3bbab86917 --- /dev/null +++ b/server/sonar-web/design-system/src/components/icons/PinIcon.tsx @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { PinIcon as OcticonPinIcon } from '@primer/octicons-react'; +import { OcticonHoc } from './Icon'; + +export const PinIcon = OcticonHoc(OcticonPinIcon, 'PinIcon'); diff --git a/server/sonar-web/design-system/src/components/icons/index.ts b/server/sonar-web/design-system/src/components/icons/index.ts index 5112f869cfd..a65e5e893fa 100644 --- a/server/sonar-web/design-system/src/components/icons/index.ts +++ b/server/sonar-web/design-system/src/components/icons/index.ts @@ -54,6 +54,7 @@ export { OpenNewTabIcon } from './OpenNewTabIcon'; export { OverviewQGNotComputedIcon } from './OverviewQGNotComputedIcon'; export { OverviewQGPassedIcon } from './OverviewQGPassedIcon'; export { PencilIcon } from './PencilIcon'; +export { PinIcon } from './PinIcon'; export { ProjectIcon } from './ProjectIcon'; export { PullRequestIcon } from './PullRequestIcon'; export { QualifierIcon } from './QualifierIcon'; diff --git a/server/sonar-web/src/main/js/apps/code/__tests__/Code-it.ts b/server/sonar-web/src/main/js/apps/code/__tests__/Code-it.ts index bb2b888e1aa..61b202ab4dc 100644 --- a/server/sonar-web/src/main/js/apps/code/__tests__/Code-it.ts +++ b/server/sonar-web/src/main/js/apps/code/__tests__/Code-it.ts @@ -78,7 +78,7 @@ it('should allow navigating through the tree', async () => { // Navigate by clicking on an element. await ui.clickOnChildComponent(/folderA$/); - expect(await ui.childComponent(/out\.tsx/).find()).toBeInTheDocument(); + expect(await ui.childComponent(/out\.tsx/).findAll()).toHaveLength(2); // One for the pin, one for the name column // Navigate back using the breadcrumb. await ui.clickOnBreadcrumb(/Foo$/); 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 f440a027ece..47cab3fba89 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 @@ -17,8 +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 classNames from 'classnames'; -import { FlagMessage, HelperHintIcon, LargeCenteredLayout } from 'design-system'; +import { + Card, + DeferredSpinner, + FlagMessage, + HelperHintIcon, + LargeCenteredLayout, + LightLabel, +} from 'design-system'; import { intersection } from 'lodash'; import * as React from 'react'; import { Helmet } from 'react-helmet-async'; @@ -36,7 +42,6 @@ import { getCodeMetrics } from '../utils'; import CodeBreadcrumbs from './CodeBreadcrumbs'; import Components from './Components'; import Search from './Search'; -import SearchResults from './SearchResults'; import SourceViewerWrapper from './SourceViewerWrapper'; interface Props { @@ -90,11 +95,6 @@ export default function CodeAppRenderer(props: Props) { const showComponentList = sourceViewer === undefined && components.length > 0 && !showSearch; - const componentsClassName = classNames('boxed-group', 'spacer-top', { - 'new-loading': loading, - 'search-results': showSearch, - }); - const metricKeys = intersection( getCodeMetrics(component.qualifier, branchLike, { newCode: newCodeSelected }), Object.keys(metrics) @@ -111,7 +111,7 @@ export default function CodeAppRenderer(props: Props) { const isPortfolio = isPortfolioLike(qualifier); return ( - + @@ -145,15 +145,15 @@ export default function CodeAppRenderer(props: Props) { /> )} -
+
{!hasComponents && sourceViewer === undefined && ( -
- +
+ {translate( 'code_viewer.no_source_code_displayed_due_to_empty_analysis', component.qualifier )} - +
)} @@ -165,47 +165,46 @@ export default function CodeAppRenderer(props: Props) { /> )} -
- {showComponentList && ( - - )} - - {showSearch && ( - - )} - -
- {searchResults?.length === 0 && translate('no_results')} -
-
+ + + {showComponentList && ( + + )} + + {showSearch && ( + + )} + + {showComponentList && ( )} {sourceViewer !== undefined && !showSearch && ( -
+
{ - render() { - const { - branchLike, - canBePinned = true, - canBrowse = false, - component, - hasBaseComponent, - isBaseComponent = false, - metrics, - previous, - rootComponent, - selected = false, - newCodeSelected, - showAnalysisDate, - } = this.props; +export default function Component(props: Props) { + const { + branchLike, + canBePinned = true, + canBrowse = false, + component, + isBaseComponent = false, + metrics, + previous, + rootComponent, + selected = false, + newCodeSelected, + showAnalysisDate, + } = props; - const isFile = - component.qualifier === ComponentQualifier.File || - component.qualifier === ComponentQualifier.TestFile; + const isFile = + component.qualifier === ComponentQualifier.File || + component.qualifier === ComponentQualifier.TestFile; - return ( - - {canBePinned && ( - - {isFile && ( - - {({ openComponent }) => ( - - )} - - )} - - )} - -
- {hasBaseComponent &&
} - -
- + return ( + + {canBePinned && ( + + {isFile && ( + + {({ openComponent }) => ( + + )} + + )} + + )} + + + - {metrics.map((metric) => ( - - - - ))} + {metrics.map((metric) => ( + + ))} - {showAnalysisDate && isBaseComponent && } - - {showAnalysisDate && !isBaseComponent && ( - - {component.analysisDate ? : '—'} - - )} - - ); - } + {showAnalysisDate && ( + + {!isBaseComponent && + (component.analysisDate ? : '—')} + + )} + + ); } - -export default withScrollTo(Component); diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx b/server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx index 3b2392fbc57..e6ec611dc88 100644 --- a/server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx @@ -17,35 +17,73 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { + ContentCell, + MetricsEnum, + MetricsRatingBadge, + NumericalCell, + QualityGateIndicator, + RatingCell, +} from 'design-system'; import * as React from 'react'; import Measure from '../../../components/measure/Measure'; import { getLeakValue } from '../../../components/measure/utils'; -import { isDiffMetric } from '../../../helpers/measures'; -import { ComponentMeasure as TypeComponentMeasure, Metric } from '../../../types/types'; +import { translateWithParameters } from '../../../helpers/l10n'; +import { formatMeasure, isDiffMetric } from '../../../helpers/measures'; +import { isApplication, isProject } from '../../../types/component'; +import { MetricKey, MetricType } from '../../../types/metrics'; +import { Metric, Status, ComponentMeasure as TypeComponentMeasure } from '../../../types/types'; interface Props { component: TypeComponentMeasure; metric: Metric; } -export default class ComponentMeasure extends React.PureComponent { - render() { - const { component, metric } = this.props; - const isProject = component.qualifier === 'TRK'; - const isReleasability = metric.key === 'releasability_rating'; +export default function ComponentMeasure(props: Props) { + const { component, metric } = props; + const isProjectLike = isProject(component.qualifier) || isApplication(component.qualifier); + const isReleasability = metric.key === MetricKey.releasability_rating; - const finalMetricKey = isProject && isReleasability ? 'alert_status' : metric.key; - const finalMetricType = isProject && isReleasability ? 'LEVEL' : metric.type; + const finalMetricKey = isProjectLike && isReleasability ? MetricKey.alert_status : metric.key; + const finalMetricType = isProjectLike && isReleasability ? MetricType.Level : metric.type; - const measure = - Array.isArray(component.measures) && - component.measures.find((measure) => measure.metric === finalMetricKey); + const measure = Array.isArray(component.measures) + ? component.measures.find((measure) => measure.metric === finalMetricKey) + : undefined; - if (!measure) { - return measure === false ? : —; - } + const value = isDiffMetric(metric.key) ? getLeakValue(measure) : measure?.value; + + switch (finalMetricType) { + case MetricType.Level: { + const formatted = formatMeasure(value, MetricType.Level); + const ariaLabel = translateWithParameters('overview.quality_gate_x', formatted); - const value = isDiffMetric(metric.key) ? getLeakValue(measure) : measure.value; - return ; + return ( + + + {formatted} + + ); + } + case MetricType.Rating: + return ( + + + + ); + default: + return ( + + + + ); } } diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx b/server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx index dbe0000d59d..d3a5baf2cef 100644 --- a/server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx @@ -17,9 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { HoverLink, LightLabel, QualifierIcon } from 'design-system'; +import { Badge, BranchIcon, HoverLink, LightLabel, Note, QualifierIcon } from 'design-system'; import * as React from 'react'; -import BranchIcon from '../../../components/icons/BranchIcon'; import { getBranchLikeQuery } from '../../../helpers/branch-like'; import { translate } from '../../../helpers/l10n'; import { CodeScope, getComponentOverviewUrl, queryToSearch } from '../../../helpers/urls'; @@ -75,8 +74,8 @@ export default function ComponentName({ ) ) { return ( - - + +
{renderNameWithIcon( branchLike, component, @@ -86,14 +85,16 @@ export default function ComponentName({ canBrowse, newCodeSelected )} - +
{component.branch ? ( - +
- {component.branch} - + {component.branch} +
) : ( - {translate('branches.main_branch')} + + {translate('branches.main_branch')} + )}
); @@ -129,6 +130,7 @@ function renderNameWithIcon( : undefined; return ( } to={getComponentOverviewUrl( component.refKey ?? component.key, component.qualifier, @@ -136,8 +138,7 @@ function renderNameWithIcon( codeType )} > - - {name} + {name} ); } else if (canBrowse) { @@ -146,15 +147,17 @@ function renderNameWithIcon( Object.assign(query, { selected: component.key }); } return ( - - - {name} + } + to={{ pathname: '/code', search: queryToSearch(query) }} + > + {name} ); } return ( - - + + {name} ); diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx b/server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx index 05a31151eb0..15de4e4d04c 100644 --- a/server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx @@ -17,10 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { InteractiveIcon, PinIcon } from 'design-system'; import * as React from 'react'; -import theme from '../../../app/theme'; -import { ButtonPlain } from '../../../components/controls/buttons'; -import PinIcon from '../../../components/icons/PinIcon'; import { WorkspaceContextShape } from '../../../components/workspace/context'; import { translateWithParameters } from '../../../helpers/l10n'; import { BranchLike } from '../../../types/branch-like'; @@ -32,27 +30,23 @@ interface Props { openComponent: WorkspaceContextShape['openComponent']; } -export default class ComponentPin extends React.PureComponent { - handleClick = () => { - this.props.openComponent({ - branchLike: this.props.branchLike, - key: this.props.component.key, - name: this.props.component.path, - qualifier: this.props.component.qualifier, +export default function ComponentPin(props: Props) { + const { branchLike, component, openComponent } = props; + + const handleClick = React.useCallback(() => { + openComponent({ + branchLike, + key: component.key, + name: component.path, + qualifier: component.qualifier, }); - }; + }, [branchLike, component, openComponent]); + + const label = translateWithParameters('component_viewer.open_in_workspace_X', component.name); - render() { - const { name } = this.props.component; - return ( - - - - ); - } + return ( + + + + ); } diff --git a/server/sonar-web/src/main/js/apps/code/components/Components.tsx b/server/sonar-web/src/main/js/apps/code/components/Components.tsx index 738cc70f5a9..588d0c8c198 100644 --- a/server/sonar-web/src/main/js/apps/code/components/Components.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/Components.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 { ContentCell, Table, TableRow } from 'design-system'; import { sortBy } from 'lodash'; import * as React from 'react'; import withKeyboardNavigation from '../../../components/hoc/withKeyboardNavigation'; @@ -52,7 +53,7 @@ function Components(props: ComponentsProps) { } = props; const canBePinned = - baseComponent && + baseComponent !== undefined && ![ ComponentQualifier.Application, ComponentQualifier.Portfolio, @@ -61,69 +62,71 @@ function Components(props: ComponentsProps) { return (
- - {baseComponent && ( - metric.key)} - rootComponent={rootComponent} - showAnalysisDate={showAnalysisDate} - /> - )} - - {baseComponent && ( - <> - + metric.key)} rootComponent={rootComponent} - newCodeSelected={newCodeSelected} showAnalysisDate={showAnalysisDate} /> - - - - )} + + ) + } + > + {baseComponent && ( + <> + + + + + + )} - {components.length ? ( - sortBy( - components, - (c) => c.qualifier, - (c) => c.name.toLowerCase(), - (c) => (c.branch ? c.branch.toLowerCase() : '') - ).map((component, index, list) => ( - 0 ? list[index - 1] : undefined} - rootComponent={rootComponent} - newCodeSelected={newCodeSelected} - showAnalysisDate={showAnalysisDate} - selected={ - selected && - getComponentMeasureUniqueKey(component) === getComponentMeasureUniqueKey(selected) - } - /> - )) - ) : ( - - )} - -
-
+ {components.length ? ( + sortBy( + components, + (c) => c.qualifier, + (c) => c.name.toLowerCase(), + (c) => (c.branch ? c.branch.toLowerCase() : '') + ).map((component, index, list) => ( + 0 ? list[index - 1] : undefined} + rootComponent={rootComponent} + newCodeSelected={newCodeSelected} + showAnalysisDate={showAnalysisDate} + selected={ + selected && + getComponentMeasureUniqueKey(component) === getComponentMeasureUniqueKey(selected) + } + /> + )) + ) : ( + + )} +
); } diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentsEmpty.tsx b/server/sonar-web/src/main/js/apps/code/components/ComponentsEmpty.tsx index c6214240a13..ea492fc6c2e 100644 --- a/server/sonar-web/src/main/js/apps/code/components/ComponentsEmpty.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/ComponentsEmpty.tsx @@ -17,20 +17,16 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { ContentCell, Note, TableRow } from 'design-system'; import * as React from 'react'; import { translate } from '../../../helpers/l10n'; -interface Props { - canBePinned?: boolean; -} - -export default function ComponentsEmpty({ canBePinned = true }: Props) { +export default function ComponentsEmpty() { return ( - - {canBePinned && } - - {translate('no_results')} - - + + + {translate('no_results')} + + ); } diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentsHeader.tsx b/server/sonar-web/src/main/js/apps/code/components/ComponentsHeader.tsx index 6041ebe807f..d37f4a9c544 100644 --- a/server/sonar-web/src/main/js/apps/code/components/ComponentsHeader.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/ComponentsHeader.tsx @@ -17,9 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { ContentCell, NumericalCell, RatingCell } from 'design-system'; import * as React from 'react'; import { translate } from '../../../helpers/l10n'; import { isPortfolioLike } from '../../../types/component'; +import { MetricKey } from '../../../types/metrics'; import { ComponentMeasure } from '../../../types/types'; interface ComponentsHeaderProps { @@ -31,16 +33,17 @@ interface ComponentsHeaderProps { } const SHORT_NAME_METRICS = [ - 'duplicated_lines_density', - 'new_lines', - 'new_coverage', - 'new_duplicated_lines_density', + MetricKey.duplicated_lines_density, + MetricKey.new_lines, + MetricKey.new_coverage, + MetricKey.new_duplicated_lines_density, ]; export default function ComponentsHeader(props: ComponentsHeaderProps) { const { baseComponent, canBePinned = true, metrics, rootComponent, showAnalysisDate } = props; const isPortfolio = isPortfolioLike(rootComponent.qualifier); let columns: string[] = []; + let Cell: typeof NumericalCell; if (isPortfolio) { columns = [ translate('metric_domain.Releasability'), @@ -54,24 +57,25 @@ export default function ComponentsHeader(props: ComponentsHeaderProps) { if (showAnalysisDate) { columns.push(translate('code.last_analysis_date')); } + + Cell = RatingCell; } else { columns = metrics.map((metric) => - translate('metric', metric, SHORT_NAME_METRICS.includes(metric) ? 'short_name' : 'name') + translate( + 'metric', + metric, + SHORT_NAME_METRICS.includes(metric as MetricKey) ? 'short_name' : 'name' + ) ); + + Cell = NumericalCell; } return ( - - - {canBePinned && } - - {baseComponent && - columns.map((column) => ( - - {column} - - ))} - - + <> + {canBePinned && } + + {baseComponent && columns.map((column) => {column})} + ); } diff --git a/server/sonar-web/src/main/js/apps/code/components/SearchResults.tsx b/server/sonar-web/src/main/js/apps/code/components/SearchResults.tsx deleted file mode 100644 index db9e03c4672..00000000000 --- a/server/sonar-web/src/main/js/apps/code/components/SearchResults.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import classNames from 'classnames'; -import { sortBy } from 'lodash'; -import * as React from 'react'; -import withKeyboardNavigation, { - WithKeyboardNavigationProps, -} from '../../../components/hoc/withKeyboardNavigation'; -import { getComponentMeasureUniqueKey } from '../../../helpers/component'; -import { BranchLike } from '../../../types/branch-like'; -import { ComponentMeasure } from '../../../types/types'; -import ComponentName from './ComponentName'; - -export interface SearchResultsProps extends WithKeyboardNavigationProps { - branchLike?: BranchLike; - rootComponent: ComponentMeasure; - newCodeSelected?: boolean; -} - -function SearchResults(props: SearchResultsProps) { - const { branchLike, components, newCodeSelected, rootComponent, selected } = props; - - return ( -
    - {components && - components.length > 0 && - sortBy( - components, - (c) => c.qualifier, - (c) => c.name.toLowerCase(), - (c) => (c.branch ? c.branch.toLowerCase() : '') - ).map((component) => ( -
  • - -
  • - ))} -
- ); -} - -export default withKeyboardNavigation(SearchResults); diff --git a/server/sonar-web/src/main/js/components/measure/Measure.tsx b/server/sonar-web/src/main/js/components/measure/Measure.tsx index bf83d336a6a..4ed74baf0ae 100644 --- a/server/sonar-web/src/main/js/components/measure/Measure.tsx +++ b/server/sonar-web/src/main/js/components/measure/Measure.tsx @@ -45,7 +45,7 @@ export default function Measure({ ratingComponent, }: Props) { if (value === undefined) { - return –; + return —; } if (metricType === MetricType.Level) { @@ -57,11 +57,11 @@ export default function Measure({ decimals, omitExtraDecimalZeros: metricType === MetricType.Percent, }); - return {formattedValue != null ? formattedValue : '–'}; + return {formattedValue ?? '—'}; } const tooltip = ; - const rating = ratingComponent || ; + const rating = ratingComponent ?? ; if (tooltip) { return ( diff --git a/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap b/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap index 29e685efb6b..3a30c9bed84 100644 --- a/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap @@ -37,6 +37,6 @@ exports[`renders trivial measure 1`] = ` exports[`renders undefined measure 1`] = ` - – + — `; -- 2.39.5