From ad4424906d27fc58f51e250532e49009a421fb87 Mon Sep 17 00:00:00 2001 From: Mathieu Suen Date: Thu, 18 Jun 2020 15:42:33 +0200 Subject: [PATCH] SONAR-13421 Improve project cards --- .../src/main/js/app/styles/init/misc.css | 12 + .../apps/projects/components/ProjectCard.tsx | 200 ------- .../components/ProjectCardLeakMeasures.tsx | 105 ---- .../components/ProjectCardOrganization.tsx | 39 -- .../components/ProjectCardOverallMeasures.tsx | 140 ----- .../components/ProjectCardRatingMeasure.tsx | 59 -- .../apps/projects/components/ProjectsList.tsx | 18 +- .../ProjectCardLeakMeasures-test.tsx | 53 -- .../ProjectCardOverallMeasures-test.tsx | 71 --- .../ProjectCardRatingMeasure-test.tsx | 47 -- .../__snapshots__/ProjectCard-test.tsx.snap | 416 -------------- .../ProjectCardLanguages-test.tsx.snap | 41 -- .../ProjectCardLeakMeasures-test.tsx.snap | 278 --------- .../ProjectCardOverallMeasures-test.tsx.snap | 232 -------- .../ProjectCardQualityGate-test.tsx.snap | 21 - .../ProjectCardRatingMeasure-test.tsx.snap | 69 --- .../__snapshots__/ProjectsList-test.tsx.snap | 6 +- .../components/project-card/ProjectCard.css | 70 +++ .../components/project-card/ProjectCard.tsx | 242 ++++++++ .../ProjectCardLanguages.tsx | 26 +- .../ProjectCardLanguagesContainer.tsx | 2 +- .../project-card/ProjectCardMeasure.tsx | 55 ++ .../project-card/ProjectCardMeasures.tsx | 201 +++++++ .../ProjectCardQualityGate.tsx | 28 +- .../__tests__/ProjectCard-test.tsx | 42 +- .../__tests__/ProjectCardLanguages-test.tsx | 0 .../__tests__/ProjectCardMeasure-test.tsx} | 18 +- .../__tests__/ProjectCardMeasures-test.tsx | 99 ++++ .../__tests__/ProjectCardQualityGate-test.tsx | 0 .../__snapshots__/ProjectCard-test.tsx.snap | 540 ++++++++++++++++++ .../ProjectCardLanguages-test.tsx.snap | 33 ++ .../ProjectCardMeasure-test.tsx.snap | 38 ++ .../ProjectCardMeasures-test.tsx.snap | 314 ++++++++++ .../ProjectCardQualityGate-test.tsx.snap | 14 + .../src/main/js/apps/projects/styles.css | 163 ------ .../resources/org/sonar/l10n/core.properties | 3 +- 36 files changed, 1673 insertions(+), 2022 deletions(-) delete mode 100644 server/sonar-web/src/main/js/apps/projects/components/ProjectCard.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projects/components/ProjectCardOrganization.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projects/components/ProjectCardRatingMeasure.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeakMeasures-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverallMeasures-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardRatingMeasure-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCard-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLanguages-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardQualityGate-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardRatingMeasure-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.css create mode 100644 server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx rename server/sonar-web/src/main/js/apps/projects/components/{ => project-card}/ProjectCardLanguages.tsx (74%) rename server/sonar-web/src/main/js/apps/projects/components/{ => project-card}/ProjectCardLanguagesContainer.tsx (94%) create mode 100644 server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasure.tsx create mode 100644 server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasures.tsx rename server/sonar-web/src/main/js/apps/projects/components/{ => project-card}/ProjectCardQualityGate.tsx (62%) rename server/sonar-web/src/main/js/apps/projects/components/{ => project-card}/__tests__/ProjectCard-test.tsx (76%) rename server/sonar-web/src/main/js/apps/projects/components/{ => project-card}/__tests__/ProjectCardLanguages-test.tsx (100%) rename server/sonar-web/src/main/js/apps/projects/components/{ProjectCardOrganizationContainer.tsx => project-card/__tests__/ProjectCardMeasure-test.tsx} (64%) create mode 100644 server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardMeasures-test.tsx rename server/sonar-web/src/main/js/apps/projects/components/{ => project-card}/__tests__/ProjectCardQualityGate-test.tsx (100%) create mode 100644 server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCard-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCardLanguages-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCardMeasure-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCardMeasures-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCardQualityGate-test.tsx.snap diff --git a/server/sonar-web/src/main/js/app/styles/init/misc.css b/server/sonar-web/src/main/js/app/styles/init/misc.css index a5133b41d99..7b277f0c018 100644 --- a/server/sonar-web/src/main/js/app/styles/init/misc.css +++ b/server/sonar-web/src/main/js/app/styles/init/misc.css @@ -179,11 +179,18 @@ th.hide-overflow { .big-padded-top { padding-top: calc(2 * var(--gridSize)); } +.big-padded-bottom { + padding-bottom: calc(2 * var(--gridSize)); +} .big-padded-right { padding-right: calc(2 * var(--gridSize)); } +.big-padded-left { + padding-left: calc(2 * var(--gridSize)); +} + .huge-padded-top { padding-top: 40px; } @@ -430,6 +437,11 @@ th.huge-spacer-right { align-items: flex-start; } +.display-flex-end { + display: flex !important; + align-items: flex-end; +} + .display-flex-wrap { display: flex !important; flex-wrap: wrap; diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.tsx deleted file mode 100644 index a5b7f1407e7..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.tsx +++ /dev/null @@ -1,200 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 * as classNames from 'classnames'; -import * as difference from 'date-fns/difference_in_milliseconds'; -import * as React from 'react'; -import { Link } from 'react-router'; -import QualifierIcon from 'sonar-ui-common/components/icons/QualifierIcon'; -import DateTimeFormatter from 'sonar-ui-common/components/intl/DateTimeFormatter'; -import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; -import PrivacyBadgeContainer from '../../../components/common/PrivacyBadgeContainer'; -import Favorite from '../../../components/controls/Favorite'; -import TagsList from '../../../components/tags/TagsList'; -import { getProjectUrl } from '../../../helpers/urls'; -import { isLoggedIn } from '../../../helpers/users'; -import { ComponentQualifier } from '../../../types/component'; -import { Project } from '../types'; -import { formatDuration } from '../utils'; -import ProjectCardLeakMeasures from './ProjectCardLeakMeasures'; -import ProjectCardOverallMeasures from './ProjectCardOverallMeasures'; -import ProjectCardQualityGate from './ProjectCardQualityGate'; - -interface Props { - currentUser: T.CurrentUser; - handleFavorite: (component: string, isFavorite: boolean) => void; - height: number; - project: Project; - type?: string; -} - -interface Dates { - analysisDate: string; - leakPeriodDate?: string; -} - -function getDates(project: Project, type: string | undefined) { - const { analysisDate, leakPeriodDate } = project; - if (!analysisDate || (type === 'leak' && !leakPeriodDate)) { - return undefined; - } else { - return { analysisDate, leakPeriodDate }; - } -} - -function renderHeader(props: Props) { - const { project } = props; - const hasTags = project.tags.length > 0; - return ( -
- {project.isFavorite !== undefined && ( - - )} -

- {props.project.needIssueSync ? ( - props.project.name - ) : ( - {props.project.name} - )} -

- {project.analysisDate && } -
- - - {hasTags && } -
-
- ); -} - -function renderDates(dates: Dates, type: string | undefined) { - const { analysisDate, leakPeriodDate } = dates; - const periodMs = leakPeriodDate ? difference(Date.now(), leakPeriodDate) : 0; - - return ( - <> - - {formattedDate => ( - - {translateWithParameters('projects.last_analysis_on_x', formattedDate)} - - )} - - {type === 'leak' && periodMs !== undefined && ( - - {translateWithParameters('projects.new_code_period_x', formatDuration(periodMs))} - - )} - - ); -} - -function renderDateRow(project: Project, dates: Dates | undefined, type: string | undefined) { - if (project.qualifier === ComponentQualifier.Application || dates) { - return ( -
- {dates && renderDates(dates, type)} - - {project.qualifier === ComponentQualifier.Application && ( -
- - {translate('qualifier.APP')} - {project.measures.projects && ( - <> - {' ‒ '} - {translateWithParameters('x_projects_', project.measures.projects)} - - )} -
- )} -
- ); - } else { - return null; - } -} - -function renderMeasures(props: Props, dates: Dates | undefined) { - const { currentUser, project, type } = props; - - const { measures } = project; - - if (dates) { - return type === 'leak' ? ( - - ) : ( - - ); - } else { - return ( -
- - {type === 'leak' && project.analysisDate - ? translate('projects.no_new_code_period', project.qualifier) - : translate('projects.not_analyzed', project.qualifier)} - - {project.qualifier !== ComponentQualifier.Application && - !project.analysisDate && - isLoggedIn(currentUser) && ( - - {translate('projects.configure_analysis')} - - )} -
- ); - } -} - -export default function ProjectCard(props: Props) { - const { height, project, type } = props; - const { needIssueSync, key } = project; - - const dates = getDates(project, type); - - return ( -
-
- {renderHeader(props)} - {renderDateRow(project, dates, type)} -
- {renderMeasures(props, dates)} -
- ); -} diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.tsx deleted file mode 100644 index 8357df3a7c2..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.tsx +++ /dev/null @@ -1,105 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 * as React from 'react'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import Measure from '../../../components/measure/Measure'; -import ProjectCardRatingMeasure from './ProjectCardRatingMeasure'; - -interface Props { - measures: T.Dict; -} - -export default function ProjectCardLeakMeasures({ measures }: Props) { - return ( -
- - - - - - - - - {measures['new_coverage'] != null && ( -
-
-
- -
-
{translate('metric.coverage.name')}
-
-
- )} - -
-
-
- -
-
- {translate('metric.duplicated_lines_density.short_name')} -
-
-
- -
-
-
- -
-
{translate('metric.lines.name')}
-
-
-
- ); -} diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOrganization.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOrganization.tsx deleted file mode 100644 index 6a653279f43..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOrganization.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 * as React from 'react'; -import OrganizationLink from '../../../components/ui/OrganizationLink'; - -interface Props { - organization?: { key: string; name: string }; - organizationsEnabled?: boolean; -} - -export default function ProjectCardOrganization({ organization, organizationsEnabled }: Props) { - if (!organization || !organizationsEnabled) { - return null; - } - - return ( - - {organization.name} - - - ); -} diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx deleted file mode 100644 index 93ee98af1e6..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx +++ /dev/null @@ -1,140 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 * as React from 'react'; -import DuplicationsRating from 'sonar-ui-common/components/ui/DuplicationsRating'; -import SizeRating from 'sonar-ui-common/components/ui/SizeRating'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import Measure from '../../../components/measure/Measure'; -import CoverageRating from '../../../components/ui/CoverageRating'; -import { ComponentQualifier } from '../../../types/component'; -import ProjectCardLanguagesContainer from './ProjectCardLanguagesContainer'; -import ProjectCardRatingMeasure from './ProjectCardRatingMeasure'; - -interface Props { - componentQualifier: ComponentQualifier; - measures: T.Dict; -} - -export default function ProjectCardOverallMeasures({ componentQualifier, measures }: Props) { - if (measures === undefined) { - return null; - } - - const { ncloc } = measures; - if (!ncloc) { - return ( -
- {componentQualifier === ComponentQualifier.Application - ? translate('portfolio.app.empty') - : translate('overview.project.main_branch_empty')} -
- ); - } - - return ( -
- - - - - - - - - {measures['coverage'] != null && ( -
-
-
- - - - -
-
{translate('metric.coverage.name')}
-
-
- )} - -
-
-
- {measures['duplicated_lines_density'] != null && ( - - - - )} - -
-
- {translate('metric.duplicated_lines_density.short_name')} -
-
-
- - {measures['ncloc'] != null && ( -
-
-
- - - - -
-
- -
-
-
- )} -
- ); -} diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardRatingMeasure.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardRatingMeasure.tsx deleted file mode 100644 index 6ad1796094b..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardRatingMeasure.tsx +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 * as React from 'react'; -import IssueTypeIcon from 'sonar-ui-common/components/icons/IssueTypeIcon'; -import Rating from 'sonar-ui-common/components/ui/Rating'; -import Measure from '../../../components/measure/Measure'; - -export interface ProjectCardRatingMeasureProps { - iconKey?: string; - iconLabel: string; - measures: T.Dict; - metricKey: string; - metricRatingKey: string; - metricType: string; -} - -export default function ProjectCardRatingMeasure(props: ProjectCardRatingMeasureProps) { - const { iconKey, iconLabel, measures, metricKey, metricRatingKey, metricType } = props; - - return ( -
-
-
- - - -
- -
- - - {iconLabel} -
-
-
- ); -} diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx index 535e3b80a09..d054555d6e8 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx @@ -28,7 +28,10 @@ import { Project } from '../types'; import EmptyFavoriteSearch from './EmptyFavoriteSearch'; import EmptyInstance from './EmptyInstance'; import NoFavoriteProjects from './NoFavoriteProjects'; -import ProjectCard from './ProjectCard'; +import ProjectCard from './project-card/ProjectCard'; + +const PROJECT_CARD_HEIGHT = 145; +const PROJECT_CARD_MARGIN = 20; interface Props { cardType?: string; @@ -41,10 +44,6 @@ interface Props { } export default class ProjectsList extends React.PureComponent { - getCardHeight = () => { - return this.props.cardType === 'leak' ? 159 : 143; - }; - renderNoProjects() { const { currentUser, isFavorite, isFiltered, query } = this.props; if (isFiltered) { @@ -55,14 +54,14 @@ export default class ProjectsList extends React.PureComponent { renderRow = ({ index, key, style }: ListRowProps) => { const project = this.props.projects[index]; - const height = this.getCardHeight(); + return ( -
+
{ }; renderList() { - const cardHeight = this.getCardHeight(); return ( {({ height, isScrolling, onChildScroll, scrollTop }) => ( @@ -88,7 +86,7 @@ export default class ProjectsList extends React.PureComponent { onScroll={onChildScroll} overscanRowCount={2} rowCount={this.props.projects.length} - rowHeight={cardHeight + 20} + rowHeight={PROJECT_CARD_HEIGHT + PROJECT_CARD_MARGIN} rowRenderer={this.renderRow} scrollTop={scrollTop} style={{ outline: 'none' }} diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeakMeasures-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeakMeasures-test.tsx deleted file mode 100644 index ef39c12f927..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeakMeasures-test.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import ProjectCardLeakMeasures from '../ProjectCardLeakMeasures'; - -it('should render correctly with all data', () => { - const measures = { - alert_status: 'ERROR', - new_reliability_rating: '1.0', - new_bugs: '8', - new_security_rating: '2.0', - new_vulnerabilities: '2', - new_maintainability_rating: '1.0', - new_code_smells: '0', - new_coverage: '26.55', - new_duplicated_lines_density: '0.55', - new_lines: '87' - }; - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); -}); - -it('should render no data style new coverage, new duplications and new lines', () => { - const measures = { - alert_status: 'ERROR', - new_reliability_rating: '1.0', - new_bugs: '8', - new_security_rating: '2.0', - new_vulnerabilities: '2', - new_maintainability_rating: '1.0', - new_code_smells: '0' - }; - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverallMeasures-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverallMeasures-test.tsx deleted file mode 100644 index 4516ca937b6..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverallMeasures-test.tsx +++ /dev/null @@ -1,71 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import { ComponentQualifier } from '../../../../types/component'; -import ProjectCardOverallMeasures from '../ProjectCardOverallMeasures'; - -it('should render correctly with all data', () => { - expect(shallowRender()).toMatchSnapshot(); -}); - -it('should not render coverage', () => { - expect( - shallowRender({ coverage: undefined }) - .find('[data-key="coverage"]') - .exists() - ).toBe(false); -}); - -it('should render empty', () => { - expect(shallowRender({ ncloc: undefined })).toMatchSnapshot('project'); - expect(shallowRender({ ncloc: undefined }, ComponentQualifier.Application)).toMatchSnapshot( - 'application' - ); -}); - -it('should render ncloc correctly', () => { - expect(shallowRender({ ncloc: '16549887' }).find('[data-key="ncloc"]')).toMatchSnapshot(); -}); - -function shallowRender( - overriddenMeasures: T.Dict = {}, - componentQualifier?: ComponentQualifier -) { - const measures = { - alert_status: 'ERROR', - bugs: '17', - code_smells: '132', - coverage: '88.3', - duplicated_lines_density: '9.8', - ncloc: '2053', - reliability_rating: '1.0', - security_rating: '1.0', - sqale_rating: '1.0', - vulnerabilities: '0', - ...overriddenMeasures - }; - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardRatingMeasure-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardRatingMeasure-test.tsx deleted file mode 100644 index 58f4dd65714..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardRatingMeasure-test.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import ProjectCardRatingMeasure, { - ProjectCardRatingMeasureProps -} from '../ProjectCardRatingMeasure'; - -it('renders', () => { - expect(shallowRender()).toMatchSnapshot(); - expect(shallowRender({ iconKey: 'overriddenIconKey' })).toMatchSnapshot('iconKey'); -}); - -function shallowRender(overrides: Partial = {}) { - const measures = { - security_rating: '1', - vulnerabilities: '0', - dummyThatShouldBeIgnored: 'yes' - }; - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCard-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCard-test.tsx.snap deleted file mode 100644 index e8188017818..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCard-test.tsx.snap +++ /dev/null @@ -1,416 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should display applications 1`] = ` -
-
-
-

- - Foo - -

- -
- -
-
-
- - - -
- - qualifier.APP -
-
-
- -
-`; - -exports[`should display applications: with project count 1`] = ` -
-
-
-

- - Foo - -

- -
- -
-
-
- - - -
- - qualifier.APP - ‒ - x_projects_.3 -
-
-
- -
-`; - -exports[`should display configure analysis button for logged in user 1`] = ` -
-
-
-

- - Foo - -

-
- -
-
-
-
- - projects.not_analyzed.TRK - - - projects.configure_analysis - -
-
-`; - -exports[`should display correclty when project need issue synch 1`] = ` -
-
-
-

- Foo -

- -
- -
-
-
- - - -
-
- -
-`; - -exports[`should display not analyzed yet 1`] = ` -
-
-
-

- - Foo - -

-
- -
-
-
-
- - projects.not_analyzed.TRK - -
-
-`; - -exports[`should display the overall measures and quality gate 1`] = ` -
-
-
-

- - Foo - -

- -
- -
-
-
- - - -
-
- -
-`; diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLanguages-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLanguages-test.tsx.snap deleted file mode 100644 index 30fdc18ecc1..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLanguages-test.tsx.snap +++ /dev/null @@ -1,41 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`handles unknown languages 1`] = ` -
- - - cpp, Java - - -
-`; - -exports[`handles unknown languages 2`] = ` -
- - - unknown, Java - - -
-`; - -exports[`renders 1`] = ` -
- - - Java, JavaScript - - -
-`; - -exports[`sorts languages 1`] = ` -
- - - JavaScript, Java - - -
-`; diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.tsx.snap deleted file mode 100644 index cf796735609..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.tsx.snap +++ /dev/null @@ -1,278 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly with all data 1`] = ` -
- - - - -
-
-
- -
-
- metric.coverage.name -
-
-
-
-
-
- -
-
- metric.duplicated_lines_density.short_name -
-
-
-
-
-
- -
-
- metric.lines.name -
-
-
-
-`; - -exports[`should render no data style new coverage, new duplications and new lines 1`] = ` -
- - - - -
-
-
- -
-
- metric.duplicated_lines_density.short_name -
-
-
-
-
-
- -
-
- metric.lines.name -
-
-
-
-`; diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap deleted file mode 100644 index 69a88c913e4..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap +++ /dev/null @@ -1,232 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly with all data 1`] = ` -
- - - - -
-
-
- - - - -
-
- metric.coverage.name -
-
-
-
-
-
- - - - -
-
- metric.duplicated_lines_density.short_name -
-
-
-
-
-
- - - - -
-
- -
-
-
-
-`; - -exports[`should render empty: application 1`] = ` -
- portfolio.app.empty -
-`; - -exports[`should render empty: project 1`] = ` -
- overview.project.main_branch_empty -
-`; - -exports[`should render ncloc correctly 1`] = ` -
-
-
- - - - -
-
- -
-
-
-`; diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardQualityGate-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardQualityGate-test.tsx.snap deleted file mode 100644 index cbf5ab95857..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardQualityGate-test.tsx.snap +++ /dev/null @@ -1,21 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders 1`] = ` -
- -
- -
-
-
-`; diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardRatingMeasure-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardRatingMeasure-test.tsx.snap deleted file mode 100644 index 730efde8faa..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardRatingMeasure-test.tsx.snap +++ /dev/null @@ -1,69 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders 1`] = ` -
-
-
- - -
-
- - label -
-
-
-`; - -exports[`renders: iconKey 1`] = ` -
-
-
- - -
-
- - label -
-
-
-`; diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsList-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsList-test.tsx.snap index 7df128ac05e..166b28db57e 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsList-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsList-test.tsx.snap @@ -14,7 +14,7 @@ exports[`renders correctly: list element 1`] = ` overscanIndicesGetter={[Function]} overscanRowCount={2} rowCount={2} - rowHeight={163} + rowHeight={165} rowRenderer={[Function]} scrollToAlignment="auto" scrollToIndex={-1} @@ -34,7 +34,7 @@ exports[`renders correctly: row element 1`] = ` role="row" style={ Object { - "height": 143, + "height": 145, } } > @@ -47,7 +47,7 @@ exports[`renders correctly: row element 1`] = ` "isLoggedIn": true, } } - height={143} + height={145} project={ Object { "key": "foo", diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.css b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.css new file mode 100644 index 00000000000..3dc105a138b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.css @@ -0,0 +1,70 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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. + */ + +.project-card-main { + flex: 1 1 auto; + overflow: hidden; +} + +.project-card-meta { + flex: 0 0 170px; + overflow: hidden; + background-color: rgba(230, 230, 230, 0.25); + height: 100%; + box-sizing: border-box; +} + +.project-card-meta .tags-list span { + display: inline; +} + +.project-card-measure-value-line { + height: calc(3 * var(--gridSize)); +} + +@media (max-width: 1320px) { + .project-card-measure-secondary-info { + display: none; + } +} + +.project-card-leak { + background-color: var(--leakPrimaryColor); +} + +.project-card-disabled *:not(g):not(path) { + color: var(--disableGrayText); +} + +.project-card-disabled .rating, +.project-card-disabled .size-rating, +.project-card-disabled .duplications-rating:after, +.project-card-disabled .level { + background-color: var(--disableGrayBg); +} + +.project-card-disabled .duplications-rating { + border-color: var(--disableGrayBg); +} + +.project-card-disabled .project-card-main *:not(.favorite-link) svg path, +.project-card-disabled .project-card-meta path { + fill: var(--disableGrayBg) !important; +} diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx new file mode 100644 index 00000000000..96012b34c8f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx @@ -0,0 +1,242 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 * as classNames from 'classnames'; +import * as React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { Link } from 'react-router'; +import Tooltip from 'sonar-ui-common/components/controls/Tooltip'; +import QualifierIcon from 'sonar-ui-common/components/icons/QualifierIcon'; +import DateFromNow from 'sonar-ui-common/components/intl/DateFromNow'; +import DateTimeFormatter from 'sonar-ui-common/components/intl/DateTimeFormatter'; +import SizeRating from 'sonar-ui-common/components/ui/SizeRating'; +import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; +import PrivacyBadgeContainer from '../../../../components/common/PrivacyBadgeContainer'; +import Favorite from '../../../../components/controls/Favorite'; +import Measure from '../../../../components/measure/Measure'; +import TagsList from '../../../../components/tags/TagsList'; +import { getProjectUrl } from '../../../../helpers/urls'; +import { isLoggedIn } from '../../../../helpers/users'; +import { ComponentQualifier } from '../../../../types/component'; +import { MetricKey } from '../../../../types/metrics'; +import { Project } from '../../types'; +import './ProjectCard.css'; +import ProjectCardLanguagesContainer from './ProjectCardLanguagesContainer'; +import ProjectCardMeasure from './ProjectCardMeasure'; +import ProjectCardMeasures from './ProjectCardMeasures'; +import ProjectCardQualityGate from './ProjectCardQualityGate'; + +interface Props { + currentUser: T.CurrentUser; + handleFavorite: (component: string, isFavorite: boolean) => void; + height: number; + project: Project; + type?: string; +} + +function renderFirstLine(props: Props) { + const { + project: { + analysisDate, + tags, + qualifier, + isFavorite, + key, + name, + measures, + needIssueSync, + visibility + } + } = props; + + return ( +
+
+ {isFavorite !== undefined && ( + + )} + {qualifier === ComponentQualifier.Application && ( + + {translate('qualifier.APP')} + {measures.projects && ( + + {' ‒ '} + {translateWithParameters('x_projects_', measures.projects)} + + )} + + }> + + + + + )} +

+ {needIssueSync ? name : {name}} +

+ + {analysisDate && ( + <> + + + + {formattedAnalysisDate => ( + + + }} + /> + + )} + + + )} +
+
+
+ + {tags.length > 0 && } +
+
+
+ ); +} + +function renderSecondLine(props: Props, isNewCode: boolean) { + const { + project: { measures } + } = props; + + return ( +
+
+ {renderMeasures(props, isNewCode)} +
+
+ {isNewCode + ? measures[MetricKey.new_lines] != null && ( + + + + ) + : measures[MetricKey.ncloc] != null && ( + +
+ + + + + +
+
+ )} +
+
+ ); +} + +function renderMeasures(props: Props, isNewCode: boolean) { + const { + currentUser, + project: { measures, analysisDate, leakPeriodDate, qualifier, key } + } = props; + + if (analysisDate && (!isNewCode || leakPeriodDate)) { + return ( + + ); + } else { + return ( +
+ + {isNewCode && analysisDate + ? translate('projects.no_new_code_period', qualifier) + : translate('projects.not_analyzed', qualifier)} + + {qualifier !== ComponentQualifier.Application && !analysisDate && isLoggedIn(currentUser) && ( + + {translate('projects.configure_analysis')} + + )} +
+ ); + } +} + +export default function ProjectCard(props: Props) { + const { + height, + type, + project: { needIssueSync, key } + } = props; + const isNewCode = type === 'leak'; + + return ( +
+ {renderFirstLine(props)} + {renderSecondLine(props, isNewCode)} +
+ ); +} diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguages.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardLanguages.tsx similarity index 74% rename from server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguages.tsx rename to server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardLanguages.tsx index 2814fa79d15..fa413ef8b2f 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguages.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardLanguages.tsx @@ -19,7 +19,6 @@ */ import { sortBy } from 'lodash'; import * as React from 'react'; -import Tooltip from 'sonar-ui-common/components/controls/Tooltip'; import { translate } from 'sonar-ui-common/helpers/l10n'; interface Props { @@ -38,29 +37,12 @@ export default function ProjectCardLanguages({ className, distribution, language getLanguageName(languages, l[0]) ); - const languagesText = - finalLanguages.slice(0, 2).join(', ') + (finalLanguages.length > 2 ? ', ...' : ''); - - let tooltip; - if (finalLanguages.length > 2) { - tooltip = ( - - {finalLanguages.map(language => ( - - {language} -
-
- ))} -
- ); - } + const languagesText = finalLanguages.join(', '); return ( -
- - {languagesText} - -
+ + {languagesText} + ); } diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguagesContainer.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardLanguagesContainer.tsx similarity index 94% rename from server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguagesContainer.tsx rename to server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardLanguagesContainer.tsx index fb6f9e677d5..dc5af06a0e3 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguagesContainer.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardLanguagesContainer.tsx @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { connect } from 'react-redux'; -import { getLanguages, Store } from '../../../store/rootReducer'; +import { getLanguages, Store } from '../../../../store/rootReducer'; import ProjectCardLanguages from './ProjectCardLanguages'; const stateToProps = (state: Store) => ({ diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasure.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasure.tsx new file mode 100644 index 00000000000..4581ed80d96 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasure.tsx @@ -0,0 +1,55 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 * as classNames from 'classnames'; +import * as React from 'react'; +import IssueTypeIcon from 'sonar-ui-common/components/icons/IssueTypeIcon'; + +export interface ProjectCardMeasureProps { + iconKey?: string; + label: string; + metricKey: string; + className?: string; +} + +export default function ProjectCardMeasure( + props: React.PropsWithChildren +) { + const { iconKey, label, metricKey, children, className } = props; + + const icon = ; + + return ( +
+
+ {icon} + {label} +
+
+ {icon} + {children} +
+
+ ); +} diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasures.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasures.tsx new file mode 100644 index 00000000000..b538ad475da --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasures.tsx @@ -0,0 +1,201 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 * as classNames from 'classnames'; +import * as difference from 'date-fns/difference_in_milliseconds'; +import * as React from 'react'; +import DateTimeFormatter from 'sonar-ui-common/components/intl/DateTimeFormatter'; +import DuplicationsRating from 'sonar-ui-common/components/ui/DuplicationsRating'; +import Rating from 'sonar-ui-common/components/ui/Rating'; +import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; +import { isDefined } from 'sonar-ui-common/helpers/types'; +import Measure from '../../../../components/measure/Measure'; +import CoverageRating from '../../../../components/ui/CoverageRating'; +import { ComponentQualifier } from '../../../../types/component'; +import { MetricKey } from '../../../../types/metrics'; +import { formatDuration } from '../../utils'; +import ProjectCardMeasure from './ProjectCardMeasure'; + +export interface ProjectCardMeasuresProps { + isNewCode: boolean; + measures: T.Dict; + componentQualifier: ComponentQualifier; + newCodeStartingDate?: string; +} + +function renderCoverage(props: ProjectCardMeasuresProps) { + const { measures, isNewCode } = props; + const coverageMetric = isNewCode ? MetricKey.new_coverage : MetricKey.coverage; + + return ( + +
+ + {measures[coverageMetric] && ( + + + + )} +
+
+ ); +} + +function renderDuplication(props: ProjectCardMeasuresProps) { + const { measures, isNewCode } = props; + const duplicationMetric = isNewCode + ? MetricKey.new_duplicated_lines_density + : MetricKey.duplicated_lines_density; + + return ( + +
+ + {measures[duplicationMetric] != null && ( + + + + )} +
+
+ ); +} + +function renderRatings(props: ProjectCardMeasuresProps) { + const { isNewCode, measures } = props; + + const measureList = [ + { + iconLabel: translate('metric.bugs.name'), + noShrink: true, + metricKey: isNewCode ? MetricKey.new_bugs : MetricKey.bugs, + metricRatingKey: isNewCode ? MetricKey.new_reliability_rating : MetricKey.reliability_rating, + metricType: 'SHORT_INT' + }, + { + iconLabel: translate('metric.vulnerabilities.name'), + metricKey: isNewCode ? MetricKey.new_vulnerabilities : MetricKey.vulnerabilities, + metricRatingKey: isNewCode ? MetricKey.new_security_rating : MetricKey.security_rating, + metricType: 'SHORT_INT' + }, + { + iconKey: 'security_hotspots', + iconLabel: translate('projects.security_hotspots_reviewed'), + metricKey: isNewCode + ? MetricKey.new_security_hotspots_reviewed + : MetricKey.security_hotspots_reviewed, + metricRatingKey: isNewCode + ? MetricKey.new_security_review_rating + : MetricKey.security_review_rating, + metricType: 'PERCENT' + }, + { + iconLabel: translate('metric.code_smells.name'), + metricKey: isNewCode ? MetricKey.new_code_smells : MetricKey.code_smells, + metricRatingKey: isNewCode ? MetricKey.new_maintainability_rating : MetricKey.sqale_rating, + metricType: 'SHORT_INT' + } + ]; + + return measureList.map(measure => { + const { iconKey, iconLabel, metricKey, metricRatingKey, metricType, noShrink } = measure; + + return ( + + + + + + + ); + }); +} + +export default function ProjectCardMeasures(props: ProjectCardMeasuresProps) { + const { isNewCode, measures, componentQualifier, newCodeStartingDate } = props; + + const { ncloc } = measures; + + if (!isNewCode && !ncloc) { + return ( +
+ {componentQualifier === ComponentQualifier.Application + ? translate('portfolio.app.empty') + : translate('overview.project.main_branch_empty')} +
+ ); + } + + const newCodeTimespan = newCodeStartingDate ? difference(Date.now(), newCodeStartingDate) : 0; + + const measureList = [ + ...renderRatings(props), + renderCoverage(props), + renderDuplication(props) + ].filter(isDefined); + + return ( + <> + {isNewCode && newCodeTimespan !== undefined && newCodeStartingDate && ( + + {formattedNewCodeStartingDate => ( +

+ {translateWithParameters( + 'projects.new_code_period_x', + formatDuration(newCodeTimespan) + )} +

+ )} +
+ )} +
+ {measureList.map((measure, i) => ( + // eslint-disable-next-line react/no-array-index-key + + {i > 0 && } + {measure} + + ))} +
+ + ); +} diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardQualityGate.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardQualityGate.tsx similarity index 62% rename from server/sonar-web/src/main/js/apps/projects/components/ProjectCardQualityGate.tsx rename to server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardQualityGate.tsx index 50cc00dc38a..adb96da828e 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardQualityGate.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardQualityGate.tsx @@ -19,9 +19,8 @@ */ import * as React from 'react'; import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip'; -import Tooltip from 'sonar-ui-common/components/controls/Tooltip'; import Level from 'sonar-ui-common/components/ui/Level'; -import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; +import { translate } from 'sonar-ui-common/helpers/l10n'; import { formatMeasure } from 'sonar-ui-common/helpers/measures'; interface Props { @@ -33,24 +32,17 @@ export default function ProjectCardQualityGate({ status }: Props) { return null; } - const tooltip = translateWithParameters( - 'overview.quality_gate_x', - formatMeasure(status, 'LEVEL') - ); + const title = `${translate('quality_gates.status')}: ${formatMeasure(status, 'LEVEL')}`; return ( -
- -
- - {status === 'WARN' && ( - - )} -
-
+
+ + {status === 'WARN' && ( + + )}
); } diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCard-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx similarity index 76% rename from server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCard-test.tsx rename to server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx index 96ce0408ee0..b10a24efa40 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCard-test.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx @@ -19,16 +19,13 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; -import PrivacyBadgeContainer from '../../../../components/common/PrivacyBadgeContainer'; -import { mockCurrentUser, mockLoggedInUser } from '../../../../helpers/testMocks'; -import { ComponentQualifier } from '../../../../types/component'; -import { Project } from '../../types'; +import PrivacyBadgeContainer from '../../../../../components/common/PrivacyBadgeContainer'; +import TagsList from '../../../../../components/tags/TagsList'; +import { mockCurrentUser, mockLoggedInUser } from '../../../../../helpers/testMocks'; +import { ComponentQualifier } from '../../../../../types/component'; +import { Project } from '../../../types'; import ProjectCard from '../ProjectCard'; - -jest.mock( - 'date-fns/difference_in_milliseconds', - () => () => 1000 * 60 * 60 * 24 * 30 * 8 // ~ 8 months -); +import ProjectCardQualityGate from '../ProjectCardQualityGate'; const MEASURES = { alert_status: 'OK', @@ -55,24 +52,11 @@ it('should display correclty when project need issue synch', () => { expect(shallowRender({ ...PROJECT, needIssueSync: true })).toMatchSnapshot(); }); -it('should display analysis date (and not leak period) when defined', () => { - expect( - shallowRender(PROJECT) - .find('.project-card-dates') - .exists() - ).toBe(true); - expect( - shallowRender({ ...PROJECT, analysisDate: undefined }) - .find('.project-card-dates') - .exists() - ).toBe(false); -}); - it('should not display the quality gate', () => { const project = { ...PROJECT, analysisDate: undefined }; expect( shallowRender(project) - .find('ProjectCardOverallQualityGate') + .find(ProjectCardQualityGate) .exists() ).toBe(false); }); @@ -81,7 +65,7 @@ it('should display tags', () => { const project = { ...PROJECT, tags: ['foo', 'bar'] }; expect( shallowRender(project) - .find('TagsList') + .find(TagsList) .exists() ).toBe(true); }); @@ -120,8 +104,14 @@ it('should display applications', () => { ).toMatchSnapshot('with project count'); }); -function shallowRender(project: Project, user: T.CurrentUser = USER_LOGGED_OUT) { +function shallowRender(project: Project, user: T.CurrentUser = USER_LOGGED_OUT, type?: string) { return shallow( - + ); } diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLanguages-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardLanguages-test.tsx similarity index 100% rename from server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLanguages-test.tsx rename to server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardLanguages-test.tsx diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOrganizationContainer.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardMeasure-test.tsx similarity index 64% rename from server/sonar-web/src/main/js/apps/projects/components/ProjectCardOrganizationContainer.tsx rename to server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardMeasure-test.tsx index d7141981780..558b3ddba8e 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOrganizationContainer.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardMeasure-test.tsx @@ -17,12 +17,18 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { connect } from 'react-redux'; -import { areThereCustomOrganizations, Store } from '../../../store/rootReducer'; -import ProjectCardOrganization from './ProjectCardOrganization'; -const stateToProps = (state: Store) => ({ - organizationsEnabled: areThereCustomOrganizations(state) +import { shallow } from 'enzyme'; +import * as React from 'react'; +import ProjectCardMeasure, { ProjectCardMeasureProps } from '../ProjectCardMeasure'; + +it('should render correctly', () => { + const wrapper = shallowRender(); + expect(wrapper).toMatchSnapshot(); }); -export default connect(stateToProps)(ProjectCardOrganization); +function shallowRender(props: Partial = {}) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardMeasures-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardMeasures-test.tsx new file mode 100644 index 00000000000..7e0d61fe323 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardMeasures-test.tsx @@ -0,0 +1,99 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; +import * as React from 'react'; +import CoverageRating from '../../../../../components/ui/CoverageRating'; +import { ComponentQualifier } from '../../../../../types/component'; +import { MetricKey } from '../../../../../types/metrics'; +import ProjectCardMeasures, { ProjectCardMeasuresProps } from '../ProjectCardMeasures'; + +jest.mock( + 'date-fns/difference_in_milliseconds', + () => () => 1000 * 60 * 60 * 24 * 30 * 8 // ~ 8 months +); + +describe('Overall measures', () => { + it('should be rendered properly', () => { + const wrapper = shallowRender(); + expect(wrapper).toMatchSnapshot(); + }); + + it("should be not be rendered if there's no line of code", () => { + let wrapper = shallowRender({ [MetricKey.ncloc]: undefined }); + expect(wrapper).toMatchSnapshot('project'); + + wrapper = shallowRender( + { ncloc: undefined }, + { componentQualifier: ComponentQualifier.Application } + ); + expect(wrapper).toMatchSnapshot('application'); + }); + + it('should not render coverage graph if there is no value for it', () => { + const wrapper = shallowRender({ [MetricKey.coverage]: undefined }); + expect(wrapper.find(CoverageRating).exists()).toBe(false); + }); +}); + +describe('New code measures', () => { + it('should be rendered properly', () => { + const wrapper = shallowRender({}, { isNewCode: true }); + expect(wrapper).toMatchSnapshot(); + }); +}); + +function shallowRender( + measuresOverride: T.Dict = {}, + props: Partial = {} +) { + const measures = { + [MetricKey.alert_status]: 'ERROR', + [MetricKey.bugs]: '17', + [MetricKey.code_smells]: '132', + [MetricKey.coverage]: '88.3', + [MetricKey.duplicated_lines_density]: '9.8', + [MetricKey.ncloc]: '2053', + [MetricKey.reliability_rating]: '1.0', + [MetricKey.security_rating]: '1.0', + [MetricKey.sqale_rating]: '1.0', + [MetricKey.vulnerabilities]: '0', + [MetricKey.new_reliability_rating]: '1.0', + [MetricKey.new_bugs]: '8', + [MetricKey.new_security_rating]: '2.0', + [MetricKey.new_vulnerabilities]: '2', + [MetricKey.new_maintainability_rating]: '1.0', + [MetricKey.new_code_smells]: '0', + [MetricKey.new_coverage]: '26.55', + [MetricKey.new_duplicated_lines_density]: '0.55', + [MetricKey.new_lines]: '87', + ...measuresOverride + }; + + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardQualityGate-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardQualityGate-test.tsx similarity index 100% rename from server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardQualityGate-test.tsx rename to server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardQualityGate-test.tsx diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCard-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCard-test.tsx.snap new file mode 100644 index 00000000000..9b08f38ca38 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCard-test.tsx.snap @@ -0,0 +1,540 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should display applications 1`] = ` +
+
+
+ + qualifier.APP + + } + placement="top" + > + + + + +

+ + Foo + +

+ + + + + +
+
+
+ +
+
+
+
+
+ +
+
+
+
+`; + +exports[`should display applications: with project count 1`] = ` +
+
+
+ + qualifier.APP + + ‒ + x_projects_.3 + + + } + placement="top" + > + + + + +

+ + Foo + +

+ + + + + +
+
+
+ +
+
+
+
+
+ +
+
+
+
+`; + +exports[`should display configure analysis button for logged in user 1`] = ` +
+
+
+

+ + Foo + +

+
+
+
+ +
+
+
+
+
+
+ + projects.not_analyzed.TRK + + + projects.configure_analysis + +
+
+
+
+
+`; + +exports[`should display correclty when project need issue synch 1`] = ` +
+
+
+

+ Foo +

+ + + + + +
+
+
+ +
+
+
+
+
+ +
+
+
+
+`; + +exports[`should display not analyzed yet 1`] = ` +
+
+
+

+ + Foo + +

+
+
+
+ +
+
+
+
+
+
+ + projects.not_analyzed.TRK + +
+
+
+
+
+`; + +exports[`should display the overall measures and quality gate 1`] = ` +
+
+
+

+ + Foo + +

+ + + + + +
+
+
+ +
+
+
+
+
+ +
+
+
+
+`; diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCardLanguages-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCardLanguages-test.tsx.snap new file mode 100644 index 00000000000..a83d0479a91 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCardLanguages-test.tsx.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`handles unknown languages 1`] = ` + + cpp, Java + +`; + +exports[`handles unknown languages 2`] = ` + + unknown, Java + +`; + +exports[`renders 1`] = ` + + Java, JavaScript + +`; + +exports[`sorts languages 1`] = ` + + JavaScript, Java + +`; diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCardMeasure-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCardMeasure-test.tsx.snap new file mode 100644 index 00000000000..b0c7f29bb95 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCardMeasure-test.tsx.snap @@ -0,0 +1,38 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +
+
+ + + test-label + +
+
+ + + + +
+
+`; diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCardMeasures-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCardMeasures-test.tsx.snap new file mode 100644 index 00000000000..20d8cf07dd1 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCardMeasures-test.tsx.snap @@ -0,0 +1,314 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`New code measures should be rendered properly 1`] = ` + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+ + +
+ + + + +
+
+
+
+`; + +exports[`Overall measures should be not be rendered if there's no line of code: application 1`] = ` +
+ portfolio.app.empty +
+`; + +exports[`Overall measures should be not be rendered if there's no line of code: project 1`] = ` +
+ overview.project.main_branch_empty +
+`; + +exports[`Overall measures should be rendered properly 1`] = ` + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+ + +
+ + + + +
+
+
+
+`; diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCardQualityGate-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCardQualityGate-test.tsx.snap new file mode 100644 index 00000000000..1cf84c5f4b5 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/__snapshots__/ProjectCardQualityGate-test.tsx.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders 1`] = ` +
+ +
+`; diff --git a/server/sonar-web/src/main/js/apps/projects/styles.css b/server/sonar-web/src/main/js/apps/projects/styles.css index 32f24fed987..f2fd47f42bd 100644 --- a/server/sonar-web/src/main/js/apps/projects/styles.css +++ b/server/sonar-web/src/main/js/apps/projects/styles.css @@ -55,144 +55,14 @@ margin-bottom: 0; } -.project-card { - position: relative; - min-height: 114px; - box-sizing: border-box; -} - -.project-card-header { - display: flex; - align-items: center; -} - -.project-card-header-right { - margin-left: auto; -} - .project-card-name { font-weight: 600; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.project-card-leak-date { - padding: 4px 8px; - margin: -5px -4px -5px 24px; - background-color: var(--leakPrimaryColor); - border: 1px solid var(--leakSecondaryColor); -} - -.project-card-measures { - display: flex; - padding-top: 8px; - margin: 0 -15px 0 -30px; -} - -.project-card-leak-measures { - display: flex; - padding: 8px 0; - margin: 4px -4px; - background-color: var(--leakPrimaryColor); - border: 1px solid var(--leakSecondaryColor); -} - -.projects-leak-sorting-option { - background-color: var(--leakPrimaryColor); - margin-bottom: 2px; } .projects-leak-sorting-option.is-focused { background-color: var(--leakSecondaryColor); } -.project-card-measure { - flex: 0 1 15%; -} - -.project-card-measure { - position: relative; - display: inline-block; - vertical-align: top; - text-align: center; - box-sizing: border-box; - padding: 0 var(--gridSize); -} - -.project-card-measure + .project-card-measure:before { - position: absolute; - top: 50%; - left: 0; - width: 0; - height: var(--controlHeight); - margin-top: -12px; - border-left: 1px solid var(--barBorderColor); - content: ''; -} - -.project-card-measure .level { - margin-top: 0; - margin-bottom: 0; -} - -.project-card-measure-inner { - display: inline-block; - vertical-align: top; - text-align: center; -} - -.project-card-measure-number { - line-height: 25px; - font-size: 18px; - white-space: nowrap; -} - -.project-card-measure-label { - margin-top: 4px; - font-size: var(--smallFontSize); -} - -.project-card-measure-label-with-icon { - margin-top: 2px; - font-size: var(--smallFontSize); - white-space: nowrap; -} - -.project-card-ncloc { - margin-left: auto; - text-align: right; -} - -.project-card-ncloc:before { - display: none; -} - -.project-card-ncloc .project-card-measure-inner { - text-align: right; -} - -.project-card-languages { - display: inline-block; - max-width: 120px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.project-card-quality-gate { - line-height: var(--controlHeight); -} - -.project-card-not-analyzed { - padding: 14px 0; -} - -.projects-facet-header { - padding: 10px; - transition: none; -} - .projects-facet-list { padding-left: 10px; padding-right: 10px; @@ -208,9 +78,6 @@ float: right; } -.projects-facets-reset .button { -} - .projects-facet-bar { display: inline-block; width: 60px; @@ -295,33 +162,3 @@ padding: calc(4 * var(--gridSize)) 0; text-align: center; } - -.need-issue-sync .rating, -.need-issue-sync .size-rating, -.need-issue-sync .duplications-rating:after, -.need-issue-sync .level { - background-color: lightgray; -} - -.need-issue-sync .project-card-dates path, -.need-issue-sync .project-card-measure path, -.need-issue-sync .project-card-leak-measures path { - fill: lightgray !important; -} - -.need-issue-sync .duplications-rating { - border-color: lightgray; -} - -.need-issue-sync * { - color: #adadad; -} - -.need-issue-sync .project-card-header .icon-outline.is-filled path { - color: rgb(237, 125, 32); -} - -.need-issue-sync .rating, -.need-issue-sync .size-rating { - color: white !important; -} diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 4f1f00eaf45..f2a2941693e 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -913,7 +913,7 @@ projects.no_new_code_period.TRK=Project has no new code data yet. projects.no_new_code_period.APP=Application has no new code data yet. projects.new_code_period_x=New code: last {0} projects.configure_analysis=Configure analysis -projects.last_analysis_on_x=Last analysis: {0} +projects.last_analysis_on_x=Last analysis: {date} projects.search=Search by project name or key projects.perspective=Perspective projects.skip_to_filters=Skip to project filters @@ -2736,7 +2736,6 @@ overview.failed_conditions=Failed conditions overview.X_more_failed_conditions={0} more failed conditions overview.X_conditions_failed={0} conditions failed overview.quality_gate=Quality Gate Status -overview.quality_gate_x=Quality Gate: {0} overview.quality_gate_failed_with_x=with {0} errors overview.quality_gate_code_clean=Your code is clean! overview.quality_gate_all_conditions_passed=All conditions passed. -- 2.39.5