@@ -17,6 +17,7 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import styled from '@emotion/styled'; | |||
import classNames from 'classnames'; | |||
import { Location } from 'history'; | |||
import { debounce, intersection } from 'lodash'; | |||
@@ -26,13 +27,16 @@ import { connect } from 'react-redux'; | |||
import { InjectedRouter } from 'react-router'; | |||
import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget'; | |||
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; | |||
import HelpTooltip from '../../../components/controls/HelpTooltip'; | |||
import ListFooter from '../../../components/controls/ListFooter'; | |||
import { Alert } from '../../../components/ui/Alert'; | |||
import { isPullRequest, isSameBranchLike } from '../../../helpers/branch-like'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { getCodeUrl, getProjectUrl } from '../../../helpers/urls'; | |||
import { fetchBranchStatus, fetchMetrics } from '../../../store/rootActions'; | |||
import { getMetrics } from '../../../store/rootReducer'; | |||
import { BranchLike } from '../../../types/branch-like'; | |||
import { isPortfolioLike } from '../../../types/component'; | |||
import { addComponent, addComponentBreadcrumbs, clearBucket } from '../bucket'; | |||
import '../code.css'; | |||
import { | |||
@@ -264,6 +268,7 @@ export class CodeApp extends React.Component<Props, State> { | |||
searchResults, | |||
sourceViewer | |||
} = this.state; | |||
const { canBrowseAllChildProjects, qualifier } = component; | |||
const showSearch = searchResults !== undefined; | |||
@@ -291,6 +296,16 @@ export class CodeApp extends React.Component<Props, State> { | |||
return ( | |||
<div className="page page-limited"> | |||
{!canBrowseAllChildProjects && isPortfolioLike(qualifier) && ( | |||
<StyledAlert variant="warning"> | |||
{translate('component_measures.not_all_measures_are_shown')} | |||
<HelpTooltip | |||
className="spacer-left" | |||
ariaLabel={translate('component_measures.not_all_measures_are_shown.help')} | |||
overlay={translate('component_measures.not_all_measures_are_shown.help')} | |||
/> | |||
</StyledAlert> | |||
)} | |||
<Suggestions suggestions="code" /> | |||
<Helmet | |||
defer={false} | |||
@@ -381,6 +396,10 @@ export class CodeApp extends React.Component<Props, State> { | |||
); | |||
} | |||
} | |||
const StyledAlert = styled(Alert)` | |||
display: inline-flex; | |||
margin-bottom: 15px; | |||
`; | |||
const mapStateToProps = (state: any): StateToProps => ({ | |||
metrics: getMetrics(state) |
@@ -58,7 +58,13 @@ it.each([ | |||
[ComponentQualifier.Portfolio], | |||
[ComponentQualifier.SubPortfolio] | |||
])('should render correclty when no sub component for %s', async qualifier => { | |||
const component = { breadcrumbs: [], name: 'foo', key: 'foo', qualifier }; | |||
const component = { | |||
breadcrumbs: [], | |||
name: 'foo', | |||
key: 'foo', | |||
qualifier, | |||
canBrowseAllChildProjects: true | |||
}; | |||
(retrieveComponent as jest.Mock<any>).mockResolvedValueOnce({ | |||
breadcrumbs: [], | |||
component, | |||
@@ -186,7 +192,10 @@ it('should correcly display new/overall measure for portfolio', async () => { | |||
}); | |||
const wrapper = shallowRender({ | |||
component: mockComponent({ qualifier: ComponentQualifier.Portfolio }), | |||
component: mockComponent({ | |||
qualifier: ComponentQualifier.Portfolio, | |||
canBrowseAllChildProjects: true | |||
}), | |||
metrics | |||
}); | |||
await waitAndUpdate(wrapper); | |||
@@ -216,6 +225,39 @@ it('should handle select correctly', () => { | |||
}); | |||
}); | |||
it('should render a warning message when user does not have access to all projects whithin a Portfolio', async () => { | |||
const wrapper = shallowRender({ | |||
component: mockComponent({ | |||
qualifier: ComponentQualifier.Portfolio, | |||
canBrowseAllChildProjects: false | |||
}) | |||
}); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper).toMatchSnapshot('Project page with warning'); | |||
}); | |||
it.each([ | |||
[ComponentQualifier.Portfolio, true, false], | |||
[ComponentQualifier.Project, false, false], | |||
[ComponentQualifier.Portfolio, false, true] | |||
])( | |||
'should not render a warning message', | |||
async ( | |||
componentQualifier: ComponentQualifier, | |||
canBrowseAllChildProjects: boolean, | |||
alertIsVisible: boolean | |||
) => { | |||
const wrapper = shallowRender({ | |||
component: mockComponent({ | |||
qualifier: componentQualifier, | |||
canBrowseAllChildProjects | |||
}) | |||
}); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper.find('Styled(Alert)').exists()).toBe(alertIsVisible); | |||
} | |||
); | |||
function shallowRender(props: Partial<CodeApp['props']> = {}) { | |||
return shallow<CodeApp>( | |||
<CodeApp |
@@ -61,6 +61,7 @@ Object { | |||
"onSelect": [Function], | |||
"rootComponent": Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"qualifier": "VW", | |||
@@ -144,6 +145,7 @@ Object { | |||
"onSelect": [Function], | |||
"rootComponent": Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"qualifier": "VW", | |||
@@ -166,6 +168,47 @@ Object { | |||
} | |||
`; | |||
exports[`should render a warning message when user does not have access to all projects whithin a Portfolio: Project page with warning 1`] = ` | |||
<div | |||
className="page page-limited" | |||
> | |||
<Styled(Alert) | |||
variant="warning" | |||
> | |||
component_measures.not_all_measures_are_shown | |||
<HelpTooltip | |||
ariaLabel="component_measures.not_all_measures_are_shown.help" | |||
className="spacer-left" | |||
overlay="component_measures.not_all_measures_are_shown.help" | |||
/> | |||
</Styled(Alert)> | |||
<Suggestions | |||
suggestions="code" | |||
/> | |||
<Helmet | |||
defer={false} | |||
encodeSpecialCharacters={true} | |||
title="projects.page" | |||
/> | |||
<A11ySkipTarget | |||
anchor="code_main" | |||
/> | |||
<div | |||
className="code-components" | |||
> | |||
<div | |||
className="display-flex-center display-flex-column no-file" | |||
> | |||
<span | |||
className="h1 text-muted" | |||
> | |||
code_viewer.no_source_code_displayed_due_to_empty_analysis.VW | |||
</span> | |||
</div> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render correclty when no sub component for APP 1`] = ` | |||
<div | |||
className="page page-limited" | |||
@@ -216,6 +259,7 @@ exports[`should render correclty when no sub component for APP: no search 1`] = | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "APP", | |||
@@ -240,6 +284,7 @@ exports[`should render correclty when no sub component for APP: no search 1`] = | |||
rootComponent={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "APP", | |||
@@ -270,6 +315,7 @@ exports[`should render correclty when no sub component for APP: with sub compone | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "APP", | |||
@@ -290,6 +336,7 @@ exports[`should render correclty when no sub component for APP: with sub compone | |||
baseComponent={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "APP", | |||
@@ -338,6 +385,7 @@ exports[`should render correclty when no sub component for APP: with sub compone | |||
rootComponent={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "APP", | |||
@@ -404,6 +452,7 @@ exports[`should render correclty when no sub component for SVW: no search 1`] = | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "SVW", | |||
@@ -428,6 +477,7 @@ exports[`should render correclty when no sub component for SVW: no search 1`] = | |||
rootComponent={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "SVW", | |||
@@ -458,6 +508,7 @@ exports[`should render correclty when no sub component for SVW: with sub compone | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "SVW", | |||
@@ -478,6 +529,7 @@ exports[`should render correclty when no sub component for SVW: with sub compone | |||
baseComponent={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "SVW", | |||
@@ -516,6 +568,7 @@ exports[`should render correclty when no sub component for SVW: with sub compone | |||
rootComponent={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "SVW", | |||
@@ -582,6 +635,7 @@ exports[`should render correclty when no sub component for TRK: no search 1`] = | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "TRK", | |||
@@ -606,6 +660,7 @@ exports[`should render correclty when no sub component for TRK: no search 1`] = | |||
rootComponent={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "TRK", | |||
@@ -636,6 +691,7 @@ exports[`should render correclty when no sub component for TRK: with sub compone | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "TRK", | |||
@@ -656,6 +712,7 @@ exports[`should render correclty when no sub component for TRK: with sub compone | |||
baseComponent={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "TRK", | |||
@@ -704,6 +761,7 @@ exports[`should render correclty when no sub component for TRK: with sub compone | |||
rootComponent={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "TRK", | |||
@@ -770,6 +828,7 @@ exports[`should render correclty when no sub component for VW: no search 1`] = ` | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "VW", | |||
@@ -794,6 +853,7 @@ exports[`should render correclty when no sub component for VW: no search 1`] = ` | |||
rootComponent={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "VW", | |||
@@ -824,6 +884,7 @@ exports[`should render correclty when no sub component for VW: with sub componen | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "VW", | |||
@@ -844,6 +905,7 @@ exports[`should render correclty when no sub component for VW: with sub componen | |||
baseComponent={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "VW", | |||
@@ -882,6 +944,7 @@ exports[`should render correclty when no sub component for VW: with sub componen | |||
rootComponent={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"canBrowseAllChildProjects": true, | |||
"key": "foo", | |||
"name": "foo", | |||
"qualifier": "VW", |
@@ -305,7 +305,9 @@ export class App extends React.PureComponent<Props, State> { | |||
<div className="layout-page-side" style={{ top }}> | |||
<div className="layout-page-side-inner"> | |||
{!canBrowseAllChildProjects && isPortfolioLike(qualifier) && ( | |||
<Alert className="big-spacer-top big-spacer-right" variant="warning"> | |||
<Alert | |||
className="big-spacer-top big-spacer-right big-spacer-left" | |||
variant="warning"> | |||
{translate('component_measures.not_all_measures_are_shown')} | |||
<HelpTooltip | |||
className="spacer-left" |
@@ -69,7 +69,7 @@ exports[`should render a warning message when user does not have access to all p | |||
className="layout-page-side-inner" | |||
> | |||
<Alert | |||
className="big-spacer-top big-spacer-right" | |||
className="big-spacer-top big-spacer-right big-spacer-left" | |||
variant="warning" | |||
> | |||
component_measures.not_all_measures_are_shown |
@@ -959,7 +959,9 @@ export default class App extends React.PureComponent<Props, State> { | |||
style={{ top }}> | |||
<div className="layout-page-side-inner"> | |||
{!canBrowseAllChildProjects && isPortfolioLike(qualifier) && ( | |||
<Alert className="big-spacer-top big-spacer-right" variant="warning"> | |||
<Alert | |||
className="big-spacer-top big-spacer-right big-spacer-left" | |||
variant="warning"> | |||
{translate('issues.not_all_issue_show')} | |||
<HelpTooltip | |||
className="spacer-left" |
@@ -37,7 +37,7 @@ exports[`should show warnning when not all projects are accessible 1`] = ` | |||
className="layout-page-side-inner" | |||
> | |||
<Alert | |||
className="big-spacer-top big-spacer-right" | |||
className="big-spacer-top big-spacer-right big-spacer-left" | |||
variant="warning" | |||
> | |||
issues.not_all_issue_show |
@@ -3280,8 +3280,8 @@ component_measures.facet_category.overall_category=Overall | |||
component_measures.facet_category.overall_category.estimated=Estimated after merge | |||
component_measures.facet_category.tests_category=Tests | |||
component_measures.bubble_chart.zoom_level=Current zoom level. Scroll on the chart to zoom or unzoom, click here to reset. | |||
component_measures.not_all_measures_are_shown=Not all projects are included | |||
component_measures.not_all_measures_are_shown.help=You do not have access to all projects | |||
component_measures.not_all_measures_are_shown=Not all projects and applications are included | |||
component_measures.not_all_measures_are_shown.help=You do not have access to all projects and/or applications | |||
#------------------------------------------------------------------------------ | |||
# |