diff options
author | Wouter Admiraal <wouter.admiraal@sonarsource.com> | 2020-05-28 17:56:02 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2020-06-02 20:05:09 +0000 |
commit | 947685909fbdee4146a59c67cc706b5e7f530876 (patch) | |
tree | b2432f20571c6358e7fc7944246ce9ae00c27b0b /server/sonar-web | |
parent | 1e44d80e9c900ac61274c7c3ada3c4c2df3e10ee (diff) | |
download | sonarqube-947685909fbdee4146a59c67cc706b5e7f530876.tar.gz sonarqube-947685909fbdee4146a59c67cc706b5e7f530876.zip |
SONAR-12920 Add product footer to the Hotspots page
Diffstat (limited to 'server/sonar-web')
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<Props, State> { componentDidMount() { this.mounted = true; - addNoFooterPageClass(); + addSideBarClass(); this.fetchInitialData(); } @@ -114,7 +114,7 @@ export class SecurityHotspotsApp extends React.PureComponent<Props, State> { } 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 ( <div id="security_hotspots"> + <Suggestions suggestions="security_hotspots" /> + <Helmet title={translate('hotspots.page')} /> + <A11ySkipTarget anchor="security_hotspots_main" /> + <FilterBar component={component} filters={filters} @@ -93,29 +97,25 @@ export default function SecurityHotspotsAppRenderer(props: SecurityHotspotsAppRe onChangeFilters={props.onChangeFilters} onShowAllHotspots={props.onShowAllHotspots} /> - <ScreenPositionHelper> - {({ top }) => ( - <div className="wrapper" style={{ top }}> - <Suggestions suggestions="security_hotspots" /> - <Helmet title={translate('hotspots.page')} /> - - <A11ySkipTarget anchor="security_hotspots_main" /> - {loading && <DeferredSpinner className="huge-spacer-left big-spacer-top" />} + {loading && <DeferredSpinner className="huge-spacer-left big-spacer-top" />} - {!loading && - (hotspots.length === 0 || !selectedHotspot ? ( - <EmptyHotspotsPage - filtered={ - filters.assignedToMe || - (isBranch(branchLike) && filters.sinceLeakPeriod) || - filters.status !== HotspotStatusFilter.TO_REVIEW - } - isStaticListOfHotspots={isStaticListOfHotspots} - /> - ) : ( - <div className="layout-page"> - <div className="sidebar" ref={scrollableRef}> + {!loading && + (hotspots.length === 0 || !selectedHotspot ? ( + <EmptyHotspotsPage + filtered={ + filters.assignedToMe || + (isBranch(branchLike) && filters.sinceLeakPeriod) || + filters.status !== HotspotStatusFilter.TO_REVIEW + } + isStaticListOfHotspots={isStaticListOfHotspots} + /> + ) : ( + <div className="layout-page"> + <ScreenPositionHelper className="layout-page-side-outer"> + {({ top }) => ( + <div className="layout-page-side" ref={scrollableRef} style={{ top }}> + <div className="layout-page-side-inner"> <HotspotList hotspots={hotspots} hotspotsTotal={hotspotsTotal} @@ -128,20 +128,21 @@ export default function SecurityHotspotsAppRenderer(props: SecurityHotspotsAppRe statusFilter={filters.status} /> </div> - <div className="main"> - <HotspotViewer - branchLike={branchLike} - component={component} - hotspotKey={selectedHotspot.key} - onUpdateHotspot={props.onUpdateHotspot} - securityCategories={securityCategories} - /> - </div> </div> - ))} + )} + </ScreenPositionHelper> + + <div className="layout-page-main"> + <HotspotViewer + branchLike={branchLike} + component={component} + hotspotKey={selectedHotspot.key} + onUpdateHotspot={props.onUpdateHotspot} + securityCategories={securityCategories} + /> + </div> </div> - )} - </ScreenPositionHelper> + ))} </div> ); } 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`] = ` <div id="security_hotspots" > + <Suggestions + suggestions="security_hotspots" + /> + <Helmet + defer={true} + encodeSpecialCharacters={true} + title="hotspots.page" + /> + <A11ySkipTarget + anchor="security_hotspots_main" + /> <Connect(withCurrentUser(FilterBar)) component={ Object { @@ -41,249 +52,262 @@ exports[`should render correctly 1`] = ` onChangeFilters={[MockFunction]} onShowAllHotspots={[MockFunction]} /> - <ScreenPositionHelper> - <Component /> - </ScreenPositionHelper> + <EmptyHotspotsPage + filtered={false} + isStaticListOfHotspots={true} + /> </div> `; exports[`should render correctly with hotspots 1`] = ` -<div> - <div - className="wrapper" - style={ +<div + id="security_hotspots" +> + <Suggestions + suggestions="security_hotspots" + /> + <Helmet + defer={true} + encodeSpecialCharacters={true} + title="hotspots.page" + /> + <A11ySkipTarget + anchor="security_hotspots_main" + /> + <Connect(withCurrentUser(FilterBar)) + component={ Object { - "top": 0, + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "organization": "foo", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], } } - > - <Suggestions - suggestions="security_hotspots" - /> - <Helmet - defer={true} - encodeSpecialCharacters={true} - title="hotspots.page" - /> - <A11ySkipTarget - anchor="security_hotspots_main" - /> - <EmptyHotspotsPage - filtered={false} - isStaticListOfHotspots={true} - /> - </div> + filters={ + Object { + "assignedToMe": false, + "sinceLeakPeriod": false, + "status": "TO_REVIEW", + } + } + isStaticListOfHotspots={true} + loadingMeasure={false} + onBranch={false} + onChangeFilters={[MockFunction]} + onShowAllHotspots={[MockFunction]} + /> + <EmptyHotspotsPage + filtered={false} + isStaticListOfHotspots={true} + /> </div> `; exports[`should render correctly with hotspots 2`] = ` -<div> - <div - className="wrapper" - style={ - Object { - "top": 0, - } +<div + className="layout-page-side" + style={ + Object { + "top": 0, } + } +> + <div + className="layout-page-side-inner" > - <Suggestions - suggestions="security_hotspots" - /> - <Helmet - defer={true} - encodeSpecialCharacters={true} - title="hotspots.page" - /> - <A11ySkipTarget - anchor="security_hotspots_main" + <HotspotList + hotspots={ + Array [ + Object { + "author": "Developer 1", + "component": "com.github.kevinsawicki:http-request:com.github.kevinsawicki.http.HttpRequest", + "creationDate": "2013-05-13T17:55:39+0200", + "key": "h1", + "line": 81, + "message": "'3' is a magic number.", + "project": "com.github.kevinsawicki:http-request", + "resolution": undefined, + "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck", + "securityCategory": "command-injection", + "status": "TO_REVIEW", + "updateDate": "2013-05-13T17:55:39+0200", + "vulnerabilityProbability": "HIGH", + }, + Object { + "author": "Developer 1", + "component": "com.github.kevinsawicki:http-request:com.github.kevinsawicki.http.HttpRequest", + "creationDate": "2013-05-13T17:55:39+0200", + "key": "h2", + "line": 81, + "message": "'3' is a magic number.", + "project": "com.github.kevinsawicki:http-request", + "resolution": undefined, + "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck", + "securityCategory": "command-injection", + "status": "TO_REVIEW", + "updateDate": "2013-05-13T17:55:39+0200", + "vulnerabilityProbability": "HIGH", + }, + ] + } + hotspotsTotal={3} + isStaticListOfHotspots={true} + loadingMore={false} + onHotspotClick={[MockFunction]} + onLoadMore={[MockFunction]} + securityCategories={Object {}} + selectedHotspot={ + Object { + "author": "Developer 1", + "component": "com.github.kevinsawicki:http-request:com.github.kevinsawicki.http.HttpRequest", + "creationDate": "2013-05-13T17:55:39+0200", + "key": "h2", + "line": 81, + "message": "'3' is a magic number.", + "project": "com.github.kevinsawicki:http-request", + "resolution": undefined, + "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck", + "securityCategory": "command-injection", + "status": "TO_REVIEW", + "updateDate": "2013-05-13T17:55:39+0200", + "vulnerabilityProbability": "HIGH", + } + } + statusFilter="TO_REVIEW" /> - <div - className="layout-page" - > - <div - className="sidebar" - > - <HotspotList - hotspots={ - Array [ - Object { - "author": "Developer 1", - "component": "com.github.kevinsawicki:http-request:com.github.kevinsawicki.http.HttpRequest", - "creationDate": "2013-05-13T17:55:39+0200", - "key": "h1", - "line": 81, - "message": "'3' is a magic number.", - "project": "com.github.kevinsawicki:http-request", - "resolution": undefined, - "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck", - "securityCategory": "command-injection", - "status": "TO_REVIEW", - "updateDate": "2013-05-13T17:55:39+0200", - "vulnerabilityProbability": "HIGH", - }, - Object { - "author": "Developer 1", - "component": "com.github.kevinsawicki:http-request:com.github.kevinsawicki.http.HttpRequest", - "creationDate": "2013-05-13T17:55:39+0200", - "key": "h2", - "line": 81, - "message": "'3' is a magic number.", - "project": "com.github.kevinsawicki:http-request", - "resolution": undefined, - "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck", - "securityCategory": "command-injection", - "status": "TO_REVIEW", - "updateDate": "2013-05-13T17:55:39+0200", - "vulnerabilityProbability": "HIGH", - }, - ] - } - hotspotsTotal={3} - isStaticListOfHotspots={true} - loadingMore={false} - onHotspotClick={[MockFunction]} - onLoadMore={[MockFunction]} - securityCategories={Object {}} - selectedHotspot={ - Object { - "author": "Developer 1", - "component": "com.github.kevinsawicki:http-request:com.github.kevinsawicki.http.HttpRequest", - "creationDate": "2013-05-13T17:55:39+0200", - "key": "h2", - "line": 81, - "message": "'3' is a magic number.", - "project": "com.github.kevinsawicki:http-request", - "resolution": undefined, - "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck", - "securityCategory": "command-injection", - "status": "TO_REVIEW", - "updateDate": "2013-05-13T17:55:39+0200", - "vulnerabilityProbability": "HIGH", - } - } - statusFilter="TO_REVIEW" - /> - </div> - <div - className="main" - > - <HotspotViewer - component={ - Object { - "breadcrumbs": Array [], - "key": "my-project", - "name": "MyProject", - "organization": "foo", - "qualifier": "TRK", - "qualityGate": Object { - "isDefault": true, - "key": "30", - "name": "Sonar way", - }, - "qualityProfiles": Array [ - Object { - "deleted": false, - "key": "my-qp", - "language": "ts", - "name": "Sonar way", - }, - ], - "tags": Array [], - } - } - hotspotKey="h2" - onUpdateHotspot={[MockFunction]} - securityCategories={Object {}} - /> - </div> - </div> </div> </div> `; exports[`should render correctly: loading 1`] = ` -<div> - <div - className="wrapper" - style={ +<div + id="security_hotspots" +> + <Suggestions + suggestions="security_hotspots" + /> + <Helmet + defer={true} + encodeSpecialCharacters={true} + title="hotspots.page" + /> + <A11ySkipTarget + anchor="security_hotspots_main" + /> + <Connect(withCurrentUser(FilterBar)) + component={ Object { - "top": 0, + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "organization": "foo", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], } } - > - <Suggestions - suggestions="security_hotspots" - /> - <Helmet - defer={true} - encodeSpecialCharacters={true} - title="hotspots.page" - /> - <A11ySkipTarget - anchor="security_hotspots_main" - /> - <DeferredSpinner - className="huge-spacer-left big-spacer-top" - timeout={100} - /> - </div> -</div> -`; - -exports[`should render correctly: no hotspots 1`] = ` -<div> - <div - className="wrapper" - style={ + filters={ Object { - "top": 0, + "assignedToMe": false, + "sinceLeakPeriod": false, + "status": "TO_REVIEW", } } - > - <Suggestions - suggestions="security_hotspots" - /> - <Helmet - defer={true} - encodeSpecialCharacters={true} - title="hotspots.page" - /> - <A11ySkipTarget - anchor="security_hotspots_main" - /> - <EmptyHotspotsPage - filtered={false} - isStaticListOfHotspots={true} - /> - </div> + isStaticListOfHotspots={true} + loadingMeasure={false} + onBranch={false} + onChangeFilters={[MockFunction]} + onShowAllHotspots={[MockFunction]} + /> + <DeferredSpinner + className="huge-spacer-left big-spacer-top" + timeout={100} + /> </div> `; exports[`should render correctly: no hotspots with filters 1`] = ` -<div> - <div - className="wrapper" - style={ +<div + id="security_hotspots" +> + <Suggestions + suggestions="security_hotspots" + /> + <Helmet + defer={true} + encodeSpecialCharacters={true} + title="hotspots.page" + /> + <A11ySkipTarget + anchor="security_hotspots_main" + /> + <Connect(withCurrentUser(FilterBar)) + component={ Object { - "top": 0, + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "organization": "foo", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], } } - > - <Suggestions - suggestions="security_hotspots" - /> - <Helmet - defer={true} - encodeSpecialCharacters={true} - title="hotspots.page" - /> - <A11ySkipTarget - anchor="security_hotspots_main" - /> - <EmptyHotspotsPage - filtered={true} - isStaticListOfHotspots={true} - /> - </div> + filters={ + Object { + "assignedToMe": true, + "sinceLeakPeriod": false, + "status": "TO_REVIEW", + } + } + isStaticListOfHotspots={true} + loadingMeasure={false} + onBranch={false} + onChangeFilters={[MockFunction]} + onShowAllHotspots={[MockFunction]} + /> + <EmptyHotspotsPage + filtered={true} + isStaticListOfHotspots={true} + /> </div> `; 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 ( - <div className="filter-bar display-flex-center"> - {isStaticListOfHotspots ? ( - <a id="show_all_hotspot" onClick={() => props.onShowAllHotspots()} role="link" tabIndex={0}> - {translate('hotspot.filters.show_all')} - </a> - ) : ( - <div className="display-flex-space-between width-100"> - <div className="display-flex-center"> - <h3 className="huge-spacer-right">{translate('hotspot.filters.title')}</h3> + <div className="filter-bar-outer"> + <div className="filter-bar"> + <div className="filter-bar-inner display-flex-center"> + {isStaticListOfHotspots ? ( + <a + id="show_all_hotspot" + onClick={() => props.onShowAllHotspots()} + role="link" + tabIndex={0}> + {translate('hotspot.filters.show_all')} + </a> + ) : ( + <div className="display-flex-space-between width-100"> + <div className="display-flex-center"> + <h3 className="huge-spacer-right">{translate('hotspot.filters.title')}</h3> - {isLoggedIn(currentUser) && ( - <RadioToggle - className="huge-spacer-right" - name="assignee-filter" - onCheck={(value: AssigneeFilterOption) => - props.onChangeFilters({ assignedToMe: value === AssigneeFilterOption.ME }) - } - options={assigneeFilterOptions} - value={filters.assignedToMe ? AssigneeFilterOption.ME : AssigneeFilterOption.ALL} - /> - )} + {isLoggedIn(currentUser) && ( + <RadioToggle + className="huge-spacer-right" + name="assignee-filter" + onCheck={(value: AssigneeFilterOption) => + props.onChangeFilters({ assignedToMe: value === AssigneeFilterOption.ME }) + } + options={assigneeFilterOptions} + value={ + filters.assignedToMe ? AssigneeFilterOption.ME : AssigneeFilterOption.ALL + } + /> + )} - <span className="spacer-right">{translate('status')}</span> - <Select - className="input-medium big-spacer-right" - clearable={false} - onChange={(option: { value: HotspotStatusFilter }) => - props.onChangeFilters({ status: option.value }) - } - options={statusOptions} - searchable={false} - value={filters.status} - /> - - {onBranch && ( - <Select - className="input-medium big-spacer-right" - clearable={false} - onChange={(option: { value: boolean }) => - props.onChangeFilters({ sinceLeakPeriod: option.value }) - } - options={periodOptions} - searchable={false} - value={filters.sinceLeakPeriod} - /> - )} - </div> - - {isProject && ( - <div className="display-flex-center"> - <span className="little-spacer-right"> - {translate('metric.security_hotspots_reviewed.name')} - </span> - <HelpTooltip - className="big-spacer-right" - overlay={translate('hotspots.reviewed.tooltip')} - /> - <DeferredSpinner loading={loadingMeasure}> - {hotspotsReviewedMeasure && <CoverageRating value={hotspotsReviewedMeasure} />} - <Measure - className="spacer-left huge" - metricKey={ - onBranch && !filters.sinceLeakPeriod - ? 'security_hotspots_reviewed' - : 'new_security_hotspots_reviewed' + <span className="spacer-right">{translate('status')}</span> + <Select + className="input-medium big-spacer-right" + clearable={false} + onChange={(option: { value: HotspotStatusFilter }) => + props.onChangeFilters({ status: option.value }) } - metricType="PERCENT" - value={hotspotsReviewedMeasure} + options={statusOptions} + searchable={false} + value={filters.status} /> - </DeferredSpinner> + + {onBranch && ( + <Select + className="input-medium big-spacer-right" + clearable={false} + onChange={(option: { value: boolean }) => + props.onChangeFilters({ sinceLeakPeriod: option.value }) + } + options={periodOptions} + searchable={false} + value={filters.sinceLeakPeriod} + /> + )} + </div> + + {isProject && ( + <div className="display-flex-center"> + <span className="little-spacer-right"> + {translate('metric.security_hotspots_reviewed.name')} + </span> + <HelpTooltip + className="big-spacer-right" + overlay={translate('hotspots.reviewed.tooltip')} + /> + <DeferredSpinner loading={loadingMeasure}> + {hotspotsReviewedMeasure && <CoverageRating value={hotspotsReviewedMeasure} />} + <Measure + className="spacer-left huge" + metricKey={ + onBranch && !filters.sinceLeakPeriod + ? 'security_hotspots_reviewed' + : 'new_security_hotspots_reviewed' + } + metricType="PERCENT" + value={hotspotsReviewedMeasure} + /> + </DeferredSpinner> + </div> + )} </div> )} </div> - )} + </div> </div> ); } 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<Props, State> { const { expandedCategories, groupedHotspots } = this.state; return ( - <div className="huge-spacer-bottom big-padded-right hotspot-list"> + <div className="huge-spacer-bottom"> <h1 className="hotspot-list-header bordered-bottom"> <SecurityHotspotIcon className="spacer-right" /> {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<Props, State> { mounted = false; state: State; commentTextRef: React.RefObject<HTMLTextAreaElement>; - parentScrollRef: React.RefObject<HTMLDivElement>; constructor(props: Props) { super(props); this.commentTextRef = React.createRef<HTMLTextAreaElement>(); - this.parentScrollRef = React.createRef<HTMLDivElement>(); this.state = { loading: false, commentVisible: false }; } @@ -96,9 +94,8 @@ export default class HotspotViewer extends React.PureComponent<Props, State> { // 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<Props, State> { 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<void>; - parentScrollRef: React.RefObject<HTMLDivElement>; 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 ( <DeferredSpinner loading={loading}> {hotspot && ( - <div className="big-padded hotspot-content" ref={parentScrollRef}> + <div className="big-padded hotspot-content"> <div className="huge-spacer-bottom display-flex-space-between"> <strong className="big big-spacer-right">{hotspot.message}</strong> <div className="display-flex-row flex-0"> 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<HTMLDivElement>; - 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<HotspotViewerRendererProps>) { 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`] = ` <div - className="filter-bar display-flex-center" + className="filter-bar-outer" > - <a - id="show_all_hotspot" - onClick={[Function]} - role="link" - tabIndex={0} + <div + className="filter-bar" > - hotspot.filters.show_all - </a> + <div + className="filter-bar-inner display-flex-center" + > + <a + id="show_all_hotspot" + onClick={[Function]} + role="link" + tabIndex={0} + > + hotspot.filters.show_all + </a> + </div> + </div> </div> `; exports[`should render correctly: anonymous 1`] = ` <div - className="filter-bar display-flex-center" + className="filter-bar-outer" > <div - className="display-flex-space-between width-100" + className="filter-bar" > <div - className="display-flex-center" - > - <h3 - className="huge-spacer-right" - > - hotspot.filters.title - </h3> - <span - className="spacer-right" - > - status - </span> - <Select - className="input-medium big-spacer-right" - clearable={false} - onChange={[Function]} - options={ - Array [ - Object { - "label": "hotspot.filters.status.to_review", - "value": "TO_REVIEW", - }, - Object { - "label": "hotspot.filters.status.fixed", - "value": "FIXED", - }, - Object { - "label": "hotspot.filters.status.safe", - "value": "SAFE", - }, - ] - } - searchable={false} - value="TO_REVIEW" - /> - <Select - className="input-medium big-spacer-right" - clearable={false} - onChange={[Function]} - options={ - Array [ - Object { - "label": "hotspot.filters.period.since_leak_period", - "value": true, - }, - Object { - "label": "hotspot.filters.period.overall", - "value": false, - }, - ] - } - searchable={false} - value={false} - /> - </div> - <div - className="display-flex-center" + className="filter-bar-inner display-flex-center" > - <span - className="little-spacer-right" - > - metric.security_hotspots_reviewed.name - </span> - <HelpTooltip - className="big-spacer-right" - overlay="hotspots.reviewed.tooltip" - /> - <DeferredSpinner - loading={false} - timeout={100} + <div + className="display-flex-space-between width-100" > - <Measure - className="spacer-left huge" - metricKey="security_hotspots_reviewed" - metricType="PERCENT" - /> - </DeferredSpinner> + <div + className="display-flex-center" + > + <h3 + className="huge-spacer-right" + > + hotspot.filters.title + </h3> + <span + className="spacer-right" + > + status + </span> + <Select + className="input-medium big-spacer-right" + clearable={false} + onChange={[Function]} + options={ + Array [ + Object { + "label": "hotspot.filters.status.to_review", + "value": "TO_REVIEW", + }, + Object { + "label": "hotspot.filters.status.fixed", + "value": "FIXED", + }, + Object { + "label": "hotspot.filters.status.safe", + "value": "SAFE", + }, + ] + } + searchable={false} + value="TO_REVIEW" + /> + <Select + className="input-medium big-spacer-right" + clearable={false} + onChange={[Function]} + options={ + Array [ + Object { + "label": "hotspot.filters.period.since_leak_period", + "value": true, + }, + Object { + "label": "hotspot.filters.period.overall", + "value": false, + }, + ] + } + searchable={false} + value={false} + /> + </div> + <div + className="display-flex-center" + > + <span + className="little-spacer-right" + > + metric.security_hotspots_reviewed.name + </span> + <HelpTooltip + className="big-spacer-right" + overlay="hotspots.reviewed.tooltip" + /> + <DeferredSpinner + loading={false} + timeout={100} + > + <Measure + className="spacer-left huge" + metricKey="security_hotspots_reviewed" + metricType="PERCENT" + /> + </DeferredSpinner> + </div> + </div> </div> </div> </div> @@ -107,108 +123,116 @@ exports[`should render correctly: anonymous 1`] = ` exports[`should render correctly: logged-in 1`] = ` <div - className="filter-bar display-flex-center" + className="filter-bar-outer" > <div - className="display-flex-space-between width-100" + className="filter-bar" > <div - className="display-flex-center" + className="filter-bar-inner display-flex-center" > - <h3 - className="huge-spacer-right" + <div + className="display-flex-space-between width-100" > - hotspot.filters.title - </h3> - <RadioToggle - className="huge-spacer-right" - disabled={false} - name="assignee-filter" - onCheck={[Function]} - options={ - Array [ - Object { - "label": "hotspot.filters.assignee.assigned_to_me", - "value": "me", - }, - Object { - "label": "hotspot.filters.assignee.all", - "value": "all", - }, - ] - } - value="all" - /> - <span - className="spacer-right" - > - status - </span> - <Select - className="input-medium big-spacer-right" - clearable={false} - onChange={[Function]} - options={ - Array [ - Object { - "label": "hotspot.filters.status.to_review", - "value": "TO_REVIEW", - }, - Object { - "label": "hotspot.filters.status.fixed", - "value": "FIXED", - }, - Object { - "label": "hotspot.filters.status.safe", - "value": "SAFE", - }, - ] - } - searchable={false} - value="TO_REVIEW" - /> - <Select - className="input-medium big-spacer-right" - clearable={false} - onChange={[Function]} - options={ - Array [ - Object { - "label": "hotspot.filters.period.since_leak_period", - "value": true, - }, - Object { - "label": "hotspot.filters.period.overall", - "value": false, - }, - ] - } - searchable={false} - value={false} - /> - </div> - <div - className="display-flex-center" - > - <span - className="little-spacer-right" - > - metric.security_hotspots_reviewed.name - </span> - <HelpTooltip - className="big-spacer-right" - overlay="hotspots.reviewed.tooltip" - /> - <DeferredSpinner - loading={false} - timeout={100} - > - <Measure - className="spacer-left huge" - metricKey="security_hotspots_reviewed" - metricType="PERCENT" - /> - </DeferredSpinner> + <div + className="display-flex-center" + > + <h3 + className="huge-spacer-right" + > + hotspot.filters.title + </h3> + <RadioToggle + className="huge-spacer-right" + disabled={false} + name="assignee-filter" + onCheck={[Function]} + options={ + Array [ + Object { + "label": "hotspot.filters.assignee.assigned_to_me", + "value": "me", + }, + Object { + "label": "hotspot.filters.assignee.all", + "value": "all", + }, + ] + } + value="all" + /> + <span + className="spacer-right" + > + status + </span> + <Select + className="input-medium big-spacer-right" + clearable={false} + onChange={[Function]} + options={ + Array [ + Object { + "label": "hotspot.filters.status.to_review", + "value": "TO_REVIEW", + }, + Object { + "label": "hotspot.filters.status.fixed", + "value": "FIXED", + }, + Object { + "label": "hotspot.filters.status.safe", + "value": "SAFE", + }, + ] + } + searchable={false} + value="TO_REVIEW" + /> + <Select + className="input-medium big-spacer-right" + clearable={false} + onChange={[Function]} + options={ + Array [ + Object { + "label": "hotspot.filters.period.since_leak_period", + "value": true, + }, + Object { + "label": "hotspot.filters.period.overall", + "value": false, + }, + ] + } + searchable={false} + value={false} + /> + </div> + <div + className="display-flex-center" + > + <span + className="little-spacer-right" + > + metric.security_hotspots_reviewed.name + </span> + <HelpTooltip + className="big-spacer-right" + overlay="hotspots.reviewed.tooltip" + /> + <DeferredSpinner + loading={false} + timeout={100} + > + <Measure + className="spacer-left huge" + metricKey="security_hotspots_reviewed" + metricType="PERCENT" + /> + </DeferredSpinner> + </div> + </div> </div> </div> </div> @@ -216,85 +240,93 @@ exports[`should render correctly: logged-in 1`] = ` exports[`should render correctly: non-project 1`] = ` <div - className="filter-bar display-flex-center" + className="filter-bar-outer" > <div - className="display-flex-space-between width-100" + className="filter-bar" > <div - className="display-flex-center" + className="filter-bar-inner display-flex-center" > - <h3 - className="huge-spacer-right" + <div + className="display-flex-space-between width-100" > - hotspot.filters.title - </h3> - <RadioToggle - className="huge-spacer-right" - disabled={false} - name="assignee-filter" - onCheck={[Function]} - options={ - Array [ - Object { - "label": "hotspot.filters.assignee.assigned_to_me", - "value": "me", - }, - Object { - "label": "hotspot.filters.assignee.all", - "value": "all", - }, - ] - } - value="all" - /> - <span - className="spacer-right" - > - status - </span> - <Select - className="input-medium big-spacer-right" - clearable={false} - onChange={[Function]} - options={ - Array [ - Object { - "label": "hotspot.filters.status.to_review", - "value": "TO_REVIEW", - }, - Object { - "label": "hotspot.filters.status.fixed", - "value": "FIXED", - }, - Object { - "label": "hotspot.filters.status.safe", - "value": "SAFE", - }, - ] - } - searchable={false} - value="TO_REVIEW" - /> - <Select - className="input-medium big-spacer-right" - clearable={false} - onChange={[Function]} - options={ - Array [ - Object { - "label": "hotspot.filters.period.since_leak_period", - "value": true, - }, - Object { - "label": "hotspot.filters.period.overall", - "value": false, - }, - ] - } - searchable={false} - value={false} - /> + <div + className="display-flex-center" + > + <h3 + className="huge-spacer-right" + > + hotspot.filters.title + </h3> + <RadioToggle + className="huge-spacer-right" + disabled={false} + name="assignee-filter" + onCheck={[Function]} + options={ + Array [ + Object { + "label": "hotspot.filters.assignee.assigned_to_me", + "value": "me", + }, + Object { + "label": "hotspot.filters.assignee.all", + "value": "all", + }, + ] + } + value="all" + /> + <span + className="spacer-right" + > + status + </span> + <Select + className="input-medium big-spacer-right" + clearable={false} + onChange={[Function]} + options={ + Array [ + Object { + "label": "hotspot.filters.status.to_review", + "value": "TO_REVIEW", + }, + Object { + "label": "hotspot.filters.status.fixed", + "value": "FIXED", + }, + Object { + "label": "hotspot.filters.status.safe", + "value": "SAFE", + }, + ] + } + searchable={false} + value="TO_REVIEW" + /> + <Select + className="input-medium big-spacer-right" + clearable={false} + onChange={[Function]} + options={ + Array [ + Object { + "label": "hotspot.filters.period.since_leak_period", + "value": true, + }, + Object { + "label": "hotspot.filters.period.overall", + "value": false, + }, + ] + } + searchable={false} + value={false} + /> + </div> + </div> </div> </div> </div> @@ -302,70 +334,78 @@ exports[`should render correctly: non-project 1`] = ` exports[`should render correctly: on Pull request 1`] = ` <div - className="filter-bar display-flex-center" + className="filter-bar-outer" > <div - className="display-flex-space-between width-100" + className="filter-bar" > <div - className="display-flex-center" + className="filter-bar-inner display-flex-center" > - <h3 - className="huge-spacer-right" - > - hotspot.filters.title - </h3> - <span - className="spacer-right" + <div + className="display-flex-space-between width-100" > - status - </span> - <Select - className="input-medium big-spacer-right" - clearable={false} - onChange={[Function]} - options={ - Array [ - Object { - "label": "hotspot.filters.status.to_review", - "value": "TO_REVIEW", - }, - Object { - "label": "hotspot.filters.status.fixed", - "value": "FIXED", - }, - Object { - "label": "hotspot.filters.status.safe", - "value": "SAFE", - }, - ] - } - searchable={false} - value="TO_REVIEW" - /> - </div> - <div - className="display-flex-center" - > - <span - className="little-spacer-right" - > - metric.security_hotspots_reviewed.name - </span> - <HelpTooltip - className="big-spacer-right" - overlay="hotspots.reviewed.tooltip" - /> - <DeferredSpinner - loading={false} - timeout={100} - > - <Measure - className="spacer-left huge" - metricKey="new_security_hotspots_reviewed" - metricType="PERCENT" - /> - </DeferredSpinner> + <div + className="display-flex-center" + > + <h3 + className="huge-spacer-right" + > + hotspot.filters.title + </h3> + <span + className="spacer-right" + > + status + </span> + <Select + className="input-medium big-spacer-right" + clearable={false} + onChange={[Function]} + options={ + Array [ + Object { + "label": "hotspot.filters.status.to_review", + "value": "TO_REVIEW", + }, + Object { + "label": "hotspot.filters.status.fixed", + "value": "FIXED", + }, + Object { + "label": "hotspot.filters.status.safe", + "value": "SAFE", + }, + ] + } + searchable={false} + value="TO_REVIEW" + /> + </div> + <div + className="display-flex-center" + > + <span + className="little-spacer-right" + > + metric.security_hotspots_reviewed.name + </span> + <HelpTooltip + className="big-spacer-right" + overlay="hotspots.reviewed.tooltip" + /> + <DeferredSpinner + loading={false} + timeout={100} + > + <Measure + className="spacer-left huge" + metricKey="new_security_hotspots_reviewed" + metricType="PERCENT" + /> + </DeferredSpinner> + </div> + </div> </div> </div> </div> @@ -373,93 +413,101 @@ exports[`should render correctly: on Pull request 1`] = ` exports[`should render correctly: with hotspots reviewed measure 1`] = ` <div - className="filter-bar display-flex-center" + className="filter-bar-outer" > <div - className="display-flex-space-between width-100" + className="filter-bar" > <div - className="display-flex-center" + className="filter-bar-inner display-flex-center" > - <h3 - className="huge-spacer-right" - > - hotspot.filters.title - </h3> - <span - className="spacer-right" - > - status - </span> - <Select - className="input-medium big-spacer-right" - clearable={false} - onChange={[Function]} - options={ - Array [ - Object { - "label": "hotspot.filters.status.to_review", - "value": "TO_REVIEW", - }, - Object { - "label": "hotspot.filters.status.fixed", - "value": "FIXED", - }, - Object { - "label": "hotspot.filters.status.safe", - "value": "SAFE", - }, - ] - } - searchable={false} - value="TO_REVIEW" - /> - <Select - className="input-medium big-spacer-right" - clearable={false} - onChange={[Function]} - options={ - Array [ - Object { - "label": "hotspot.filters.period.since_leak_period", - "value": true, - }, - Object { - "label": "hotspot.filters.period.overall", - "value": false, - }, - ] - } - searchable={false} - value={false} - /> - </div> - <div - className="display-flex-center" - > - <span - className="little-spacer-right" - > - metric.security_hotspots_reviewed.name - </span> - <HelpTooltip - className="big-spacer-right" - overlay="hotspots.reviewed.tooltip" - /> - <DeferredSpinner - loading={false} - timeout={100} + <div + className="display-flex-space-between width-100" > - <CoverageRating - value="23.30" - /> - <Measure - className="spacer-left huge" - metricKey="security_hotspots_reviewed" - metricType="PERCENT" - value="23.30" - /> - </DeferredSpinner> + <div + className="display-flex-center" + > + <h3 + className="huge-spacer-right" + > + hotspot.filters.title + </h3> + <span + className="spacer-right" + > + status + </span> + <Select + className="input-medium big-spacer-right" + clearable={false} + onChange={[Function]} + options={ + Array [ + Object { + "label": "hotspot.filters.status.to_review", + "value": "TO_REVIEW", + }, + Object { + "label": "hotspot.filters.status.fixed", + "value": "FIXED", + }, + Object { + "label": "hotspot.filters.status.safe", + "value": "SAFE", + }, + ] + } + searchable={false} + value="TO_REVIEW" + /> + <Select + className="input-medium big-spacer-right" + clearable={false} + onChange={[Function]} + options={ + Array [ + Object { + "label": "hotspot.filters.period.since_leak_period", + "value": true, + }, + Object { + "label": "hotspot.filters.period.overall", + "value": false, + }, + ] + } + searchable={false} + value={false} + /> + </div> + <div + className="display-flex-center" + > + <span + className="little-spacer-right" + > + metric.security_hotspots_reviewed.name + </span> + <HelpTooltip + className="big-spacer-right" + overlay="hotspots.reviewed.tooltip" + /> + <DeferredSpinner + loading={false} + timeout={100} + > + <CoverageRating + value="23.30" + /> + <Measure + className="spacer-left huge" + metricKey="security_hotspots_reviewed" + metricType="PERCENT" + value="23.30" + /> + </DeferredSpinner> + </div> + </div> </div> </div> </div> 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`] = ` <div - className="huge-spacer-bottom big-padded-right hotspot-list" + className="huge-spacer-bottom" > <h1 className="hotspot-list-header bordered-bottom" @@ -26,7 +26,7 @@ exports[`should render correctly 1`] = ` exports[`should render correctly 2`] = ` <div - className="huge-spacer-bottom big-padded-right hotspot-list" + className="huge-spacer-bottom" > <h1 className="hotspot-list-header bordered-bottom" @@ -49,7 +49,7 @@ exports[`should render correctly 2`] = ` exports[`should render correctly when the list of hotspot is static 1`] = ` <div - className="huge-spacer-bottom big-padded-right hotspot-list" + className="huge-spacer-bottom" > <h1 className="hotspot-list-header bordered-bottom" @@ -73,7 +73,7 @@ exports[`should render correctly when the list of hotspot is static 1`] = ` exports[`should render correctly with hotspots: no pagination 1`] = ` <div - className="huge-spacer-bottom big-padded-right hotspot-list" + className="huge-spacer-bottom" > <h1 className="hotspot-list-header bordered-bottom" @@ -341,7 +341,7 @@ exports[`should render correctly with hotspots: no pagination 1`] = ` exports[`should render correctly with hotspots: pagination 1`] = ` <div - className="huge-spacer-bottom big-padded-right hotspot-list" + className="huge-spacer-bottom" > <h1 className="hotspot-list-header bordered-bottom" diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewer-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewer-test.tsx.snap index 40d870f0e77..d92ff4dff2d 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewer-test.tsx.snap @@ -35,11 +35,6 @@ exports[`should render correctly 1`] = ` onCloseComment={[Function]} onOpenComment={[Function]} onUpdateHotspot={[Function]} - parentScrollRef={ - Object { - "current": null, - } - } securityCategories={ Object { "cat1": Object { @@ -90,11 +85,6 @@ exports[`should render correctly 2`] = ` onCloseComment={[Function]} onOpenComment={[Function]} onUpdateHotspot={[Function]} - parentScrollRef={ - Object { - "current": null, - } - } securityCategories={ Object { "cat1": Object { diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/styles.css b/server/sonar-web/src/main/js/apps/security-hotspots/styles.css index b0a0f304706..e9095d08038 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/styles.css +++ b/server/sonar-web/src/main/js/apps/security-hotspots/styles.css @@ -17,49 +17,57 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#security_hotspots .wrapper { - position: fixed; - /* top is defined programatically */ - bottom: 0; - width: 100%; +#security_hotspots .filter-bar-outer { + height: 62px; } -#security_hotspots .layout-page { - margin: 0 auto; - min-width: var(--minPageWidth); - max-width: 1280px; - height: 100%; +#security_hotspots .filter-bar { + position: fixed; + background-color: var(--barBackgroundColor); + z-index: var(--pageSideZIndex); + left: 0; + right: 0; } -#security_hotspots .filter-bar { +#security_hotspots .filter-bar-inner { max-width: 1280px; - box-sizing: border-box; margin: 0 auto; padding: calc(2 * var(--gridSize)) var(--gridSize); + box-sizing: border-box; border-bottom: 1px solid var(--barBorderColor); } -#security_hotspots .sidebar { - flex: 0 0 300px; - border-right: 1px solid var(--barBorderColor); - height: 100%; - overflow-y: auto; - overflow-x: hidden; +#security_hotspots .layout-page-side, +#security_hotspots .layout-page-side-outer { + width: calc(50vw - 330px); +} + +#security_hotspots .layout-page-side-inner { + margin-left: calc(50vw - 645px); } -#security_hotspots .main { - flex: 1 1 auto; - background-color: white; - /* Force flex to take parent width. */ - overflow-x: auto; +#security_hotspots .layout-page-main { + padding: 0; +} + +@media (max-width: 1320px) { + #security_hotspots .layout-page-side-outer, + #security_hotspots .layout-page-side { + width: 316px; + } + + #security_hotspots .layout-page-side-inner { + margin-left: 0; + } } -#security_hotspots .main .hotspot-content { - overflow-y: auto; - height: 100%; +#security_hotspots .hotspot-content { + max-width: 962px; /* 1280px - 300px - 18px */ + background: white; box-sizing: border-box; } -.invisible { - visibility: hidden; +#security_hotspots .invisible { + height: 0; + overflow: hidden; } |