describe('fetchProjects', () => {
it('correctly converts the passed arguments to the desired query format', async () => {
- await utils.fetchProjects({ view: 'visualizations' }, true);
+ await utils.fetchProjects({}, true);
expect(searchProjects).toBeCalledWith({
f: 'analysisDate,leakPeriodDate',
facets: utils.FACETS.join(),
filter: 'isFavorite',
p: undefined,
- ps: 99
+ ps: 50
});
await utils.fetchProjects({ view: 'leak' }, false, 3);
],
paging: { total: 2 }
});
- await utils.fetchProjects({ view: 'visualizations' }, true).then(r => {
+ await utils.fetchProjects({}, true).then(r => {
expect(r).toEqual({
facets: {
new_coverage: { NO_DATA: 0 },
import { isLoggedIn } from '../../../helpers/users';
import { ComponentQualifier } from '../../../types/component';
import { CurrentUser, RawQuery } from '../../../types/types';
-import { hasFilterParams, hasVisualizationParams, parseUrlQuery, Query } from '../query';
+import { hasFilterParams, hasViewParams, parseUrlQuery, Query } from '../query';
import '../styles.css';
import { Facets, Project } from '../types';
import { fetchProjects, parseSorting, SORTING_SWITCH } from '../utils';
-import Visualizations from '../visualizations/Visualizations';
import PageHeader from './PageHeader';
import PageSidebar from './PageSidebar';
import ProjectsList from './ProjectsList';
export const LS_PROJECTS_SORT = 'sonarqube.projects.sort';
export const LS_PROJECTS_VIEW = 'sonarqube.projects.view';
-export const LS_PROJECTS_VISUALIZATION = 'sonarqube.projects.visualization';
export class AllProjects extends React.PureComponent<Props, State> {
mounted = false;
const options: {
sort?: string;
view?: string;
- visualization?: string;
} = {};
if (get(LS_PROJECTS_SORT)) {
options.sort = get(LS_PROJECTS_SORT) || undefined;
if (get(LS_PROJECTS_VIEW)) {
options.view = get(LS_PROJECTS_VIEW) || undefined;
}
- if (get(LS_PROJECTS_VISUALIZATION)) {
- options.visualization = get(LS_PROJECTS_VISUALIZATION) || undefined;
- }
return options;
};
getView = () => this.state.query.view || 'overall';
- getVisualization = () => this.state.query.visualization || 'risk';
-
handleClearAll = () => {
this.props.router.push({ pathname: this.props.location.pathname });
};
});
};
- handlePerspectiveChange = ({ view, visualization }: { view: string; visualization?: string }) => {
+ handlePerspectiveChange = ({ view }: { view: string }) => {
const query: {
view: string | undefined;
- visualization: string | undefined;
sort?: string | undefined;
} = {
- view: view === 'overall' ? undefined : view,
- visualization
+ view: view === 'overall' ? undefined : view
};
if (this.state.query.view === 'leak' || view === 'leak') {
save(LS_PROJECTS_SORT, query.sort);
save(LS_PROJECTS_VIEW, query.view);
- save(LS_PROJECTS_VISUALIZATION, visualization);
};
handleQueryChange(initialMount: boolean) {
const query = parseUrlQuery(this.props.location.query);
const savedOptions = this.getStorageOptions();
- const savedOptionsSet = savedOptions.sort || savedOptions.view || savedOptions.visualization;
+ const savedOptionsSet = savedOptions.sort || savedOptions.view;
- // if there is no visualization parameters (sort, view, visualization), but there are saved preferences in the localStorage
- if (initialMount && !hasVisualizationParams(query) && savedOptionsSet) {
+ if (initialMount && !hasViewParams(query) && savedOptionsSet) {
this.props.router.replace({ pathname: this.props.location.pathname, query: savedOptions });
} else {
this.fetchProjects(query);
onQueryChange={this.updateLocationQuery}
query={this.state.query}
view={this.getView()}
- visualization={this.getVisualization()}
/>
</div>
</div>
onPerspectiveChange={this.handlePerspectiveChange}
onQueryChange={this.updateLocationQuery}
onSortChange={this.handleSortChange}
- projects={this.state.projects}
query={this.state.query}
selectedSort={this.getSort()}
total={this.state.total}
view={this.getView()}
- visualization={this.getVisualization()}
/>
</div>
</div>
return <DeferredSpinner />;
}
- return this.getView() === 'visualizations' ? (
- <div className="layout-page-main-inner">
- {this.state.projects && (
- <Visualizations
- projects={this.state.projects}
- sort={this.state.query.sort}
- total={this.state.total}
- visualization={this.getVisualization()}
- />
- )}
- </div>
- ) : (
+ return (
<div className="layout-page-main-inner">
{this.state.projects && (
<ProjectsList
import classNames from 'classnames';
import * as React from 'react';
import HomePageSelect from '../../../components/controls/HomePageSelect';
-import Tooltip from '../../../components/controls/Tooltip';
import { translate } from '../../../helpers/l10n';
import { isLoggedIn } from '../../../helpers/users';
import { CurrentUser, RawQuery } from '../../../types/types';
import SearchFilterContainer from '../filters/SearchFilterContainer';
-import { Project } from '../types';
import ApplicationCreation from './ApplicationCreation';
import PerspectiveSelect from './PerspectiveSelect';
import ProjectCreationMenu from './ProjectCreationMenu';
interface Props {
currentUser: CurrentUser;
loading: boolean;
- onPerspectiveChange: (x: { view: string; visualization?: string }) => void;
+ onPerspectiveChange: (x: { view: string }) => void;
onQueryChange: (change: RawQuery) => void;
onSortChange: (sort: string, desc: boolean) => void;
- projects?: Project[];
query: RawQuery;
selectedSort: string;
total?: number;
view: string;
- visualization?: string;
}
export default function PageHeader(props: Props) {
- const { loading, total, projects, currentUser, view } = props;
- const limitReached = projects != null && total != null && projects.length < total;
+ const { loading, total, currentUser, view } = props;
const defaultOption = isLoggedIn(currentUser) ? 'name' : 'analysis_date';
- const sortingDisabled = view === 'visualizations' && !limitReached;
-
return (
<div className="page-header">
<div className="display-flex-space-between spacer-top">
<PerspectiveSelect
className="projects-topbar-item js-projects-perspective-select"
onChange={props.onPerspectiveChange}
- view={props.view}
- visualization={props.visualization}
+ view={view}
/>
- <Tooltip overlay={sortingDisabled ? translate('projects.sort.disabled') : undefined}>
- <div className={classNames('projects-topbar-item', { disabled: sortingDisabled })}>
- <ProjectsSortingSelect
- className="js-projects-sorting-select"
- defaultOption={defaultOption}
- onChange={props.onSortChange}
- selectedSort={props.selectedSort}
- view={props.view}
- />
- </div>
- </Tooltip>
+ <div className={classNames('projects-topbar-item')}>
+ <ProjectsSortingSelect
+ className="js-projects-sorting-select"
+ defaultOption={defaultOption}
+ onChange={props.onSortChange}
+ selectedSort={props.selectedSort}
+ view={view}
+ />
+ </div>
</div>
</div>
</div>
onQueryChange: (change: RawQuery) => void;
query: RawQuery;
view: string;
- visualization: string;
}
export default function PageSidebar(props: PageSidebarProps) {
- const { applicationsEnabled, facets, onQueryChange, query, view, visualization } = props;
+ const { applicationsEnabled, facets, onQueryChange, query, view } = props;
const isFiltered = hasFilterParams(query);
const isLeakView = view === 'leak';
const maxFacetValue = getMaxFacetValue(facets);
let linkQuery: RawQuery | undefined = undefined;
if (view !== 'overall') {
linkQuery = { view };
-
- if (view === 'visualizations') {
- linkQuery.visualization = visualization;
- }
}
return (
import * as React from 'react';
import { components, OptionProps } from 'react-select';
import Select from '../../../components/controls/Select';
-import BubblesIcon from '../../../components/icons/BubblesIcon';
import ListIcon from '../../../components/icons/ListIcon';
import { translate } from '../../../helpers/l10n';
-import { VIEWS, VISUALIZATIONS } from '../utils';
+import { VIEWS } from '../utils';
interface Props {
className?: string;
- onChange: (x: { view: string; visualization?: string }) => void;
+ onChange: (x: { view: string }) => void;
view: string;
- visualization?: string;
}
export interface PerspectiveOption {
- type: string;
value: string;
label: string;
}
export default class PerspectiveSelect extends React.PureComponent<Props> {
handleChange = (option: PerspectiveOption) => {
- if (option && option.type === 'view') {
- this.props.onChange({ view: option.value });
- } else if (option && option.type === 'visualization') {
- this.props.onChange({ view: 'visualizations', visualization: option.value });
- }
+ this.props.onChange({ view: option.value });
};
perspectiveOptionRender = (props: OptionProps<PerspectiveOption, false>) => {
<components.Option
{...omit(props, ['children', 'className'])}
className={`it__projects-perspective-option-${data.value} ${className}`}>
- {data.type === 'view' && <ListIcon className="little-spacer-right" />}
- {data.type === 'visualization' && <BubblesIcon className="little-spacer-right" />}
+ <ListIcon className="little-spacer-right" />
{props.children}
</components.Option>
);
};
render() {
- const { view, visualization } = this.props;
- const perspective = view === 'visualizations' ? visualization : view;
+ const { view } = this.props;
const options: PerspectiveOption[] = [
...VIEWS.map(opt => ({
- type: 'view',
value: opt.value,
label: translate('projects.view', opt.label)
- })),
- ...VISUALIZATIONS.map(opt => ({
- type: 'visualization',
- value: opt,
- label: translate('projects.visualization', opt)
}))
];
return (
}}
options={options}
isSearchable={false}
- value={options.find(option => option.value === perspective)}
+ value={options.find(option => option.value === view)}
/>
</div>
);
import { get, save } from '../../../../helpers/storage';
import { ComponentQualifier } from '../../../../types/component';
import { Dict } from '../../../../types/types';
-import {
- AllProjects,
- LS_PROJECTS_SORT,
- LS_PROJECTS_VIEW,
- LS_PROJECTS_VISUALIZATION
-} from '../AllProjects';
+import { AllProjects, LS_PROJECTS_SORT, LS_PROJECTS_VIEW } from '../AllProjects';
jest.mock(
'../ProjectsList',
it('renders', () => {
const wrapper = shallowRender();
expect(wrapper).toMatchSnapshot();
- wrapper.setState({ query: { view: 'visualizations' } });
- expect(wrapper).toMatchSnapshot();
});
it('fetches projects', () => {
size: undefined,
sort: undefined,
tags: undefined,
- view: undefined,
- visualization: undefined
+ view: undefined
},
false
);
it('redirects to the saved search', () => {
const localeStorageMock: Dict<string> = {
[LS_PROJECTS_VIEW]: 'leak',
- [LS_PROJECTS_SORT]: 'coverage',
- [LS_PROJECTS_VISUALIZATION]: 'security'
+ [LS_PROJECTS_SORT]: 'coverage'
};
(get as jest.Mock).mockImplementation((key: string) => localeStorageMock[key]);
pathname: '/projects',
query: {
view: localeStorageMock[LS_PROJECTS_VIEW],
- sort: localeStorageMock[LS_PROJECTS_SORT],
- visualization: localeStorageMock[LS_PROJECTS_VISUALIZATION]
+ sort: localeStorageMock[LS_PROJECTS_SORT]
}
});
});
wrapper.find('PageHeader').prop<Function>('onPerspectiveChange')({ view: 'leak' });
expect(push).lastCalledWith({
pathname: '/projects',
- query: { view: 'leak', visualization: undefined }
+ query: { view: 'leak' }
});
expect(save).toHaveBeenCalledWith(LS_PROJECTS_SORT, undefined);
expect(save).toHaveBeenCalledWith(LS_PROJECTS_VIEW, 'leak');
- expect(save).toHaveBeenCalledWith(LS_PROJECTS_VISUALIZATION, undefined);
});
it('updates sorting when changing perspective from leak', () => {
});
expect(push).lastCalledWith({
pathname: '/projects',
- query: { sort: 'coverage', view: undefined, visualization: undefined }
+ query: { sort: 'coverage', view: undefined }
});
expect(save).toHaveBeenCalledWith(LS_PROJECTS_SORT, 'coverage');
expect(save).toHaveBeenCalledWith(LS_PROJECTS_VIEW, undefined);
- expect(save).toHaveBeenCalledWith(LS_PROJECTS_VISUALIZATION, undefined);
-});
-
-it('changes perspective to risk visualization', () => {
- const push = jest.fn();
- const wrapper = shallowRender({}, push);
- wrapper.find('PageHeader').prop<Function>('onPerspectiveChange')({
- view: 'visualizations',
- visualization: 'risk'
- });
- expect(push).lastCalledWith({
- pathname: '/projects',
- query: { view: 'visualizations', visualization: 'risk' }
- });
- expect(save).toHaveBeenCalledWith(LS_PROJECTS_SORT, undefined);
- expect(save).toHaveBeenCalledWith(LS_PROJECTS_VIEW, 'visualizations');
- expect(save).toHaveBeenCalledWith(LS_PROJECTS_VISUALIZATION, 'risk');
});
it('handles favorite projects', () => {
).toBe(false);
});
-it('should render disabled sorting options for visualizations', () => {
- expect(
- shallowRender({
- open: true,
- total: undefined,
- view: 'visualizations',
- visualization: 'coverage'
- })
- ).toMatchSnapshot();
-});
-
it('should render switch the default sorting option for anonymous users', () => {
expect(
shallowRender({
currentUser: { isLoggedIn: true },
- open: true,
- visualization: 'risk'
+ open: true
}).find('ProjectsSortingSelect')
).toMatchSnapshot();
shallowRender({
currentUser: { isLoggedIn: false },
open: true,
- view: 'leak',
- visualization: 'risk'
+ view: 'leak'
}).find('ProjectsSortingSelect')
).toMatchSnapshot();
});
onPerspectiveChange={jest.fn()}
onQueryChange={jest.fn()}
onSortChange={jest.fn()}
- projects={[]}
query={{ search: 'test' }}
selectedSort="size"
total={12}
it('should render correctly', () => {
const sidebar = shallowRender({
query: { size: '3' },
- view: 'overall',
- visualization: 'risk'
+ view: 'overall'
});
expect(sidebar).toMatchSnapshot();
const sidebar = shallowRender({
applicationsEnabled: false,
query: { size: '3' },
- view: 'overall',
- visualization: 'risk'
+ view: 'overall'
});
expect(sidebar).toMatchSnapshot();
it('should render `leak` view correctly', () => {
const sidebar = shallowRender({
query: { view: 'leak' },
- view: 'leak',
- visualization: 'risk'
+ view: 'leak'
});
expect(sidebar).toMatchSnapshot();
});
const sidebar = shallowRender({
applicationsEnabled: false,
query: { view: 'leak' },
- view: 'leak',
- visualization: 'risk'
+ view: 'leak'
});
expect(sidebar).toMatchSnapshot();
});
-it('reset function should work correctly with view and visualizations', () => {
- const sidebar = shallowRender({
- query: { view: 'visualizations', visualization: 'bugs' },
- view: 'visualizations',
- visualization: 'bugs'
- });
-
- expect(sidebar.find('ClearAll').exists()).toBe(false);
- sidebar.setProps({ query: { size: '3' } });
- expect(sidebar.find('ClearAll').exists()).toBe(true);
-});
-
function shallowRender(overrides: Partial<PageSidebarProps> = {}) {
return shallow(
<PageSidebar
applicationsEnabled={true}
onClearAll={jest.fn()}
onQueryChange={jest.fn()}
- query={{ view: 'visualizations', visualization: 'bugs' }}
+ query={{ view: 'overall' }}
view="overall"
- visualization="bugs"
{...overrides}
/>
);
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import BubblesIcon from '../../../../components/icons/BubblesIcon';
import ListIcon from '../../../../components/icons/ListIcon';
import { mockReactSelectOptionProps } from '../../../../helpers/mocks/react-select';
import PerspectiveSelect from '../PerspectiveSelect';
expect(shallow(<PerspectiveSelect onChange={jest.fn()} view="overall" />)).toMatchSnapshot();
});
-it('should render with coverage selected', () => {
- expect(shallowRender({ view: 'visualizations', visualization: 'coverage' })).toMatchSnapshot();
-});
-
it('should render option correctly', () => {
const wrapper = shallowRender();
const OptionRender = wrapper.instance().perspectiveOptionRender;
- let option = shallow(
- <OptionRender {...mockReactSelectOptionProps({ type: 'view', value: 'test', label: 'test' })} />
+ const option = shallow(
+ <OptionRender {...mockReactSelectOptionProps({ value: 'test', label: 'test' })} />
);
-
expect(option.find(ListIcon).type).toBeDefined();
- option = shallow(
- <OptionRender
- {...mockReactSelectOptionProps({ type: 'visualization', value: 'test', label: '' })}
- />
- );
- expect(option.find(BubblesIcon).type).toBeDefined();
});
it('should handle perspective change correctly', () => {
const onChange = jest.fn();
const instance = shallowRender({ onChange }).instance();
- instance.handleChange({ label: 'overall', value: 'overall', type: 'view' });
- instance.handleChange({ label: 'leak', value: 'leak', type: 'view' });
- instance.handleChange({ label: 'coverage', value: 'coverage', type: 'visualization' });
+ instance.handleChange({ label: 'overall', value: 'overall' });
+ instance.handleChange({ label: 'leak', value: 'leak' });
expect(onChange.mock.calls).toMatchSnapshot();
});
function shallowRender(overrides: Partial<PerspectiveSelect['props']> = {}) {
return shallow<PerspectiveSelect>(
- <PerspectiveSelect onChange={jest.fn()} view="visualizations" {...overrides} />
+ <PerspectiveSelect onChange={jest.fn()} view="overall" {...overrides} />
);
}
onPerspectiveChange={[Function]}
onQueryChange={[Function]}
onSortChange={[Function]}
- projects={
- Array [
- Object {
- "key": "foo",
- "measures": Object {},
- "name": "Foo",
- "qualifier": "TRK",
- "tags": Array [],
- "visibility": "public",
- },
- ]
- }
query={
Object {
"coverage": undefined,
"sort": undefined,
"tags": undefined,
"view": undefined,
- "visualization": undefined,
}
}
selectedSort="name"
total={0}
view="overall"
- visualization="risk"
/>
</div>
</div>
"sort": undefined,
"tags": undefined,
"view": undefined,
- "visualization": undefined,
}
}
/>
</div>
</div>
`;
-
-exports[`renders 2`] = `
-<div
- className="layout-page projects-page"
- id="projects-page"
->
- <Suggestions
- suggestions="projects"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="projects.page"
- />
- <h1
- className="a11y-hidden"
- >
- projects.page
- </h1>
- <ScreenPositionHelper
- className="layout-page-side-outer"
- >
- <Component />
- </ScreenPositionHelper>
- <div
- className="layout-page-main"
- >
- <A11ySkipTarget
- anchor="projects_main"
- />
- <div
- role="main"
- >
- <h2
- className="a11y-hidden"
- >
- list_of_projects
- </h2>
- <div
- className="layout-page-header-panel layout-page-main-header"
- >
- <div
- className="layout-page-header-panel-inner layout-page-main-header-inner"
- >
- <div
- className="layout-page-main-inner"
- >
- <PageHeader
- currentUser={
- Object {
- "isLoggedIn": true,
- }
- }
- loading={false}
- onPerspectiveChange={[Function]}
- onQueryChange={[Function]}
- onSortChange={[Function]}
- projects={
- Array [
- Object {
- "key": "foo",
- "measures": Object {},
- "name": "Foo",
- "qualifier": "TRK",
- "tags": Array [],
- "visibility": "public",
- },
- ]
- }
- query={
- Object {
- "view": "visualizations",
- }
- }
- selectedSort="name"
- total={0}
- view="visualizations"
- visualization="risk"
- />
- </div>
- </div>
- </div>
- <div
- className="layout-page-main-inner"
- >
- <Visualizations
- projects={
- Array [
- Object {
- "key": "foo",
- "measures": Object {},
- "name": "Foo",
- "qualifier": "TRK",
- "tags": Array [],
- "visibility": "public",
- },
- ]
- }
- total={0}
- visualization="risk"
- />
- </div>
- </div>
- </div>
-</div>
-`;
onChange={[MockFunction]}
view="overall"
/>
- <Tooltip>
- <div
- className="projects-topbar-item"
- >
- <ProjectsSortingSelect
- className="js-projects-sorting-select"
- defaultOption="analysis_date"
- onChange={[MockFunction]}
- selectedSort="size"
- view="overall"
- />
- </div>
- </Tooltip>
+ <div
+ className="projects-topbar-item"
+ >
+ <ProjectsSortingSelect
+ className="js-projects-sorting-select"
+ defaultOption="analysis_date"
+ onChange={[MockFunction]}
+ selectedSort="size"
+ view="overall"
+ />
+ </div>
</div>
</div>
</div>
onChange={[MockFunction]}
view="overall"
/>
- <Tooltip>
- <div
- className="projects-topbar-item"
- >
- <ProjectsSortingSelect
- className="js-projects-sorting-select"
- defaultOption="analysis_date"
- onChange={[MockFunction]}
- selectedSort="size"
- view="overall"
- />
- </div>
- </Tooltip>
- </div>
- </div>
-</div>
-`;
-
-exports[`should render disabled sorting options for visualizations 1`] = `
-<div
- className="page-header"
->
- <div
- className="display-flex-space-between spacer-top"
- >
- <SearchFilterContainer
- onQueryChange={[MockFunction]}
- query={
- Object {
- "search": "test",
- }
- }
- />
- <div
- className="display-flex-center"
- >
- <Connect(withCurrentUser(ProjectCreationMenu))
- className="little-spacer-right"
- />
- <Connect(withAppState(Connect(withCurrentUser(withRouter(ApplicationCreation)))))
- className="little-spacer-right"
- />
- <Connect(HomePageSelect)
- className="spacer-left little-spacer-right"
- currentPage={
- Object {
- "type": "PROJECTS",
- }
- }
- />
- </div>
- </div>
- <div
- className="big-spacer-top display-flex-space-between"
- >
- <div
- className="display-flex-center"
- />
- <div
- className="display-flex-center"
- >
- <PerspectiveSelect
- className="projects-topbar-item js-projects-perspective-select"
- onChange={[MockFunction]}
- view="visualizations"
- visualization="coverage"
- />
- <Tooltip
- overlay="projects.sort.disabled"
+ <div
+ className="projects-topbar-item"
>
- <div
- className="projects-topbar-item disabled"
- >
- <ProjectsSortingSelect
- className="js-projects-sorting-select"
- defaultOption="analysis_date"
- onChange={[MockFunction]}
- selectedSort="size"
- view="visualizations"
- />
- </div>
- </Tooltip>
+ <ProjectsSortingSelect
+ className="js-projects-sorting-select"
+ defaultOption="analysis_date"
+ onChange={[MockFunction]}
+ selectedSort="size"
+ view="overall"
+ />
+ </div>
</div>
</div>
</div>
"view": "leak",
},
],
- Array [
- Object {
- "view": "visualizations",
- "visualization": "coverage",
- },
- ],
]
`;
Array [
Object {
"label": "projects.view.overall",
- "type": "view",
"value": "overall",
},
Object {
"label": "projects.view.new_code",
- "type": "view",
"value": "leak",
},
- Object {
- "label": "projects.visualization.risk",
- "type": "visualization",
- "value": "risk",
- },
- Object {
- "label": "projects.visualization.reliability",
- "type": "visualization",
- "value": "reliability",
- },
- Object {
- "label": "projects.visualization.security",
- "type": "visualization",
- "value": "security",
- },
- Object {
- "label": "projects.visualization.maintainability",
- "type": "visualization",
- "value": "maintainability",
- },
- Object {
- "label": "projects.visualization.coverage",
- "type": "visualization",
- "value": "coverage",
- },
- Object {
- "label": "projects.visualization.duplications",
- "type": "visualization",
- "value": "duplications",
- },
]
}
value={
Object {
"label": "projects.view.overall",
- "type": "view",
"value": "overall",
}
}
/>
</div>
`;
-
-exports[`should render with coverage selected 1`] = `
-<div>
- <label
- id="aria-projects-perspective"
- >
- projects.perspective
- :
- </label>
- <Select
- aria-labelledby="aria-projects-perspective"
- className="little-spacer-left input-medium it__projects-perspective-select"
- components={
- Object {
- "Option": [Function],
- }
- }
- isClearable={false}
- isSearchable={false}
- onChange={[Function]}
- options={
- Array [
- Object {
- "label": "projects.view.overall",
- "type": "view",
- "value": "overall",
- },
- Object {
- "label": "projects.view.new_code",
- "type": "view",
- "value": "leak",
- },
- Object {
- "label": "projects.visualization.risk",
- "type": "visualization",
- "value": "risk",
- },
- Object {
- "label": "projects.visualization.reliability",
- "type": "visualization",
- "value": "reliability",
- },
- Object {
- "label": "projects.visualization.security",
- "type": "visualization",
- "value": "security",
- },
- Object {
- "label": "projects.visualization.maintainability",
- "type": "visualization",
- "value": "maintainability",
- },
- Object {
- "label": "projects.visualization.coverage",
- "type": "visualization",
- "value": "coverage",
- },
- Object {
- "label": "projects.visualization.duplications",
- "type": "visualization",
- "value": "duplications",
- },
- ]
- }
- value={
- Object {
- "label": "projects.visualization.coverage",
- "type": "visualization",
- "value": "coverage",
- }
- }
- />
-</div>
-`;
*/
import { ComponentQualifier } from '../../types/component';
import { Dict, RawQuery } from '../../types/types';
-import { VISUALIZATIONS } from './utils';
type Level = 'ERROR' | 'WARN' | 'OK';
search?: string;
sort?: string;
view?: string;
- visualization?: string;
[x: string]: string | number | string[] | undefined;
}
qualifier: getAsQualifier(urlQuery['qualifier']),
search: getAsString(urlQuery['search']),
sort: getAsString(urlQuery['sort']),
- view: getView(urlQuery['view']),
- visualization: getVisualization(urlQuery['visualization'])
+ view: getView(urlQuery['view'])
};
}
return conditions.join(' and ');
}
-const visualizationParams = ['sort', 'view', 'visualization'];
+const viewParems = ['sort', 'view'];
export function hasFilterParams(query: Query) {
return Object.keys(query)
- .filter(key => !visualizationParams.includes(key))
+ .filter(key => !viewParems.includes(key))
.some(key => query[key] !== undefined);
}
-export function hasVisualizationParams(query: Query) {
+export function hasViewParams(query: Query) {
return Object.keys(query)
- .filter(key => visualizationParams.includes(key))
+ .filter(key => viewParems.includes(key))
.some(key => query[key] !== undefined);
}
return typeof value !== 'string' || value === 'overall' ? undefined : value;
}
-function getVisualization(value: string): string | undefined {
- return VISUALIZATIONS.includes(value) ? value : undefined;
-}
-
function convertIssuesRating(metric: string, rating: number): string {
if (rating > 1 && rating < 5) {
return `${metric} >= ${rating}`;
padding-left: 32px;
}
-.projects-topbar-item.disabled {
- cursor: not-allowed;
- opacity: 0.4;
-}
-
-.projects-topbar-item.disabled * {
- pointer-events: none !important;
-}
-
.projects-topbar-item-search {
position: relative;
flex: 1;
background-color: var(--blue);
}
-.projects-visualization {
- position: relative;
- height: 600px;
-}
-
-.projects-visualization .measure-details-bubble-chart-axis.y {
- width: 300px;
- left: 15px;
- margin-top: 150px;
- transform-origin: 0 0;
- text-align: center;
-}
-
-.projects-visualizations-footer {
- margin-top: 8px;
- padding: 15px 60px;
- border-top: 1px solid var(--barBorderColor);
- font-size: var(--smallFontSize);
- line-height: 1.4;
- text-align: center;
-}
-
-.projects-visualizations-footer .note {
- font-style: italic;
-}
-
-.measure-details-bubble-chart-title {
- position: absolute;
- left: 20px;
-}
-
-.measure-details-bubble-chart-axis {
- position: absolute;
- color: var(--secondFontColor);
- font-size: var(--smallFontSize);
-}
-
-.measure-details-bubble-chart-axis.x {
- left: 50%;
- bottom: 16px;
- width: 500px;
- margin-left: -250px;
- text-align: center;
-}
-
-.measure-details-bubble-chart-axis.y {
- top: 50%;
- left: -20px;
- transform: rotate(-90deg);
-}
-
-.measure-details-bubble-chart-axis.size {
- display: flex;
- align-items: center;
- justify-content: space-around;
- top: 16px;
- width: 100%;
-}
-
.projects-empty-list {
padding: calc(4 * var(--gridSize)) 0;
text-align: center;
{ value: 'leak', label: 'new_code' }
];
-export const VISUALIZATIONS = [
- 'risk',
- 'reliability',
- 'security',
- 'maintainability',
- 'coverage',
- 'duplications'
-];
-
const PAGE_SIZE = 50;
-const PAGE_SIZE_VISUALIZATIONS = 99;
const METRICS = [
MetricKey.alert_status,
MetricKey.projects
];
-const METRICS_BY_VISUALIZATION: Dict<string[]> = {
- risk: [
- MetricKey.reliability_rating,
- MetricKey.security_rating,
- MetricKey.coverage,
- MetricKey.ncloc,
- MetricKey.sqale_index
- ],
- // x, y, size, color
- reliability: [
- MetricKey.ncloc,
- MetricKey.reliability_remediation_effort,
- MetricKey.bugs,
- MetricKey.reliability_rating
- ],
- security: [
- MetricKey.ncloc,
- MetricKey.security_remediation_effort,
- MetricKey.vulnerabilities,
- MetricKey.security_rating
- ],
- maintainability: [
- MetricKey.ncloc,
- MetricKey.sqale_index,
- MetricKey.code_smells,
- MetricKey.sqale_rating
- ],
- coverage: [MetricKey.complexity, MetricKey.coverage, MetricKey.uncovered_lines],
- duplications: [MetricKey.ncloc, MetricKey.duplicated_lines_density, MetricKey.duplicated_blocks]
-};
-
export const FACETS = [
'reliability_rating',
'security_rating',
}
export function fetchProjects(query: Query, isFavorite: boolean, pageIndex = 1) {
- const ps = query.view === 'visualizations' ? PAGE_SIZE_VISUALIZATIONS : PAGE_SIZE;
+ const ps = PAGE_SIZE;
const data = convertToQueryData(query, isFavorite, {
p: pageIndex > 1 ? pageIndex : undefined,
ps,
function defineMetrics(query: Query): string[] {
switch (query.view) {
- case 'visualizations':
- return METRICS_BY_VISUALIZATION[query.visualization || 'risk'];
case 'leak':
return LEAK_METRICS;
default:
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import * as React from 'react';
-import { translate } from '../../../helpers/l10n';
-import { Project } from '../types';
-import SimpleBubbleChart from './SimpleBubbleChart';
-
-interface Props {
- helpText: string;
- projects: Project[];
-}
-
-export default function Coverage(props: Props) {
- return (
- <SimpleBubbleChart
- {...props}
- sizeMetric={{ key: 'uncovered_lines', type: 'SHORT_INT' }}
- title={translate('projects.visualization', 'coverage')}
- xMetric={{ key: 'complexity', type: 'SHORT_INT' }}
- yDomain={[100, 0]}
- yMetric={{ key: 'coverage', type: 'PERCENT' }}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import * as React from 'react';
-import { translate } from '../../../helpers/l10n';
-import { Project } from '../types';
-import SimpleBubbleChart from './SimpleBubbleChart';
-
-interface Props {
- helpText: string;
- projects: Project[];
-}
-
-export default function Duplications(props: Props) {
- return (
- <SimpleBubbleChart
- {...props}
- sizeMetric={{ key: 'duplicated_blocks', type: 'SHORT_INT' }}
- title={translate('projects.visualization', 'duplications')}
- xMetric={{ key: 'ncloc', type: 'SHORT_INT' }}
- yMetric={{ key: 'duplicated_lines_density', type: 'PERCENT' }}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import * as React from 'react';
-import { translate } from '../../../helpers/l10n';
-import { Project } from '../types';
-import SimpleBubbleChart from './SimpleBubbleChart';
-
-interface Props {
- helpText: string;
- projects: Project[];
-}
-
-export default function Maintainability(props: Props) {
- return (
- <SimpleBubbleChart
- {...props}
- colorMetric="sqale_rating"
- sizeMetric={{ key: 'code_smells', type: 'SHORT_INT' }}
- title={translate('projects.visualization', 'maintainability')}
- xMetric={{ key: 'ncloc', type: 'SHORT_INT' }}
- yMetric={{ key: 'sqale_index', type: 'SHORT_WORK_DUR' }}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import * as React from 'react';
-import { translate } from '../../../helpers/l10n';
-import { Project } from '../types';
-import SimpleBubbleChart from './SimpleBubbleChart';
-
-interface Props {
- helpText: string;
- projects: Project[];
-}
-
-export default function Reliability(props: Props) {
- return (
- <SimpleBubbleChart
- {...props}
- colorMetric="reliability_rating"
- sizeMetric={{ key: 'bugs', type: 'SHORT_INT' }}
- title={translate('projects.visualization', 'reliability')}
- xMetric={{ key: 'ncloc', type: 'SHORT_INT' }}
- yMetric={{ key: 'reliability_remediation_effort', type: 'SHORT_WORK_DUR' }}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 BubbleChart from '../../../components/charts/BubbleChart';
-import ColorRatingsLegend from '../../../components/charts/ColorRatingsLegend';
-import HelpTooltip from '../../../components/controls/HelpTooltip';
-import QualifierIcon from '../../../components/icons/QualifierIcon';
-import { RATING_COLORS } from '../../../helpers/constants';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
-import { isDefined } from '../../../helpers/types';
-import { getProjectUrl } from '../../../helpers/urls';
-import { ComponentQualifier } from '../../../types/component';
-import { Project } from '../types';
-
-const X_METRIC = 'sqale_index';
-const X_METRIC_TYPE = 'SHORT_WORK_DUR';
-const Y_METRIC = 'coverage';
-const Y_METRIC_TYPE = 'PERCENT';
-const SIZE_METRIC = 'ncloc';
-const SIZE_METRIC_TYPE = 'SHORT_INT';
-const COLOR_METRIC_1 = 'reliability_rating';
-const COLOR_METRIC_2 = 'security_rating';
-const COLOR_METRIC_TYPE = 'RATING';
-
-interface Props {
- helpText: string;
- projects: Project[];
-}
-
-interface State {
- ratingFilters: { [rating: number]: boolean };
-}
-
-export default class Risk extends React.PureComponent<Props, State> {
- state: State = {
- ratingFilters: {}
- };
-
- getMetricTooltip(metric: { key: string; type: string }, value?: number) {
- const name = translate('metric', metric.key, 'name');
- const formattedValue = value != null ? formatMeasure(value, metric.type) : '–';
- return (
- <div>
- {name}
- {': '}
- {formattedValue}
- </div>
- );
- }
-
- getTooltip(
- project: Project,
- x?: number,
- y?: number,
- size?: number,
- color1?: number,
- color2?: number
- ) {
- return (
- <div className="text-left">
- <div className="little-spacer-bottom display-flex-center display-flex-space-between">
- <strong>{project.name}</strong>
-
- {project.qualifier === ComponentQualifier.Application && (
- <div className="big-spacer-left nowrap">
- <QualifierIcon
- className="little-spacer-right"
- fill="currentColor"
- qualifier={ComponentQualifier.Application}
- />
- {translate('qualifier.APP')}
- </div>
- )}
- </div>
- {this.getMetricTooltip({ key: COLOR_METRIC_1, type: COLOR_METRIC_TYPE }, color1)}
- {this.getMetricTooltip({ key: COLOR_METRIC_2, type: COLOR_METRIC_TYPE }, color2)}
- {this.getMetricTooltip({ key: Y_METRIC, type: Y_METRIC_TYPE }, y)}
- {this.getMetricTooltip({ key: X_METRIC, type: X_METRIC_TYPE }, x)}
- {this.getMetricTooltip({ key: SIZE_METRIC, type: SIZE_METRIC_TYPE }, size)}
- </div>
- );
- }
-
- handleRatingFilterClick = (selection: number) => {
- this.setState(({ ratingFilters }) => {
- return { ratingFilters: { ...ratingFilters, [selection]: !ratingFilters[selection] } };
- });
- };
-
- render() {
- const { ratingFilters } = this.state;
-
- const items = this.props.projects
- .map(project => {
- const x =
- project.measures[X_METRIC] != null ? Number(project.measures[X_METRIC]) : undefined;
- const y =
- project.measures[Y_METRIC] != null ? Number(project.measures[Y_METRIC]) : undefined;
- const size =
- project.measures[SIZE_METRIC] != null ? Number(project.measures[SIZE_METRIC]) : undefined;
- const color1 =
- project.measures[COLOR_METRIC_1] != null
- ? Number(project.measures[COLOR_METRIC_1])
- : undefined;
- const color2 =
- project.measures[COLOR_METRIC_2] != null
- ? Number(project.measures[COLOR_METRIC_2])
- : undefined;
-
- const colorRating =
- color1 !== undefined && color2 !== undefined ? Math.max(color1, color2) : undefined;
-
- // Filter out items that match ratingFilters
- if (colorRating !== undefined && ratingFilters[colorRating]) {
- return undefined;
- }
-
- return {
- x: x || 0,
- y: y || 0,
- size: size || 0,
- color: colorRating !== undefined ? RATING_COLORS[colorRating - 1] : undefined,
- key: project.key,
- tooltip: this.getTooltip(project, x, y, size, color1, color2),
- link: getProjectUrl(project.key)
- };
- })
- .filter(isDefined);
-
- const formatXTick = (tick: number) => formatMeasure(tick, X_METRIC_TYPE);
- const formatYTick = (tick: number) => formatMeasure(tick, Y_METRIC_TYPE);
-
- return (
- <div>
- <BubbleChart
- formatXTick={formatXTick}
- formatYTick={formatYTick}
- height={600}
- items={items}
- padding={[80, 20, 60, 100]}
- yDomain={[100, 0]}
- />
-
- <div className="measure-details-bubble-chart-axis x">
- {translate('metric', X_METRIC, 'name')}
- </div>
- <div className="measure-details-bubble-chart-axis y">
- {translate('metric', Y_METRIC, 'name')}
- </div>
- <div className="measure-details-bubble-chart-axis size">
- <span className="measure-details-bubble-chart-title">
- <span className="text-middle">{translate('projects.visualization.risk')}</span>
- <HelpTooltip className="spacer-left" overlay={this.props.helpText} />
- </span>
- <div>
- <span className="spacer-right">
- {translateWithParameters(
- 'component_measures.legend.color_x',
- translate('projects.worse_of_reliablity_and_security')
- )}
- </span>
- {translateWithParameters(
- 'component_measures.legend.size_x',
- translate('metric', SIZE_METRIC, 'name')
- )}
- <ColorRatingsLegend
- className="big-spacer-top"
- filters={ratingFilters}
- onRatingClick={this.handleRatingFilterClick}
- />
- </div>
- </div>
- </div>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import * as React from 'react';
-import { translate } from '../../../helpers/l10n';
-import { Project } from '../types';
-import SimpleBubbleChart from './SimpleBubbleChart';
-
-interface Props {
- helpText: string;
- projects: Project[];
-}
-
-export default function Security(props: Props) {
- return (
- <SimpleBubbleChart
- {...props}
- colorMetric="security_rating"
- sizeMetric={{ key: 'vulnerabilities', type: 'SHORT_INT' }}
- title={translate('projects.visualization', 'security')}
- xMetric={{ key: 'ncloc', type: 'SHORT_INT' }}
- yMetric={{ key: 'security_remediation_effort', type: 'SHORT_WORK_DUR' }}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 BubbleChart from '../../../components/charts/BubbleChart';
-import ColorRatingsLegend from '../../../components/charts/ColorRatingsLegend';
-import HelpTooltip from '../../../components/controls/HelpTooltip';
-import QualifierIcon from '../../../components/icons/QualifierIcon';
-import { RATING_COLORS } from '../../../helpers/constants';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure } from '../../../helpers/measures';
-import { isDefined } from '../../../helpers/types';
-import { getProjectUrl } from '../../../helpers/urls';
-import { ComponentQualifier } from '../../../types/component';
-import { Project } from '../types';
-
-interface Metric {
- key: string;
- type: string;
-}
-
-interface Props {
- colorMetric?: string;
- helpText: string;
- projects: Project[];
- sizeMetric: Metric;
- title?: string;
- xMetric: Metric;
- yDomain?: [number, number];
- yMetric: Metric;
-}
-
-interface State {
- ratingFilters: { [rating: number]: boolean };
-}
-
-export default class SimpleBubbleChart extends React.PureComponent<Props, State> {
- state: State = {
- ratingFilters: {}
- };
-
- getMetricTooltip(metric: Metric, value?: number) {
- const name = translate('metric', metric.key, 'name');
- const formattedValue = value != null ? formatMeasure(value, metric.type) : '–';
- return (
- <div>
- {name}
- {': '}
- {formattedValue}
- </div>
- );
- }
-
- getTooltip(project: Project, x?: number, y?: number, size?: number, color?: number) {
- return (
- <div className="text-left">
- <div className="little-spacer-bottom display-flex-center display-flex-space-between">
- <strong>{project.name}</strong>
-
- {project.qualifier === ComponentQualifier.Application && (
- <div className="big-spacer-left nowrap">
- <QualifierIcon
- className="little-spacer-right"
- fill="currentColor"
- qualifier={ComponentQualifier.Application}
- />
- {translate('qualifier.APP')}
- </div>
- )}
- </div>
- {this.getMetricTooltip(this.props.xMetric, x)}
- {this.getMetricTooltip(this.props.yMetric, y)}
- {this.getMetricTooltip(this.props.sizeMetric, size)}
- {/* if `color` is defined then `this.props.colorMetric` is defined too */}
- {color && this.getMetricTooltip({ key: this.props.colorMetric!, type: 'RATING' }, color)}
- </div>
- );
- }
-
- handleRatingFilterClick = (selection: number) => {
- this.setState(({ ratingFilters }) => {
- return { ratingFilters: { ...ratingFilters, [selection]: !ratingFilters[selection] } };
- });
- };
-
- render() {
- const { xMetric, yMetric, sizeMetric, colorMetric } = this.props;
- const { ratingFilters } = this.state;
-
- const items = this.props.projects
- .filter(project => colorMetric == null || project.measures[colorMetric] !== null)
- .map(project => {
- const x =
- project.measures[xMetric.key] != null ? Number(project.measures[xMetric.key]) : undefined;
- const y =
- project.measures[yMetric.key] != null ? Number(project.measures[yMetric.key]) : undefined;
- const size =
- project.measures[sizeMetric.key] != null
- ? Number(project.measures[sizeMetric.key])
- : undefined;
- const color = colorMetric ? Number(project.measures[colorMetric]) : undefined;
-
- // Filter out items that match ratingFilters
- if (color && ratingFilters[color]) {
- return undefined;
- }
-
- return {
- x: x || 0,
- y: y || 0,
- size: size || 0,
- color: color ? RATING_COLORS[color - 1] : undefined,
- key: project.key,
- tooltip: this.getTooltip(project, x, y, size, color),
- link: getProjectUrl(project.key)
- };
- })
- .filter(isDefined);
-
- const formatXTick = (tick: number) => formatMeasure(tick, xMetric.type);
- const formatYTick = (tick: number) => formatMeasure(tick, yMetric.type);
-
- return (
- <div>
- <BubbleChart
- formatXTick={formatXTick}
- formatYTick={formatYTick}
- height={600}
- items={items}
- padding={[colorMetric ? 80 : 40, 20, 60, 100]}
- yDomain={this.props.yDomain}
- />
- <div className="measure-details-bubble-chart-axis x">
- {translate('metric', xMetric.key, 'name')}
- </div>
- <div className="measure-details-bubble-chart-axis y">
- {translate('metric', yMetric.key, 'name')}
- </div>
- <div className="measure-details-bubble-chart-axis size">
- <span className="measure-details-bubble-chart-title">
- <span className="text-middle">{this.props.title}</span>
- <HelpTooltip className="spacer-left" overlay={this.props.helpText} />
- </span>
- <div>
- {colorMetric != null && (
- <span className="spacer-right">
- {translateWithParameters(
- 'component_measures.legend.color_x',
- translate('metric', colorMetric, 'name')
- )}
- </span>
- )}
- {translateWithParameters(
- 'component_measures.legend.size_x',
- translate('metric', sizeMetric.key, 'name')
- )}
- {colorMetric != null && (
- <ColorRatingsLegend
- className="big-spacer-top"
- filters={ratingFilters}
- onRatingClick={this.handleRatingFilterClick}
- />
- )}
- </div>
- </div>
- </div>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import * as React from 'react';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { Dict } from '../../../types/types';
-import { Project } from '../types';
-import { localizeSorting } from '../utils';
-import Coverage from './Coverage';
-import Duplications from './Duplications';
-import Maintainability from './Maintainability';
-import Reliability from './Reliability';
-import Risk from './Risk';
-import Security from './Security';
-
-interface Props {
- projects: Project[];
- sort?: string;
- total?: number;
- visualization: string;
-}
-
-export default class Visualizations extends React.PureComponent<Props> {
- renderVisualization(projects: Project[]) {
- const visualizationToComponent: Dict<any> = {
- risk: Risk,
- reliability: Reliability,
- security: Security,
- maintainability: Maintainability,
- coverage: Coverage,
- duplications: Duplications
- };
- const Component = visualizationToComponent[this.props.visualization];
-
- return Component ? (
- <Component
- helpText={translate('projects.visualization', this.props.visualization, 'description')}
- projects={projects}
- />
- ) : null;
- }
-
- renderFooter() {
- const { projects, total, sort } = this.props;
-
- const limitReached = projects != null && total != null && projects.length < total;
-
- return limitReached ? (
- <footer className="projects-visualizations-footer">
- <p className="note spacer-top">
- {translateWithParameters(
- 'projects.limited_set_of_projects',
- projects!.length,
- localizeSorting(sort)
- )}
- </p>
- </footer>
- ) : null;
- }
-
- render() {
- const { projects } = this.props;
-
- return (
- <div className="boxed-group projects-visualizations">
- <main className="projects-visualization">
- {projects != null && this.renderVisualization(projects)}
- </main>
- {this.renderFooter()}
- </div>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 Coverage from '../Coverage';
-
-it('renders', () => {
- expect(shallow(<Coverage helpText="foobar" projects={[]} />)).toMatchSnapshot();
-});
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 Duplications from '../Duplications';
-
-it('renders', () => {
- expect(shallow(<Duplications helpText="foobar" projects={[]} />)).toMatchSnapshot();
-});
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 Maintainability from '../Maintainability';
-
-it('renders', () => {
- expect(shallow(<Maintainability helpText="foobar" projects={[]} />)).toMatchSnapshot();
-});
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 Reliability from '../Reliability';
-
-it('renders', () => {
- expect(shallow(<Reliability helpText="foobar" projects={[]} />)).toMatchSnapshot();
-});
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 { mockProject } from '../../../../helpers/mocks/projects';
-import Risk from '../Risk';
-
-it('renders', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should handle filtering', () => {
- const wrapper = shallowRender();
-
- wrapper.instance().handleRatingFilterClick(2);
-
- expect(wrapper.state().ratingFilters).toEqual({ 2: true });
-});
-
-function shallowRender(overrides: Partial<Risk['props']> = {}) {
- const project1 = mockProject({
- key: 'foo',
- measures: {
- complexity: '17.2',
- coverage: '53.5',
- ncloc: '1734',
- sqale_index: '1',
- reliability_rating: '3',
- security_rating: '2'
- },
- name: 'Foo'
- });
- const project2 = mockProject({
- key: 'bar',
- name: 'Bar',
- measures: {}
- });
- return shallow<Risk>(<Risk helpText="foobar" projects={[project1, project2]} {...overrides} />);
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 Security from '../Security';
-
-it('renders', () => {
- expect(shallow(<Security helpText="foobar" projects={[]} />)).toMatchSnapshot();
-});
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 { mockProject } from '../../../../helpers/mocks/projects';
-import { ComponentQualifier } from '../../../../types/component';
-import SimpleBubbleChart from '../SimpleBubbleChart';
-
-it('renders', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should handle filtering', () => {
- const wrapper = shallowRender();
-
- wrapper.instance().handleRatingFilterClick(2);
-
- expect(wrapper.state().ratingFilters).toEqual({ 2: true });
-});
-
-function shallowRender(overrides: Partial<SimpleBubbleChart['props']> = {}) {
- const project1 = mockProject({
- measures: { complexity: '17.2', coverage: '53.5', ncloc: '1734', security_rating: '2' }
- });
- const app = mockProject({
- key: 'app',
- measures: { complexity: '23.1', coverage: '87.3', ncloc: '32478', security_rating: '1' },
- name: 'App',
- qualifier: ComponentQualifier.Application
- });
- return shallow<SimpleBubbleChart>(
- <SimpleBubbleChart
- colorMetric="security_rating"
- helpText="foobar"
- projects={[app, project1]}
- sizeMetric={{ key: 'ncloc', type: 'INT' }}
- xMetric={{ key: 'complexity', type: 'INT' }}
- yMetric={{ key: 'coverage', type: 'PERCENT' }}
- {...overrides}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 Visualizations from '../Visualizations';
-
-it('renders', () => {
- expect(shallow(<Visualizations projects={[]} visualization="coverage" />)).toMatchSnapshot();
-});
-
-it('renders when limit is reached', () => {
- expect(
- shallow(<Visualizations projects={[]} total={1000} visualization="coverage" />)
- ).toMatchSnapshot();
-});
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<SimpleBubbleChart
- helpText="foobar"
- projects={Array []}
- sizeMetric={
- Object {
- "key": "uncovered_lines",
- "type": "SHORT_INT",
- }
- }
- title="projects.visualization.coverage"
- xMetric={
- Object {
- "key": "complexity",
- "type": "SHORT_INT",
- }
- }
- yDomain={
- Array [
- 100,
- 0,
- ]
- }
- yMetric={
- Object {
- "key": "coverage",
- "type": "PERCENT",
- }
- }
-/>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<SimpleBubbleChart
- helpText="foobar"
- projects={Array []}
- sizeMetric={
- Object {
- "key": "duplicated_blocks",
- "type": "SHORT_INT",
- }
- }
- title="projects.visualization.duplications"
- xMetric={
- Object {
- "key": "ncloc",
- "type": "SHORT_INT",
- }
- }
- yMetric={
- Object {
- "key": "duplicated_lines_density",
- "type": "PERCENT",
- }
- }
-/>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<SimpleBubbleChart
- colorMetric="sqale_rating"
- helpText="foobar"
- projects={Array []}
- sizeMetric={
- Object {
- "key": "code_smells",
- "type": "SHORT_INT",
- }
- }
- title="projects.visualization.maintainability"
- xMetric={
- Object {
- "key": "ncloc",
- "type": "SHORT_INT",
- }
- }
- yMetric={
- Object {
- "key": "sqale_index",
- "type": "SHORT_WORK_DUR",
- }
- }
-/>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<SimpleBubbleChart
- colorMetric="reliability_rating"
- helpText="foobar"
- projects={Array []}
- sizeMetric={
- Object {
- "key": "bugs",
- "type": "SHORT_INT",
- }
- }
- title="projects.visualization.reliability"
- xMetric={
- Object {
- "key": "ncloc",
- "type": "SHORT_INT",
- }
- }
- yMetric={
- Object {
- "key": "reliability_remediation_effort",
- "type": "SHORT_WORK_DUR",
- }
- }
-/>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<div>
- <BubbleChart
- displayXGrid={true}
- displayXTicks={true}
- displayYGrid={true}
- displayYTicks={true}
- formatXTick={[Function]}
- formatYTick={[Function]}
- height={600}
- items={
- Array [
- Object {
- "color": "#eabe06",
- "key": "foo",
- "link": Object {
- "pathname": "/dashboard",
- "query": Object {
- "branch": undefined,
- "id": "foo",
- },
- },
- "size": 1734,
- "tooltip": <div
- className="text-left"
- >
- <div
- className="little-spacer-bottom display-flex-center display-flex-space-between"
- >
- <strong>
- Foo
- </strong>
- </div>
- <div>
- metric.reliability_rating.name
- :
- C
- </div>
- <div>
- metric.security_rating.name
- :
- B
- </div>
- <div>
- metric.coverage.name
- :
- 53.5%
- </div>
- <div>
- metric.sqale_index.name
- :
- work_duration.x_minutes.1
- </div>
- <div>
- metric.ncloc.name
- :
- 1.7short_number_suffix.k
- </div>
- </div>,
- "x": 1,
- "y": 53.5,
- },
- Object {
- "color": undefined,
- "key": "bar",
- "link": Object {
- "pathname": "/dashboard",
- "query": Object {
- "branch": undefined,
- "id": "bar",
- },
- },
- "size": 0,
- "tooltip": <div
- className="text-left"
- >
- <div
- className="little-spacer-bottom display-flex-center display-flex-space-between"
- >
- <strong>
- Bar
- </strong>
- </div>
- <div>
- metric.reliability_rating.name
- :
- –
- </div>
- <div>
- metric.security_rating.name
- :
- –
- </div>
- <div>
- metric.coverage.name
- :
- –
- </div>
- <div>
- metric.sqale_index.name
- :
- –
- </div>
- <div>
- metric.ncloc.name
- :
- –
- </div>
- </div>,
- "x": 0,
- "y": 0,
- },
- ]
- }
- padding={
- Array [
- 80,
- 20,
- 60,
- 100,
- ]
- }
- sizeRange={
- Array [
- 5,
- 45,
- ]
- }
- yDomain={
- Array [
- 100,
- 0,
- ]
- }
- />
- <div
- className="measure-details-bubble-chart-axis x"
- >
- metric.sqale_index.name
- </div>
- <div
- className="measure-details-bubble-chart-axis y"
- >
- metric.coverage.name
- </div>
- <div
- className="measure-details-bubble-chart-axis size"
- >
- <span
- className="measure-details-bubble-chart-title"
- >
- <span
- className="text-middle"
- >
- projects.visualization.risk
- </span>
- <HelpTooltip
- className="spacer-left"
- overlay="foobar"
- />
- </span>
- <div>
- <span
- className="spacer-right"
- >
- component_measures.legend.color_x.projects.worse_of_reliablity_and_security
- </span>
- component_measures.legend.size_x.metric.ncloc.name
- <ColorRatingsLegend
- className="big-spacer-top"
- filters={Object {}}
- onRatingClick={[Function]}
- />
- </div>
- </div>
-</div>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<SimpleBubbleChart
- colorMetric="security_rating"
- helpText="foobar"
- projects={Array []}
- sizeMetric={
- Object {
- "key": "vulnerabilities",
- "type": "SHORT_INT",
- }
- }
- title="projects.visualization.security"
- xMetric={
- Object {
- "key": "ncloc",
- "type": "SHORT_INT",
- }
- }
- yMetric={
- Object {
- "key": "security_remediation_effort",
- "type": "SHORT_WORK_DUR",
- }
- }
-/>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<div>
- <BubbleChart
- displayXGrid={true}
- displayXTicks={true}
- displayYGrid={true}
- displayYTicks={true}
- formatXTick={[Function]}
- formatYTick={[Function]}
- height={600}
- items={
- Array [
- Object {
- "color": "#00aa00",
- "key": "app",
- "link": Object {
- "pathname": "/dashboard",
- "query": Object {
- "branch": undefined,
- "id": "app",
- },
- },
- "size": 32478,
- "tooltip": <div
- className="text-left"
- >
- <div
- className="little-spacer-bottom display-flex-center display-flex-space-between"
- >
- <strong>
- App
- </strong>
- <div
- className="big-spacer-left nowrap"
- >
- <QualifierIcon
- className="little-spacer-right"
- fill="currentColor"
- qualifier="APP"
- />
- qualifier.APP
- </div>
- </div>
- <div>
- metric.complexity.name
- :
- 23
- </div>
- <div>
- metric.coverage.name
- :
- 87.3%
- </div>
- <div>
- metric.ncloc.name
- :
- 32,478
- </div>
- <div>
- metric.security_rating.name
- :
- A
- </div>
- </div>,
- "x": 23.1,
- "y": 87.3,
- },
- Object {
- "color": "#b0d513",
- "key": "foo",
- "link": Object {
- "pathname": "/dashboard",
- "query": Object {
- "branch": undefined,
- "id": "foo",
- },
- },
- "size": 1734,
- "tooltip": <div
- className="text-left"
- >
- <div
- className="little-spacer-bottom display-flex-center display-flex-space-between"
- >
- <strong>
- Foo
- </strong>
- </div>
- <div>
- metric.complexity.name
- :
- 17
- </div>
- <div>
- metric.coverage.name
- :
- 53.5%
- </div>
- <div>
- metric.ncloc.name
- :
- 1,734
- </div>
- <div>
- metric.security_rating.name
- :
- B
- </div>
- </div>,
- "x": 17.2,
- "y": 53.5,
- },
- ]
- }
- padding={
- Array [
- 80,
- 20,
- 60,
- 100,
- ]
- }
- sizeRange={
- Array [
- 5,
- 45,
- ]
- }
- />
- <div
- className="measure-details-bubble-chart-axis x"
- >
- metric.complexity.name
- </div>
- <div
- className="measure-details-bubble-chart-axis y"
- >
- metric.coverage.name
- </div>
- <div
- className="measure-details-bubble-chart-axis size"
- >
- <span
- className="measure-details-bubble-chart-title"
- >
- <span
- className="text-middle"
- />
- <HelpTooltip
- className="spacer-left"
- overlay="foobar"
- />
- </span>
- <div>
- <span
- className="spacer-right"
- >
- component_measures.legend.color_x.metric.security_rating.name
- </span>
- component_measures.legend.size_x.metric.ncloc.name
- <ColorRatingsLegend
- className="big-spacer-top"
- filters={Object {}}
- onRatingClick={[Function]}
- />
- </div>
- </div>
-</div>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<div
- className="boxed-group projects-visualizations"
->
- <main
- className="projects-visualization"
- >
- <Coverage
- helpText="projects.visualization.coverage.description"
- projects={Array []}
- />
- </main>
-</div>
-`;
-
-exports[`renders when limit is reached 1`] = `
-<div
- className="boxed-group projects-visualizations"
->
- <main
- className="projects-visualization"
- >
- <Coverage
- helpText="projects.visualization.coverage.description"
- projects={Array []}
- />
- </main>
- <footer
- className="projects-visualizations-footer"
- >
- <p
- className="note spacer-top"
- >
- projects.limited_set_of_projects.0.projects.sort.name
- </p>
- </footer>
-</div>
-`;
projects.view.overall_code=Overall Code
projects.view.new_code=New Code
projects.worse_of_reliablity_and_security=Worse of Reliability and Security
-projects.visualization.risk=Risk
-projects.visualization.risk.description=Get quick insights into the operational risks in your projects. Any color but green indicates immediate risks: Bugs or Vulnerabilities that should be examined. A position at the top or right of the graph means that the longer-term health of the project may be at risk. Green bubbles at the bottom-left are best.
-projects.visualization.reliability=Reliability
-projects.visualization.reliability.description=See bugs' operational risks to your projects. The closer a bubble's color is to red, the more severe the worst bugs in the project. Bubble size indicates bug volume in the project, and each bubble's vertical position reflects the estimated time to address the bugs in the project. Small green bubbles on the bottom edge are best.
-projects.visualization.security=Security
-projects.visualization.security.description=See vulnerabilities' operational risks to your projects. The closer a bubble's color is to red, the more severe the worst vulnerabilities in the project. Bubble size indicates vulnerability volume in the project, and each bubble's vertical position reflects the estimated time to address the vulnerabilities in the project. Small green bubbles on the bottom edge are best.
-projects.visualization.maintainability=Maintainability
-projects.visualization.maintainability.description=See code smells' long-term risks to your projects. The closer a bubble's color is to red, the higher the ratio of technical debt to project size. Bubble size indicates code smell volume in the project, and each bubble's vertical position reflects the estimated time to address the code smells in the project. Small green bubbles on the bottom edge are best.
-projects.visualization.coverage=Coverage
-projects.visualization.coverage.description=See missing test coverage's long-term risks to your projects. Bubble size indicates the volume of uncovered lines in the project, and each bubble's vertical position reflects the volume of missing coverage. Small bubbles on the bottom edge are best.
-projects.visualization.duplications=Duplications
-projects.visualization.duplications.description=See duplications' long-term risks to your projects. Bubble size indicates the volume of duplicated blocks in the project, and each bubble's vertical position reflects the volume of lines in those blocks. Small bubbles on the bottom edge are best.
projects.limited_set_of_projects=Displayed project set limited to the top {0} projects based on current sort: {1}.
projects.facets.quality_gate=Quality Gate
projects.facets.quality_gate.warning_help=Warning status is deprecated. This filter will disappear when no Warning Quality Gate remains.