export * from './modal/Modal';
export * from './popups';
export * from './subnavigation';
+export * from './visual-components';
--- /dev/null
+/*
+ * 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 { useTheme } from '@emotion/react';
+import { themeColor } from '../../helpers/theme';
+
+interface Props {
+ className?: string;
+}
+
+export function FishVisual({ className }: Props) {
+ const theme = useTheme();
+
+ return (
+ <svg
+ className={className}
+ fill="none"
+ height="168"
+ width="168"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <g clipPath="url(#a)">
+ <path
+ d="M7 96.939c44.414-18.728 131.812 14.152 153-.439-.653 2.194-2.176 6.256-3.482 9.214C126.569 125.531 82.424 95.788 17.229 118 9.612 105.934 7 96.939 7 96.939ZM163.931 77.355C115.704 96.06 27.656 67.785 4.649 82.357 4.216 79.85 4 75.245 4 71.765 36.52 51.972 92.273 80.185 163.065 58c1.299 15.294.866 19.355.866 19.355ZM161 51.205C146.124 49.19 78.726 42.909 5 56a122.739 122.739 0 0 1 2.813-10.99c28.002-12.889 125.425-4.826 151.24-.798A745.03 745.03 0 0 1 161 51.205Z"
+ fill={themeColor('illustrationShade')({ theme })}
+ />
+ <path
+ d="M134 133.844C120.137 144.595 102.699 151 83.758 151 57.4 151 33.956 138.598 19 119.341c61.278-7.139 76.068 16.657 115 14.503Z"
+ fill={themeColor('illustrationSecondary')({ theme })}
+ />
+ <path
+ clipRule="evenodd"
+ d="M26.371 19.118C13.431 33.093 5.526 51.785 5.526 72.328c0 25.124 11.826 47.485 30.228 61.828 13.287 10.356 29.995 16.525 48.152 16.525 43.288 0 78.379-35.08 78.379-78.353 0-20.543-7.905-39.235-20.845-53.21l3.691-3.416c13.767 14.868 22.184 34.767 22.184 56.626 0 46.051-37.344 83.382-83.41 83.382-19.315 0-37.104-6.567-51.244-17.588C13.091 122.868.496 99.068.496 72.328c0-21.86 8.418-41.758 22.184-56.626l3.691 3.416Z"
+ fill={themeColor('illustrationOutline')({ theme })}
+ fillRule="evenodd"
+ />
+ <path
+ clipRule="evenodd"
+ d="m27.073 19.091-.338.364C13.877 33.343 6.023 51.916 6.023 72.328c0 24.964 11.75 47.184 30.036 61.436 13.203 10.291 29.805 16.42 47.847 16.42 43.014 0 77.883-34.858 77.883-77.856 0-20.413-7.855-38.986-20.713-52.873l-.337-.364L145.158 15l.337.364c13.849 14.956 22.317 34.975 22.317 56.964 0 46.325-37.567 83.879-83.906 83.879-19.43 0-37.326-6.607-51.55-17.693C12.669 123.17 0 99.227 0 72.328c0-21.99 8.468-42.008 22.316-56.964l.337-.364 4.42 4.091Zm-.702.027-.013.015C13.426 33.105 5.526 51.791 5.526 72.328c0 25.124 11.826 47.485 30.228 61.828 13.287 10.356 29.995 16.525 48.152 16.525 43.288 0 78.379-35.08 78.379-78.353 0-20.536-7.9-39.223-20.832-53.196a.18.18 0 0 0-.013-.014l3.691-3.416.014.015.322.35c13.568 14.828 21.848 34.58 21.848 56.26 0 46.052-37.344 83.383-83.41 83.383-19.315 0-37.104-6.567-51.244-17.588C13.091 122.868.496 99.068.496 72.328c0-21.68 8.28-41.433 21.848-56.261l.32-.347.016-.018 3.691 3.416Z"
+ fill={themeColor('illustrationOutline')({ theme })}
+ fillRule="evenodd"
+ />
+ <path
+ d="M68 19a5 5 0 1 0 10 0 5 5 0 0 0-10 0ZM76 40.925a5 5 0 1 0 10 0 5 5 0 0 0-10 0ZM68 62.12a5 5 0 1 0 10 0 5 5 0 0 0-10 0Z"
+ fill={themeColor('illustrationSecondary')({ theme })}
+ />
+ <path
+ d="M152.238 54.288c-2.47.206-5.832 4.717-7.72 6.69l1.544 9.264c1.544 1.287 3.603 5.661 7.205 5.918 3.603.258 5.662-5.918 5.662-11.58 0-5.753-3.603-10.55-6.691-10.292Z"
+ fill={themeColor('illustrationPrimary')({ theme })}
+ />
+ <path
+ d="M146.062 70.242c10.035 34.224-51.723 17.498-63.303 6.176-.772-.258-1.08-3.448 1.802-2.83 9.263 1.028 10.035-7.206 10.55-11.066.515-3.86 3.345-17.755 25.218-21.1 21.873-3.346 27.019 14.152 23.931 19.814 4.375-.258 7.72 6.433 1.802 9.006Z"
+ fill={themeColor('illustrationPrimary')({ theme })}
+ />
+ <path
+ d="M100.49 70.868a5.844 5.844 0 1 0 11.689 0 5.844 5.844 0 0 0-11.689 0Z"
+ fill={themeColor('illustrationPrimary')({ theme })}
+ />
+ <path
+ clipRule="evenodd"
+ d="M106.334 72.878a2.01 2.01 0 1 1 0-4.02 2.01 2.01 0 0 1 0 4.02Zm0 3.834a5.844 5.844 0 1 1 0-11.688 5.844 5.844 0 0 1 0 11.688Z"
+ fill={themeColor('backgroundSecondary')({ theme })}
+ fillRule="evenodd"
+ />
+ <path
+ clipRule="evenodd"
+ d="M115.047 107.823a1.78 1.78 0 1 1-3.301-1.332 1.78 1.78 0 0 1 3.301 1.332Zm-1.879 4.479a5.15 5.15 0 0 0 2.154-9.918 5.15 5.15 0 0 0-5.285 8.676l-1.572 3.898-8.012-3.231-1.26 3.123 8.012 3.232-6.841 16.96c-6.675-3.451-9.804-11.129-8.727-13.8l-2.842-1.147c-2.348 5.824 2.488 12.284 6.155 17.183 1.94 2.591 3.552 4.745 3.601 6.137.995-.964 3.62-1.374 6.785-1.869 6.029-.943 14.017-2.191 16.422-8.155l-2.842-1.146c-1.125 2.79-8.357 5.916-15.41 4.013l6.822-16.916 7.526 3.035 1.26-3.124-7.526-3.035 1.58-3.916Zm8.945 16.385-5.849 2.487 8.336 3.363-2.487-5.85ZM91.8 116.461l2.487 5.849-8.336-3.362 5.85-2.487Z"
+ fill={themeColor('illustrationOutline')({ theme })}
+ fillRule="evenodd"
+ />
+ </g>
+ <defs>
+ <clipPath id="a">
+ <path d="M0 0h168v168H0z" fill={themeColor('backgroundSecondary')({ theme })} />
+ </clipPath>
+ </defs>
+ </svg>
+ );
+}
--- /dev/null
+/*
+ * 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.
+ */
+export * from './FishVisual';
const isLast = !isNotLast;
return (
- <div key={breadcrumbElement.key} className="sw-flex">
+ <div key={breadcrumbElement.key} className="sw-flex sw-items-center">
{isLast && isLoggedIn(currentUser) && (
<Favorite
className="sw-mr-2"
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Highlight } from 'design-system';
import * as React from 'react';
-import '../../../components/common/EmptySearch.css';
import { translate } from '../../../helpers/l10n';
export default function NoIssues() {
return (
- <div className="empty-search">
- <h3>{translate('issues.no_issues')}</h3>
+ <div className="sw-text-center sw-py-8">
+ <Highlight as="h3" className="sw-body-md-highlight">
+ {translate('issues.no_issues')}
+ </Highlight>
</div>
);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Highlight } from 'design-system';
import * as React from 'react';
-import '../../../components/common/EmptySearch.css';
import { translate } from '../../../helpers/l10n';
export default function NoMyIssues() {
return (
- <div className="empty-search">
- <h3>{translate('issues.no_my_issues')}</h3>
+ <div className="sw-text-center sw-py-8">
+ <Highlight as="h3" className="sw-body-md-highlight">
+ {translate('issues.no_my_issues')}
+ </Highlight>
</div>
);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Card, Note, StandoutLink } from 'design-system';
+import { FishVisual, Highlight, StandoutLink } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
-import '../../../components/common/EmptySearch.css';
import { translate } from '../../../helpers/l10n';
import { queryToSearch } from '../../../helpers/urls';
import { Dict } from '../../../types/types';
export default function EmptyFavoriteSearch({ query }: { query: Query }) {
return (
- <Card aria-live="assertive">
- <Note className="sw-text-center sw-flex-column">
- <h3>{translate('no_results_search.favorites')}</h3>
- <div>
- <FormattedMessage
- defaultMessage={translate('no_results_search.favorites.2')}
- id="no_results_search.favorites.2"
- values={{
- url: (
- <StandoutLink
- to={{
- pathname: '/projects',
- search: queryToSearch(query as Dict<string | undefined | number>),
- }}
- >
- {translate('all')}
- </StandoutLink>
- ),
- }}
- />
- </div>
- </Note>
- </Card>
+ <div aria-live="assertive" className="sw-py-8 sw-text-center">
+ <FishVisual />
+ <Highlight as="h3" className="sw-body-md-highlight sw-mt-6">
+ {translate('no_results_search.favorites')}
+ </Highlight>
+ <div className="sw-my-4 sw-body-sm">
+ <FormattedMessage
+ defaultMessage={translate('no_results_search.favorites.2')}
+ id="no_results_search.favorites.2"
+ values={{
+ url: (
+ <StandoutLink
+ to={{
+ pathname: '/projects',
+ search: queryToSearch(query as Dict<string | undefined | number>),
+ }}
+ >
+ {translate('all')}
+ </StandoutLink>
+ ),
+ }}
+ />
+ </div>
+ </div>
);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { ButtonPrimary, FishVisual, Highlight } from 'design-system';
import * as React from 'react';
-import { Button } from '../../../components/controls/buttons';
import { Router, withRouter } from '../../../components/hoc/withRouter';
import { translate } from '../../../helpers/l10n';
import { hasGlobalPermission } from '../../../helpers/users';
isLoggedIn(currentUser) && hasGlobalPermission(currentUser, Permissions.ProjectCreation);
return (
- <div className="projects-empty-list">
- <h3>
+ <div className="sw-text-center sw-py-8">
+ <FishVisual />
+ <Highlight as="h3" className="sw-body-md-highlight sw-mt-6">
{showNewProjectButton
? translate('projects.no_projects.empty_instance.new_project')
: translate('projects.no_projects.empty_instance')}
- </h3>
+ </Highlight>
{showNewProjectButton && (
<div>
- <p className="big-spacer-top">
+ <p className="sw-mt-2 sw-body-sm">
{translate('projects.no_projects.empty_instance.how_to_add_projects')}
</p>
- <p className="big-spacer-top">
- <Button
- onClick={() => {
- router.push('/projects/create');
- }}
- >
- {translate('my_account.create_new.TRK')}
- </Button>
- </p>
+ <ButtonPrimary
+ className="sw-mt-6"
+ onClick={() => {
+ router.push('/projects/create');
+ }}
+ >
+ {translate('my_account.create_new.TRK')}
+ </ButtonPrimary>
</div>
)}
</div>
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Highlight, StandoutLink } from 'design-system';
import * as React from 'react';
-import Link from '../../../components/common/Link';
import { translate } from '../../../helpers/l10n';
export default function NoFavoriteProjects() {
return (
- <div className="projects-empty-list">
- <h3>{translate('projects.no_favorite_projects')}</h3>
+ <div className="sw-py-8 sw-text-center">
+ <Highlight as="h3" className="sw-mb-2 sw-body-md-highlight">
+ {translate('projects.no_favorite_projects')}
+ </Highlight>
<div>
- <p className="big-spacer-top">{translate('projects.no_favorite_projects.engagement')}</p>
- <p className="big-spacer-top">
- <Link className="button" to="/projects/all">
+ <p className="sw-mt-2 sw-body-sm">
+ {translate('projects.no_favorite_projects.engagement')}
+ </p>
+ <p className="sw-mt-6">
+ <StandoutLink className="sw-mt-6 sw-body-sm-highlight" to="/projects/all">
{translate('projects.explore_projects')}
- </Link>
+ </StandoutLink>
</p>
</div>
</div>
<div className="sw-flex sw-items-center">
{total != null && (
<>
- <LightPrimary id="projects-total" className="sw-font-semibold sw-mr-1">
+ <LightPrimary id="projects-total" className="sw-body-sm-highlight sw-mr-1">
{total}
</LightPrimary>
- <LightLabel>{translate('projects_')}</LightLabel>
+ <LightLabel className="sw-body-sm">{translate('projects_')}</LightLabel>
</>
)}
<HomePageSelect currentPage={{ type: 'PROJECTS' }} />
import NoFavoriteProjects from './NoFavoriteProjects';
import ProjectCard from './project-card/ProjectCard';
-const PROJECT_CARD_HEIGHT = 185;
+const PROJECT_CARD_HEIGHT = 181;
const PROJECT_CARD_MARGIN = 20;
interface Props {
+++ /dev/null
-/*
- * 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 { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockRouter } from '../../../../helpers/testMocks';
-import { EmptyInstance } from '../EmptyInstance';
-
-it('renders correctly for SQ', () => {
- expect(
- shallow(
- <EmptyInstance
- currentUser={{ isLoggedIn: false, dismissedNotices: {} }}
- router={mockRouter()}
- />
- )
- ).toMatchSnapshot();
- expect(
- shallow(
- <EmptyInstance
- currentUser={{
- isLoggedIn: true,
- permissions: { global: ['provisioning'] },
- dismissedNotices: {},
- }}
- router={mockRouter()}
- />
- )
- ).toMatchSnapshot();
-});
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders correctly for SQ 1`] = `
-<div
- className="projects-empty-list"
->
- <h3>
- projects.no_projects.empty_instance
- </h3>
-</div>
-`;
-
-exports[`renders correctly for SQ 2`] = `
-<div
- className="projects-empty-list"
->
- <h3>
- projects.no_projects.empty_instance.new_project
- </h3>
- <div>
- <p
- className="big-spacer-top"
- >
- projects.no_projects.empty_instance.how_to_add_projects
- </p>
- <p
- className="big-spacer-top"
- >
- <Button
- onClick={[Function]}
- >
- my_account.create_new.TRK
- </Button>
- </p>
- </div>
-</div>
-`;
exports[`renders 1`] = `
<div
- className="projects-empty-list"
+ className="sw-py-8 sw-text-center"
>
- <h3>
+ <Highlight
+ as="h3"
+ className="sw-mb-2 sw-body-md-highlight"
+ >
projects.no_favorite_projects
- </h3>
+ </Highlight>
<div>
<p
- className="big-spacer-top"
+ className="sw-mt-2 sw-body-sm"
>
projects.no_favorite_projects.engagement
</p>
<p
- className="big-spacer-top"
+ className="sw-mt-6"
>
- <ForwardRef(Link)
- className="button"
+ <StandoutLink
+ className="sw-mt-6 sw-body-sm-highlight"
to="/projects/all"
>
projects.explore_projects
- </ForwardRef(Link)>
+ </StandoutLink>
</p>
</div>
</div>
+++ /dev/null
-/*
- * 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.
- */
-.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);
-}
import {
Badge,
Card,
+ LightLabel,
+ LightPrimary,
Note,
QualityGateIndicator,
SeparatorCircleIcon,
StandoutLink,
+ SubnavigationFlowSeparator,
Tags,
themeBorder,
themeColor,
import { Status } from '../../../../types/types';
import { CurrentUser, isLoggedIn } from '../../../../types/users';
import { Project } from '../../types';
-import './ProjectCard.css';
import ProjectCardLanguages from './ProjectCardLanguages';
import ProjectCardMeasures from './ProjectCardMeasures';
const formatted = formatMeasure(measures[MetricKey.alert_status], MetricType.Level);
const qualityGateLabel = translateWithParameters('overview.quality_gate_x', formatted);
return (
- <div>
+ <>
<div className="sw-flex sw-justify-between sw-items-center ">
<div className="sw-flex sw-items-center ">
{isFavorite !== undefined && (
<Favorite
- className="spacer-right"
+ className="sw-mr-2"
component={key}
componentName={name}
favorite={isFavorite}
</span>
}
>
- <span className="sw-ml-2">
- <Badge>{translate('qualifier.APP')}</Badge>
+ <span>
+ <Badge className="sw-ml-2 sw-font-sans">{translate('qualifier.APP')}</Badge>
</span>
</Tooltip>
)}
{visibility === Visibility.Private && (
<Tooltip overlay={translate('visibility', visibility, 'description', qualifier)}>
- <span className="sw-ml-2">
- <Badge>{translate('visibility', visibility)}</Badge>
+ <span>
+ <Badge className="sw-ml-2 sw-font-sans">
+ {translate('visibility', visibility)}
+ </Badge>
</span>
</Tooltip>
)}
</div>
{analysisDate && (
- <div>
- <Tooltip overlay={qualityGateLabel}>
- <span className="sw-flex sw-items-center">
- <QualityGateIndicator
- status={(measures[MetricKey.alert_status] as Status) ?? 'NONE'}
- className="sw-mr-2"
- ariaLabel={qualityGateLabel}
- />
- <span className="sw-ml-2 sw-body-sm-highlight">{formatted}</span>
- </span>
- </Tooltip>
- </div>
+ <Tooltip overlay={qualityGateLabel}>
+ <span className="sw-flex sw-items-center">
+ <QualityGateIndicator
+ status={(measures[MetricKey.alert_status] as Status) ?? 'NONE'}
+ ariaLabel={qualityGateLabel}
+ />
+ <LightPrimary className="sw-ml-2 sw-body-sm-highlight">{formatted}</LightPrimary>
+ </span>
+ </Tooltip>
)}
</div>
- <div className="sw-flex sw-items-center sw-mt-4">
+ <LightLabel as="div" className="sw-flex sw-items-center sw-mt-3">
{analysisDate && (
<DateTimeFormatter date={analysisDate}>
{(formattedAnalysisDate) => (
<>
<SeparatorCircleIcon className="sw-mx-1" />
<div>
- <span
- className="js-project-card-measure sw-body-sm-highlight"
- data-key={MetricKey.ncloc}
- >
+ <span className="sw-body-sm-highlight sw-mr-1" data-key={MetricKey.new_lines}>
<Measure
- metricKey={MetricKey.ncloc}
+ metricKey={MetricKey.new_lines}
metricType={MetricType.ShortInteger}
value={measures.new_lines}
- />{' '}
+ />
</span>
<span>{translate('metric.new_lines.name')}</span>
</div>
<>
<SeparatorCircleIcon className="sw-mx-1" />
<div>
- <span
- className="js-project-card-measure sw-body-sm-highlight"
- data-key={MetricKey.ncloc}
- >
+ <span className="sw-body-sm-highlight sw-mr-1" data-key={MetricKey.ncloc}>
<Measure
metricKey={MetricKey.ncloc}
metricType={MetricType.ShortInteger}
value={measures.ncloc}
- />{' '}
+ />
</span>
<span>{translate('metric.ncloc.name')}</span>
</div>
<SeparatorCircleIcon className="sw-mx-1" />
- <span
- className="js-project-card-measure sw-body-sm"
- data-key={MetricKey.ncloc_language_distribution}
- >
+ <span className="sw-body-sm" data-key={MetricKey.ncloc_language_distribution}>
<ProjectCardLanguages distribution={measures.ncloc_language_distribution} />
</span>
</>
{tags.length > 0 && (
<>
<SeparatorCircleIcon className="sw-mx-1" />
- <Tags emptyText="random" ariaTagsListLabel="why not" tooltip={Tooltip} tags={tags} />
+ <Tags
+ emptyText={translate('issue.no_tag')}
+ ariaTagsListLabel={translate('issue.tags')}
+ tooltip={Tooltip}
+ tags={tags}
+ tagsToDisplay={2}
+ />
</>
)}
- </div>
- </div>
+ </LightLabel>
+ </>
);
}
}
return (
- <div className="sw-flex">
- <Note>
+ <div className="sw-flex sw-items-center">
+ <Note className="sw-py-4">
{isNewCode && analysisDate
? translate('projects.no_new_code_period', qualifier)
: translate('projects.not_analyzed', qualifier)}
!analysisDate &&
isLoggedIn(currentUser) &&
!needIssueSync && (
- <StandoutLink className="sw-ml-1" to={getProjectUrl(key)}>
+ <StandoutLink className="sw-ml-2 sw-body-sm-highlight" to={getProjectUrl(key)}>
{translate('projects.configure_analysis')}
</StandoutLink>
)}
data-key={project.key}
>
{renderFirstLine(project, props.handleFavorite, isNewCode)}
- <Separator className="sw-h-0 sw-mx-1 sw-my-3" />
+ <SubnavigationFlowSeparator className="sw-my-3" />
{renderSecondLine(currentUser, project, isNewCode)}
</ProjectCardWrapper>
);
}
-const Separator = styled.hr`
- border-top: ${themeBorder('default', 'projectCardBorder')};
-`;
-
const ProjectCardWrapper = styled(Card)`
background-color: ${themeColor('projectCardBackground')};
border: ${themeBorder('default', 'projectCardBorder')};
- &.project-card-disabled {
+ &.project-card-disabled *:not(g):not(path) {
color: ${themeColor('projectCardDisabled')} !important;
}
`;
import { sortBy } from 'lodash';
import * as React from 'react';
import withLanguagesContext from '../../../../app/components/languages/withLanguagesContext';
+import Tooltip from '../../../../components/controls/Tooltip';
import { translate } from '../../../../helpers/l10n';
import { Languages } from '../../../../types/languages';
languages: Languages;
}
+const MAX_DISPLAYED_LANGUAGES = 2;
+
export function ProjectCardLanguages({ className, distribution, languages }: Props) {
if (distribution === undefined) {
return null;
getLanguageName(languages, l[0])
);
- const languagesText = finalLanguages.join(', ');
+ const languagesText =
+ finalLanguages.slice(0, MAX_DISPLAYED_LANGUAGES).join(', ') +
+ (finalLanguages.length > MAX_DISPLAYED_LANGUAGES ? ', ...' : '');
+
+ const tooltip =
+ finalLanguages.length > MAX_DISPLAYED_LANGUAGES ? (
+ <span>
+ {finalLanguages.map((language) => (
+ <span key={language}>
+ {language}
+ <br />
+ </span>
+ ))}
+ </span>
+ ) : null;
return (
- <span className={className} title={languagesText}>
- {languagesText}
- </span>
+ <Tooltip overlay={tooltip}>
+ <span className={className} title={languagesText}>
+ {languagesText}
+ </span>
+ </Tooltip>
);
}
return (
<div
data-key={metricKey}
- className={classNames(
- 'it__project_card_measure sw-flex-column sw-box-border sw-text-center',
- className
- )}
+ className={classNames('it__project_card_measure sw-text-center', className)}
>
<div className="sw-text-center">{children}</div>
<div className="sw-body-sm sw-mt-1/2 sw-whitespace-nowrap" title={label}>
MetricsLabel,
MetricsRatingBadge,
Note,
+ PageContentFontWrapper,
} from 'design-system';
import * as React from 'react';
import Measure from '../../../../components/measure/Measure';
return (
<ProjectCardMeasure metricKey={coverageMetric} label={translate('metric.coverage.name')}>
<div>
- {measures[coverageMetric] && (
- <span>
- <CoverageIndicator value={measures[coverageMetric]} />
- </span>
- )}
- <span className="sw-ml-2 sw-body-md-highlight">
- <Measure
- metricKey={coverageMetric}
- metricType={MetricType.Percent}
- value={measures[coverageMetric]}
- />
- </span>
+ {measures[coverageMetric] && <CoverageIndicator value={measures[coverageMetric]} />}
+ <Measure
+ metricKey={coverageMetric}
+ metricType={MetricType.Percent}
+ value={measures[coverageMetric]}
+ className="sw-ml-2 sw-body-md-highlight"
+ />
</div>
</ProjectCardMeasure>
);
label={translate('metric.duplicated_lines_density.short_name')}
>
<div>
- {measures[duplicationMetric] != null && (
- <span>
- <DuplicationsIndicator rating={rating} />
- </span>
- )}
- <span className="sw-ml-2 sw-body-md-highlight">
- <Measure
- metricKey={duplicationMetric}
- metricType={MetricType.Percent}
- value={measures[duplicationMetric]}
- />
- </span>
+ {measures[duplicationMetric] != null && <DuplicationsIndicator rating={rating} />}
+ <Measure
+ metricKey={duplicationMetric}
+ metricType={MetricType.Percent}
+ value={measures[duplicationMetric]}
+ className="sw-ml-2 sw-body-md-highlight"
+ />
</div>
</ProjectCardMeasure>
);
return (
<ProjectCardMeasure key={metricKey} metricKey={metricKey} label={iconLabel}>
<MetricsRatingBadge label={metricKey} rating={value as MetricsLabel} />
-
- <span className="sw-ml-2 sw-body-md-highlight">
- <Measure metricKey={metricKey} metricType={metricType} value={measures[metricKey]} />
- </span>
+ <Measure
+ metricKey={metricKey}
+ metricType={metricType}
+ value={measures[metricKey]}
+ className="sw-ml-2 sw-body-md-highlight"
+ />
</ProjectCardMeasure>
);
});
if (!isNewCode && !ncloc) {
return (
- <Note>
+ <Note className="sw-py-4">
{componentQualifier === ComponentQualifier.Application
? translate('portfolio.app.empty')
: translate('overview.project.main_branch_empty')}
].filter(isDefined);
return (
- <div className="sw-flex sw-gap-8">
+ <PageContentFontWrapper className="sw-flex sw-gap-8">
{measureList.map((measure, i) => (
// eslint-disable-next-line react/no-array-index-key
<React.Fragment key={i}>{measure}</React.Fragment>
))}
- </div>
+ </PageContentFontWrapper>
);
}
+++ /dev/null
-/*
- * 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 * as React from 'react';
-import HelpTooltip from '../../../../components/controls/HelpTooltip';
-import Level from '../../../../components/ui/Level';
-import { translate } from '../../../../helpers/l10n';
-import { formatMeasure } from '../../../../helpers/measures';
-
-interface Props {
- status?: string;
-}
-
-export default function ProjectCardQualityGate({ status }: Props) {
- if (!status) {
- return null;
- }
-
- const title = `${translate('quality_gates.status')}: ${formatMeasure(status, 'LEVEL')}`;
-
- return (
- <div className="big-spacer-left" title={title}>
- <Level aria-label={title} level={status} small />
- {status === 'WARN' && (
- <HelpTooltip
- className="little-spacer-left"
- overlay={translate('quality_gates.conditions.warning.tooltip')}
- />
- )}
- </div>
- );
-}
const USER_LOGGED_OUT = mockCurrentUser();
const USER_LOGGED_IN = mockLoggedInUser();
-it('should display correclty when project need issue synch and not setup', () => {
+it('should display correclty when project need issue sync and not setup', () => {
renderProjectCard({ ...PROJECT, needIssueSync: true });
expect(screen.getByLabelText('overview.quality_gate_x.OK')).toBeInTheDocument();
expect(screen.getByText('overview.project.main_branch_empty')).toBeInTheDocument();
js: { key: 'js', name: 'JavaScript' },
};
-it('renders', () => {
+it('should render normally', () => {
renderProjectCardLanguages('java=137;js=15');
expect(screen.getByText('Java, JavaScript')).toBeInTheDocument();
});
-it('sorts languages', () => {
+it('shoould sorts languages', () => {
renderProjectCardLanguages('java=13;js=152');
expect(screen.getByText('JavaScript, Java')).toBeInTheDocument();
});
-it('handles unknown languages', () => {
+it('should handle unknown languages', () => {
renderProjectCardLanguages('java=13;cpp=18');
expect(screen.getByText('cpp, Java')).toBeInTheDocument();
});
+it('should handle more then 3 languages', async () => {
+ renderProjectCardLanguages('java=137;js=18;cpp=10;c=8;php=4');
+ await expect(screen.getByText('Java, JavaScript, ...')).toHaveATooltipWithContent(
+ 'JavaJavaScriptcppcphp'
+ );
+});
+
function renderProjectCardLanguages(distribution?: string) {
- renderComponent(<ProjectCardLanguages languages={languages} distribution={distribution} />);
+ return renderComponent(
+ <ProjectCardLanguages languages={languages} distribution={distribution} />
+ );
}
+++ /dev/null
-/*
- * 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 { screen } from '@testing-library/react';
-import * as React from 'react';
-import { renderComponent } from '../../../../../helpers/testReactTestingUtils';
-import ProjectCardQualityGate from '../ProjectCardQualityGate';
-
-it('renders', () => {
- renderProjectCardQualityGate('ERROR');
- expect(screen.getByTitle('quality_gates.status: ERROR')).toBeInTheDocument();
-});
-
-function renderProjectCardQualityGate(status?: string) {
- renderComponent(<ProjectCardQualityGate status={status} />);
-}
+++ /dev/null
-/*
- * 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.
- */
-.empty-search {
- padding: 60px 0;
- border: 1px solid var(--barBorderColor);
- border-radius: 2px;
- color: var(--secondFontColor);
- text-align: center;
-}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { FishVisual, Highlight } from 'design-system';
import * as React from 'react';
import { translate } from '../../helpers/l10n';
-import './EmptySearch.css';
export default function EmptySearch() {
return (
- <div aria-live="assertive" className="empty-search">
- <h3>{translate('no_results_search')}</h3>
- <p className="big-spacer-top">{translate('no_results_search.2')}</p>
+ <div aria-live="assertive" className="sw-text-center sw-py-8">
+ <FishVisual />
+ <Highlight as="h3" className="sw-body-md-highlight sw-mt-6">
+ {translate('no_results_search')}
+ </Highlight>
+ <p className="sw-body-sm sw-mt-2">{translate('no_results_search.2')}</p>
</div>
);
}
+++ /dev/null
-/*
- * 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 { shallow } from 'enzyme';
-import * as React from 'react';
-import EmptySearch from '../EmptySearch';
-
-it('renders', () => {
- expect(shallow(<EmptySearch />)).toMatchSnapshot();
-});
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<div
- aria-live="assertive"
- className="empty-search"
->
- <h3>
- no_results_search
- </h3>
- <p
- className="big-spacer-top"
- >
- no_results_search.2
- </p>
-</div>
-`;