diff options
40 files changed, 73 insertions, 2122 deletions
diff --git a/server/sonar-web/src/main/js/apps/projects/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/projects/__tests__/utils-test.ts index 53b73377297..9661ddc8a29 100644 --- a/server/sonar-web/src/main/js/apps/projects/__tests__/utils-test.ts +++ b/server/sonar-web/src/main/js/apps/projects/__tests__/utils-test.ts @@ -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 }, diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx index 812d23ac385..be94169551e 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx @@ -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 diff --git a/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx b/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx index 04b1e10c34e..594cea686cb 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx @@ -20,12 +20,10 @@ 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> diff --git a/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.tsx b/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.tsx index 417b9e7af82..0c487635681 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.tsx @@ -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 ( diff --git a/server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelect.tsx b/server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelect.tsx index 8a9e0a71fee..a9a39b06aa4 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelect.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelect.tsx @@ -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> ); diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx index 721837c89a5..819e956a14a 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx @@ -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', () => { diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.tsx index b563f6d487b..c50803a8740 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.tsx @@ -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} diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.tsx index 4a6feb9d70f..2940711cbbc 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.tsx @@ -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} /> ); diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelect-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelect-test.tsx index 53b6abc36aa..37f00bee449 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelect-test.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelect-test.tsx @@ -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} /> ); } diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap index 566c969e57a..229c1295c61 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap @@ -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> -`; diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap index 31ec1cb22bf..6f89a384375 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap @@ -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> diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelect-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelect-test.tsx.snap index 24bf353513a..2bc12d3f297 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelect-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelect-test.tsx.snap @@ -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> -`; diff --git a/server/sonar-web/src/main/js/apps/projects/query.ts b/server/sonar-web/src/main/js/apps/projects/query.ts index 5a457130958..888c45841fb 100644 --- a/server/sonar-web/src/main/js/apps/projects/query.ts +++ b/server/sonar-web/src/main/js/apps/projects/query.ts @@ -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}`; diff --git a/server/sonar-web/src/main/js/apps/projects/styles.css b/server/sonar-web/src/main/js/apps/projects/styles.css index 605f5f16453..e6881bc56a7 100644 --- a/server/sonar-web/src/main/js/apps/projects/styles.css +++ b/server/sonar-web/src/main/js/apps/projects/styles.css @@ -36,15 +36,6 @@ 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; @@ -99,65 +90,6 @@ 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; diff --git a/server/sonar-web/src/main/js/apps/projects/utils.ts b/server/sonar-web/src/main/js/apps/projects/utils.ts index 91547de10ef..58916044832 100644 --- a/server/sonar-web/src/main/js/apps/projects/utils.ts +++ b/server/sonar-web/src/main/js/apps/projects/utils.ts @@ -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 index b5b7629d8f0..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/Coverage.tsx +++ /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 index 32cd26b0498..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/Duplications.tsx +++ /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 index d49d3de2e1b..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/Maintainability.tsx +++ /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 index f214cc62472..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/Reliability.tsx +++ /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 index efc148b2cad..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/Risk.tsx +++ /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 index 1abfc615c77..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/Security.tsx +++ /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 index e8376adf59e..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.tsx +++ /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 index f25ce4fb271..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/Visualizations.tsx +++ /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 index 9fa76d3f3d9..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Coverage-test.tsx +++ /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 index 6fe9cca6eaf..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Duplications-test.tsx +++ /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 index 940d8974c84..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Maintainability-test.tsx +++ /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 index 56be0e385d3..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Reliability-test.tsx +++ /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 index d56b65fa874..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Risk-test.tsx +++ /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 index fb3cb5d35e7..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Security-test.tsx +++ /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 index 399d308bcf0..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/SimpleBubbleChart-test.tsx +++ /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 index 37815b9f1ad..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Visualizations-test.tsx +++ /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 index 469481fc92f..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Coverage-test.tsx.snap +++ /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 index 67fba43c178..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Duplications-test.tsx.snap +++ /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 index e69496b58a5..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Maintainability-test.tsx.snap +++ /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 index e018ca736b8..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Reliability-test.tsx.snap +++ /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 index e62a6b6a11e..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap +++ /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 index 17e548978ec..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Security-test.tsx.snap +++ /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 index bc2daa21d57..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap +++ /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 index a74c4bec92d..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Visualizations-test.tsx.snap +++ /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> -`; diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index ddb16c6434a..f72d1fd1dbe 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -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. |