From 947685909fbdee4146a59c67cc706b5e7f530876 Mon Sep 17 00:00:00 2001 From: Wouter Admiraal Date: Thu, 28 May 2020 17:56:02 +0200 Subject: [PATCH] SONAR-12920 Add product footer to the Hotspots page --- .../main/js/app/styles/components/page.css | 4 - .../src/main/js/app/styles/init/misc.css | 4 + .../main/js/apps/projectBaseline/styles.css | 4 - .../security-hotspots/SecurityHotspotsApp.tsx | 6 +- .../SecurityHotspotsAppRenderer.tsx | 67 +- .../__tests__/SecurityHotspotsApp-test.tsx | 8 +- .../SecurityHotspotsAppRenderer-test.tsx | 21 +- .../SecurityHotspotsAppRenderer-test.tsx.snap | 448 +++++----- .../components/FilterBar.tsx | 144 +-- .../components/HotspotList.css | 5 - .../components/HotspotList.tsx | 2 +- .../components/HotspotViewer.tsx | 6 +- .../components/HotspotViewerRenderer.tsx | 6 +- .../__tests__/HotspotViewer-test.tsx | 2 - .../__tests__/HotspotViewerRenderer-test.tsx | 1 - .../__snapshots__/FilterBar-test.tsx.snap | 834 +++++++++--------- .../__snapshots__/HotspotList-test.tsx.snap | 10 +- .../__snapshots__/HotspotViewer-test.tsx.snap | 10 - .../main/js/apps/security-hotspots/styles.css | 64 +- 19 files changed, 848 insertions(+), 798 deletions(-) diff --git a/server/sonar-web/src/main/js/app/styles/components/page.css b/server/sonar-web/src/main/js/app/styles/components/page.css index c00695e5bea..01555387dd5 100644 --- a/server/sonar-web/src/main/js/app/styles/components/page.css +++ b/server/sonar-web/src/main/js/app/styles/components/page.css @@ -161,10 +161,6 @@ max-width: 980px; } -.no-footer-page #footer { - display: none; -} - .page-footer-menu-item { display: inline-block; } diff --git a/server/sonar-web/src/main/js/app/styles/init/misc.css b/server/sonar-web/src/main/js/app/styles/init/misc.css index 921d893c8f8..b05d35cd039 100644 --- a/server/sonar-web/src/main/js/app/styles/init/misc.css +++ b/server/sonar-web/src/main/js/app/styles/init/misc.css @@ -39,6 +39,10 @@ th.hide-overflow { visibility: hidden !important; } +.invisible { + visibility: hidden; +} + .note { color: var(--secondFontColor); font-size: var(--smallFontSize); diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/styles.css b/server/sonar-web/src/main/js/apps/projectBaseline/styles.css index 04e71f3813a..0d7889c79a6 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/styles.css +++ b/server/sonar-web/src/main/js/apps/projectBaseline/styles.css @@ -144,7 +144,3 @@ .project-activity-event-icon.OTHER { color: #442d1b; } - -.invisible { - visibility: hidden; -} diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx index 94b6b17b860..f4dc3e08c16 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx @@ -20,7 +20,7 @@ import { Location } from 'history'; import { flatMap, range } from 'lodash'; import * as React from 'react'; -import { addNoFooterPageClass, removeNoFooterPageClass } from 'sonar-ui-common/helpers/pages'; +import { addSideBarClass, removeSideBarClass } from 'sonar-ui-common/helpers/pages'; import { getMeasures } from '../../api/measures'; import { getSecurityHotspotList, getSecurityHotspots } from '../../api/security-hotspots'; import { withCurrentUser } from '../../components/hoc/withCurrentUser'; @@ -89,7 +89,7 @@ export class SecurityHotspotsApp extends React.PureComponent { componentDidMount() { this.mounted = true; - addNoFooterPageClass(); + addSideBarClass(); this.fetchInitialData(); } @@ -114,7 +114,7 @@ export class SecurityHotspotsApp extends React.PureComponent { } componentWillUnmount() { - removeNoFooterPageClass(); + removeSideBarClass(); this.mounted = false; } diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsAppRenderer.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsAppRenderer.tsx index ca548d38de6..06862abf159 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsAppRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsAppRenderer.tsx @@ -83,6 +83,10 @@ export default function SecurityHotspotsAppRenderer(props: SecurityHotspotsAppRe return (
+ + + + - - {({ top }) => ( -
- - - - - {loading && } + {loading && } - {!loading && - (hotspots.length === 0 || !selectedHotspot ? ( - - ) : ( -
-
+ {!loading && + (hotspots.length === 0 || !selectedHotspot ? ( + + ) : ( +
+ + {({ top }) => ( +
+
-
- -
- ))} + )} +
+ +
+ +
- )} - + ))}
); } diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-test.tsx index 4921949f20b..caf2e3ba6f1 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-test.tsx @@ -19,7 +19,7 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; -import { addNoFooterPageClass } from 'sonar-ui-common/helpers/pages'; +import { addSideBarClass } from 'sonar-ui-common/helpers/pages'; import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; import { getMeasures } from '../../../api/measures'; import { getSecurityHotspotList, getSecurityHotspots } from '../../../api/security-hotspots'; @@ -44,8 +44,8 @@ import SecurityHotspotsAppRenderer from '../SecurityHotspotsAppRenderer'; beforeEach(() => jest.clearAllMocks()); jest.mock('sonar-ui-common/helpers/pages', () => ({ - addNoFooterPageClass: jest.fn(), - removeNoFooterPageClass: jest.fn() + addSideBarClass: jest.fn(), + removeSideBarClass: jest.fn() })); jest.mock('../../../api/measures', () => ({ @@ -82,7 +82,7 @@ it('should load data correctly', async () => { expect(wrapper.state().loading).toBe(true); expect(wrapper.state().loadingMeasure).toBe(true); - expect(addNoFooterPageClass).toBeCalled(); + expect(addSideBarClass).toBeCalled(); expect(getStandards).toBeCalled(); expect(getSecurityHotspots).toBeCalledWith( expect.objectContaining({ diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsAppRenderer-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsAppRenderer-test.tsx index 7491ad6bd48..de4d561db97 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsAppRenderer-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsAppRenderer-test.tsx @@ -33,6 +33,8 @@ jest.mock('sonar-ui-common/helpers/scrolling', () => ({ scrollToElement: jest.fn() })); +jest.mock('../../../components/common/ScreenPositionHelper'); + beforeEach(() => { jest.clearAllMocks(); }); @@ -43,28 +45,13 @@ it('should render correctly', () => { shallowRender({ filters: { assignedToMe: true, sinceLeakPeriod: false, status: HotspotStatusFilter.TO_REVIEW } }) - .find(ScreenPositionHelper) - .dive() ).toMatchSnapshot('no hotspots with filters'); - expect( - shallowRender() - .find(ScreenPositionHelper) - .dive() - ).toMatchSnapshot('no hotspots'); - expect( - shallowRender({ loading: true }) - .find(ScreenPositionHelper) - .dive() - ).toMatchSnapshot('loading'); + expect(shallowRender({ loading: true })).toMatchSnapshot('loading'); }); it('should render correctly with hotspots', () => { const hotspots = [mockRawHotspot({ key: 'h1' }), mockRawHotspot({ key: 'h2' })]; - expect( - shallowRender({ hotspots, hotspotsTotal: 2 }) - .find(ScreenPositionHelper) - .dive() - ).toMatchSnapshot(); + expect(shallowRender({ hotspots, hotspotsTotal: 2 })).toMatchSnapshot(); expect( shallowRender({ hotspots, hotspotsTotal: 3, selectedHotspot: mockRawHotspot({ key: 'h2' }) }) .find(ScreenPositionHelper) diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap index cfbe10346e0..315e1a63280 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap @@ -4,6 +4,17 @@ exports[`should render correctly 1`] = `
+ + + - - - +
`; exports[`should render correctly with hotspots 1`] = ` -
-
+ + + + - - - - -
+ filters={ + Object { + "assignedToMe": false, + "sinceLeakPeriod": false, + "status": "TO_REVIEW", + } + } + isStaticListOfHotspots={true} + loadingMeasure={false} + onBranch={false} + onChangeFilters={[MockFunction]} + onShowAllHotspots={[MockFunction]} + /> +
`; exports[`should render correctly with hotspots 2`] = ` -
-
+
- - - -
-
- -
-
- -
-
`; exports[`should render correctly: loading 1`] = ` -
-
+ + + + - - - - -
-
-`; - -exports[`should render correctly: no hotspots 1`] = ` -
-
- - - - -
+ isStaticListOfHotspots={true} + loadingMeasure={false} + onBranch={false} + onChangeFilters={[MockFunction]} + onShowAllHotspots={[MockFunction]} + /> +
`; exports[`should render correctly: no hotspots with filters 1`] = ` -
-
+ + + + - - - - -
+ filters={ + Object { + "assignedToMe": true, + "sinceLeakPeriod": false, + "status": "TO_REVIEW", + } + } + isStaticListOfHotspots={true} + loadingMeasure={false} + onBranch={false} + onChangeFilters={[MockFunction]} + onShowAllHotspots={[MockFunction]} + /> +
`; diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/FilterBar.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/FilterBar.tsx index 283c57fa5e8..8cb4bbed905 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/FilterBar.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/FilterBar.tsx @@ -76,80 +76,90 @@ export function FilterBar(props: FilterBarProps) { const isProject = component.qualifier === ComponentQualifier.Project; return ( -
- {isStaticListOfHotspots ? ( - props.onShowAllHotspots()} role="link" tabIndex={0}> - {translate('hotspot.filters.show_all')} - - ) : ( -
-
-

{translate('hotspot.filters.title')}

+
+
+
+ {isStaticListOfHotspots ? ( + props.onShowAllHotspots()} + role="link" + tabIndex={0}> + {translate('hotspot.filters.show_all')} + + ) : ( +
+
+

{translate('hotspot.filters.title')}

- {isLoggedIn(currentUser) && ( - - props.onChangeFilters({ assignedToMe: value === AssigneeFilterOption.ME }) - } - options={assigneeFilterOptions} - value={filters.assignedToMe ? AssigneeFilterOption.ME : AssigneeFilterOption.ALL} - /> - )} + {isLoggedIn(currentUser) && ( + + props.onChangeFilters({ assignedToMe: value === AssigneeFilterOption.ME }) + } + options={assigneeFilterOptions} + value={ + filters.assignedToMe ? AssigneeFilterOption.ME : AssigneeFilterOption.ALL + } + /> + )} - {translate('status')} - - props.onChangeFilters({ sinceLeakPeriod: option.value }) - } - options={periodOptions} - searchable={false} - value={filters.sinceLeakPeriod} - /> - )} -
- - {isProject && ( -
- - {translate('metric.security_hotspots_reviewed.name')} - - - - {hotspotsReviewedMeasure && } - {translate('status')} + + props.onChangeFilters({ sinceLeakPeriod: option.value }) + } + options={periodOptions} + searchable={false} + value={filters.sinceLeakPeriod} + /> + )} +
+ + {isProject && ( +
+ + {translate('metric.security_hotspots_reviewed.name')} + + + + {hotspotsReviewedMeasure && } + + +
+ )}
)}
- )} +
); } diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.css b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.css index 566c79cc44e..2e8a86dc8b4 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.css +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.css @@ -17,11 +17,6 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -.hotspot-list { - /* sidebar width - 16px padding-right, required so as to ignore the scrollbar */ - width: 284px; -} - .hotspot-list-header { padding: calc(2 * var(--gridSize)) var(--gridSize); } diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.tsx index 60addc3677a..ca0fe5bafec 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.tsx @@ -107,7 +107,7 @@ export default class HotspotList extends React.Component { const { expandedCategories, groupedHotspots } = this.state; return ( -
+

{translateWithParameters( diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewer.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewer.tsx index 58a2789fdef..7ff33fc0b19 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewer.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewer.tsx @@ -43,12 +43,10 @@ export default class HotspotViewer extends React.PureComponent { mounted = false; state: State; commentTextRef: React.RefObject; - parentScrollRef: React.RefObject; constructor(props: Props) { super(props); this.commentTextRef = React.createRef(); - this.parentScrollRef = React.createRef(); this.state = { loading: false, commentVisible: false }; } @@ -96,9 +94,8 @@ export default class HotspotViewer extends React.PureComponent { // Edge case when the comment is already open and unfocus. this.commentTextRef.current.focus({ preventScroll: true }); } - if (this.commentTextRef.current && this.parentScrollRef.current) { + if (this.commentTextRef.current) { scrollToElement(this.commentTextRef.current, { - parent: this.parentScrollRef.current, bottomOffset: 100 }); } @@ -123,7 +120,6 @@ export default class HotspotViewer extends React.PureComponent { onCloseComment={this.handleCloseComment} onOpenComment={this.handleOpenComment} onUpdateHotspot={this.handleHotspotUpdate} - parentScrollRef={this.parentScrollRef} securityCategories={securityCategories} /> ); diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerRenderer.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerRenderer.tsx index 7ef7c2e7c17..9bb83fc1f6f 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerRenderer.tsx @@ -49,7 +49,6 @@ export interface HotspotViewerRendererProps { onOpenComment: () => void; onCloseComment: () => void; onUpdateHotspot: () => Promise; - parentScrollRef: React.RefObject; securityCategories: T.StandardSecurityCategories; } @@ -62,8 +61,7 @@ export function HotspotViewerRenderer(props: HotspotViewerRendererProps) { loading, securityCategories, commentTextRef, - commentVisible, - parentScrollRef + commentVisible } = props; const permalink = getPathUrlAsString( @@ -77,7 +75,7 @@ export function HotspotViewerRenderer(props: HotspotViewerRendererProps) { return ( {hotspot && ( -
+
{hotspot.message}
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewer-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewer-test.tsx index 5fce1d9f64b..aed74e3b773 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewer-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewer-test.tsx @@ -64,8 +64,6 @@ it('should open comment form when scroll to comment', () => { const mockTextRef = ({ current: { focus: jest.fn() } } as any) as React.RefObject< HTMLTextAreaElement >; - const mockParentRef = ({ current: {} } as any) as React.RefObject; - wrapper.instance().parentScrollRef = mockParentRef; wrapper.instance().commentTextRef = mockTextRef; wrapper.find(HotspotViewerRenderer).simulate('openComment'); diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewerRenderer-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewerRenderer-test.tsx index f9207838319..71ae598f1cb 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewerRenderer-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewerRenderer-test.tsx @@ -59,7 +59,6 @@ function shallowRender(props?: Partial) { onCloseComment={jest.fn()} onOpenComment={jest.fn()} onUpdateHotspot={jest.fn()} - parentScrollRef={React.createRef()} securityCategories={{ 'sql-injection': { title: 'SQL injection' } }} {...props} /> diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/FilterBar-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/FilterBar-test.tsx.snap index 2e6f744c0a7..2a6b58d6aaf 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/FilterBar-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/FilterBar-test.tsx.snap @@ -2,104 +2,120 @@ exports[`should render correctly when the list of hotspot is static 1`] = `
`; exports[`should render correctly: anonymous 1`] = `
-

- hotspot.filters.title -

- - status - - -
-
- - metric.security_hotspots_reviewed.name - - - - - +
+

+ hotspot.filters.title +

+ + status + + +
+
+ + metric.security_hotspots_reviewed.name + + + + + +
+
@@ -107,108 +123,116 @@ exports[`should render correctly: anonymous 1`] = ` exports[`should render correctly: logged-in 1`] = `
-

- hotspot.filters.title -

- - - status - - -
-
- - metric.security_hotspots_reviewed.name - - - - - +
+

+ hotspot.filters.title +

+ + + status + + +
+
+ + metric.security_hotspots_reviewed.name + + + + + +
+
@@ -216,85 +240,93 @@ exports[`should render correctly: logged-in 1`] = ` exports[`should render correctly: non-project 1`] = `
-

- hotspot.filters.title -

- - - status - - +
+

+ hotspot.filters.title +

+ + + status + + +
+
@@ -302,70 +334,78 @@ exports[`should render correctly: non-project 1`] = ` exports[`should render correctly: on Pull request 1`] = `
-

- hotspot.filters.title -

- - status - - +
+
+ + metric.security_hotspots_reviewed.name + + + + + +
+

@@ -373,93 +413,101 @@ exports[`should render correctly: on Pull request 1`] = ` exports[`should render correctly: with hotspots reviewed measure 1`] = `
-

- hotspot.filters.title -

- - status - - -
-
- - metric.security_hotspots_reviewed.name - - - - - - +
+

+ hotspot.filters.title +

+ + status + + +
+
+ + metric.security_hotspots_reviewed.name + + + + + + +
+
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotList-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotList-test.tsx.snap index cbe8a6ad04e..4aba60b522a 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotList-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotList-test.tsx.snap @@ -2,7 +2,7 @@ exports[`should render correctly 1`] = `