aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/components
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-web/src/main/js/components')
-rw-r--r--server/sonar-web/src/main/js/components/charts/BubbleChart.d.ts48
-rw-r--r--server/sonar-web/src/main/js/components/common/EmptySearch.tsx (renamed from server/sonar-web/src/main/js/components/common/EmptySearch.js)19
-rw-r--r--server/sonar-web/src/main/js/components/common/__tests__/EmptySearch-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/EmptySearch-test.tsx.snap16
-rw-r--r--server/sonar-web/src/main/js/components/controls/FavoriteBase.js2
-rw-r--r--server/sonar-web/src/main/js/components/controls/FavoriteBaseStateless.tsx (renamed from server/sonar-web/src/main/js/components/controls/FavoriteBaseStateless.js)25
-rw-r--r--server/sonar-web/src/main/js/components/controls/FavoriteContainer.ts (renamed from server/sonar-web/src/main/js/components/controls/FavoriteContainer.js)8
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/FavoriteBaseStateless-test.tsx53
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/FavoriteBaseStateless-test.tsx.snap13
-rw-r--r--server/sonar-web/src/main/js/components/icons-components/FavoriteIcon.tsx (renamed from server/sonar-web/src/main/js/components/common/FavoriteIcon.js)33
-rw-r--r--server/sonar-web/src/main/js/components/measure/Measure.tsx (renamed from server/sonar-web/src/main/js/components/measure/Measure.js)28
-rw-r--r--server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx65
-rw-r--r--server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap38
-rw-r--r--server/sonar-web/src/main/js/components/measure/utils.ts (renamed from server/sonar-web/src/main/js/components/measure/utils.js)36
-rw-r--r--server/sonar-web/src/main/js/components/shared/Organization.tsx (renamed from server/sonar-web/src/main/js/components/shared/Organization.js)72
-rw-r--r--server/sonar-web/src/main/js/components/shared/__tests__/Organization-test.tsx (renamed from server/sonar-web/src/main/js/components/shared/__tests__/Organization-test.js)2
-rw-r--r--server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/Organization-test.tsx.snap (renamed from server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/Organization-test.js.snap)0
-rw-r--r--server/sonar-web/src/main/js/components/tags/TagsList.tsx (renamed from server/sonar-web/src/main/js/components/tags/TagsList.js)46
-rw-r--r--server/sonar-web/src/main/js/components/tags/__tests__/TagsList-test.tsx (renamed from server/sonar-web/src/main/js/components/tags/__tests__/TagsList-test.js)8
-rw-r--r--server/sonar-web/src/main/js/components/ui/CoverageRating.js70
-rw-r--r--server/sonar-web/src/main/js/components/ui/CoverageRating.tsx48
-rw-r--r--server/sonar-web/src/main/js/components/ui/DuplicationsRating.js55
-rw-r--r--server/sonar-web/src/main/js/components/ui/DuplicationsRating.tsx45
-rw-r--r--server/sonar-web/src/main/js/components/ui/OrganizationLink.tsx (renamed from server/sonar-web/src/main/js/components/ui/OrganizationLink.js)18
-rw-r--r--server/sonar-web/src/main/js/components/ui/Rating.js58
-rw-r--r--server/sonar-web/src/main/js/components/ui/Rating.tsx45
-rw-r--r--server/sonar-web/src/main/js/components/ui/SizeRating.js66
-rw-r--r--server/sonar-web/src/main/js/components/ui/SizeRating.tsx55
-rw-r--r--server/sonar-web/src/main/js/components/ui/__tests__/OrganizationLink-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/OrganizationLink-test.tsx.snap9
30 files changed, 619 insertions, 414 deletions
diff --git a/server/sonar-web/src/main/js/components/charts/BubbleChart.d.ts b/server/sonar-web/src/main/js/components/charts/BubbleChart.d.ts
new file mode 100644
index 00000000000..e1623c22d73
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/charts/BubbleChart.d.ts
@@ -0,0 +1,48 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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';
+
+interface Item {
+ x: number;
+ y: number;
+ size: number;
+ color?: string;
+ key?: string;
+ link?: any;
+ tooltip?: string;
+}
+
+interface Props {
+ items: Item[];
+ sizeRange?: [number, number];
+ displayXGrid?: boolean;
+ displayXTicks?: boolean;
+ displayYGrid?: boolean;
+ displayYTicks?: boolean;
+ height: number;
+ padding: [number, number, number, number];
+ formatXTick: (tick: number) => string;
+ formatYTick: (tick: number) => string;
+ onBubbleClick?: (link?: any) => void;
+ xDomain?: [number, number];
+ yDomain?: [number, number];
+}
+
+export default class BubbleChart extends React.Component<Props> {}
diff --git a/server/sonar-web/src/main/js/components/common/EmptySearch.js b/server/sonar-web/src/main/js/components/common/EmptySearch.tsx
index 719a2239c90..24a00094ada 100644
--- a/server/sonar-web/src/main/js/components/common/EmptySearch.js
+++ b/server/sonar-web/src/main/js/components/common/EmptySearch.tsx
@@ -17,16 +17,15 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import { translate } from '../../helpers/l10n';
import './EmptySearch.css';
-const EmptySearch = () => (
- <div className="empty-search">
- <h3>{translate('no_results_search')}</h3>
- <p className="big-spacer-top">{translate('no_results_search.2')}</p>
- </div>
-);
-
-export default EmptySearch;
+export default function EmptySearch() {
+ return (
+ <div className="empty-search">
+ <h3>{translate('no_results_search')}</h3>
+ <p className="big-spacer-top">{translate('no_results_search.2')}</p>
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/components/common/__tests__/EmptySearch-test.tsx b/server/sonar-web/src/main/js/components/common/__tests__/EmptySearch-test.tsx
new file mode 100644
index 00000000000..c9738908af1
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/common/__tests__/EmptySearch-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 EmptySearch from '../EmptySearch';
+
+it('renders', () => {
+ expect(shallow(<EmptySearch />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/EmptySearch-test.tsx.snap b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/EmptySearch-test.tsx.snap
new file mode 100644
index 00000000000..34dd6bf65a4
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/EmptySearch-test.tsx.snap
@@ -0,0 +1,16 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<div
+ className="empty-search"
+>
+ <h3>
+ no_results_search
+ </h3>
+ <p
+ className="big-spacer-top"
+ >
+ no_results_search.2
+ </p>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/controls/FavoriteBase.js b/server/sonar-web/src/main/js/components/controls/FavoriteBase.js
index ac1d58186b3..d35340dbec5 100644
--- a/server/sonar-web/src/main/js/components/controls/FavoriteBase.js
+++ b/server/sonar-web/src/main/js/components/controls/FavoriteBase.js
@@ -20,7 +20,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
-import FavoriteIcon from '../common/FavoriteIcon';
+import FavoriteIcon from '../icons-components/FavoriteIcon';
export default class FavoriteBase extends React.PureComponent {
static propTypes = {
diff --git a/server/sonar-web/src/main/js/components/controls/FavoriteBaseStateless.js b/server/sonar-web/src/main/js/components/controls/FavoriteBaseStateless.tsx
index 35260d19d85..0f63872c043 100644
--- a/server/sonar-web/src/main/js/components/controls/FavoriteBaseStateless.js
+++ b/server/sonar-web/src/main/js/components/controls/FavoriteBaseStateless.tsx
@@ -17,21 +17,20 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-import FavoriteIcon from '../common/FavoriteIcon';
+import * as React from 'react';
+import * as classNames from 'classnames';
+import FavoriteIcon from '../icons-components/FavoriteIcon';
-export default class FavoriteBaseStateless extends React.PureComponent {
- static propTypes = {
- favorite: PropTypes.bool.isRequired,
- addFavorite: PropTypes.func.isRequired,
- removeFavorite: PropTypes.func.isRequired,
- className: PropTypes.string
- };
+interface Props {
+ addFavorite: () => void;
+ className?: string;
+ favorite: boolean;
+ removeFavorite: () => void;
+}
- toggleFavorite = e => {
- e.preventDefault();
+export default class FavoriteBaseStateless extends React.PureComponent<Props> {
+ toggleFavorite = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
+ event.preventDefault();
if (this.props.favorite) {
this.props.removeFavorite();
} else {
diff --git a/server/sonar-web/src/main/js/components/controls/FavoriteContainer.js b/server/sonar-web/src/main/js/components/controls/FavoriteContainer.ts
index 372cb3b182e..087ace39dbb 100644
--- a/server/sonar-web/src/main/js/components/controls/FavoriteContainer.js
+++ b/server/sonar-web/src/main/js/components/controls/FavoriteContainer.ts
@@ -25,7 +25,7 @@ import * as api from '../../api/favorites';
import { addGlobalErrorMessage } from '../../store/globalMessages/duck';
import { parseError } from '../../apps/code/utils';
-const addFavorite = componentKey => dispatch => {
+const addFavorite = (componentKey: string) => (dispatch: Function) => {
// optimistic update
dispatch(actionCreators.addFavorite(componentKey));
api.addFavorite(componentKey).catch(error => {
@@ -34,7 +34,7 @@ const addFavorite = componentKey => dispatch => {
});
};
-const removeFavorite = componentKey => dispatch => {
+const removeFavorite = (componentKey: string) => (dispatch: Function) => {
// optimistic update
dispatch(actionCreators.removeFavorite(componentKey));
api.removeFavorite(componentKey).catch(error => {
@@ -43,11 +43,11 @@ const removeFavorite = componentKey => dispatch => {
});
};
-const mapStateToProps = (state, ownProps) => ({
+const mapStateToProps = (state: any, ownProps: any) => ({
favorite: isFavorite(state, ownProps.componentKey)
});
-const mapDispatchToProps = (dispatch, ownProps) => ({
+const mapDispatchToProps = (dispatch: Function, ownProps: any) => ({
addFavorite: () => dispatch(addFavorite(ownProps.componentKey)),
removeFavorite: () => dispatch(removeFavorite(ownProps.componentKey))
});
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/FavoriteBaseStateless-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/FavoriteBaseStateless-test.tsx
new file mode 100644
index 00000000000..d3ed6358256
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/FavoriteBaseStateless-test.tsx
@@ -0,0 +1,53 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 FavoriteBaseStateless from '../FavoriteBaseStateless';
+import { click } from '../../../helpers/testUtils';
+
+it('renders', () => {
+ expect(
+ shallow(
+ <FavoriteBaseStateless addFavorite={jest.fn()} favorite={true} removeFavorite={jest.fn()} />
+ )
+ ).toMatchSnapshot();
+});
+
+it('adds favorite', () => {
+ const addFavorite = jest.fn();
+ const wrapper = shallow(
+ <FavoriteBaseStateless addFavorite={addFavorite} favorite={false} removeFavorite={jest.fn()} />
+ );
+ click(wrapper);
+ expect(addFavorite).toBeCalled();
+});
+
+it('removes favorite', () => {
+ const removeFavorite = jest.fn();
+ const wrapper = shallow(
+ <FavoriteBaseStateless
+ addFavorite={jest.fn()}
+ favorite={true}
+ removeFavorite={removeFavorite}
+ />
+ );
+ click(wrapper);
+ expect(removeFavorite).toBeCalled();
+});
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/FavoriteBaseStateless-test.tsx.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/FavoriteBaseStateless-test.tsx.snap
new file mode 100644
index 00000000000..9edbec0cb0f
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/FavoriteBaseStateless-test.tsx.snap
@@ -0,0 +1,13 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<a
+ className="link-no-underline"
+ href="#"
+ onClick={[Function]}
+>
+ <FavoriteIcon
+ favorite={true}
+ />
+</a>
+`;
diff --git a/server/sonar-web/src/main/js/components/common/FavoriteIcon.js b/server/sonar-web/src/main/js/components/icons-components/FavoriteIcon.tsx
index 725f2ce960e..a07533ae13f 100644
--- a/server/sonar-web/src/main/js/components/common/FavoriteIcon.js
+++ b/server/sonar-web/src/main/js/components/icons-components/FavoriteIcon.tsx
@@ -17,34 +17,21 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
-import classNames from 'classnames';
+import * as React from 'react';
+import * as classNames from 'classnames';
-/*::
-type Props = {
- className?: string,
- favorite: boolean,
- size?: number
-};
-*/
+interface Props {
+ className?: string;
+ favorite: boolean;
+ size?: number;
+}
-export default function FavoriteIcon(props /*: Props */) {
- /* eslint max-len: 0 */
+export default function FavoriteIcon({ className, favorite, size = 16 }: Props) {
return (
- <span
- className={classNames(
- 'icon-star',
- { 'icon-star-favorite': props.favorite },
- props.className
- )}>
- <svg width={props.size} height={props.size} viewBox="0 0 16 16">
+ <span className={classNames('icon-star', { 'icon-star-favorite': favorite }, className)}>
+ <svg width={size} height={size} viewBox="0 0 16 16">
<path d="M15.4275,5.77678C15.4275,5.90773 15.3501,6.05059 15.1953,6.20536L11.9542,9.36608L12.7221,13.8304C12.728,13.872 12.731,13.9316 12.731,14.0089C12.731,14.1339 12.6998,14.2396 12.6373,14.3259C12.5748,14.4122 12.484,14.4554 12.3649,14.4554C12.2518,14.4554 12.1328,14.4197 12.0078,14.3482L7.99888,12.2411L3.98995,14.3482C3.85901,14.4197 3.73996,14.4554 3.63281,14.4554C3.50781,14.4554 3.41406,14.4122 3.35156,14.3259C3.28906,14.2396 3.25781,14.1339 3.25781,14.0089C3.25781,13.9732 3.26377,13.9137 3.27567,13.8304L4.04353,9.36608L0.793531,6.20536C0.644719,6.04464 0.570313,5.90178 0.570313,5.77678C0.570313,5.55654 0.736979,5.41964 1.07031,5.36606L5.55245,4.71428L7.56138,0.651781C7.67447,0.407729 7.8203,0.285703 7.99888,0.285703C8.17745,0.285703 8.32328,0.407729 8.43638,0.651781L10.4453,4.71428L14.9274,5.36606C15.2608,5.41964 15.4274,5.55654 15.4274,5.77678L15.4275,5.77678Z" />
</svg>
</span>
);
}
-
-FavoriteIcon.defaultProps = {
- size: 16
-};
diff --git a/server/sonar-web/src/main/js/components/measure/Measure.js b/server/sonar-web/src/main/js/components/measure/Measure.tsx
index c95d52f8280..35177b49b95 100644
--- a/server/sonar-web/src/main/js/components/measure/Measure.js
+++ b/server/sonar-web/src/main/js/components/measure/Measure.tsx
@@ -17,26 +17,29 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import Rating from '../ui/Rating';
import Level from '../ui/Level';
import Tooltips from '../controls/Tooltip';
import { formatMeasure, isDiffMetric } from '../../helpers/measures';
-import { formatLeak, getRatingTooltip } from './utils';
-/*:: import type { MeasureEnhanced } from './types'; */
+import { formatLeak, getRatingTooltip, MeasureEnhanced } from './utils';
-/*:: type Props = {
- className?: string,
- decimals?: ?number,
- measure: MeasureEnhanced
-}; */
+interface Props {
+ className?: string;
+ decimals?: number | null;
+ measure: MeasureEnhanced;
+}
-export default function Measure({ className, decimals, measure } /*: Props */) {
+export default function Measure({ className, decimals, measure }: Props) {
const metric = measure.metric;
+ const value = isDiffMetric(metric.key) ? measure.leak : measure.value;
+
+ if (value == undefined) {
+ return null;
+ }
if (metric.type === 'LEVEL') {
- return <Level className={className} level={measure.value} />;
+ return <Level className={className} level={value} />;
}
if (metric.type !== 'RATING') {
@@ -46,8 +49,7 @@ export default function Measure({ className, decimals, measure } /*: Props */) {
return <span className={className}>{formattedValue != null ? formattedValue : '–'}</span>;
}
- const value = isDiffMetric(metric.key) ? measure.leak : measure.value;
- const tooltip = getRatingTooltip(metric.key, value);
+ const tooltip = getRatingTooltip(metric.key, Number(value));
const rating = <Rating value={value} />;
if (tooltip) {
return (
diff --git a/server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx b/server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx
new file mode 100644
index 00000000000..8929a8bc1c5
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx
@@ -0,0 +1,65 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.
+ */
+jest.mock('../../../helpers/measures', () => {
+ const measures = require.requireActual('../../../helpers/measures');
+ measures.getRatingTooltip = jest.fn(() => 'tooltip');
+ return measures;
+});
+
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import Measure from '../Measure';
+
+it('renders trivial measure', () => {
+ const measure = { metric: { key: 'coverage', name: 'Coverage', type: 'PERCENT' }, value: '73.0' };
+ expect(shallow(<Measure measure={measure} />)).toMatchSnapshot();
+});
+
+it('renders leak measure', () => {
+ const measure = {
+ metric: { key: 'new_coverage', name: 'Coverage on New Code', type: 'PERCENT' },
+ leak: '36.0'
+ };
+ expect(shallow(<Measure measure={measure} />)).toMatchSnapshot();
+});
+
+it('renders LEVEL', () => {
+ const measure = {
+ metric: { key: 'quality_gate_status', name: 'Quality Gate', type: 'LEVEL' },
+ value: 'ERROR'
+ };
+ expect(shallow(<Measure measure={measure} />)).toMatchSnapshot();
+});
+
+it('renders known RATING', () => {
+ const measure = {
+ metric: { key: 'sqale_rating', name: 'Maintainability Rating', type: 'RATING' },
+ value: '3'
+ };
+ expect(shallow(<Measure measure={measure} />)).toMatchSnapshot();
+});
+
+it('renders unknown RATING', () => {
+ const measure = {
+ metric: { key: 'foo_rating', name: 'Foo Rating', type: 'RATING' },
+ value: '4'
+ };
+ expect(shallow(<Measure measure={measure} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap b/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap
new file mode 100644
index 00000000000..2988d9210e8
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap
@@ -0,0 +1,38 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders LEVEL 1`] = `
+<Level
+ level="ERROR"
+/>
+`;
+
+exports[`renders known RATING 1`] = `
+<Tooltip
+ overlay="tooltip"
+ placement="bottom"
+>
+ <span>
+ <Rating
+ value="3"
+ />
+ </span>
+</Tooltip>
+`;
+
+exports[`renders leak measure 1`] = `
+<span>
+ 36.0%
+</span>
+`;
+
+exports[`renders trivial measure 1`] = `
+<span>
+ 73.0%
+</span>
+`;
+
+exports[`renders unknown RATING 1`] = `
+<Rating
+ value="4"
+/>
+`;
diff --git a/server/sonar-web/src/main/js/components/measure/utils.js b/server/sonar-web/src/main/js/components/measure/utils.ts
index 62fef4790d0..017e709491d 100644
--- a/server/sonar-web/src/main/js/components/measure/utils.js
+++ b/server/sonar-web/src/main/js/components/measure/utils.ts
@@ -17,22 +17,34 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
import {
formatMeasure,
formatMeasureVariation,
getRatingTooltip as nextGetRatingTooltip,
isDiffMetric
} from '../../helpers/measures';
-/*:: import type { Measure, MeasureEnhanced } from './types'; */
-/*:: import type { Metric } from '../../store/metrics/actions'; */
+import { Metric } from '../../app/types';
const KNOWN_RATINGS = ['sqale_rating', 'reliability_rating', 'security_rating'];
+export interface MeasureIntern {
+ value?: string;
+ periods?: Array<{ index: number; value: string }>;
+}
+
+export interface Measure extends MeasureIntern {
+ metric: string;
+}
+
+export interface MeasureEnhanced extends MeasureIntern {
+ metric: Metric;
+ leak?: string | undefined | undefined;
+}
+
export function enhanceMeasure(
- measure /*: Measure */,
- metrics /*: { [string]: Metric } */
-) /*: MeasureEnhanced */ {
+ measure: Measure,
+ metrics: { [key: string]: Metric }
+): MeasureEnhanced {
return {
value: measure.value,
periods: measure.periods,
@@ -41,7 +53,7 @@ export function enhanceMeasure(
};
}
-export function formatLeak(value /*: ?string*/, metric /*: Metric*/, options /*: Object*/) {
+export function formatLeak(value: string | undefined, metric: Metric, options: any): string {
if (isDiffMetric(metric.key)) {
return formatMeasure(value, metric.type, options);
} else {
@@ -49,18 +61,18 @@ export function formatLeak(value /*: ?string*/, metric /*: Metric*/, options /*:
}
}
-export function getLeakValue(measure /*: ?Measure*/) /*: ?string*/ {
+export function getLeakValue(measure: Measure | undefined): string | undefined {
if (!measure || !measure.periods) {
- return null;
+ return undefined;
}
const period = measure.periods.find(period => period.index === 1);
- return period ? period.value : null;
+ return period && period.value;
}
-export function getRatingTooltip(metricKey /*: string */, value /*: ?string*/) {
+export function getRatingTooltip(metricKey: string, value: number): string | undefined {
const finalMetricKey = isDiffMetric(metricKey) ? metricKey.substr(4) : metricKey;
if (KNOWN_RATINGS.includes(finalMetricKey)) {
return nextGetRatingTooltip(finalMetricKey, value);
}
- return null;
+ return undefined;
}
diff --git a/server/sonar-web/src/main/js/components/shared/Organization.js b/server/sonar-web/src/main/js/components/shared/Organization.tsx
index 1c3d5abf574..f411de59496 100644
--- a/server/sonar-web/src/main/js/components/shared/Organization.js
+++ b/server/sonar-web/src/main/js/components/shared/Organization.tsx
@@ -17,62 +17,48 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import { connect } from 'react-redux';
import { getOrganizationByKey, areThereCustomOrganizations } from '../../store/rootReducer';
import OrganizationLink from '../ui/OrganizationLink';
-/*::
-type OwnProps = {
- organizationKey: string
-};
-*/
-
-/*::
-type Props = {
- link?: boolean,
- linkClassName?: string,
- organizationKey: string,
- organization: { key: string, name: string } | null,
- shouldBeDisplayed: boolean
-};
-*/
-
-class Organization extends React.PureComponent {
- /*:: props: Props; */
-
- static defaultProps = {
- link: true
- };
+interface OwnProps {
+ organizationKey: string;
+}
- render() {
- const { organization, shouldBeDisplayed } = this.props;
+interface Props {
+ link?: boolean;
+ linkClassName?: string;
+ organization: { key: string; name: string } | null;
+ shouldBeDisplayed: boolean;
+}
- if (!shouldBeDisplayed || !organization) {
- return null;
- }
+function Organization(props: Props) {
+ const { link = true, organization, shouldBeDisplayed } = props;
- return (
- <span>
- {this.props.link ? (
- <OrganizationLink className={this.props.linkClassName} organization={organization}>
- {organization.name}
- </OrganizationLink>
- ) : (
- organization.name
- )}
- <span className="slash-separator" />
- </span>
- );
+ if (!shouldBeDisplayed || !organization) {
+ return null;
}
+
+ return (
+ <span>
+ {link ? (
+ <OrganizationLink className={props.linkClassName} organization={organization}>
+ {organization.name}
+ </OrganizationLink>
+ ) : (
+ organization.name
+ )}
+ <span className="slash-separator" />
+ </span>
+ );
}
-const mapStateToProps = (state, ownProps /*: OwnProps */) => ({
+const mapStateToProps = (state: any, ownProps: OwnProps) => ({
organization: getOrganizationByKey(state, ownProps.organizationKey),
shouldBeDisplayed: areThereCustomOrganizations(state)
});
-export default connect(mapStateToProps)(Organization);
+export default connect<any, any, any>(mapStateToProps)(Organization);
export const UnconnectedOrganization = Organization;
diff --git a/server/sonar-web/src/main/js/components/shared/__tests__/Organization-test.js b/server/sonar-web/src/main/js/components/shared/__tests__/Organization-test.tsx
index 7bb6ae80476..34f6ace01b2 100644
--- a/server/sonar-web/src/main/js/components/shared/__tests__/Organization-test.js
+++ b/server/sonar-web/src/main/js/components/shared/__tests__/Organization-test.tsx
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
import { UnconnectedOrganization } from '../Organization';
diff --git a/server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/Organization-test.js.snap b/server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/Organization-test.tsx.snap
index 72ffd1b6d84..72ffd1b6d84 100644
--- a/server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/Organization-test.js.snap
+++ b/server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/Organization-test.tsx.snap
diff --git a/server/sonar-web/src/main/js/components/tags/TagsList.js b/server/sonar-web/src/main/js/components/tags/TagsList.tsx
index c85ad16c00a..a35b0465358 100644
--- a/server/sonar-web/src/main/js/components/tags/TagsList.js
+++ b/server/sonar-web/src/main/js/components/tags/TagsList.tsx
@@ -17,37 +17,25 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
-import classNames from 'classnames';
+import * as React from 'react';
+import * as classNames from 'classnames';
import './TagsList.css';
-/*::
-type Props = {
- tags: Array<string>,
- allowUpdate: boolean,
- customClass?: string
-};
-*/
-
-export default class TagsList extends React.PureComponent {
- /*:: props: Props; */
-
- static defaultProps = {
- allowUpdate: false
- };
+interface Props {
+ allowUpdate?: boolean;
+ customClass?: string;
+ tags: string[];
+}
- render() {
- const { tags, allowUpdate } = this.props;
- const spanClass = classNames('text-ellipsis', { note: !allowUpdate });
- const tagListClass = classNames('tags-list', this.props.customClass);
+export default function TagsList({ allowUpdate = false, customClass, tags }: Props) {
+ const spanClass = classNames('text-ellipsis', { note: !allowUpdate });
+ const tagListClass = classNames('tags-list', customClass);
- return (
- <span className={tagListClass} title={tags.join(', ')}>
- <i className="icon-tags icon-half-transparent" />
- <span className={spanClass}>{tags.join(', ')}</span>
- {allowUpdate && <i className="icon-dropdown" />}
- </span>
- );
- }
+ return (
+ <span className={tagListClass} title={tags.join(', ')}>
+ <i className="icon-tags icon-half-transparent" />
+ <span className={spanClass}>{tags.join(', ')}</span>
+ {allowUpdate && <i className="icon-dropdown" />}
+ </span>
+ );
}
diff --git a/server/sonar-web/src/main/js/components/tags/__tests__/TagsList-test.js b/server/sonar-web/src/main/js/components/tags/__tests__/TagsList-test.tsx
index 438730aff86..d093e16e7fa 100644
--- a/server/sonar-web/src/main/js/components/tags/__tests__/TagsList-test.js
+++ b/server/sonar-web/src/main/js/components/tags/__tests__/TagsList-test.tsx
@@ -17,8 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import * as React from 'react';
import { shallow } from 'enzyme';
-import React from 'react';
import TagsList from '../TagsList';
const tags = ['foo', 'bar'];
@@ -30,14 +30,10 @@ it('should render with a list of tag', () => {
expect(taglist.find('span.note').hasClass('text-ellipsis')).toBe(true);
});
-it('should FAIL to render without tags', () => {
- expect(() => shallow(<TagsList />)).toThrow();
-});
-
it('should correctly handle a lot of tags', () => {
const lotOfTags = [];
for (let i = 0; i < 20; i++) {
- lotOfTags.push(tags);
+ lotOfTags.push(String(tags));
}
const taglist = shallow(<TagsList tags={lotOfTags} />);
expect(taglist.text()).toBe(lotOfTags.join(', '));
diff --git a/server/sonar-web/src/main/js/components/ui/CoverageRating.js b/server/sonar-web/src/main/js/components/ui/CoverageRating.js
deleted file mode 100644
index 6fe1c7cc09e..00000000000
--- a/server/sonar-web/src/main/js/components/ui/CoverageRating.js
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import { DonutChart } from '../charts/donut-chart';
-
-const SIZE_TO_WIDTH_MAPPING = {
- small: 16,
- normal: 24,
- big: 40,
- huge: 60
-};
-
-const SIZE_TO_THICKNESS_MAPPING = {
- small: 2,
- normal: 3,
- big: 3,
- huge: 4
-};
-
-export default class CoverageRating extends React.PureComponent {
- /*:: props: {
- value: number | string,
- size?: 'small' | 'normal' | 'big' | 'huge',
- muted?: boolean
- };
-*/
-
- static defaultProps = {
- size: 'normal',
- muted: false
- };
-
- render() {
- let data = [{ value: 100, fill: '#ccc ' }];
-
- if (this.props.value != null) {
- const value = Number(this.props.value);
- data = [
- { value, fill: this.props.muted ? '#bdbdbd' : '#00aa00' },
- { value: 100 - value, fill: this.props.muted ? '#f3f3f3' : '#d4333f' }
- ];
- }
-
- // $FlowFixMe
- const size = SIZE_TO_WIDTH_MAPPING[this.props.size];
-
- // $FlowFixMe
- const thickness = SIZE_TO_THICKNESS_MAPPING[this.props.size];
-
- return <DonutChart data={data} width={size} height={size} thickness={thickness} />;
- }
-}
diff --git a/server/sonar-web/src/main/js/components/ui/CoverageRating.tsx b/server/sonar-web/src/main/js/components/ui/CoverageRating.tsx
new file mode 100644
index 00000000000..e7eda495d7f
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/ui/CoverageRating.tsx
@@ -0,0 +1,48 @@
+/*
+ * 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 { DonutChart } from '../charts/donut-chart';
+
+const SIZE_TO_WIDTH_MAPPING = { small: 16, normal: 24, big: 40, huge: 60 };
+
+const SIZE_TO_THICKNESS_MAPPING = { small: 2, normal: 3, big: 3, huge: 4 };
+
+interface Props {
+ muted?: boolean;
+ size?: 'small' | 'normal' | 'big' | 'huge';
+ value: number | string | null | undefined;
+}
+
+export default function CoverageRating({ muted = false, size = 'normal', value }: Props) {
+ let data = [{ value: 100, fill: '#ccc ' }];
+
+ if (value != null) {
+ const numberValue = Number(value);
+ data = [
+ { value: numberValue, fill: muted ? '#bdbdbd' : '#00aa00' },
+ { value: 100 - numberValue, fill: muted ? '#f3f3f3' : '#d4333f' }
+ ];
+ }
+
+ const width = SIZE_TO_WIDTH_MAPPING[size];
+ const thickness = SIZE_TO_THICKNESS_MAPPING[size];
+
+ return <DonutChart data={data} width={width} height={width} thickness={thickness} />;
+}
diff --git a/server/sonar-web/src/main/js/components/ui/DuplicationsRating.js b/server/sonar-web/src/main/js/components/ui/DuplicationsRating.js
deleted file mode 100644
index 628adac1d1b..00000000000
--- a/server/sonar-web/src/main/js/components/ui/DuplicationsRating.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import classNames from 'classnames';
-import { inRange } from 'lodash';
-import './DuplicationsRating.css';
-
-export default class DuplicationsRating extends React.PureComponent {
- /*:: props: {
- value: number,
- size?: 'small' | 'normal' | 'big' | 'huge',
- muted?: boolean
- };
-*/
-
- static defaultProps = {
- small: false,
- muted: false
- };
-
- render() {
- const { value, size, muted } = this.props;
- const className = classNames('duplications-rating', {
- 'duplications-rating-small': size === 'small',
- 'duplications-rating-big': size === 'big',
- 'duplications-rating-huge': size === 'huge',
- 'duplications-rating-muted': muted,
- 'duplications-rating-A': inRange(value, 0, 3),
- 'duplications-rating-B': inRange(value, 3, 5),
- 'duplications-rating-C': inRange(value, 5, 10),
- 'duplications-rating-D': inRange(value, 10, 20),
- 'duplications-rating-E': value >= 20
- });
-
- return <div className={className} />;
- }
-}
diff --git a/server/sonar-web/src/main/js/components/ui/DuplicationsRating.tsx b/server/sonar-web/src/main/js/components/ui/DuplicationsRating.tsx
new file mode 100644
index 00000000000..01703bafa31
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/ui/DuplicationsRating.tsx
@@ -0,0 +1,45 @@
+/*
+ * 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 * as classNames from 'classnames';
+import { inRange } from 'lodash';
+import './DuplicationsRating.css';
+
+interface Props {
+ muted?: boolean;
+ size?: 'small' | 'normal' | 'big' | 'huge';
+ value: number;
+}
+
+export default function DuplicationsRating({ muted = false, size = 'normal', value }: Props) {
+ const className = classNames('duplications-rating', {
+ 'duplications-rating-small': size === 'small',
+ 'duplications-rating-big': size === 'big',
+ 'duplications-rating-huge': size === 'huge',
+ 'duplications-rating-muted': muted,
+ 'duplications-rating-A': inRange(value, 0, 3),
+ 'duplications-rating-B': inRange(value, 3, 5),
+ 'duplications-rating-C': inRange(value, 5, 10),
+ 'duplications-rating-D': inRange(value, 10, 20),
+ 'duplications-rating-E': value >= 20
+ });
+
+ return <div className={className} />;
+}
diff --git a/server/sonar-web/src/main/js/components/ui/OrganizationLink.js b/server/sonar-web/src/main/js/components/ui/OrganizationLink.tsx
index 9251d1ae863..a5cc9d56804 100644
--- a/server/sonar-web/src/main/js/components/ui/OrganizationLink.js
+++ b/server/sonar-web/src/main/js/components/ui/OrganizationLink.tsx
@@ -17,18 +17,16 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import { Link } from 'react-router';
-export default function OrganizationLink(
- props /*: {
- children?: React.Element<*>,
- organization: {
- key: string
- }
-} */
-) {
+interface Props {
+ children?: React.ReactNode;
+ organization: { key: string };
+ [x: string]: any;
+}
+
+export default function OrganizationLink(props: Props) {
const { children, organization, ...other } = props;
return (
diff --git a/server/sonar-web/src/main/js/components/ui/Rating.js b/server/sonar-web/src/main/js/components/ui/Rating.js
deleted file mode 100644
index 0ec18df9e7c..00000000000
--- a/server/sonar-web/src/main/js/components/ui/Rating.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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 React from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-import { formatMeasure } from '../../helpers/measures';
-import './Rating.css';
-
-export default class Rating extends React.PureComponent {
- static propTypes = {
- className: PropTypes.string,
- value: (props, propName, componentName) => {
- // allow both numbers and strings
- const numberValue = Number(props[propName]);
- if (numberValue < 1 || numberValue > 5) {
- throw new Error(`Invalid prop "${propName}" passed to "${componentName}".`);
- }
- },
- small: PropTypes.bool,
- muted: PropTypes.bool
- };
-
- static defaultProps = {
- small: false,
- muted: false
- };
-
- render() {
- const formatted = formatMeasure(this.props.value, 'RATING');
- const className = classNames(
- 'rating',
- 'rating-' + formatted,
- {
- 'rating-small': this.props.small,
- 'rating-muted': this.props.muted
- },
- this.props.className
- );
- return <span className={className}>{formatted}</span>;
- }
-}
diff --git a/server/sonar-web/src/main/js/components/ui/Rating.tsx b/server/sonar-web/src/main/js/components/ui/Rating.tsx
new file mode 100644
index 00000000000..67abfe61782
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/ui/Rating.tsx
@@ -0,0 +1,45 @@
+/*
+ * 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 * as classNames from 'classnames';
+import { formatMeasure } from '../../helpers/measures';
+import './Rating.css';
+
+interface Props {
+ className?: string;
+ muted?: boolean;
+ small?: boolean;
+ value: string | number;
+}
+
+export default function Rating({ className, muted = false, small = false, value }: Props) {
+ const formatted = formatMeasure(value, 'RATING');
+ return (
+ <span
+ className={classNames(
+ 'rating',
+ 'rating-' + formatted,
+ { 'rating-small': small, 'rating-muted': muted },
+ className
+ )}>
+ {formatted}
+ </span>
+ );
+}
diff --git a/server/sonar-web/src/main/js/components/ui/SizeRating.js b/server/sonar-web/src/main/js/components/ui/SizeRating.js
deleted file mode 100644
index 3518c901225..00000000000
--- a/server/sonar-web/src/main/js/components/ui/SizeRating.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.
- */
-// @flow
-import React from 'react';
-import classNames from 'classnames';
-import { inRange } from 'lodash';
-import './SizeRating.css';
-
-export default class SizeRating extends React.PureComponent {
- /*:: props: {
- value: number,
- small?: boolean,
- muted?: boolean
- };
-*/
-
- static defaultProps = {
- small: false,
- muted: false
- };
-
- render() {
- const { value } = this.props;
-
- if (value == null) {
- return <div className="size-rating size-rating-muted">&nbsp;</div>;
- }
-
- let letter;
- if (inRange(value, 0, 1000)) {
- letter = 'XS';
- } else if (inRange(value, 1000, 10000)) {
- letter = 'S';
- } else if (inRange(value, 10000, 100000)) {
- letter = 'M';
- } else if (inRange(value, 100000, 500000)) {
- letter = 'L';
- } else if (value >= 500000) {
- letter = 'XL';
- }
-
- const className = classNames('size-rating', {
- 'size-rating-small': this.props.small,
- 'size-rating-muted': this.props.muted
- });
-
- return <div className={className}>{letter}</div>;
- }
-}
diff --git a/server/sonar-web/src/main/js/components/ui/SizeRating.tsx b/server/sonar-web/src/main/js/components/ui/SizeRating.tsx
new file mode 100644
index 00000000000..d4a657676c0
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/ui/SizeRating.tsx
@@ -0,0 +1,55 @@
+/*
+ * 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 * as classNames from 'classnames';
+import { inRange } from 'lodash';
+import './SizeRating.css';
+
+interface Props {
+ muted?: boolean;
+ small?: boolean;
+ value: number | null | undefined;
+}
+
+export default function SizeRating({ small = false, muted = false, value }: Props) {
+ if (value == null) {
+ return <div className="size-rating size-rating-muted">&nbsp;</div>;
+ }
+
+ let letter;
+ if (inRange(value, 0, 1000)) {
+ letter = 'XS';
+ } else if (inRange(value, 1000, 10000)) {
+ letter = 'S';
+ } else if (inRange(value, 10000, 100000)) {
+ letter = 'M';
+ } else if (inRange(value, 100000, 500000)) {
+ letter = 'L';
+ } else if (value >= 500000) {
+ letter = 'XL';
+ }
+
+ const className = classNames('size-rating', {
+ 'size-rating-small': small,
+ 'size-rating-muted': muted
+ });
+
+ return <div className={className}>{letter}</div>;
+}
diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/OrganizationLink-test.tsx b/server/sonar-web/src/main/js/components/ui/__tests__/OrganizationLink-test.tsx
new file mode 100644
index 00000000000..feffbe8c87f
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/ui/__tests__/OrganizationLink-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 OrganizationLink from '../OrganizationLink';
+
+it('renders', () => {
+ expect(shallow(<OrganizationLink organization={{ key: 'org' }} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/OrganizationLink-test.tsx.snap b/server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/OrganizationLink-test.tsx.snap
new file mode 100644
index 00000000000..147ffccb64f
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/ui/__tests__/__snapshots__/OrganizationLink-test.tsx.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/organizations/org"
+/>
+`;