]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-12751 Add Select to filter by status
authorJeremy Davis <jeremy.davis@sonarsource.com>
Tue, 17 Dec 2019 14:17:54 +0000 (15:17 +0100)
committerSonarTech <sonartech@sonarsource.com>
Mon, 13 Jan 2020 19:46:31 +0000 (20:46 +0100)
18 files changed:
server/sonar-web/src/main/js/api/security-hotspots.ts
server/sonar-web/src/main/js/apps/securityHotspots/SecurityHotspotsApp.tsx
server/sonar-web/src/main/js/apps/securityHotspots/SecurityHotspotsAppRenderer.tsx
server/sonar-web/src/main/js/apps/securityHotspots/__tests__/SecurityHotspotsApp-test.tsx
server/sonar-web/src/main/js/apps/securityHotspots/__tests__/SecurityHotspotsAppRenderer-test.tsx
server/sonar-web/src/main/js/apps/securityHotspots/__tests__/__snapshots__/SecurityHotspotsApp-test.tsx.snap
server/sonar-web/src/main/js/apps/securityHotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap
server/sonar-web/src/main/js/apps/securityHotspots/components/FilterBar.tsx
server/sonar-web/src/main/js/apps/securityHotspots/components/HotspotList.tsx
server/sonar-web/src/main/js/apps/securityHotspots/components/HotspotListItem.tsx
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/FilterBar-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/HotspotList-test.tsx
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/HotspotListItem-test.tsx
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/FilterBar-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/HotspotListItem-test.tsx.snap
server/sonar-web/src/main/js/apps/securityHotspots/styles.css
server/sonar-web/src/main/js/types/security-hotspots.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index cea427e8c121d8ea5190be988de346b109ec3609..5806bed3679c4dafcc3aa4478e3008aef011d0c6 100644 (file)
@@ -23,8 +23,10 @@ import { BranchParameters } from '../types/branch-like';
 import {
   DetailedHotspot,
   HotspotAssignRequest,
+  HotspotResolution,
   HotspotSearchResponse,
-  HotspotSetStatusRequest
+  HotspotSetStatusRequest,
+  HotspotStatus
 } from '../types/security-hotspots';
 
 export function assignSecurityHotspot(
@@ -48,6 +50,8 @@ export function getSecurityHotspots(
     projectKey: string;
     p: number;
     ps: number;
+    status?: HotspotStatus;
+    resolution?: HotspotResolution;
   } & BranchParameters
 ): Promise<HotspotSearchResponse> {
   return getJSON('/api/hotspots/search', data).catch(throwGlobalError);
index 768643395e2e1c34c22dd981808c3407da6c32d5..72f28db40999db86829a2c9fe78ee342681ee67b 100644 (file)
@@ -23,7 +23,13 @@ import { getSecurityHotspots } from '../../api/security-hotspots';
 import { getBranchLikeQuery } from '../../helpers/branch-like';
 import { getStandards } from '../../helpers/security-standard';
 import { BranchLike } from '../../types/branch-like';
-import { HotspotUpdate, RawHotspot } from '../../types/security-hotspots';
+import {
+  HotspotResolution,
+  HotspotStatus,
+  HotspotStatusFilters,
+  HotspotUpdate,
+  RawHotspot
+} from '../../types/security-hotspots';
 import SecurityHotspotsAppRenderer from './SecurityHotspotsAppRenderer';
 import './styles.css';
 import { sortHotspots } from './utils';
@@ -40,6 +46,7 @@ interface State {
   loading: boolean;
   securityCategories: T.StandardSecurityCategories;
   selectedHotspotKey: string | undefined;
+  statusFilter: HotspotStatusFilters;
 }
 
 export default class SecurityHotspotsApp extends React.PureComponent<Props, State> {
@@ -48,7 +55,8 @@ export default class SecurityHotspotsApp extends React.PureComponent<Props, Stat
     loading: true,
     hotspots: [],
     securityCategories: {},
-    selectedHotspotKey: undefined
+    selectedHotspotKey: undefined,
+    statusFilter: HotspotStatusFilters.TO_REVIEW
   };
 
   componentDidMount() {
@@ -68,18 +76,14 @@ export default class SecurityHotspotsApp extends React.PureComponent<Props, Stat
     this.mounted = false;
   }
 
-  fetchInitialData() {
-    const { branchLike, component } = this.props;
+  handleCallFailure = () => {
+    if (this.mounted) {
+      this.setState({ loading: false });
+    }
+  };
 
-    return Promise.all([
-      getStandards(),
-      getSecurityHotspots({
-        projectKey: component.key,
-        p: 1,
-        ps: PAGE_SIZE,
-        ...getBranchLikeQuery(branchLike)
-      })
-    ])
+  fetchInitialData() {
+    return Promise.all([getStandards(), this.fetchSecurityHotspots()])
       .then(([{ sonarsourceSecurity }, response]) => {
         if (!this.mounted) {
           return;
@@ -94,13 +98,57 @@ export default class SecurityHotspotsApp extends React.PureComponent<Props, Stat
           selectedHotspotKey: hotspots.length > 0 ? hotspots[0].key : undefined
         });
       })
-      .catch(() => {
-        if (this.mounted) {
-          this.setState({ loading: false });
-        }
-      });
+      .catch(this.handleCallFailure);
   }
 
+  fetchSecurityHotspots() {
+    const { branchLike, component } = this.props;
+    const { statusFilter } = this.state;
+
+    const status =
+      statusFilter === HotspotStatusFilters.TO_REVIEW
+        ? HotspotStatus.TO_REVIEW
+        : HotspotStatus.REVIEWED;
+
+    const resolution =
+      statusFilter === HotspotStatusFilters.TO_REVIEW ? undefined : HotspotResolution[statusFilter];
+
+    return getSecurityHotspots({
+      projectKey: component.key,
+      p: 1,
+      ps: PAGE_SIZE,
+      status,
+      resolution,
+      ...getBranchLikeQuery(branchLike)
+    });
+  }
+
+  reloadSecurityHotspotList = () => {
+    const { securityCategories } = this.state;
+
+    this.setState({ loading: true });
+
+    return this.fetchSecurityHotspots()
+      .then(response => {
+        if (!this.mounted) {
+          return;
+        }
+
+        const hotspots = sortHotspots(response.hotspots, securityCategories);
+
+        this.setState({
+          hotspots,
+          loading: false,
+          selectedHotspotKey: hotspots.length > 0 ? hotspots[0].key : undefined
+        });
+      })
+      .catch(this.handleCallFailure);
+  };
+
+  handleChangeStatusFilter = (statusFilter: HotspotStatusFilters) => {
+    this.setState({ statusFilter }, this.reloadSecurityHotspotList);
+  };
+
   handleHotspotClick = (key: string) => this.setState({ selectedHotspotKey: key });
 
   handleHotspotUpdate = ({ key, status, resolution }: HotspotUpdate) => {
@@ -122,17 +170,19 @@ export default class SecurityHotspotsApp extends React.PureComponent<Props, Stat
 
   render() {
     const { branchLike } = this.props;
-    const { hotspots, loading, securityCategories, selectedHotspotKey } = this.state;
+    const { hotspots, loading, securityCategories, selectedHotspotKey, statusFilter } = this.state;
 
     return (
       <SecurityHotspotsAppRenderer
         branchLike={branchLike}
         hotspots={hotspots}
         loading={loading}
+        onChangeStatusFilter={this.handleChangeStatusFilter}
         onHotspotClick={this.handleHotspotClick}
         onUpdateHotspot={this.handleHotspotUpdate}
         securityCategories={securityCategories}
         selectedHotspotKey={selectedHotspotKey}
+        statusFilter={statusFilter}
       />
     );
   }
index 9ba9dc438701dbd95e055b5f7de03403557bf173..cf7d1b7df2787de04b716611b883726103b6669d 100644 (file)
@@ -27,7 +27,7 @@ import A11ySkipTarget from '../../app/components/a11y/A11ySkipTarget';
 import Suggestions from '../../app/components/embed-docs-modal/Suggestions';
 import ScreenPositionHelper from '../../components/common/ScreenPositionHelper';
 import { BranchLike } from '../../types/branch-like';
-import { HotspotUpdate, RawHotspot } from '../../types/security-hotspots';
+import { HotspotStatusFilters, HotspotUpdate, RawHotspot } from '../../types/security-hotspots';
 import FilterBar from './components/FilterBar';
 import HotspotList from './components/HotspotList';
 import HotspotViewer from './components/HotspotViewer';
@@ -37,18 +37,27 @@ export interface SecurityHotspotsAppRendererProps {
   branchLike?: BranchLike;
   hotspots: RawHotspot[];
   loading: boolean;
+  onChangeStatusFilter: (status: HotspotStatusFilters) => void;
   onHotspotClick: (key: string) => void;
   onUpdateHotspot: (hotspot: HotspotUpdate) => void;
   selectedHotspotKey?: string;
   securityCategories: T.StandardSecurityCategories;
+  statusFilter: HotspotStatusFilters;
 }
 
 export default function SecurityHotspotsAppRenderer(props: SecurityHotspotsAppRendererProps) {
-  const { branchLike, hotspots, loading, securityCategories, selectedHotspotKey } = props;
+  const {
+    branchLike,
+    hotspots,
+    loading,
+    securityCategories,
+    selectedHotspotKey,
+    statusFilter
+  } = props;
 
   return (
     <div id="security_hotspots">
-      <FilterBar />
+      <FilterBar onChangeStatus={props.onChangeStatusFilter} statusFilter={statusFilter} />
       <ScreenPositionHelper>
         {({ top }) => (
           <div className="wrapper" style={{ top }}>
@@ -85,6 +94,7 @@ export default function SecurityHotspotsAppRenderer(props: SecurityHotspotsAppRe
                       onHotspotClick={props.onHotspotClick}
                       securityCategories={securityCategories}
                       selectedHotspotKey={selectedHotspotKey}
+                      statusFilter={statusFilter}
                     />
                   </div>
                   <div className="main">
index 9e73887780fee44524def166051130585ba6e2f8..887a2d53d4de4b9f27db0f8d46f43ef9ba543bcb 100644 (file)
@@ -26,7 +26,11 @@ import { mockBranch } from '../../../helpers/mocks/branch-like';
 import { mockRawHotspot } from '../../../helpers/mocks/security-hotspots';
 import { getStandards } from '../../../helpers/security-standard';
 import { mockComponent } from '../../../helpers/testMocks';
-import { HotspotResolution, HotspotStatus } from '../../../types/security-hotspots';
+import {
+  HotspotResolution,
+  HotspotStatus,
+  HotspotStatusFilters
+} from '../../../types/security-hotspots';
 import SecurityHotspotsApp from '../SecurityHotspotsApp';
 import SecurityHotspotsAppRenderer from '../SecurityHotspotsAppRenderer';
 
@@ -54,7 +58,7 @@ it('should load data correctly', async () => {
   (getStandards as jest.Mock).mockResolvedValue({ sonarsourceSecurity });
 
   const hotspots = [mockRawHotspot()];
-  (getSecurityHotspots as jest.Mock).mockResolvedValue({
+  (getSecurityHotspots as jest.Mock).mockResolvedValueOnce({
     hotspots
   });
 
@@ -104,6 +108,45 @@ it('should handle hotspot update', async () => {
   });
 });
 
+it('should handle status filter change', async () => {
+  const hotspots = [mockRawHotspot({ key: 'key1' })];
+  const hotspots2 = [mockRawHotspot({ key: 'key2' })];
+  (getSecurityHotspots as jest.Mock)
+    .mockResolvedValueOnce({ hotspots })
+    .mockResolvedValueOnce({ hotspots: hotspots2 })
+    .mockResolvedValueOnce({ hotspots: [] });
+
+  const wrapper = shallowRender();
+
+  expect(getSecurityHotspots).toBeCalledWith(
+    expect.objectContaining({ status: HotspotStatus.TO_REVIEW, resolution: undefined })
+  );
+
+  await waitAndUpdate(wrapper);
+
+  // Set filter to SAFE:
+  wrapper.instance().handleChangeStatusFilter(HotspotStatusFilters.SAFE);
+
+  expect(getSecurityHotspots).toBeCalledWith(
+    expect.objectContaining({ status: HotspotStatus.REVIEWED, resolution: HotspotResolution.SAFE })
+  );
+
+  await waitAndUpdate(wrapper);
+
+  expect(wrapper.state().hotspots[0]).toBe(hotspots2[0]);
+
+  // Set filter to FIXED
+  wrapper.instance().handleChangeStatusFilter(HotspotStatusFilters.FIXED);
+
+  expect(getSecurityHotspots).toBeCalledWith(
+    expect.objectContaining({ status: HotspotStatus.REVIEWED, resolution: HotspotResolution.FIXED })
+  );
+
+  await waitAndUpdate(wrapper);
+
+  expect(wrapper.state().hotspots).toHaveLength(0);
+});
+
 function shallowRender(props: Partial<SecurityHotspotsApp['props']> = {}) {
   return shallow<SecurityHotspotsApp>(
     <SecurityHotspotsApp branchLike={branch} component={mockComponent()} {...props} />
index c46274751bf10f7c6d7dd0611248ba9545a82eca..2cfb546bbbba77177dcd022e2ba77bdc05cc878c 100644 (file)
@@ -21,6 +21,7 @@ import { shallow } from 'enzyme';
 import * as React from 'react';
 import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
 import { mockRawHotspot } from '../../../helpers/mocks/security-hotspots';
+import { HotspotStatusFilters } from '../../../types/security-hotspots';
 import SecurityHotspotsAppRenderer, {
   SecurityHotspotsAppRendererProps
 } from '../SecurityHotspotsAppRenderer';
@@ -53,9 +54,11 @@ function shallowRender(props: Partial<SecurityHotspotsAppRendererProps> = {}) {
     <SecurityHotspotsAppRenderer
       hotspots={[]}
       loading={false}
+      onChangeStatusFilter={jest.fn()}
       onHotspotClick={jest.fn()}
       onUpdateHotspot={jest.fn()}
       securityCategories={{}}
+      statusFilter={HotspotStatusFilters.TO_REVIEW}
       {...props}
     />
   );
index b2a09e10b46f83d2a7b48b9a353cb7707bafec5c..4ff7c27204018cc76ed57654064c49c561d0cdfa 100644 (file)
@@ -12,8 +12,10 @@ exports[`should render correctly 1`] = `
   }
   hotspots={Array []}
   loading={true}
+  onChangeStatusFilter={[Function]}
   onHotspotClick={[Function]}
   onUpdateHotspot={[Function]}
   securityCategories={Object {}}
+  statusFilter="TO_REVIEW"
 />
 `;
index 4df9e59c2e91241f8c64562fb6da90ba1189e848..722fc972730daa4520a72157ab923e33dd26984d 100644 (file)
@@ -4,7 +4,10 @@ exports[`should render correctly 1`] = `
 <div
   id="security_hotspots"
 >
-  <FilterBar />
+  <FilterBar
+    onChangeStatus={[MockFunction]}
+    statusFilter="TO_REVIEW"
+  />
   <ScreenPositionHelper>
     <Component />
   </ScreenPositionHelper>
@@ -144,6 +147,7 @@ exports[`should render correctly with hotspots 1`] = `
             }
             onHotspotClick={[MockFunction]}
             securityCategories={Object {}}
+            statusFilter="TO_REVIEW"
           />
         </div>
         <div
@@ -225,6 +229,7 @@ exports[`should render correctly with hotspots 2`] = `
             onHotspotClick={[MockFunction]}
             securityCategories={Object {}}
             selectedHotspotKey="h2"
+            statusFilter="TO_REVIEW"
           />
         </div>
         <div
index 0927843ed8498d75456e504f44065e2ff09ccb85..372f710dec3c05caafbbdfe9a50c726f8dc07cf1 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import Select from 'sonar-ui-common/components/controls/Select';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { HotspotStatusFilters } from '../../../types/security-hotspots';
 
-export interface FilterBarProps {}
+export interface FilterBarProps {
+  onChangeStatus: (status: HotspotStatusFilters) => void;
+  statusFilter: HotspotStatusFilters;
+}
+
+const statusOptions: Array<{ label: string; value: string }> = [
+  { label: translate('hotspot.filters.status.to_review'), value: HotspotStatusFilters.TO_REVIEW },
+  { label: translate('hotspot.filters.status.fixed'), value: HotspotStatusFilters.FIXED },
+  { label: translate('hotspot.filters.status.safe'), value: HotspotStatusFilters.SAFE }
+];
 
 export default function FilterBar(props: FilterBarProps) {
+  const { statusFilter } = props;
   return (
     <div className="filter-bar display-flex-center">
-      <h3 {...props}>Filter</h3>
+      <h3 className="big-spacer-right">{translate('hotspot.filters.title')}</h3>
+
+      <span className="spacer-right">{translate('status')}</span>
+      <Select
+        className="input-medium big-spacer-right"
+        clearable={false}
+        onChange={(option: { value: HotspotStatusFilters }) => props.onChangeStatus(option.value)}
+        options={statusOptions}
+        searchable={false}
+        value={statusFilter}
+      />
     </div>
   );
 }
index d9a054f379e6ed22eaea9fbe3cd61f257844e37f..8d9e33fb7f94945ab224ab9bb4f34bee17a0ebd4 100644 (file)
@@ -22,7 +22,7 @@ import { groupBy } from 'lodash';
 import * as React from 'react';
 import SecurityHotspotIcon from 'sonar-ui-common/components/icons/SecurityHotspotIcon';
 import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
-import { RawHotspot, RiskExposure } from '../../../types/security-hotspots';
+import { HotspotStatusFilters, RawHotspot, RiskExposure } from '../../../types/security-hotspots';
 import { groupByCategory, RISK_EXPOSURE_LEVELS } from '../utils';
 import HotspotCategory from './HotspotCategory';
 import './HotspotList.css';
@@ -32,10 +32,11 @@ export interface HotspotListProps {
   onHotspotClick: (key: string) => void;
   securityCategories: T.StandardSecurityCategories;
   selectedHotspotKey: string | undefined;
+  statusFilter: HotspotStatusFilters;
 }
 
 export default function HotspotList(props: HotspotListProps) {
-  const { hotspots, securityCategories, selectedHotspotKey } = props;
+  const { hotspots, securityCategories, selectedHotspotKey, statusFilter } = props;
 
   const groupedHotspots: Array<{
     risk: RiskExposure;
@@ -53,7 +54,7 @@ export default function HotspotList(props: HotspotListProps) {
     <>
       <h1 className="hotspot-list-header bordered-bottom">
         <SecurityHotspotIcon className="spacer-right" />
-        {translateWithParameters(`hotspots.list_title.TO_REVIEW`, hotspots.length)}
+        {translateWithParameters(`hotspots.list_title.${statusFilter}`, hotspots.length)}
       </h1>
       <ul className="huge-spacer-bottom">
         {groupedHotspots.map(riskGroup => (
index 1bdfa7830a3fbe63e51fb9029bcf5175fdbbcb5f..95a9826cc7de01653f74757f8378ba6eddc9e6a0 100644 (file)
@@ -28,7 +28,7 @@ export interface HotspotListItemProps {
   selected: boolean;
 }
 
-export function HotspotListItem(props: HotspotListItemProps) {
+export default function HotspotListItem(props: HotspotListItemProps) {
   const { hotspot, selected } = props;
   return (
     <a
@@ -36,9 +36,9 @@ export function HotspotListItem(props: HotspotListItemProps) {
       href="#"
       onClick={() => !selected && props.onClick(hotspot.key)}>
       <div className="little-spacer-left">{hotspot.message}</div>
-      <div className="badge spacer-top">{translate('issue.status', hotspot.status)}</div>
+      <div className="badge spacer-top">
+        {translate('hotspot.status', hotspot.resolution || hotspot.status)}
+      </div>
     </a>
   );
 }
-
-export default React.memo(HotspotListItem);
diff --git a/server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/FilterBar-test.tsx b/server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/FilterBar-test.tsx
new file mode 100644 (file)
index 0000000..c3e5ad7
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import Select from 'sonar-ui-common/components/controls/Select';
+import { HotspotStatusFilters } from '../../../../types/security-hotspots';
+import FilterBar, { FilterBarProps } from '../FilterBar';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot();
+});
+
+it('should trigger onChange', () => {
+  const onChangeStatus = jest.fn();
+  const wrapper = shallowRender({ onChangeStatus });
+
+  const { onChange } = wrapper.find(Select).props();
+
+  if (!onChange) {
+    return fail("Select's onChange should be defined");
+  }
+  onChange({ value: HotspotStatusFilters.SAFE });
+  expect(onChangeStatus).toBeCalledWith(HotspotStatusFilters.SAFE);
+});
+
+function shallowRender(props: Partial<FilterBarProps> = {}) {
+  return shallow(
+    <FilterBar
+      onChangeStatus={jest.fn()}
+      statusFilter={HotspotStatusFilters.TO_REVIEW}
+      {...props}
+    />
+  );
+}
index 217a182cccda7b90aac1e0749f964af02ea9991e..2cf2e050fadc6189309dacfadfabe00bedf1076e 100644 (file)
@@ -20,7 +20,7 @@
 import { shallow } from 'enzyme';
 import * as React from 'react';
 import { mockRawHotspot } from '../../../../helpers/mocks/security-hotspots';
-import { RiskExposure } from '../../../../types/security-hotspots';
+import { HotspotStatusFilters, RiskExposure } from '../../../../types/security-hotspots';
 import HotspotList, { HotspotListProps } from '../HotspotList';
 
 it('should render correctly', () => {
@@ -57,6 +57,7 @@ function shallowRender(props: Partial<HotspotListProps> = {}) {
       onHotspotClick={jest.fn()}
       securityCategories={{}}
       selectedHotspotKey="h2"
+      statusFilter={HotspotStatusFilters.TO_REVIEW}
       {...props}
     />
   );
index d69518bed0f9b4dbe8bd70463532183177ae84ad..3e6467046a57b91e697eaacbf84d98837719b64b 100644 (file)
@@ -20,7 +20,7 @@
 import { shallow } from 'enzyme';
 import * as React from 'react';
 import { mockRawHotspot } from '../../../../helpers/mocks/security-hotspots';
-import { HotspotListItem, HotspotListItemProps } from '../HotspotListItem';
+import HotspotListItem, { HotspotListItemProps } from '../HotspotListItem';
 
 it('should render correctly', () => {
   expect(shallowRender()).toMatchSnapshot();
diff --git a/server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/FilterBar-test.tsx.snap b/server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/FilterBar-test.tsx.snap
new file mode 100644 (file)
index 0000000..cdae654
--- /dev/null
@@ -0,0 +1,41 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+  className="filter-bar display-flex-center"
+>
+  <h3
+    className="big-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>
+`;
index 7df6dbe4ac588d7ea6d6189cff6a94868b83c42a..2bf9b48a9761c821fa36befecc3e32e2cc9ccdab 100644 (file)
@@ -14,7 +14,7 @@ exports[`should render correctly 1`] = `
   <div
     className="badge spacer-top"
   >
-    issue.status.TO_REVIEW
+    hotspot.status.TO_REVIEW
   </div>
 </a>
 `;
@@ -33,7 +33,7 @@ exports[`should render correctly 2`] = `
   <div
     className="badge spacer-top"
   >
-    issue.status.TO_REVIEW
+    hotspot.status.TO_REVIEW
   </div>
 </a>
 `;
index 43cfe55e449512991fe33f3ec47661553bc1e796..146175fc7ce2354752761a030720d6bb0b386564 100644 (file)
@@ -34,7 +34,7 @@
 #security_hotspots .filter-bar {
   max-width: 1280px;
   margin: 0 auto;
-  padding: var(--gridSize) 20px;
+  padding: calc(2 * var(--gridSize)) 20px;
   border-bottom: 1px solid var(--barBorderColor);
 }
 
index 1a137246339df7d0357eae8a5170b5198b27b636..3d2fb32c7e0bb8d863395e05321b47924dd46c2e 100644 (file)
@@ -33,6 +33,12 @@ export enum HotspotResolution {
   SAFE = 'SAFE'
 }
 
+export enum HotspotStatusFilters {
+  FIXED = 'FIXED',
+  SAFE = 'SAFE',
+  TO_REVIEW = 'TO_REVIEW'
+}
+
 export enum HotspotStatusOptions {
   FIXED = 'FIXED',
   SAFE = 'SAFE',
index 3ddc3dce610f7eff471cc33264e90ea62ac07edc..9cebe11abfb007f5c099fe744a1fd6615612950c 100644 (file)
@@ -649,7 +649,8 @@ hotspots.no_hotspots.title=There are no Security Hotspots to review
 hotspots.no_hotspots.description=Next time you analyse a piece of code that contains a potential security risk, it will show up here.
 hotspots.learn_more=Learn more about Security Hotspots
 hotspots.list_title.TO_REVIEW={0} Security Hotspots to review
-hotspots.list_title.REVIEWED={0} reviewed Security Hotspots 
+hotspots.list_title.FIXED={0} Security Hotspots reviewed as fixed
+hotspots.list_title.SAFE={0} Security Hotspots reviewed as safe
 hotspots.risk_exposure=Review priority:
 
 hotspot.category=Category:
@@ -660,6 +661,15 @@ hotspot.tabs.vulnerability_description=Are you vulnerable?
 hotspot.tabs.fix_recommendations=How can you fix it?
 hotspots.review_hotspot=Review Hotspot
 
+hotspot.status.TO_REVIEW=To review
+hotspot.status.FIXED=Fixed
+hotspot.status.SAFE=Safe
+
+hotspot.filters.title=Filters
+hotspot.filters.status.to_review=To review
+hotspot.filters.status.fixed=Reviewed as fixed
+hotspot.filters.status.safe=Reviewed as safe
+
 hotspots.form.title=Mark Security Hotspot as:
 
 hotspots.form.assign_to=Assign to: