]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-15999 Drop the visualizations of projects as bubble charts
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Fri, 4 Feb 2022 16:13:53 +0000 (17:13 +0100)
committersonartech <sonartech@sonarsource.com>
Tue, 8 Feb 2022 20:03:05 +0000 (20:03 +0000)
40 files changed:
server/sonar-web/src/main/js/apps/projects/__tests__/utils-test.ts
server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx
server/sonar-web/src/main/js/apps/projects/components/PageSidebar.tsx
server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelect.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelect-test.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelect-test.tsx.snap
server/sonar-web/src/main/js/apps/projects/query.ts
server/sonar-web/src/main/js/apps/projects/styles.css
server/sonar-web/src/main/js/apps/projects/utils.ts
server/sonar-web/src/main/js/apps/projects/visualizations/Coverage.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/Duplications.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/Maintainability.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/Reliability.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/Risk.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/Security.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/Visualizations.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Coverage-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Duplications-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Maintainability-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Reliability-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Risk-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Security-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/SimpleBubbleChart-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Visualizations-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Coverage-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Duplications-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Maintainability-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Reliability-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Security-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Visualizations-test.tsx.snap [deleted file]
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 53b733772979e193bdc901859d6ee995f41d458a..9661ddc8a290880eb436dba130c5d9f52f773b04 100644 (file)
@@ -85,13 +85,13 @@ describe('formatDuration', () => {
 
 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);
@@ -119,7 +119,7 @@ describe('fetchProjects', () => {
       ],
       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 },
index 812d23ac38583bed1a56fd2b9c4985adcf0ff7b2..be94169551eb32f311d8b09266e1c7d7f9cbc90d 100644 (file)
@@ -34,11 +34,10 @@ import { get, save } from '../../../helpers/storage';
 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';
@@ -62,7 +61,6 @@ interface State {
 
 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;
@@ -131,7 +129,6 @@ export class AllProjects extends React.PureComponent<Props, State> {
     const options: {
       sort?: string;
       view?: string;
-      visualization?: string;
     } = {};
     if (get(LS_PROJECTS_SORT)) {
       options.sort = get(LS_PROJECTS_SORT) || undefined;
@@ -139,16 +136,11 @@ export class AllProjects extends React.PureComponent<Props, State> {
     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 });
   };
@@ -165,14 +157,12 @@ export class AllProjects extends React.PureComponent<Props, State> {
     });
   };
 
-  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') {
@@ -189,16 +179,14 @@ export class AllProjects extends React.PureComponent<Props, State> {
 
     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);
