From cb944cf1ff0459a8b457680fcae22ce4264aba02 Mon Sep 17 00:00:00 2001 From: Wouter Admiraal Date: Tue, 18 Jun 2019 08:53:08 +0200 Subject: [PATCH] SONAR-11830 Prevent branch names from disappearing in portfolio projects --- .../sonar-web/src/main/js/apps/code/code.css | 16 +- .../js/apps/code/components/ComponentName.tsx | 136 +++++---- .../js/apps/code/components/Truncated.tsx | 33 --- .../__tests__/ComponentName-test.tsx | 107 +++++++ .../__snapshots__/ComponentName-test.tsx.snap | 276 ++++++++++++++++++ 5 files changed, 462 insertions(+), 106 deletions(-) delete mode 100644 server/sonar-web/src/main/js/apps/code/components/Truncated.tsx create mode 100644 server/sonar-web/src/main/js/apps/code/components/__tests__/ComponentName-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/ComponentName-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/code/code.css b/server/sonar-web/src/main/js/apps/code/code.css index e9edb5007df..709cea43cf6 100644 --- a/server/sonar-web/src/main/js/apps/code/code.css +++ b/server/sonar-web/src/main/js/apps/code/code.css @@ -60,21 +60,15 @@ box-sizing: border-box; } -.code-truncated { - display: inline-block; - vertical-align: text-top; - max-width: 50vw; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - .code-name-cell { max-width: 0; } -.code-name-cell .code-truncated { - max-width: 100%; +@media (max-width: 1200px) { + .code-name-cell .badge, + .code-name-cell .outline-badge { + display: none; + } } .code-search { diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx b/server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx index ed43ecf8d5f..03669f6c250 100644 --- a/server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx @@ -19,14 +19,13 @@ */ import * as React from 'react'; import { Link } from 'react-router'; -import Truncated from './Truncated'; import * as theme from '../../../app/theme'; import QualifierIcon from '../../../components/icons-components/QualifierIcon'; import { getBranchLikeQuery } from '../../../helpers/branches'; import LongLivingBranchIcon from '../../../components/icons-components/LongLivingBranchIcon'; import { translate } from '../../../helpers/l10n'; -function getTooltip(component: T.ComponentMeasure) { +export function getTooltip(component: T.ComponentMeasure) { const isFile = component.qualifier === 'FIL' || component.qualifier === 'UTS'; if (isFile && component.path) { return component.path + '\n\n' + component.key; @@ -35,7 +34,7 @@ function getTooltip(component: T.ComponentMeasure) { } } -function mostCommitPrefix(strings: string[]) { +export function mostCommonPrefix(strings: string[]) { const sortedStrings = strings.slice(0).sort(); const firstString = sortedStrings[0]; const firstStringLength = firstString.length; @@ -50,7 +49,7 @@ function mostCommitPrefix(strings: string[]) { return prefix.substr(0, prefix.length - lastPrefixPart.length); } -interface Props { +export interface Props { branchLike?: T.BranchLike; canBrowse?: boolean; component: T.ComponentMeasure; @@ -58,68 +57,81 @@ interface Props { rootComponent: T.ComponentMeasure; } -export default class ComponentName extends React.PureComponent { - render() { - const { branchLike, component, rootComponent, previous, canBrowse = false } = this.props; - const areBothDirs = component.qualifier === 'DIR' && previous && previous.qualifier === 'DIR'; - const prefix = - areBothDirs && previous !== undefined - ? mostCommitPrefix([component.name + '/', previous.name + '/']) - : ''; - const name = prefix ? ( - - {prefix} - {component.name.substr(prefix.length)} - - ) : ( - component.name - ); +export default function ComponentName({ + branchLike, + component, + rootComponent, + previous, + canBrowse = false +}: Props) { + const areBothDirs = component.qualifier === 'DIR' && previous && previous.qualifier === 'DIR'; + const prefix = + areBothDirs && previous !== undefined + ? mostCommonPrefix([component.name + '/', previous.name + '/']) + : ''; + const name = prefix ? ( + + {prefix} + {component.name.substr(prefix.length)} + + ) : ( + component.name + ); - let inner = null; + let inner = null; - if (component.refKey && component.qualifier !== 'SVW') { - const branch = rootComponent.qualifier === 'APP' ? { branch: component.branch } : {}; - inner = ( - - {name} - - ); - } else if (canBrowse) { - const query = { id: rootComponent.key, ...getBranchLikeQuery(branchLike) }; - if (component.key !== rootComponent.key) { - Object.assign(query, { selected: component.key }); - } - inner = ( - - {name} - - ); - } else { - inner = ( - - {name} - - ); + if (component.refKey && component.qualifier !== 'SVW') { + const branch = rootComponent.qualifier === 'APP' ? { branch: component.branch } : {}; + inner = ( + + {name} + + ); + } else if (canBrowse) { + const query = { id: rootComponent.key, ...getBranchLikeQuery(branchLike) }; + if (component.key !== rootComponent.key) { + Object.assign(query, { selected: component.key }); } + inner = ( + + {name} + + ); + } else { + inner = ( + + {name} + + ); + } - if (rootComponent.qualifier === 'APP') { - inner = ( - <> + if (rootComponent.qualifier === 'APP') { + return ( + + {inner} - {component.branch ? ( - <> - - {component.branch} - - ) : ( - {translate('branches.main_branch')} - )} - - ); - } - - return {inner}; + + {component.branch ? ( + + + {component.branch} + + ) : ( + + {translate('branches.main_branch')} + + )} + + ); + } else { + return ( + + {inner} + + ); } } diff --git a/server/sonar-web/src/main/js/apps/code/components/Truncated.tsx b/server/sonar-web/src/main/js/apps/code/components/Truncated.tsx deleted file mode 100644 index 1c7bd744300..00000000000 --- a/server/sonar-web/src/main/js/apps/code/components/Truncated.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 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'; - -interface Props { - children: React.ReactNode; - title: string; -} - -export default function Truncated({ children, title }: Props) { - return ( - - {children} - - ); -} diff --git a/server/sonar-web/src/main/js/apps/code/components/__tests__/ComponentName-test.tsx b/server/sonar-web/src/main/js/apps/code/components/__tests__/ComponentName-test.tsx new file mode 100644 index 00000000000..b8ca1816795 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/code/components/__tests__/ComponentName-test.tsx @@ -0,0 +1,107 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import ComponentName, { getTooltip, mostCommonPrefix, Props } from '../ComponentName'; +import { mockMainBranch, mockComponentMeasure } from '../../../../helpers/testMocks'; + +describe('#getTooltip', () => { + it('should correctly format component information', () => { + expect(getTooltip(mockComponentMeasure(true))).toMatchSnapshot(); + expect(getTooltip(mockComponentMeasure(true, { qualifier: 'UTS' }))).toMatchSnapshot(); + expect(getTooltip(mockComponentMeasure(true, { path: undefined }))).toMatchSnapshot(); + expect(getTooltip(mockComponentMeasure(false))).toMatchSnapshot(); + }); +}); + +describe('#mostCommonPrefix', () => { + it('should correctly find the common path prefix', () => { + expect(mostCommonPrefix(['src/main/ts/tests', 'src/main/java/tests'])).toEqual('src/main/'); + expect(mostCommonPrefix(['src/main/ts/app', 'src/main/ts/app'])).toEqual('src/main/ts/'); + expect(mostCommonPrefix(['src/main/ts', 'lib/main/ts'])).toEqual(''); + }); +}); + +describe('#ComponentName', () => { + it('should render correctly for files', () => { + expect(shallowRender()).toMatchSnapshot(); + expect(shallowRender({ canBrowse: true })).toMatchSnapshot(); + expect( + shallowRender({ rootComponent: mockComponentMeasure(false, { qualifier: 'TRK' }) }) + ).toMatchSnapshot(); + expect( + shallowRender({ rootComponent: mockComponentMeasure(false, { qualifier: 'APP' }) }) + ).toMatchSnapshot(); + expect( + shallowRender({ + component: mockComponentMeasure(true, { branch: 'foo' }), + rootComponent: mockComponentMeasure(false, { qualifier: 'APP' }) + }) + ).toMatchSnapshot(); + }); + + it('should render correctly for dirs', () => { + expect( + shallowRender({ + component: mockComponentMeasure(false, { name: 'src/main/ts/app', qualifier: 'DIR' }), + previous: mockComponentMeasure(false, { name: 'src/main/ts/tests', qualifier: 'DIR' }) + }) + ).toMatchSnapshot(); + expect( + shallowRender({ + component: mockComponentMeasure(false, { name: 'src', qualifier: 'DIR' }), + previous: mockComponentMeasure(false, { name: 'lib', qualifier: 'DIR' }) + }) + ).toMatchSnapshot(); + }); + + it('should render correctly for refs', () => { + expect( + shallowRender({ + component: mockComponentMeasure(false, { + branch: 'foo', + refKey: 'src/main/ts/app', + qualifier: 'TRK' + }) + }) + ).toMatchSnapshot(); + expect( + shallowRender({ + component: mockComponentMeasure(false, { + branch: 'foo', + refKey: 'src/main/ts/app', + qualifier: 'TRK' + }), + rootComponent: mockComponentMeasure(false, { qualifier: 'APP' }) + }) + ).toMatchSnapshot(); + }); +}); + +function shallowRender(props: Partial = {}) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/ComponentName-test.tsx.snap b/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/ComponentName-test.tsx.snap new file mode 100644 index 00000000000..ff35466d366 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/ComponentName-test.tsx.snap @@ -0,0 +1,276 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#ComponentName should render correctly for dirs 1`] = ` + + + + + + + src/main/ts/ + + + app + + + + +`; + +exports[`#ComponentName should render correctly for dirs 2`] = ` + + + + + src + + +`; + +exports[`#ComponentName should render correctly for files 1`] = ` + + + + + index.tsx + + +`; + +exports[`#ComponentName should render correctly for files 2`] = ` + + + + + + index.tsx + + + +`; + +exports[`#ComponentName should render correctly for files 3`] = ` + + + + + index.tsx + + +`; + +exports[`#ComponentName should render correctly for files 4`] = ` + + + + + + index.tsx + + + + branches.main_branch + + +`; + +exports[`#ComponentName should render correctly for files 5`] = ` + + + + + + index.tsx + + + + + + foo + + + +`; + +exports[`#ComponentName should render correctly for refs 1`] = ` + + + + + + Foo + + + +`; + +exports[`#ComponentName should render correctly for refs 2`] = ` + + + + + + + Foo + + + + + + + foo + + + +`; + +exports[`#getTooltip should correctly format component information 1`] = ` +"src/index.tsx + +foo:src/index.tsx" +`; + +exports[`#getTooltip should correctly format component information 2`] = ` +"src/index.tsx + +foo:src/index.tsx" +`; + +exports[`#getTooltip should correctly format component information 3`] = ` +"index.tsx + +foo:src/index.tsx" +`; + +exports[`#getTooltip should correctly format component information 4`] = ` +"Foo + +foo" +`; -- 2.39.5