summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorPhilippe Perrin <philippe.perrin@sonarsource.com>2021-02-23 18:34:45 +0100
committersonartech <sonartech@sonarsource.com>2021-02-25 20:07:33 +0000
commitcddc7ec79336701d2e617ec2c69cfc6972891080 (patch)
tree4c4d33f450c522d954decdc4ba4c52abc10adb7d /server
parent07ac34f4fdd39b28aeece0241074f8541dd9134f (diff)
downloadsonarqube-cddc7ec79336701d2e617ec2c69cfc6972891080.tar.gz
sonarqube-cddc7ec79336701d2e617ec2c69cfc6972891080.zip
SONAR-12987 Improve loading's transition in hotspot page
Diffstat (limited to 'server')
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsAppRenderer.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-test.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap14
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSimpleList.tsx107
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotList-test.tsx19
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSimpleList-test.tsx19
8 files changed, 124 insertions, 62 deletions
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 5f15f8816dd..3ac430ec4de 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
@@ -22,7 +22,6 @@ import * as key from 'keymaster';
import { flatMap, range } from 'lodash';
import * as React from 'react';
import { connect } from 'react-redux';
-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';
@@ -107,7 +106,6 @@ export class SecurityHotspotsApp extends React.PureComponent<Props, State> {
componentDidMount() {
this.mounted = true;
- addSideBarClass();
this.fetchInitialData();
this.registerKeyboardEvents();
}
@@ -134,7 +132,6 @@ export class SecurityHotspotsApp extends React.PureComponent<Props, State> {
}
componentWillUnmount() {
- removeSideBarClass();
this.unregisterKeyboardEvents();
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 2eb9a5e8928..4a8b9ab78f2 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
@@ -109,7 +109,13 @@ export default function SecurityHotspotsAppRenderer(props: SecurityHotspotsAppRe
onShowAllHotspots={props.onShowAllHotspots}
/>
- {loading && <DeferredSpinner className="huge-spacer-left big-spacer-top" />}
+ {loading && (
+ <div className="layout-page">
+ <div className="layout-page-side-inner">
+ <DeferredSpinner className="big-spacer-top" />
+ </div>
+ </div>
+ )}
{!loading &&
(hotspots.length === 0 || !selectedHotspot ? (
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 de6de5e30c4..fd0b36779f7 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,6 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-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,11 +43,6 @@ import SecurityHotspotsAppRenderer from '../SecurityHotspotsAppRenderer';
beforeEach(() => jest.clearAllMocks());
-jest.mock('sonar-ui-common/helpers/pages', () => ({
- addSideBarClass: jest.fn(),
- removeSideBarClass: jest.fn()
-}));
-
jest.mock('../../../api/measures', () => ({
getMeasures: jest.fn().mockResolvedValue([])
}));
@@ -83,7 +77,6 @@ it('should load data correctly', async () => {
expect(wrapper.state().loading).toBe(true);
expect(wrapper.state().loadingMeasure).toBe(true);
- expect(addSideBarClass).toBeCalled();
expect(getStandards).toBeCalled();
expect(getSecurityHotspots).toBeCalledWith(
expect.objectContaining({
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 483ece47272..a5e4b851798 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
@@ -486,9 +486,17 @@ exports[`should render correctly: loading 1`] = `
onChangeFilters={[MockFunction]}
onShowAllHotspots={[MockFunction]}
/>
- <DeferredSpinner
- className="huge-spacer-left big-spacer-top"
- />
+ <div
+ className="layout-page"
+ >
+ <div
+ className="layout-page-side-inner"
+ >
+ <DeferredSpinner
+ className="big-spacer-top"
+ />
+ </div>
+ </div>
</div>
`;
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 352423ed2f8..b65a32e8cd2 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
@@ -23,6 +23,7 @@ import * as React from 'react';
import ListFooter from 'sonar-ui-common/components/controls/ListFooter';
import SecurityHotspotIcon from 'sonar-ui-common/components/icons/SecurityHotspotIcon';
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
+import { addSideBarClass, removeSideBarClass } from 'sonar-ui-common/helpers/pages';
import { HotspotStatusFilter, RawHotspot, RiskExposure } from '../../../types/security-hotspots';
import { groupByCategory, RISK_EXPOSURE_LEVELS } from '../utils';
import HotspotCategory from './HotspotCategory';
@@ -58,6 +59,10 @@ export default class HotspotList extends React.Component<Props, State> {
};
}
+ componentDidMount() {
+ addSideBarClass();
+ }
+
componentDidUpdate(prevProps: Props) {
// Force open the category of selected hotspot
if (
@@ -79,6 +84,10 @@ export default class HotspotList extends React.Component<Props, State> {
}
}
+ componentWillUnmount() {
+ removeSideBarClass();
+ }
+
groupHotspots = (hotspots: RawHotspot[], securityCategories: T.StandardSecurityCategories) => {
const risks = groupBy(hotspots, h => h.vulnerabilityProbability);
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSimpleList.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSimpleList.tsx
index b3725d4df4f..97951b7ffb2 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSimpleList.tsx
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSimpleList.tsx
@@ -21,6 +21,7 @@ import * as React from 'react';
import ListFooter from 'sonar-ui-common/components/controls/ListFooter';
import SecurityHotspotIcon from 'sonar-ui-common/components/icons/SecurityHotspotIcon';
import { translateWithParameters } from 'sonar-ui-common/helpers/l10n';
+import { addSideBarClass, removeSideBarClass } from 'sonar-ui-common/helpers/pages';
import { SecurityStandard, Standards } from '../../../types/security';
import { RawHotspot } from '../../../types/security-hotspots';
import { SECURITY_STANDARD_RENDERER } from '../utils';
@@ -41,58 +42,68 @@ export interface HotspotSimpleListProps {
standards: Standards;
}
-export default function HotspotSimpleList(props: HotspotSimpleListProps) {
- const {
- filterByCategory,
- filterByCWE,
- hotspots,
- hotspotsTotal,
- loadingMore,
- selectedHotspot,
- standards
- } = props;
+export default class HotspotSimpleList extends React.Component<HotspotSimpleListProps> {
+ componentDidMount() {
+ addSideBarClass();
+ }
- const categoryLabel =
- filterByCategory &&
- SECURITY_STANDARD_RENDERER[filterByCategory.standard](standards, filterByCategory.category);
+ componentWillUnmount() {
+ removeSideBarClass();
+ }
- const cweLabel =
- filterByCWE && SECURITY_STANDARD_RENDERER[SecurityStandard.CWE](standards, filterByCWE);
+ render() {
+ const {
+ filterByCategory,
+ filterByCWE,
+ hotspots,
+ hotspotsTotal,
+ loadingMore,
+ selectedHotspot,
+ standards
+ } = this.props;
- return (
- <div className="hotspots-list-single-category huge-spacer-bottom">
- <h1 className="hotspot-list-header bordered-bottom">
- <SecurityHotspotIcon className="spacer-right" />
- {translateWithParameters('hotspots.list_title', hotspotsTotal)}
- </h1>
- <div className="big-spacer-bottom">
- <div className="hotspot-category">
- <div className="hotspot-category-header">
- <strong className="flex-1 spacer-right break-word">
- {categoryLabel}
- {categoryLabel && cweLabel && <hr />}
- {cweLabel}
- </strong>
+ const categoryLabel =
+ filterByCategory &&
+ SECURITY_STANDARD_RENDERER[filterByCategory.standard](standards, filterByCategory.category);
+
+ const cweLabel =
+ filterByCWE && SECURITY_STANDARD_RENDERER[SecurityStandard.CWE](standards, filterByCWE);
+
+ return (
+ <div className="hotspots-list-single-category huge-spacer-bottom">
+ <h1 className="hotspot-list-header bordered-bottom">
+ <SecurityHotspotIcon className="spacer-right" />
+ {translateWithParameters('hotspots.list_title', hotspotsTotal)}
+ </h1>
+ <div className="big-spacer-bottom">
+ <div className="hotspot-category">
+ <div className="hotspot-category-header">
+ <strong className="flex-1 spacer-right break-word">
+ {categoryLabel}
+ {categoryLabel && cweLabel && <hr />}
+ {cweLabel}
+ </strong>
+ </div>
+ <ul>
+ {hotspots.map(h => (
+ <li data-hotspot-key={h.key} key={h.key}>
+ <HotspotListItem
+ hotspot={h}
+ onClick={this.props.onHotspotClick}
+ selected={h.key === selectedHotspot.key}
+ />
+ </li>
+ ))}
+ </ul>
</div>
- <ul>
- {hotspots.map(h => (
- <li data-hotspot-key={h.key} key={h.key}>
- <HotspotListItem
- hotspot={h}
- onClick={props.onHotspotClick}
- selected={h.key === selectedHotspot.key}
- />
- </li>
- ))}
- </ul>
</div>
+ <ListFooter
+ count={hotspots.length}
+ loadMore={!loadingMore ? this.props.onLoadMore : undefined}
+ loading={loadingMore}
+ total={hotspotsTotal}
+ />
</div>
- <ListFooter
- count={hotspots.length}
- loadMore={!loadingMore ? props.onLoadMore : undefined}
- loading={loadingMore}
- total={hotspotsTotal}
- />
- </div>
- );
+ );
+ }
}
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotList-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotList-test.tsx
index d1960aed000..e1c741bafd6 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotList-test.tsx
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotList-test.tsx
@@ -19,15 +19,34 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
+import { addSideBarClass, removeSideBarClass } from 'sonar-ui-common/helpers/pages';
+import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
import { mockRawHotspot } from '../../../../helpers/mocks/security-hotspots';
import { HotspotStatusFilter, RiskExposure } from '../../../../types/security-hotspots';
import HotspotList from '../HotspotList';
+jest.mock('sonar-ui-common/helpers/pages', () => ({
+ addSideBarClass: jest.fn(),
+ removeSideBarClass: jest.fn()
+}));
+
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
expect(shallowRender({ loadingMore: true })).toMatchSnapshot();
});
+it('should add/remove sidebar classes', async () => {
+ const wrapper = shallowRender();
+
+ await waitAndUpdate(wrapper);
+
+ expect(addSideBarClass).toHaveBeenCalled();
+
+ wrapper.unmount();
+
+ expect(removeSideBarClass).toHaveBeenCalled();
+});
+
it('should render correctly when the list of hotspot is static', () => {
expect(shallowRender({ isStaticListOfHotspots: true })).toMatchSnapshot();
});
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSimpleList-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSimpleList-test.tsx
index 024a4a720b5..930f45edbcf 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSimpleList-test.tsx
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSimpleList-test.tsx
@@ -19,10 +19,17 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
+import { addSideBarClass, removeSideBarClass } from 'sonar-ui-common/helpers/pages';
+import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
import { mockRawHotspot } from '../../../../helpers/mocks/security-hotspots';
import { SecurityStandard } from '../../../../types/security';
import HotspotSimpleList, { HotspotSimpleListProps } from '../HotspotSimpleList';
+jest.mock('sonar-ui-common/helpers/pages', () => ({
+ addSideBarClass: jest.fn(),
+ removeSideBarClass: jest.fn()
+}));
+
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot('filter by category');
expect(shallowRender({ filterByCategory: undefined, filterByCWE: '327' })).toMatchSnapshot(
@@ -31,6 +38,18 @@ it('should render correctly', () => {
expect(shallowRender({ filterByCWE: '327' })).toMatchSnapshot('filter by both');
});
+it('should add/remove sidebar classes', async () => {
+ const wrapper = shallowRender();
+
+ await waitAndUpdate(wrapper);
+
+ expect(addSideBarClass).toHaveBeenCalled();
+
+ wrapper.unmount();
+
+ expect(removeSideBarClass).toHaveBeenCalled();
+});
+
function shallowRender(props: Partial<HotspotSimpleListProps> = {}) {
const hotspots = [mockRawHotspot({ key: 'h1' }), mockRawHotspot({ key: 'h2' })];