@@ -244,7 +232,6 @@ export class AllProjects extends React.PureComponent<Props, State> {
                 onQueryChange={this.updateLocationQuery}
                 query={this.state.query}
                 view={this.getView()}
-                visualization={this.getVisualization()}
               />
             </div>
           </div>
@@ -263,12 +250,10 @@ export class AllProjects extends React.PureComponent<Props, State> {
             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>
@@ -280,18 +265,7 @@ export class AllProjects extends React.PureComponent<Props, State> {
       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
index 04b1e10c34eafd6451e448ffb714a1fea588d332..594cea686cbb57be50833e84cc92e278aac82540 100644 (file)
 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';
@@ -34,24 +32,19 @@ import ProjectsSortingSelect from './ProjectsSortingSelect';
 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">
@@ -81,21 +74,18 @@ export default function PageHeader(props: Props) {
           <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>
index 417b9e7af829311fe30f03eff5124f8510d32039..0c4876356819f51d89a58f97f82609da57e2f15e 100644 (file)
@@ -50,11 +50,10 @@ export interface PageSidebarProps {
   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);
@@ -63,10 +62,6 @@ export default function PageSidebar(props: PageSidebarProps) {
   let linkQuery: RawQuery | undefined = undefined;
   if (view !== 'overall') {
     linkQuery = { view };
-
-    if (view === 'visualizations') {
-      linkQuery.visualization = visualization;
-    }
   }
 
   return (
index 8a9e0a71fee01aee70d90c1b5fab2dab6c1e37a1..a9a39b06aa4f81cff99a8554b322eb63d14543f3 100644 (file)
@@ -21,31 +21,24 @@ import { omit } from 'lodash';
 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>) => {
@@ -54,26 +47,18 @@ export default class PerspectiveSelect extends React.PureComponent<Props> {
       <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 (
@@ -89,7 +74,7 @@ export default class PerspectiveSelect extends React.PureComponent<Props> {
           }}
           options={options}
           isSearchable={false}
-          value={options.find(option => option.value === perspective)}
+          value={options.find(option => option.value === view)}
         />
       </div>
     );
index 721837c89a51cb1ee1d03fe8b5668709cab9f0f8..819e956a14a20885d83ccae46041f305951830c1 100644 (file)
@@ -22,12 +22,7 @@ import * as React from 'react';
 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',
@@ -78,8 +73,6 @@ beforeEach(() => {
 it('renders', () => {
   const wrapper = shallowRender();
   expect(wrapper).toMatchSnapshot();
-  wrapper.setState({ query: { view: 'visualizations' } });
-  expect(wrapper).toMatchSnapshot();
 });
 
 it('fetches projects', () => {
@@ -103,8 +96,7 @@ it('fetches projects', () => {
       size: undefined,
       sort: undefined,
       tags: undefined,
-      view: undefined,
-      visualization: undefined
+      view: undefined
     },
     false
   );
@@ -113,8 +105,7 @@ it('fetches projects', () => {
 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]);
@@ -125,8 +116,7 @@ it('redirects to the saved search', () => {
     pathname: '/projects',
     query: {
       view: localeStorageMock[LS_PROJECTS_VIEW],
-      sort: localeStorageMock[LS_PROJECTS_SORT],
-      visualization: localeStorageMock[LS_PROJECTS_VISUALIZATION]
+      sort: localeStorageMock[LS_PROJECTS_SORT]
     }
   });
 });
@@ -145,11 +135,10 @@ it('changes perspective to leak', () => {
   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', () => {
@@ -161,27 +150,10 @@ 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', () => {
index b563f6d487b4cd47c5a23f68416640f43b201303..c50803a8740fbe0ddb0e380bf452b4747ca9a41b 100644 (file)
@@ -37,23 +37,11 @@ it('should not render projects total', () => {
   ).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();
 
@@ -61,8 +49,7 @@ it('should render switch the default sorting option for anonymous users', () =>
     shallowRender({
       currentUser: { isLoggedIn: false },
       open: true,
-      view: 'leak',
-      visualization: 'risk'
+      view: 'leak'
     }).find('ProjectsSortingSelect')
   ).toMatchSnapshot();
 });
@@ -75,7 +62,6 @@ function shallowRender(props?: {}) {
       onPerspectiveChange={jest.fn()}
       onQueryChange={jest.fn()}
       onSortChange={jest.fn()}
-      projects={[]}
       query={{ search: 'test' }}
       selectedSort="size"
       total={12}
index 4a6feb9d70fbf3ebb04cb81a7a291df68bb3951b..2940711cbbce2225df94bfec77d533cf5ddb7b20 100644 (file)
@@ -24,8 +24,7 @@ import PageSidebar, { PageSidebarProps } from '../PageSidebar';
 it('should render correctly', () => {
   const sidebar = shallowRender({
     query: { size: '3' },
-    view: 'overall',
-    visualization: 'risk'
+    view: 'overall'
   });
 
   expect(sidebar).toMatchSnapshot();
@@ -35,8 +34,7 @@ it('should render correctly with no applications', () => {
   const sidebar = shallowRender({
     applicationsEnabled: false,
     query: { size: '3' },
-    view: 'overall',
-    visualization: 'risk'
+    view: 'overall'
   });
 
   expect(sidebar).toMatchSnapshot();
@@ -45,8 +43,7 @@ it('should render correctly with no applications', () => {
 it('should render `leak` view correctly', () => {
   const sidebar = shallowRender({
     query: { view: 'leak' },
-    view: 'leak',
-    visualization: 'risk'
+    view: 'leak'
   });
   expect(sidebar).toMatchSnapshot();
 });
@@ -55,33 +52,19 @@ it('should render `leak` view correctly with no applications', () => {
   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}
     />
   );
index 53b6abc36aac562a922eacd4de38de32b8c15edf..37f00bee449e8a0a1f5426727a6b8ed52ece5dfa 100644 (file)
@@ -19,7 +19,6 @@
  */
 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';
@@ -28,37 +27,25 @@ it('should render correctly', () => {
   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} />
   );
 }
index 566c969e57a39d99716e2a1a5905b5fdc6242a7b..229c1295c61330907ea5973e620439c3eea7f78b 100644 (file)
@@ -84,18 +84,6 @@ exports[`renders 1`] = `
               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,
@@ -119,13 +107,11 @@ exports[`renders 1`] = `
                   "sort": undefined,
                   "tags": undefined,
                   "view": undefined,
-                  "visualization": undefined,
                 }
               }
               selectedSort="name"
               total={0}
               view="overall"
-              visualization="risk"
             />
           </div>
         </div>
@@ -178,7 +164,6 @@ exports[`renders 1`] = `
               "sort": undefined,
               "tags": undefined,
               "view": undefined,
-              "visualization": undefined,
             }
           }
         />
@@ -194,110 +179,3 @@ exports[`renders 1`] = `
   </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>
-`;
index 31ec1cb22bfa5061f8b4bba6b9ccbfcb182d9c2e..6f89a3843751a318c0a6149c20fcd76d81762838 100644 (file)
@@ -60,19 +60,17 @@ exports[`should render correctly 1`] = `
         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>
@@ -138,88 +136,17 @@ exports[`should render correctly while loading 1`] = `
         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>
index 24bf353513a4143c4db86373a50a11955f25d109..2bc12d3f29792c6fa5ae680ea0bf7e4eec0306ea 100644 (file)
@@ -12,12 +12,6 @@ Array [
       "view": "leak",
     },
   ],
-  Array [
-    Object {
-      "view": "visualizations",
-      "visualization": "coverage",
-    },
-  ],
 ]
 `;
 
@@ -44,127 +38,20 @@ exports[`should render correctly 1`] = `
       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>
-`;
index 5a457130958816d357f5f7cb64feeef7279aec13..888c45841fbe6e60eb8fdfad1aa2ce6d397a01bd 100644 (file)
@@ -19,7 +19,6 @@
  */
 import { ComponentQualifier } from '../../types/component';
 import { Dict, RawQuery } from '../../types/types';
-import { VISUALIZATIONS } from './utils';
 
 type Level = 'ERROR' | 'WARN' | 'OK';
 
@@ -45,7 +44,6 @@ export interface Query {
   search?: string;
   sort?: string;
   view?: string;
-  visualization?: string;
   [x: string]: string | number | string[] | undefined;
 }
 
@@ -71,8 +69,7 @@ export function parseUrlQuery(urlQuery: RawQuery): Query {
     qualifier: getAsQualifier(urlQuery['qualifier']),
     search: getAsString(urlQuery['search']),
     sort: getAsString(urlQuery['sort']),
-    view: getView(urlQuery['view']),
-    visualization: getVisualization(urlQuery['visualization'])
+    view: getView(urlQuery['view'])
   };
 }
 
@@ -121,17 +118,17 @@ export function convertToFilter(query: Query, isFavorite: boolean): string {
   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);
 }
 
@@ -172,10 +169,6 @@ function getView(value: any): string | 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}`;
index 605f5f16453d90f6108f55624dbd54340706c5b9..e6881bc56a7feba75f4b8d401a4f14864ff308b8 100644 (file)
   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;
index 91547de10efdd569c0d4244978fb2cf48cd5e69b..58916044832890878cc616df3f8cd804b5a6aa8c 100644 (file)
@@ -84,17 +84,7 @@ export const VIEWS = [
   { 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,
@@ -129,37 +119,6 @@ const LEAK_METRICS = [
   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',
@@ -200,7 +159,7 @@ export function parseSorting(sort: string): { sortValue: string; sortDesc: boole
 }
 
 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,
@@ -233,8 +192,6 @@ export function fetchProjects(query: Query, isFavorite: boolean, pageIndex = 1)
 
 function defineMetrics(query: Query): string[] {
   switch (query.view) {
-    case 'visualizations':
-      return METRICS_BY_VISUALIZATION[query.visualization || 'risk'];
     case 'leak':
       return LEAK_METRICS;
     default:
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Coverage.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/Coverage.tsx
deleted file mode 100644 (file)
index b5b7629..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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' }}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Duplications.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/Duplications.tsx
deleted file mode 100644 (file)
index 32cd26b..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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' }}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Maintainability.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/Maintainability.tsx
deleted file mode 100644 (file)
index d49d3de..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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' }}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Reliability.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/Reliability.tsx
deleted file mode 100644 (file)
index f214cc6..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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' }}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Risk.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/Risk.tsx
deleted file mode 100644 (file)
index efc148b..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * 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>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Security.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/Security.tsx
deleted file mode 100644 (file)
index 1abfc61..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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' }}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.tsx
deleted file mode 100644 (file)
index e8376ad..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * 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>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Visualizations.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/Visualizations.tsx
deleted file mode 100644 (file)
index f25ce4f..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Coverage-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Coverage-test.tsx
deleted file mode 100644 (file)
index 9fa76d3..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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();
-});
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Duplications-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Duplications-test.tsx
deleted file mode 100644 (file)
index 6fe9cca..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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();
-});
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Maintainability-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Maintainability-test.tsx
deleted file mode 100644 (file)
index 940d897..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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();
-});
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Reliability-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Reliability-test.tsx
deleted file mode 100644 (file)
index 56be0e3..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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();
-});
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Risk-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Risk-test.tsx
deleted file mode 100644 (file)
index d56b65f..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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} />);
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Security-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Security-test.tsx
deleted file mode 100644 (file)
index fb3cb5d..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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();
-});
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/SimpleBubbleChart-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/SimpleBubbleChart-test.tsx
deleted file mode 100644 (file)
index 399d308..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Visualizations-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Visualizations-test.tsx
deleted file mode 100644 (file)
index 37815b9..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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();
-});
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Coverage-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Coverage-test.tsx.snap
deleted file mode 100644 (file)
index 469481f..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-// 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",
-    }
-  }
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Duplications-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Duplications-test.tsx.snap
deleted file mode 100644 (file)
index 67fba43..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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",
-    }
-  }
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Maintainability-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Maintainability-test.tsx.snap
deleted file mode 100644 (file)
index e69496b..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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",
-    }
-  }
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Reliability-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Reliability-test.tsx.snap
deleted file mode 100644 (file)
index e018ca7..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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",
-    }
-  }
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap
deleted file mode 100644 (file)
index e62a6b6..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-// 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>
-`;
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Security-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Security-test.tsx.snap
deleted file mode 100644 (file)
index 17e5489..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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",
-    }
-  }
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap
deleted file mode 100644 (file)
index bc2daa2..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-// 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>
-`;
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Visualizations-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Visualizations-test.tsx.snap
deleted file mode 100644 (file)
index a74c4be..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-// 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>
-`;
index ddb16c6434aa176e676276ff16062ea2c4e65504..f72d1fd1dbe8dd6b3829f0d4f7bc09318bb73fb0 100644 (file)
@@ -1030,18 +1030,6 @@ projects.view.overall=Overall Status
 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.