margin-right: -20px;
padding: 8px 20px;
}
-
-.boxed-group-loading {
- position: relative;
- transition: border-color 0.25s;
-}
-
-.boxed-group-loading:before,
-.boxed-group-loading:after {
- position: absolute;
- z-index: 1;
- border: 2px solid transparent;
- box-sizing: border-box;
- content: '';
-}
-
-.boxed-group-loading:before {
- width: 100%;
- height: 100%;
- top: 0;
- left: 0;
- border-top-color: #4b9fd5;
- border-right-color: #4b9fd5;
- animation: 3s top-left-border 0s infinite;
-}
-
-.boxed-group-loading:after {
- width: 0;
- height: 0;
- bottom: 0;
- right: 0;
- border-bottom-color: #4b9fd5;
- border-left-color: #4b9fd5;
- animation: 3s border-bottom-border 0s infinite;
-}
-
-@keyframes top-left-border {
- 0% {
- width: 0;
- height: 0;
- }
-
- 25% {
- width: 100%;
- height: 0;
- }
-
- 50%,
- 100% {
- width: 100%;
- height: 100%;
- }
-}
-
-@keyframes border-bottom-border {
- 0%,
- 50% {
- width: 0;
- height: 0;
- border-width: 0;
- }
-
- 51% {
- border-width: 2px;
- }
-
- 75% {
- width: 100%;
- height: 0;
- }
-
- 100% {
- width: 100%;
- height: 100%;
- }
-}
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import * as classNames from 'classnames';
import { Link } from 'react-router';
import DateFromNow from '../../../components/intl/DateFromNow';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
export default function ProjectCardLeak({ organization, project }: Props) {
const { measures } = project;
- const isProjectAnalyzed = project.analysisDate != null;
const isPrivate = project.visibility === 'private';
- const hasLeakPeriodStart = project.leakPeriodDate != undefined;
const hasTags = project.tags.length > 0;
- // check for particular measures because only some measures can be loaded
- // if coming from visualizations tab
- const areProjectMeasuresLoaded = measures != undefined && measures['new_bugs'];
-
- const displayQualityGate = areProjectMeasuresLoaded && isProjectAnalyzed;
- const className = classNames('boxed-group', 'project-card', {
- 'boxed-group-loading': isProjectAnalyzed && hasLeakPeriodStart && !areProjectMeasuresLoaded
- });
-
return (
- <div data-key={project.key} className={className}>
+ <div data-key={project.key} className="boxed-group project-card">
<div className="boxed-group-header clearfix">
{project.isFavorite != null && (
<Favorite
{!organization && <ProjectCardOrganization organization={project.organization} />}
<Link to={{ pathname: '/dashboard', query: { id: project.key } }}>{project.name}</Link>
</h2>
- {displayQualityGate && <ProjectCardQualityGate status={measures!['alert_status']} />}
+ {project.analysisDate && <ProjectCardQualityGate status={measures!['alert_status']} />}
<div className="pull-right text-right">
{isPrivate && <PrivateBadge className="spacer-left" tooltipPlacement="left" />}
{hasTags && <TagsList tags={project.tags} customClass="spacer-left" />}
</div>
- {isProjectAnalyzed &&
- hasLeakPeriodStart && (
+ {project.analysisDate &&
+ project.leakPeriodDate && (
<div className="project-card-dates note text-right pull-right">
- {hasLeakPeriodStart && (
- <DateFromNow date={project.leakPeriodDate!}>
- {fromNow => (
- <span className="project-card-leak-date pull-right">
- {translateWithParameters('projects.leak_period_x', fromNow)}
- </span>
- )}
- </DateFromNow>
- )}
- {isProjectAnalyzed && (
- <DateTimeFormatter date={project.analysisDate!}>
- {formattedDate => (
- <span>
- {translateWithParameters('projects.last_analysis_on_x', formattedDate)}
- </span>
- )}
- </DateTimeFormatter>
- )}
+ <DateFromNow date={project.leakPeriodDate!}>
+ {fromNow => (
+ <span className="project-card-leak-date pull-right">
+ {translateWithParameters('projects.leak_period_x', fromNow)}
+ </span>
+ )}
+ </DateFromNow>
+ <DateTimeFormatter date={project.analysisDate!}>
+ {formattedDate => (
+ <span>{translateWithParameters('projects.last_analysis_on_x', formattedDate)}</span>
+ )}
+ </DateTimeFormatter>
</div>
)}
</div>
- {isProjectAnalyzed && hasLeakPeriodStart ? (
+ {project.analysisDate && project.leakPeriodDate ? (
<div className="boxed-group-inner">
- {areProjectMeasuresLoaded && <ProjectCardLeakMeasures measures={measures} />}
+ <ProjectCardLeakMeasures measures={measures} />
</div>
) : (
<div className="boxed-group-inner">
<div className="note project-card-not-analyzed">
- {isProjectAnalyzed ? (
+ {project.analysisDate ? (
translate('projects.no_leak_period')
) : (
translate('projects.not_analyzed')
import { translate } from '../../../helpers/l10n';
interface Props {
- measures?: { [key: string]: string };
+ measures: { [key: string]: string };
}
export default function ProjectCardLeakMeasures({ measures }: Props) {
- if (measures == undefined) {
- return null;
- }
-
return (
<div className="project-card-leak-measures">
<div className="project-card-measure smaller-card" data-key="new_reliability_rating">
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import * as classNames from 'classnames';
import { Link } from 'react-router';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import ProjectCardQualityGate from './ProjectCardQualityGate';
export default function ProjectCardOverall({ organization, project }: Props) {
const { measures } = project;
- const isProjectAnalyzed = project.analysisDate != undefined;
const isPrivate = project.visibility === 'private';
const hasTags = project.tags.length > 0;
- // check for particular measures because only some measures can be loaded
- // if coming from visualizations tab
- const areProjectMeasuresLoaded =
- measures != undefined &&
- measures['reliability_rating'] != undefined &&
- measures['sqale_rating'] != undefined;
-
- const displayQualityGate = areProjectMeasuresLoaded && isProjectAnalyzed;
- const className = classNames('boxed-group', 'project-card', {
- 'boxed-group-loading': isProjectAnalyzed && !areProjectMeasuresLoaded
- });
-
return (
- <div data-key={project.key} className={className}>
+ <div data-key={project.key} className="boxed-group project-card">
<div className="boxed-group-header clearfix">
{project.isFavorite != undefined && (
<Favorite
{!organization && <ProjectCardOrganization organization={project.organization} />}
<Link to={{ pathname: '/dashboard', query: { id: project.key } }}>{project.name}</Link>
</h2>
- {displayQualityGate && <ProjectCardQualityGate status={measures!['alert_status']} />}
+ {project.analysisDate && <ProjectCardQualityGate status={measures['alert_status']} />}
<div className="pull-right text-right">
{isPrivate && <PrivateBadge className="spacer-left" tooltipPlacement="left" />}
{hasTags && <TagsList tags={project.tags} customClass="spacer-left" />}
)}
</div>
- {isProjectAnalyzed ? (
+ {project.analysisDate ? (
<div className="boxed-group-inner">
- {areProjectMeasuresLoaded && <ProjectCardOverallMeasures measures={measures} />}
+ {<ProjectCardOverallMeasures measures={measures} />}
</div>
) : (
<div className="boxed-group-inner">
import { translate } from '../../../helpers/l10n';
interface Props {
- measures?: { [key: string]: string };
+ measures: { [key: string]: string | undefined };
}
export default function ProjectCardOverallMeasures({ measures }: Props) {
expect(card.find('.project-card-dates').exists()).toBeFalsy();
});
-it('should display loading', () => {
- const measures = { alert_status: 'OK', reliability_rating: '1.0', sqale_rating: '1.0' };
- expect(
- shallow(<ProjectCardLeak project={{ ...PROJECT, measures }} />)
- .find('.boxed-group')
- .hasClass('boxed-group-loading')
- ).toBeTruthy();
-});
-
it('should display tags', () => {
const project = { ...PROJECT, tags: ['foo', 'bar'] };
expect(
).toBeFalsy();
});
-it('should display loading', () => {
- expect(
- shallow(<ProjectCardOverall project={{ ...PROJECT, measures: {} }} />)
- .find('.boxed-group')
- .hasClass('boxed-group-loading')
- ).toBeTruthy();
- expect(
- shallow(<ProjectCardOverall project={{ ...PROJECT, measures: { sqale_rating: '1.0' } }} />)
- .find('.boxed-group')
- .hasClass('boxed-group-loading')
- ).toBeTruthy();
-});
-
it('should not display the quality gate', () => {
const project = { ...PROJECT, analysisDate: undefined };
expect(
const value = isDiffMetric(metric.key) ? measure.leak : measure.value;
if (value == undefined) {
- return null;
+ return <span>{'–'}</span>;
}
if (metric.type === 'LEVEL') {
};
expect(shallow(<Measure measure={measure} />)).toMatchSnapshot();
});
+
+it('renders undefined measure', () => {
+ const measure = { metric: { key: 'foo', name: 'Foo', type: 'PERCENT' } };
+ expect(shallow(<Measure measure={measure} />)).toMatchSnapshot();
+});
</span>
`;
+exports[`renders undefined measure 1`] = `
+<span>
+ –
+</span>
+`;
+
exports[`renders unknown RATING 1`] = `
<Rating
value="4"
className?: string;
muted?: boolean;
small?: boolean;
- value: string | number;
+ value: string | number | undefined;
}
export default function Rating({ className, muted = false, small = false, value }: Props) {
+ if (value == undefined) {
+ return <span>{'–'}</span>;
+ }
const formatted = formatMeasure(value, 'RATING');
return (
<span
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 React from 'react';
-import Rating from '../Rating';
-
-it('should render with numeric value', () => {
- const rating = shallow(<Rating value={2} />);
- expect(rating.is('.rating-B')).toBe(true);
-});
-
-it('should render with string value', () => {
- const rating = shallow(<Rating value="2.0" />);
- expect(rating.is('.rating-B')).toBe(true);
-});
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { shallow } from 'enzyme';
+import Rating from '../Rating';
+
+it('renders numeric value', () => {
+ expect(shallow(<Rating value={2} />)).toMatchSnapshot();
+});
+
+it('renders string value', () => {
+ expect(shallow(<Rating value="2.0" />)).toMatchSnapshot();
+});
+
+it('renders undefined value', () => {
+ expect(shallow(<Rating value={undefined} />)).toMatchSnapshot();
+});
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders numeric value 1`] = `
+<span
+ className="rating rating-B"
+>
+ B
+</span>
+`;
+
+exports[`renders string value 1`] = `
+<span
+ className="rating rating-B"
+>
+ B
+</span>
+`;
+
+exports[`renders undefined value 1`] = `
+<span>
+ –
+</span>
+`;