aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/issues/sidebar
diff options
context:
space:
mode:
authorStas Vilchik <stas.vilchik@sonarsource.com>2018-08-14 16:12:56 +0200
committerSonarTech <sonartech@sonarsource.com>2018-08-21 20:21:02 +0200
commit63055cd49b4053c4f99949f505f6ce1214cb4135 (patch)
tree77ec3f0ef3410ce84155da6d5af27132cbde8b56 /server/sonar-web/src/main/js/apps/issues/sidebar
parentc9d8fb12afc55512508c55f4026fbad3797c0439 (diff)
downloadsonarqube-63055cd49b4053c4f99949f505f6ce1214cb4135.tar.gz
sonarqube-63055cd49b4053c4f99949f505f6ce1214cb4135.zip
SONAR-6961 Add issue counts to search in rule facet on issue page (#612)
Diffstat (limited to 'server/sonar-web/src/main/js/apps/issues/sidebar')
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.tsx329
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/AuthorFacet.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/DirectoryFacet.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/FileFacet.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/LanguageFacet.tsx17
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/ModuleFacet.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/ProjectFacet.tsx16
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/ResolutionFacet.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/RuleFacet.tsx27
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/SeverityFacet.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx39
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/StandardFacet.tsx107
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/StatusFacet.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/TagFacet.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/AssigneeFacet-test.tsx71
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-test.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/StandardFacet-test.tsx17
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.tsx.snap348
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/StandardFacet-test.tsx.snap185
21 files changed, 310 insertions, 923 deletions
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.tsx
index d7b83bf6228..656f74bc72c 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.tsx
@@ -18,101 +18,32 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { sortBy, uniq, without } from 'lodash';
-import {
- searchAssignees,
- formatFacetStat,
- Query,
- ReferencedUser,
- SearchedAssignee
-} from '../utils';
-import { Component, Paging } from '../../../app/types';
-import FacetBox from '../../../components/facet/FacetBox';
-import FacetHeader from '../../../components/facet/FacetHeader';
-import FacetItem from '../../../components/facet/FacetItem';
-import FacetItemsList from '../../../components/facet/FacetItemsList';
+import { omit, sortBy, without } from 'lodash';
+import { searchAssignees, Query, ReferencedUser, SearchedAssignee } from '../utils';
+import { Component } from '../../../app/types';
import Avatar from '../../../components/ui/Avatar';
import { translate } from '../../../helpers/l10n';
-import DeferredSpinner from '../../../components/common/DeferredSpinner';
-import MultipleSelectionHint from '../../../components/facet/MultipleSelectionHint';
-import SearchBox from '../../../components/controls/SearchBox';
-import ListFooter from '../../../components/controls/ListFooter';
import { highlightTerm } from '../../../helpers/search';
+import ListStyleFacet from '../../../components/facet/ListStyleFacet';
export interface Props {
assigned: boolean;
assignees: string[];
component: Component | undefined;
fetching: boolean;
- loading?: boolean;
+ loadSearchResultCount: (changes: Partial<Query>) => Promise<number>;
onChange: (changes: Partial<Query>) => void;
onToggle: (property: string) => void;
open: boolean;
organization: string | undefined;
+ query: Query;
stats: { [x: string]: number } | undefined;
referencedUsers: { [login: string]: ReferencedUser };
}
-interface State {
- query: string;
- searching: boolean;
- searchResults?: SearchedAssignee[];
- searchPaging?: Paging;
-}
-
-export default class AssigneeFacet extends React.PureComponent<Props, State> {
- mounted = false;
- property = 'assignees';
-
- state: State = {
- query: '',
- searching: false
- };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- stopSearching = () => {
- if (this.mounted) {
- this.setState({ searching: false });
- }
- };
-
- search = (query: string) => {
- if (query.length >= 2) {
- this.setState({ query, searching: true });
- searchAssignees(query, this.props.organization).then(({ paging, results }) => {
- if (this.mounted) {
- this.setState({ searching: false, searchResults: results, searchPaging: paging });
- }
- }, this.stopSearching);
- } else {
- this.setState({ query, searching: false, searchResults: [] });
- }
- };
-
- searchMore = () => {
- const { query, searchPaging, searchResults } = this.state;
- if (query && searchResults && searchPaging) {
- this.setState({ searching: true });
- searchAssignees(query, this.props.organization, searchPaging.pageIndex + 1).then(
- ({ paging, results }) => {
- if (this.mounted) {
- this.setState({
- searching: false,
- searchResults: [...searchResults, ...results],
- searchPaging: paging
- });
- }
- },
- this.stopSearching
- );
- }
+export default class AssigneeFacet extends React.PureComponent<Props> {
+ handleSearch = (query: string, page?: number) => {
+ return searchAssignees(query, this.props.organization, page);
};
handleItemClick = (itemValue: string, multiple: boolean) => {
@@ -124,221 +55,105 @@ export default class AssigneeFacet extends React.PureComponent<Props, State> {
const newValue = sortBy(
assignees.includes(itemValue) ? without(assignees, itemValue) : [...assignees, itemValue]
);
- this.props.onChange({ assigned: true, [this.property]: newValue });
+ this.props.onChange({ assigned: true, assignees: newValue });
} else {
this.props.onChange({
assigned: true,
- [this.property]: assignees.includes(itemValue) && assignees.length < 2 ? [] : [itemValue]
+ assignees: assignees.includes(itemValue) && assignees.length < 2 ? [] : [itemValue]
});
}
};
- handleHeaderClick = () => {
- this.props.onToggle(this.property);
- };
-
handleClear = () => {
this.props.onChange({ assigned: true, assignees: [] });
};
- handleSelect = (option: { value: string }) => {
- const { assignees } = this.props;
- this.props.onChange({ assigned: true, [this.property]: uniq([...assignees, option.value]) });
- };
-
- isAssigneeActive(assignee: string) {
- return assignee === '' ? !this.props.assigned : this.props.assignees.includes(assignee);
- }
-
- getAssigneeNameAndTooltip(assignee: string) {
+ getAssigneeName = (assignee: string) => {
if (assignee === '') {
- return { name: translate('unassigned'), tooltip: translate('unassigned') };
+ return translate('unassigned');
} else {
- const { referencedUsers } = this.props;
- if (referencedUsers[assignee]) {
- return {
- name: (
- <span>
- <Avatar
- className="little-spacer-right"
- hash={referencedUsers[assignee].avatar}
- name={referencedUsers[assignee].name}
- size={16}
- />
- {referencedUsers[assignee].name}
- </span>
- ),
- tooltip: referencedUsers[assignee].name
- };
- } else {
- return { name: assignee, tooltip: assignee };
- }
- }
- }
-
- getStat(assignee: string) {
- const { stats } = this.props;
- return stats ? stats[assignee] : undefined;
- }
-
- getValues() {
- const values = this.props.assignees.map(assignee => {
const user = this.props.referencedUsers[assignee];
return user ? user.name : assignee;
- });
- if (!this.props.assigned) {
- values.push(translate('unassigned'));
}
- return values;
- }
-
- renderOption = (option: { avatar: string; label: string }) => {
- return this.renderAssignee(option.avatar, option.label);
};
- renderAssignee = (avatar: string | undefined, name: string) => (
- <span>
- {avatar !== undefined && (
- <Avatar className="little-spacer-right" hash={avatar} name={name} size={16} />
- )}
- {name}
- </span>
- );
-
- renderListItem(assignee: string) {
- const { name, tooltip } = this.getAssigneeNameAndTooltip(assignee);
- return (
- <FacetItem
- active={this.isAssigneeActive(assignee)}
- key={assignee}
- loading={this.props.loading}
- name={name}
- onClick={this.handleItemClick}
- stat={formatFacetStat(this.getStat(assignee))}
- tooltip={tooltip}
- value={assignee}
- />
- );
- }
-
- renderList() {
- const { stats } = this.props;
-
- if (!stats) {
- return null;
- }
+ loadSearchResultCount = (assignee: SearchedAssignee) => {
+ return this.props.loadSearchResultCount({ assigned: undefined, assignees: [assignee.login] });
+ };
- const assignees = sortBy(
+ getSortedItems = () => {
+ const { stats = {} } = this.props;
+ return sortBy(
Object.keys(stats),
- // put unassigned first
+ // put "not assigned" first
key => (key === '' ? 0 : 1),
// the sort by number
key => -stats[key]
);
+ };
- return (
- <FacetItemsList>{assignees.map(assignee => this.renderListItem(assignee))}</FacetItemsList>
- );
- }
-
- renderSearch() {
- if (!this.props.stats || !Object.keys(this.props.stats).length) {
- return null;
- }
-
- return (
- <SearchBox
- autoFocus={true}
- className="little-spacer-top spacer-bottom"
- loading={this.state.searching}
- minLength={2}
- onChange={this.search}
- placeholder={translate('search.search_for_users')}
- value={this.state.query}
- />
- );
- }
-
- renderSearchResults() {
- const { searching, searchResults, searchPaging } = this.state;
-
- if (!searching && (!searchResults || !searchResults.length)) {
- return <div className="note spacer-bottom">{translate('no_results')}</div>;
- }
-
- if (!searchResults || !searchPaging) {
- // initial search
- return null;
+ renderFacetItem = (assignee: string) => {
+ if (assignee === '') {
+ return translate('unassigned');
}
- return (
+ const user = this.props.referencedUsers[assignee];
+ return user ? (
<>
- <FacetItemsList>
- {searchResults.map(result => this.renderSearchResult(result))}
- </FacetItemsList>
- <ListFooter
- count={searchResults.length}
- loadMore={this.searchMore}
- ready={!searching}
- total={searchPaging.total}
- />
+ <Avatar className="little-spacer-right" hash={user.avatar} name={user.name} size={16} />
+ {user.name}
</>
+ ) : (
+ assignee
);
- }
+ };
- renderSearchResult(result: SearchedAssignee) {
- const active = this.props.assignees.includes(result.login);
- const stat = this.getStat(result.login);
+ renderSearchResult = (result: SearchedAssignee, query: string) => {
return (
- <FacetItem
- active={active}
- disabled={!active && stat === 0}
- key={result.login}
- loading={this.props.loading}
- name={
- <>
- {result.avatar !== undefined && (
- <Avatar
- className="little-spacer-right"
- hash={result.avatar}
- name={result.name}
- size={16}
- />
- )}
- {highlightTerm(result.name, this.state.query)}
- </>
- }
- onClick={this.handleItemClick}
- stat={stat && formatFacetStat(stat)}
- tooltip={result.name}
- value={result.login}
- />
+ <>
+ {result.avatar !== undefined && (
+ <Avatar
+ className="little-spacer-right"
+ hash={result.avatar}
+ name={result.name}
+ size={16}
+ />
+ )}
+ {highlightTerm(result.name, query)}
+ </>
);
- }
+ };
render() {
- const { assignees, stats = {} } = this.props;
- return (
- <FacetBox property={this.property}>
- <FacetHeader
- name={translate('issues.facet', this.property)}
- onClear={this.handleClear}
- onClick={this.handleHeaderClick}
- open={this.props.open}
- values={this.getValues()}
- />
+ const values = [...this.props.assignees];
+ if (!this.props.assigned) {
+ values.push('');
+ }
- <DeferredSpinner loading={this.props.fetching} />
- {this.props.open && (
- <>
- {this.renderSearch()}
- {this.state.query && this.state.searchResults !== undefined
- ? this.renderSearchResults()
- : this.renderList()}
- <MultipleSelectionHint options={Object.keys(stats).length} values={assignees.length} />
- </>
- )}
- </FacetBox>
+ return (
+ <ListStyleFacet<SearchedAssignee>
+ facetHeader={translate('issues.facet.assignees')}
+ fetching={this.props.fetching}
+ getFacetItemText={this.getAssigneeName}
+ getSearchResultKey={user => user.login}
+ getSearchResultText={user => user.name}
+ // put "not assigned" item first
+ getSortedItems={this.getSortedItems}
+ loadSearchResultCount={this.loadSearchResultCount}
+ onChange={this.props.onChange}
+ onClear={this.handleClear}
+ onItemClick={this.handleItemClick}
+ onSearch={this.handleSearch}
+ onToggle={this.props.onToggle}
+ open={this.props.open}
+ property="assignees"
+ query={omit(this.props.query, 'assigned', 'assignees')}
+ renderFacetItem={this.renderFacetItem}
+ renderSearchResult={this.renderSearchResult}
+ searchPlaceholder={translate('search.search_for_users')}
+ stats={this.props.stats}
+ values={values}
+ />
);
}
}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/AuthorFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/AuthorFacet.tsx
index 301506b60a1..27d4fbd8355 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/AuthorFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/AuthorFacet.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { omit } from 'lodash';
import { Query } from '../utils';
import { translate } from '../../../helpers/l10n';
import ListStyleFacet from '../../../components/facet/ListStyleFacet';
@@ -27,11 +28,12 @@ import { highlightTerm } from '../../../helpers/search';
interface Props {
componentKey: string | undefined;
fetching: boolean;
- loading?: boolean;
+ loadSearchResultCount: (changes: Partial<Query>) => Promise<number>;
onChange: (changes: Partial<Query>) => void;
onToggle: (property: string) => void;
open: boolean;
organization: string | undefined;
+ query: Query;
stats: { [x: string]: number } | undefined;
authors: string[];
}
@@ -52,23 +54,29 @@ export default class AuthorFacet extends React.PureComponent<Props> {
}).then(authors => ({ maxResults: authors.length === SEARCH_SIZE, results: authors }));
};
+ loadSearchResultCount = (author: string) => {
+ return this.props.loadSearchResultCount({ authors: [author] });
+ };
+
renderSearchResult = (author: string, term: string) => {
return highlightTerm(author, term);
};
render() {
return (
- <ListStyleFacet
+ <ListStyleFacet<string>
facetHeader={translate('issues.facet.authors')}
fetching={this.props.fetching}
getFacetItemText={this.identity}
getSearchResultKey={this.identity}
getSearchResultText={this.identity}
+ loadSearchResultCount={this.loadSearchResultCount}
onChange={this.props.onChange}
onSearch={this.handleSearch}
onToggle={this.props.onToggle}
open={this.props.open}
property="authors"
+ query={omit(this.props.query, 'authors')}
renderFacetItem={this.identity}
renderSearchResult={this.renderSearchResult}
searchPlaceholder={translate('search.search_for_authors')}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.tsx
index 03fb868e97a..76d2b6314c8 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.tsx
@@ -42,7 +42,6 @@ interface Props {
createdBefore: Date | undefined;
createdInLast: string;
fetching: boolean;
- loading?: boolean;
onChange: (changes: Partial<Query>) => void;
onToggle: (property: string) => void;
open: boolean;
@@ -173,7 +172,7 @@ export default class CreationDateFacet extends React.PureComponent<Props> {
createdBefore: endDate,
tooltip,
x: index,
- y: this.props.loading ? 0 : stats[start]
+ y: stats[start]
};
});
@@ -226,7 +225,6 @@ export default class CreationDateFacet extends React.PureComponent<Props> {
<div className="spacer-top issues-predefined-periods">
<FacetItem
active={!this.hasValue()}
- loading={this.props.loading}
name={translate('issues.facet.createdAt.all')}
onClick={this.handlePeriodClick}
tooltip={translate('issues.facet.createdAt.all')}
@@ -235,7 +233,6 @@ export default class CreationDateFacet extends React.PureComponent<Props> {
{component ? (
<FacetItem
active={sinceLeakPeriod}
- loading={this.props.loading}
name={translate('issues.new_code')}
onClick={this.handleLeakPeriodClick}
tooltip={translate('issues.leak_period')}
@@ -245,7 +242,6 @@ export default class CreationDateFacet extends React.PureComponent<Props> {
<>
<FacetItem
active={createdInLast === '1w'}
- loading={this.props.loading}
name={translate('issues.facet.createdAt.last_week')}
onClick={this.handlePeriodClick}
tooltip={translate('issues.facet.createdAt.last_week')}
@@ -253,7 +249,6 @@ export default class CreationDateFacet extends React.PureComponent<Props> {
/>
<FacetItem
active={createdInLast === '1m'}
- loading={this.props.loading}
name={translate('issues.facet.createdAt.last_month')}
onClick={this.handlePeriodClick}
tooltip={translate('issues.facet.createdAt.last_month')}
@@ -261,7 +256,6 @@ export default class CreationDateFacet extends React.PureComponent<Props> {
/>
<FacetItem
active={createdInLast === '1y'}
- loading={this.props.loading}
name={translate('issues.facet.createdAt.last_year')}
onClick={this.handlePeriodClick}
tooltip={translate('issues.facet.createdAt.last_year')}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/DirectoryFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/DirectoryFacet.tsx
index 9a776b636ff..56501ced226 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/DirectoryFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/DirectoryFacet.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { omit } from 'lodash';
import { Query } from '../utils';
import QualifierIcon from '../../../components/icons-components/QualifierIcon';
import { translate } from '../../../helpers/l10n';
@@ -30,10 +31,11 @@ interface Props {
componentKey: string;
fetching: boolean;
directories: string[];
- loading?: boolean;
+ loadSearchResultCount: (changes: Partial<Query>) => Promise<number>;
onChange: (changes: Partial<Query>) => void;
onToggle: (property: string) => void;
open: boolean;
+ query: Query;
stats: { [x: string]: number } | undefined;
}
@@ -60,6 +62,10 @@ export default class DirectoryFacet extends React.PureComponent<Props> {
}).then(({ components, paging }) => ({ paging, results: components }));
};
+ loadSearchResultCount = (directory: TreeComponent) => {
+ return this.props.loadSearchResultCount({ directories: [directory.name] });
+ };
+
renderDirectory = (directory: React.ReactNode) => (
<>
<QualifierIcon className="little-spacer-right" qualifier="DIR" />
@@ -77,18 +83,20 @@ export default class DirectoryFacet extends React.PureComponent<Props> {
render() {
return (
- <ListStyleFacet
+ <ListStyleFacet<TreeComponent>
facetHeader={translate('issues.facet.directories')}
fetching={this.props.fetching}
getFacetItemText={this.getFacetItemText}
getSearchResultKey={this.getSearchResultKey}
getSearchResultText={this.getSearchResultText}
+ loadSearchResultCount={this.loadSearchResultCount}
minSearchLength={3}
onChange={this.props.onChange}
onSearch={this.handleSearch}
onToggle={this.props.onToggle}
open={this.props.open}
property="directories"
+ query={omit(this.props.query, 'directories')}
renderFacetItem={this.renderFacetItem}
renderSearchResult={this.renderSearchResult}
searchPlaceholder={translate('search.search_for_directories')}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/FileFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/FileFacet.tsx
index 45c71c36caa..1c1f2658e94 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/FileFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/FileFacet.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { omit } from 'lodash';
import { Query, ReferencedComponent } from '../utils';
import QualifierIcon from '../../../components/icons-components/QualifierIcon';
import { translate } from '../../../helpers/l10n';
@@ -30,10 +31,11 @@ interface Props {
componentKey: string;
fetching: boolean;
files: string[];
- loading?: boolean;
+ loadSearchResultCount: (changes: Partial<Query>) => Promise<number>;
onChange: (changes: Partial<Query>) => void;
onToggle: (property: string) => void;
open: boolean;
+ query: Query;
referencedComponents: { [componentKey: string]: ReferencedComponent };
stats: { [x: string]: number } | undefined;
}
@@ -67,6 +69,10 @@ export default class FileFacet extends React.PureComponent<Props> {
}).then(({ components, paging }) => ({ paging, results: components }));
};
+ loadSearchResultCount = (file: TreeComponent) => {
+ return this.props.loadSearchResultCount({ files: [file.id] });
+ };
+
renderFile = (file: React.ReactNode) => (
<>
<QualifierIcon className="little-spacer-right" qualifier="FIL" />
@@ -85,18 +91,20 @@ export default class FileFacet extends React.PureComponent<Props> {
render() {
return (
- <ListStyleFacet
+ <ListStyleFacet<TreeComponent>
facetHeader={translate('issues.facet.files')}
fetching={this.props.fetching}
getFacetItemText={this.getFacetItemText}
getSearchResultKey={this.getSearchResultKey}
getSearchResultText={this.getSearchResultText}
+ loadSearchResultCount={this.loadSearchResultCount}
minSearchLength={3}
onChange={this.props.onChange}
onSearch={this.handleSearch}
onToggle={this.props.onToggle}
open={this.props.open}
property="files"
+ query={omit(this.props.query, 'files')}
renderFacetItem={this.renderFacetItem}
renderSearchResult={this.renderSearchResult}
searchPlaceholder={translate('search.search_for_files')}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/LanguageFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/LanguageFacet.tsx
index 65f2144844e..bfe9952e7bd 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/LanguageFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/LanguageFacet.tsx
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { uniqBy } from 'lodash';
+import { uniqBy, omit } from 'lodash';
import { connect } from 'react-redux';
import ListStyleFacet from '../../../components/facet/ListStyleFacet';
import { Query, ReferencedLanguage } from '../utils';
@@ -35,10 +35,11 @@ interface Props {
fetching: boolean;
installedLanguages: InstalledLanguage[];
languages: string[];
- loading?: boolean;
+ loadSearchResultCount: (changes: Partial<Query>) => Promise<number>;
onChange: (changes: Partial<Query>) => void;
onToggle: (property: string) => void;
open: boolean;
+ query: Query;
referencedLanguages: { [languageKey: string]: ReferencedLanguage };
stats: { [x: string]: number } | undefined;
}
@@ -70,23 +71,29 @@ class LanguageFacet extends React.PureComponent<Props> {
);
};
+ loadSearchResultCount = (language: InstalledLanguage) => {
+ return this.props.loadSearchResultCount({ languages: [language.key] });
+ };
+
renderSearchResult = ({ name }: InstalledLanguage, term: string) => {
return highlightTerm(name, term);
};
render() {
return (
- <ListStyleFacet
+ <ListStyleFacet<InstalledLanguage>
facetHeader={translate('issues.facet.languages')}
fetching={this.props.fetching}
getFacetItemText={this.getLanguageName}
- getSearchResultKey={(language: InstalledLanguage) => language.key}
- getSearchResultText={(language: InstalledLanguage) => language.name}
+ getSearchResultKey={language => language.key}
+ getSearchResultText={language => language.name}
+ loadSearchResultCount={this.loadSearchResultCount}
onChange={this.props.onChange}
onSearch={this.handleSearch}
onToggle={this.props.onToggle}
open={this.props.open}
property="languages"
+ query={omit(this.props.query, 'languages')}
renderFacetItem={this.getLanguageName}
renderSearchResult={this.renderSearchResult}
searchPlaceholder={translate('search.search_for_languages')}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/ModuleFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/ModuleFacet.tsx
index 82c767e2b4a..f20a6938ea5 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/ModuleFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/ModuleFacet.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { omit } from 'lodash';
import { Query, ReferencedComponent } from '../utils';
import QualifierIcon from '../../../components/icons-components/QualifierIcon';
import { translate } from '../../../helpers/l10n';
@@ -28,11 +29,12 @@ import { highlightTerm } from '../../../helpers/search';
interface Props {
componentKey: string;
fetching: boolean;
- loading?: boolean;
+ loadSearchResultCount: (changes: Partial<Query>) => Promise<number>;
modules: string[];
onChange: (changes: Partial<Query>) => void;
onToggle: (property: string) => void;
open: boolean;
+ query: Query;
referencedComponents: { [componentKey: string]: ReferencedComponent };
stats: { [x: string]: number } | undefined;
}
@@ -61,6 +63,10 @@ export default class ModuleFacet extends React.PureComponent<Props> {
}).then(({ components, paging }) => ({ paging, results: components }));
};
+ loadSearchResultCount = (module: TreeComponent) => {
+ return this.props.loadSearchResultCount({ files: [module.id] });
+ };
+
renderModule = (module: React.ReactNode) => (
<>
<QualifierIcon className="little-spacer-right" qualifier="BRC" />
@@ -79,18 +85,20 @@ export default class ModuleFacet extends React.PureComponent<Props> {
render() {
return (
- <ListStyleFacet
+ <ListStyleFacet<TreeComponent>
facetHeader={translate('issues.facet.modules')}
fetching={this.props.fetching}
getFacetItemText={this.getModuleName}
getSearchResultKey={this.getSearchResultKey}
getSearchResultText={this.getSearchResultText}
+ loadSearchResultCount={this.loadSearchResultCount}
minSearchLength={3}
onChange={this.props.onChange}
onSearch={this.handleSearch}
onToggle={this.props.onToggle}
open={this.props.open}
property="modules"
+ query={omit(this.props.query, 'modules')}
renderFacetItem={this.renderFacetItem}
renderSearchResult={this.renderSearchResult}
searchPlaceholder={translate('search.search_for_modules')}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/ProjectFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/ProjectFacet.tsx
index b0989c9e516..f54d8a94cd3 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/ProjectFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/ProjectFacet.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { omit } from 'lodash';
import ListStyleFacet from '../../../components/facet/ListStyleFacet';
import { Query, ReferencedComponent } from '../utils';
import { searchProjects, getTree } from '../../../api/components';
@@ -29,13 +30,14 @@ import { highlightTerm } from '../../../helpers/search';
interface Props {
component: Component | undefined;
- loading?: boolean;
+ loadSearchResultCount: (changes: Partial<Query>) => Promise<number>;
fetching: boolean;
onChange: (changes: Partial<Query>) => void;
onToggle: (property: string) => void;
open: boolean;
organization: { key: string } | undefined;
projects: string[];
+ query: Query;
referencedComponents: { [componentKey: string]: ReferencedComponent };
stats: { [x: string]: number } | undefined;
}
@@ -91,6 +93,10 @@ export default class ProjectFacet extends React.PureComponent<Props> {
return referencedComponents[project] ? referencedComponents[project].name : project;
};
+ loadSearchResultCount = (project: SearchedProject) => {
+ return this.props.loadSearchResultCount({ projects: [project.id] });
+ };
+
renderFacetItem = (project: string) => {
const { referencedComponents } = this.props;
return referencedComponents[project] ? (
@@ -125,17 +131,19 @@ export default class ProjectFacet extends React.PureComponent<Props> {
render() {
return (
- <ListStyleFacet
+ <ListStyleFacet<SearchedProject>
facetHeader={translate('issues.facet.projects')}
fetching={this.props.fetching}
getFacetItemText={this.getProjectName}
- getSearchResultKey={(project: SearchedProject) => project.id}
- getSearchResultText={(project: SearchedProject) => project.name}
+ getSearchResultKey={project => project.id}
+ getSearchResultText={project => project.name}
+ loadSearchResultCount={this.loadSearchResultCount}
onChange={this.props.onChange}
onSearch={this.handleSearch}
onToggle={this.props.onToggle}
open={this.props.open}
property="projects"
+ query={omit(this.props.query, 'projects')}
renderFacetItem={this.renderFacetItem}
renderSearchResult={this.renderSearchResult}
searchPlaceholder={translate('search.search_for_projects')}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/ResolutionFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/ResolutionFacet.tsx
index 500f1f6bf4a..eda62bd196a 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/ResolutionFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/ResolutionFacet.tsx
@@ -30,7 +30,6 @@ import MultipleSelectionHint from '../../../components/facet/MultipleSelectionHi
interface Props {
fetching: boolean;
- loading?: boolean;
onChange: (changes: Partial<Query>) => void;
onToggle: (property: string) => void;
open: boolean;
@@ -100,7 +99,6 @@ export default class ResolutionFacet extends React.PureComponent<Props> {
disabled={stat === 0 && !active}
halfWidth={true}
key={resolution}
- loading={this.props.loading}
name={this.getFacetItemName(resolution)}
onClick={this.handleItemClick}
stat={formatFacetStat(stat)}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/RuleFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/RuleFacet.tsx
index be0c32bc774..64ef05a7d1c 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/RuleFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/RuleFacet.tsx
@@ -18,33 +18,28 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { omit } from 'lodash';
import ListStyleFacet from '../../../components/facet/ListStyleFacet';
import { Query, ReferencedRule } from '../utils';
import { searchRules } from '../../../api/rules';
-import { Rule, Paging } from '../../../app/types';
+import { Rule } from '../../../app/types';
import { translate } from '../../../helpers/l10n';
interface Props {
fetching: boolean;
languages: string[];
- loading?: boolean;
+ loadSearchResultCount: (changes: Partial<Query>) => Promise<number>;
onChange: (changes: Partial<Query>) => void;
onToggle: (property: string) => void;
open: boolean;
organization: string | undefined;
+ query: Query;
referencedRules: { [ruleKey: string]: ReferencedRule };
rules: string[];
stats: { [x: string]: number } | undefined;
}
-interface State {
- query: string;
- searching: boolean;
- searchResults?: Rule[];
- searchPaging?: Paging;
-}
-
-export default class RuleFacet extends React.PureComponent<Props, State> {
+export default class RuleFacet extends React.PureComponent<Props> {
handleSearch = (query: string, page = 1) => {
const { languages, organization } = this.props;
return searchRules({
@@ -63,6 +58,10 @@ export default class RuleFacet extends React.PureComponent<Props, State> {
}));
};
+ loadSearchResultCount = (rule: Rule) => {
+ return this.props.loadSearchResultCount({ rules: [rule.key] });
+ };
+
getRuleName = (rule: string) => {
const { referencedRules } = this.props;
return referencedRules[rule]
@@ -76,17 +75,19 @@ export default class RuleFacet extends React.PureComponent<Props, State> {
render() {
return (
- <ListStyleFacet
+ <ListStyleFacet<Rule>
facetHeader={translate('issues.facet.rules')}
fetching={this.props.fetching}
getFacetItemText={this.getRuleName}
- getSearchResultKey={result => result.key}
- getSearchResultText={result => result.name}
+ getSearchResultKey={rule => rule.key}
+ getSearchResultText={rule => rule.name}
+ loadSearchResultCount={this.loadSearchResultCount}
onChange={this.props.onChange}
onSearch={this.handleSearch}
onToggle={this.props.onToggle}
open={this.props.open}
property="rules"
+ query={omit(this.props.query, 'rules')}
renderFacetItem={this.getRuleName}
renderSearchResult={this.renderSearchResult}
searchPlaceholder={translate('search.search_for_rules')}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/SeverityFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/SeverityFacet.tsx
index f8bab49546c..8f83047f9cc 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/SeverityFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/SeverityFacet.tsx
@@ -31,7 +31,6 @@ import MultipleSelectionHint from '../../../components/facet/MultipleSelectionHi
interface Props {
fetching: boolean;
- loading?: boolean;
onChange: (changes: Partial<Query>) => void;
onToggle: (property: string) => void;
open: boolean;
@@ -85,7 +84,6 @@ export default class SeverityFacet extends React.PureComponent<Props> {
disabled={stat === 0 && !active}
halfWidth={true}
key={severity}
- loading={this.props.loading}
name={<SeverityHelper severity={severity} />}
onClick={this.handleItemClick}
stat={formatFacetStat(stat)}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx
index 4a10412105f..fec7f81625a 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx
@@ -48,7 +48,7 @@ export interface Props {
component: Component | undefined;
facets: { [facet: string]: Facet };
hideAuthorFacet?: boolean;
- loading?: boolean;
+ loadSearchResultCount: (changes: Partial<Query>) => Promise<number>;
loadingFacets: { [key: string]: boolean };
myIssues: boolean;
onFacetToggle: (property: string) => void;
@@ -81,7 +81,6 @@ export default class Sidebar extends React.PureComponent<Props> {
<>
<TypeFacet
fetching={this.props.loadingFacets.types === true}
- loading={this.props.loading}
onChange={this.props.onFilterChange}
onToggle={this.props.onFacetToggle}
open={!!openFacets.types}
@@ -90,7 +89,6 @@ export default class Sidebar extends React.PureComponent<Props> {
/>
<SeverityFacet
fetching={this.props.loadingFacets.severities === true}
- loading={this.props.loading}
onChange={this.props.onFilterChange}
onToggle={this.props.onFacetToggle}
open={!!openFacets.severities}
@@ -99,7 +97,6 @@ export default class Sidebar extends React.PureComponent<Props> {
/>
<ResolutionFacet
fetching={this.props.loadingFacets.resolutions === true}
- loading={this.props.loading}
onChange={this.props.onFilterChange}
onToggle={this.props.onFacetToggle}
open={!!openFacets.resolutions}
@@ -109,7 +106,6 @@ export default class Sidebar extends React.PureComponent<Props> {
/>
<StatusFacet
fetching={this.props.loadingFacets.statuses === true}
- loading={this.props.loading}
onChange={this.props.onFilterChange}
onToggle={this.props.onFacetToggle}
open={!!openFacets.statuses}
@@ -123,7 +119,6 @@ export default class Sidebar extends React.PureComponent<Props> {
createdBefore={query.createdBefore}
createdInLast={query.createdInLast}
fetching={this.props.loadingFacets.createdAt === true}
- loading={this.props.loading}
onChange={this.props.onFilterChange}
onToggle={this.props.onFacetToggle}
open={!!openFacets.createdAt}
@@ -133,21 +128,23 @@ export default class Sidebar extends React.PureComponent<Props> {
<LanguageFacet
fetching={this.props.loadingFacets.languages === true}
languages={query.languages}
- loading={this.props.loading}
+ loadSearchResultCount={this.props.loadSearchResultCount}
onChange={this.props.onFilterChange}
onToggle={this.props.onFacetToggle}
open={!!openFacets.languages}
+ query={query}
referencedLanguages={this.props.referencedLanguages}
stats={facets.languages}
/>
<RuleFacet
fetching={this.props.loadingFacets.rules === true}
languages={query.languages}
- loading={this.props.loading}
+ loadSearchResultCount={this.props.loadSearchResultCount}
onChange={this.props.onFilterChange}
onToggle={this.props.onFacetToggle}
open={!!openFacets.rules}
organization={organizationKey}
+ query={query}
referencedRules={this.props.referencedRules}
rules={query.rules}
stats={facets.rules}
@@ -159,13 +156,14 @@ export default class Sidebar extends React.PureComponent<Props> {
fetchingCwe={this.props.loadingFacets.cwe === true}
fetchingOwaspTop10={this.props.loadingFacets.owaspTop10 === true}
fetchingSansTop25={this.props.loadingFacets.sansTop25 === true}
- loading={this.props.loading}
+ loadSearchResultCount={this.props.loadSearchResultCount}
onChange={this.props.onFilterChange}
onToggle={this.props.onFacetToggle}
open={!!openFacets[STANDARDS]}
owaspTop10={query.owaspTop10}
owaspTop10Open={!!openFacets.owaspTop10}
owaspTop10Stats={facets.owaspTop10}
+ query={query}
sansTop25={query.sansTop25}
sansTop25Open={!!openFacets.sansTop25}
sansTop25Stats={facets.sansTop25}
@@ -173,11 +171,12 @@ export default class Sidebar extends React.PureComponent<Props> {
<TagFacet
component={component}
fetching={this.props.loadingFacets.tags === true}
- loading={this.props.loading}
+ loadSearchResultCount={this.props.loadSearchResultCount}
onChange={this.props.onFilterChange}
onToggle={this.props.onFacetToggle}
open={!!openFacets.tags}
organization={organizationKey}
+ query={query}
stats={facets.tags}
tags={query.tags}
/>
@@ -185,12 +184,13 @@ export default class Sidebar extends React.PureComponent<Props> {
<ProjectFacet
component={component}
fetching={this.props.loadingFacets.projects === true}
- loading={this.props.loading}
+ loadSearchResultCount={this.props.loadSearchResultCount}
onChange={this.props.onFilterChange}
onToggle={this.props.onFacetToggle}
open={!!openFacets.projects}
organization={this.props.organization}
projects={query.projects}
+ query={query}
referencedComponents={this.props.referencedComponents}
stats={facets.projects}
/>
@@ -199,11 +199,12 @@ export default class Sidebar extends React.PureComponent<Props> {
<ModuleFacet
componentKey={this.props.component!.key}
fetching={this.props.loadingFacets.modules === true}
- loading={this.props.loading}
- modules={query.modules}
+ loadSearchResultCount={this.props.loadSearchResultCount}
+ modules={query.files}
onChange={this.props.onFilterChange}
onToggle={this.props.onFacetToggle}
open={!!openFacets.modules}
+ query={query}
referencedComponents={this.props.referencedComponents}
stats={facets.modules}
/>
@@ -213,10 +214,11 @@ export default class Sidebar extends React.PureComponent<Props> {
componentKey={this.props.component!.key}
directories={query.directories}
fetching={this.props.loadingFacets.directories === true}
- loading={this.props.loading}
+ loadSearchResultCount={this.props.loadSearchResultCount}
onChange={this.props.onFilterChange}
onToggle={this.props.onFacetToggle}
open={!!openFacets.directories}
+ query={query}
stats={facets.directories}
/>
)}
@@ -225,10 +227,11 @@ export default class Sidebar extends React.PureComponent<Props> {
componentKey={this.props.component!.key}
fetching={this.props.loadingFacets.files === true}
files={query.files}
- loading={this.props.loading}
+ loadSearchResultCount={this.props.loadSearchResultCount}
onChange={this.props.onFilterChange}
onToggle={this.props.onFacetToggle}
open={!!openFacets.files}
+ query={query}
referencedComponents={this.props.referencedComponents}
stats={facets.files}
/>
@@ -239,11 +242,12 @@ export default class Sidebar extends React.PureComponent<Props> {
assignees={query.assignees}
component={component}
fetching={this.props.loadingFacets.assignees === true}
- loading={this.props.loading}
+ loadSearchResultCount={this.props.loadSearchResultCount}
onChange={this.props.onFilterChange}
onToggle={this.props.onFacetToggle}
open={!!openFacets.assignees}
organization={organizationKey}
+ query={query}
referencedUsers={this.props.referencedUsers}
stats={facets.assignees}
/>
@@ -253,11 +257,12 @@ export default class Sidebar extends React.PureComponent<Props> {
authors={query.authors}
componentKey={this.props.component && this.props.component.key}
fetching={this.props.loadingFacets.authors === true}
- loading={this.props.loading}
+ loadSearchResultCount={this.props.loadSearchResultCount}
onChange={this.props.onFilterChange}
onToggle={this.props.onFacetToggle}
open={!!openFacets.authors}
organization={organizationKey}
+ query={query}
stats={facets.authors}
/>
)}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/StandardFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/StandardFacet.tsx
index e3cb518d74a..8c3005ae845 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/StandardFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/StandardFacet.tsx
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { sortBy, without } from 'lodash';
+import { sortBy, without, omit } from 'lodash';
import { Query, STANDARDS, formatFacetStat } from '../utils';
import FacetBox from '../../../components/facet/FacetBox';
import FacetHeader from '../../../components/facet/FacetHeader';
@@ -33,8 +33,8 @@ import {
} from '../../securityReports/utils';
import DeferredSpinner from '../../../components/common/DeferredSpinner';
import MultipleSelectionHint from '../../../components/facet/MultipleSelectionHint';
-import SearchBox from '../../../components/controls/SearchBox';
import { highlightTerm } from '../../../helpers/search';
+import ListStyleFacet from '../../../components/facet/ListStyleFacet';
export interface Props {
cwe: string[];
@@ -43,13 +43,14 @@ export interface Props {
fetchingOwaspTop10: boolean;
fetchingSansTop25: boolean;
fetchingCwe: boolean;
- loading?: boolean;
+ loadSearchResultCount: (changes: Partial<Query>) => Promise<number>;
onChange: (changes: Partial<Query>) => void;
onToggle: (property: string) => void;
open: boolean;
owaspTop10: string[];
owaspTop10Open: boolean;
owaspTop10Stats: { [x: string]: number } | undefined;
+ query: Query;
sansTop25: string[];
sansTop25Open: boolean;
sansTop25Stats: { [x: string]: number } | undefined;
@@ -132,10 +133,6 @@ export default class StandardFacet extends React.PureComponent<Props, State> {
this.props.onToggle('sansTop25');
};
- handleCWEHeaderClick = () => {
- this.props.onToggle('cwe');
- };
-
handleClear = () => {
this.props.onChange({ [this.property]: [], owaspTop10: [], sansTop25: [], cwe: [] });
};
@@ -158,20 +155,22 @@ export default class StandardFacet extends React.PureComponent<Props, State> {
this.handleItemClick('owaspTop10', itemValue, multiple);
};
- handleCWEItemClick = (itemValue: string, multiple: boolean) => {
- this.handleItemClick('cwe', itemValue, multiple);
- };
-
handleSansTop25ItemClick = (itemValue: string, multiple: boolean) => {
this.handleItemClick('sansTop25', itemValue, multiple);
};
- handleCWESelect = ({ value }: { value: string }) => {
- this.handleItemClick('cwe', value, true);
+ handleCWESearch = (query: string) => {
+ return Promise.resolve({
+ results: Object.keys(this.state.standards.cwe).filter(cwe =>
+ renderCWECategory(this.state.standards, cwe)
+ .toLowerCase()
+ .includes(query.toLowerCase())
+ )
+ });
};
- handleCWESearch = (query: string) => {
- this.setState({ cweQuery: query });
+ loadCWESearchResultCount = (category: string) => {
+ return this.props.loadSearchResultCount({ cwe: [category] });
};
renderList = (
@@ -216,7 +215,6 @@ export default class StandardFacet extends React.PureComponent<Props, State> {
<FacetItem
active={values.includes(category)}
key={category}
- loading={this.props.loading}
name={renderName(this.state.standards, category)}
onClick={onClick}
stat={formatFacetStat(getStat(category))}
@@ -247,45 +245,6 @@ export default class StandardFacet extends React.PureComponent<Props, State> {
return this.renderHint('owaspTop10Stats', 'owaspTop10');
}
- renderCWEList() {
- const { cweQuery } = this.state;
- if (cweQuery) {
- const results = Object.keys(this.state.standards.cwe).filter(cwe =>
- renderCWECategory(this.state.standards, cwe)
- .toLowerCase()
- .includes(cweQuery.toLowerCase())
- );
-
- return this.renderFacetItemsList(
- this.props.cweStats,
- this.props.cwe,
- results,
- (standards: Standards, category: string) =>
- highlightTerm(renderCWECategory(standards, category), cweQuery),
- renderCWECategory,
- this.handleCWEItemClick
- );
- } else {
- return this.renderList('cweStats', 'cwe', renderCWECategory, this.handleCWEItemClick);
- }
- }
-
- renderCWESearch() {
- return (
- <SearchBox
- autoFocus={true}
- className="little-spacer-top spacer-bottom"
- onChange={this.handleCWESearch}
- placeholder={translate('search.search_for_cwe')}
- value={this.state.cweQuery}
- />
- );
- }
-
- renderCWEHint() {
- return this.renderHint('cweStats', 'cwe');
- }
-
renderSansTop25List() {
return this.renderList(
'sansTop25Stats',
@@ -336,22 +295,28 @@ export default class StandardFacet extends React.PureComponent<Props, State> {
</>
)}
</FacetBox>
- <FacetBox className="is-inner" property="cwe">
- <FacetHeader
- name={translate('issues.facet.cwe')}
- onClick={this.handleCWEHeaderClick}
- open={this.props.cweOpen}
- values={this.props.cwe.map(item => renderCWECategory(this.state.standards, item))}
- />
- <DeferredSpinner loading={this.props.fetchingCwe} />
- {this.props.cweOpen && (
- <>
- {this.renderCWESearch()}
- {this.renderCWEList()}
- {this.renderCWEHint()}
- </>
- )}
- </FacetBox>
+ <ListStyleFacet<string>
+ className="is-inner"
+ facetHeader={translate('issues.facet.cwe')}
+ fetching={this.props.fetchingCwe}
+ getFacetItemText={item => renderCWECategory(this.state.standards, item)}
+ getSearchResultKey={item => item}
+ getSearchResultText={item => renderCWECategory(this.state.standards, item)}
+ loadSearchResultCount={this.loadCWESearchResultCount}
+ onChange={this.props.onChange}
+ onSearch={this.handleCWESearch}
+ onToggle={this.props.onToggle}
+ open={this.props.cweOpen}
+ property="cwe"
+ query={omit(this.props.query, 'cwe')}
+ renderFacetItem={item => renderCWECategory(this.state.standards, item)}
+ renderSearchResult={(item, query) =>
+ highlightTerm(renderCWECategory(this.state.standards, item), query)
+ }
+ searchPlaceholder={translate('search.search_for_cwe')}
+ stats={this.props.cweStats}
+ values={this.props.cwe}
+ />
</>
);
}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/StatusFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/StatusFacet.tsx
index f9cb69866c7..43291fe15a2 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/StatusFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/StatusFacet.tsx
@@ -31,7 +31,6 @@ import MultipleSelectionHint from '../../../components/facet/MultipleSelectionHi
interface Props {
fetching: boolean;
- loading?: boolean;
onChange: (changes: Partial<Query>) => void;
onToggle: (property: string) => void;
open: boolean;
@@ -85,7 +84,6 @@ export default class StatusFacet extends React.PureComponent<Props> {
disabled={stat === 0 && !active}
halfWidth={true}
key={status}
- loading={this.props.loading}
name={<StatusHelper resolution={undefined} status={status} />}
onClick={this.handleItemClick}
stat={formatFacetStat(stat)}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/TagFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/TagFacet.tsx
index f68af7618f4..df2fcc7a898 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/TagFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/TagFacet.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { omit } from 'lodash';
import { Query } from '../utils';
import { searchIssueTags } from '../../../api/issues';
import * as theme from '../../../app/theme';
@@ -30,11 +31,12 @@ import { highlightTerm } from '../../../helpers/search';
interface Props {
component: Component | undefined;
fetching: boolean;
- loading?: boolean;
+ loadSearchResultCount: (changes: Partial<Query>) => Promise<number>;
onChange: (changes: Partial<Query>) => void;
onToggle: (property: string) => void;
open: boolean;
organization: string | undefined;
+ query: Query;
stats: { [x: string]: number } | undefined;
tags: string[];
}
@@ -54,6 +56,10 @@ export default class TagFacet extends React.PureComponent<Props> {
return tag;
};
+ loadSearchResultCount = (tag: string) => {
+ return this.props.loadSearchResultCount({ tags: [tag] });
+ };
+
renderTag = (tag: string) => {
return (
<>
@@ -72,17 +78,19 @@ export default class TagFacet extends React.PureComponent<Props> {
render() {
return (
- <ListStyleFacet
+ <ListStyleFacet<string>
facetHeader={translate('issues.facet.tags')}
fetching={this.props.fetching}
getFacetItemText={this.getTagName}
getSearchResultKey={tag => tag}
getSearchResultText={tag => tag}
+ loadSearchResultCount={this.loadSearchResultCount}
onChange={this.props.onChange}
onSearch={this.handleSearch}
onToggle={this.props.onToggle}
open={this.props.open}
property="tags"
+ query={omit(this.props.query, 'tags')}
renderFacetItem={this.renderTag}
renderSearchResult={this.renderSearchResult}
searchPlaceholder={translate('search.search_for_tags')}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.tsx
index e3cb845070a..6dced6ed39c 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.tsx
@@ -31,7 +31,6 @@ import MultipleSelectionHint from '../../../components/facet/MultipleSelectionHi
interface Props {
fetching: boolean;
- loading?: boolean;
onChange: (changes: Partial<Query>) => void;
onToggle: (property: string) => void;
open: boolean;
@@ -94,7 +93,6 @@ export default class TypeFacet extends React.PureComponent<Props> {
active={active}
disabled={stat === 0 && !active}
key={type}
- loading={this.props.loading}
name={
<span>
<IssueTypeIcon query={type} /> {translate('issue.type', type)}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/AssigneeFacet-test.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/AssigneeFacet-test.tsx
index 0d797101882..1fa98d7098f 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/AssigneeFacet-test.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/AssigneeFacet-test.tsx
@@ -20,56 +20,26 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import AssigneeFacet, { Props } from '../AssigneeFacet';
+import { Query } from '../../utils';
jest.mock('../../../../store/rootReducer', () => ({}));
-const renderAssigneeFacet = (props?: Partial<Props>) =>
- shallow(
- <AssigneeFacet
- assigned={true}
- assignees={[]}
- component={undefined}
- fetching={false}
- onChange={jest.fn()}
- onToggle={jest.fn()}
- open={true}
- organization={undefined}
- referencedUsers={{ foo: { avatar: 'avatart-foo', name: 'name-foo' } }}
- stats={{ '': 5, foo: 13, bar: 7, baz: 6 }}
- {...props}
- />
- );
-
it('should render', () => {
- expect(renderAssigneeFacet()).toMatchSnapshot();
-});
-
-it('should render without stats', () => {
- expect(renderAssigneeFacet({ stats: undefined })).toMatchSnapshot();
-});
-
-it('should select unassigned', () => {
- expect(renderAssigneeFacet({ assigned: false })).toMatchSnapshot();
-});
-
-it('should select user', () => {
expect(renderAssigneeFacet({ assignees: ['foo'] })).toMatchSnapshot();
});
-it('should render footer select option', () => {
- const wrapper = renderAssigneeFacet();
+it('should select unassigned', () => {
expect(
- (wrapper.instance() as AssigneeFacet).renderOption({ avatar: 'avatar-foo', label: 'name-foo' })
- ).toMatchSnapshot();
+ renderAssigneeFacet({ assigned: false })
+ .find('ListStyleFacet')
+ .prop('values')
+ ).toEqual(['']);
});
it('should call onChange', () => {
const onChange = jest.fn();
const wrapper = renderAssigneeFacet({ assignees: ['foo'], onChange });
- const itemOnClick = wrapper
- .find('FacetItem')
- .first()
- .prop<Function>('onClick');
+ const itemOnClick = wrapper.find('ListStyleFacet').prop<Function>('onItemClick');
itemOnClick('');
expect(onChange).lastCalledWith({ assigned: false, assignees: [] });
@@ -81,11 +51,22 @@ it('should call onChange', () => {
expect(onChange).lastCalledWith({ assigned: true, assignees: ['baz', 'foo'] });
});
-it('should call onToggle', () => {
- const onToggle = jest.fn();
- const wrapper = renderAssigneeFacet({ onToggle });
- const headerOnClick = wrapper.find('FacetHeader').prop<Function>('onClick');
-
- headerOnClick();
- expect(onToggle).lastCalledWith('assignees');
-});
+function renderAssigneeFacet(props?: Partial<Props>) {
+ return shallow(
+ <AssigneeFacet
+ assigned={true}
+ assignees={[]}
+ component={undefined}
+ fetching={false}
+ loadSearchResultCount={jest.fn()}
+ onChange={jest.fn()}
+ onToggle={jest.fn()}
+ open={true}
+ organization={undefined}
+ query={{} as Query}
+ referencedUsers={{ foo: { avatar: 'avatart-foo', name: 'name-foo' } }}
+ stats={{ '': 5, foo: 13, bar: 7, baz: 6 }}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-test.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-test.tsx
index 7b658ae3db0..bf48d87f948 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-test.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-test.tsx
@@ -29,6 +29,7 @@ const renderSidebar = (props?: Partial<Props>) =>
<Sidebar
component={undefined}
facets={{}}
+ loadSearchResultCount={jest.fn()}
loadingFacets={{}}
myIssues={false}
onFacetToggle={jest.fn()}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/StandardFacet-test.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/StandardFacet-test.tsx
index 41d59a42a92..982c62b04c3 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/StandardFacet-test.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/StandardFacet-test.tsx
@@ -21,6 +21,7 @@ import * as React from 'react';
import { shallow } from 'enzyme';
import StandardFacet, { Props } from '../StandardFacet';
import { click } from '../../../../helpers/testUtils';
+import { Query } from '../../utils';
it('should render closed', () => {
expect(shallowRender()).toMatchSnapshot();
@@ -84,7 +85,6 @@ it('should select items', () => {
selectAndCheck('owaspTop10', 'a1');
selectAndCheck('owaspTop10', 'a1', true, ['a1', 'a3']);
selectAndCheck('sansTop25', 'foo');
- selectAndCheck('cwe', '173');
function selectAndCheck(facet: string, value: string, multiple = false, expectedValue = [value]) {
wrapper
@@ -100,8 +100,6 @@ it('should toggle sub-facets', () => {
const wrapper = shallowRender({ onToggle, open: true });
click(wrapper.find('FacetBox[property="owaspTop10"]').children('FacetHeader'));
expect(onToggle).lastCalledWith('owaspTop10');
- click(wrapper.find('FacetBox[property="cwe"]').children('FacetHeader'));
- expect(onToggle).lastCalledWith('cwe');
click(wrapper.find('FacetBox[property="sansTop25"]').children('FacetHeader'));
expect(onToggle).lastCalledWith('sansTop25');
});
@@ -124,7 +122,6 @@ it('should display correct selection', () => {
'Unknown CWE'
]);
checkValues('owaspTop10', ['A1 - a1 title', 'A3', 'Not OWAPS']);
- checkValues('cwe', ['CWE-42 - cwe-42 title', 'CWE-1111', 'Unknown CWE']);
checkValues('sansTop25', ['Risky Resource Management', 'foo']);
function checkValues(property: string, values: string[]) {
@@ -137,16 +134,6 @@ it('should display correct selection', () => {
}
});
-it('should search CWE', () => {
- const wrapper = shallowRender({ open: true, cwe: ['42'], cweOpen: true });
- wrapper
- .find('FacetBox[property="cwe"]')
- .find('SearchBox')
- .prop<Function>('onChange')('unkn');
- wrapper.update();
- expect(wrapper).toMatchSnapshot();
-});
-
function shallowRender(props: Partial<Props> = {}) {
const wrapper = shallow(
<StandardFacet
@@ -156,12 +143,14 @@ function shallowRender(props: Partial<Props> = {}) {
fetchingCwe={false}
fetchingOwaspTop10={false}
fetchingSansTop25={false}
+ loadSearchResultCount={jest.fn()}
onChange={jest.fn()}
onToggle={jest.fn()}
open={false}
owaspTop10={[]}
owaspTop10Open={false}
owaspTop10Stats={{}}
+ query={{} as Query}
sansTop25={[]}
sansTop25Open={false}
sansTop25Stats={{}}
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.tsx.snap
index 1ab0e42e2a6..5725fdffcb5 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.tsx.snap
@@ -1,321 +1,39 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should render 1`] = `
-<FacetBox
+<ListStyleFacet
+ facetHeader="issues.facet.assignees"
+ fetching={false}
+ getFacetItemText={[Function]}
+ getSearchResultKey={[Function]}
+ getSearchResultText={[Function]}
+ getSortedItems={[Function]}
+ loadSearchResultCount={[Function]}
+ maxInitialItems={15}
+ maxItems={100}
+ onChange={[MockFunction]}
+ onClear={[Function]}
+ onItemClick={[Function]}
+ onSearch={[Function]}
+ onToggle={[MockFunction]}
+ open={true}
property="assignees"
->
- <FacetHeader
- name="issues.facet.assignees"
- onClear={[Function]}
- onClick={[Function]}
- open={true}
- values={Array []}
- />
- <DeferredSpinner
- loading={false}
- timeout={100}
- />
- <React.Fragment>
- <SearchBox
- autoFocus={true}
- className="little-spacer-top spacer-bottom"
- loading={false}
- minLength={2}
- onChange={[Function]}
- placeholder="search.search_for_users"
- value=""
- />
- <FacetItemsList>
- <FacetItem
- active={false}
- disabled={false}
- halfWidth={false}
- loading={false}
- name="unassigned"
- onClick={[Function]}
- stat="5"
- tooltip="unassigned"
- value=""
- />
- <FacetItem
- active={false}
- disabled={false}
- halfWidth={false}
- key="foo"
- loading={false}
- name={
- <span>
- <Connect(Avatar)
- className="little-spacer-right"
- hash="avatart-foo"
- name="name-foo"
- size={16}
- />
- name-foo
- </span>
- }
- onClick={[Function]}
- stat="13"
- tooltip="name-foo"
- value="foo"
- />
- <FacetItem
- active={false}
- disabled={false}
- halfWidth={false}
- key="bar"
- loading={false}
- name="bar"
- onClick={[Function]}
- stat="7"
- tooltip="bar"
- value="bar"
- />
- <FacetItem
- active={false}
- disabled={false}
- halfWidth={false}
- key="baz"
- loading={false}
- name="baz"
- onClick={[Function]}
- stat="6"
- tooltip="baz"
- value="baz"
- />
- </FacetItemsList>
- <MultipleSelectionHint
- options={4}
- values={0}
- />
- </React.Fragment>
-</FacetBox>
-`;
-
-exports[`should render footer select option 1`] = `
-<span>
- <Connect(Avatar)
- className="little-spacer-right"
- hash="avatar-foo"
- name="name-foo"
- size={16}
- />
- name-foo
-</span>
-`;
-
-exports[`should render without stats 1`] = `
-<FacetBox
- property="assignees"
->
- <FacetHeader
- name="issues.facet.assignees"
- onClear={[Function]}
- onClick={[Function]}
- open={true}
- values={Array []}
- />
- <DeferredSpinner
- loading={false}
- timeout={100}
- />
- <React.Fragment>
- <MultipleSelectionHint
- options={0}
- values={0}
- />
- </React.Fragment>
-</FacetBox>
-`;
-
-exports[`should select unassigned 1`] = `
-<FacetBox
- property="assignees"
->
- <FacetHeader
- name="issues.facet.assignees"
- onClear={[Function]}
- onClick={[Function]}
- open={true}
- values={
- Array [
- "unassigned",
- ]
- }
- />
- <DeferredSpinner
- loading={false}
- timeout={100}
- />
- <React.Fragment>
- <SearchBox
- autoFocus={true}
- className="little-spacer-top spacer-bottom"
- loading={false}
- minLength={2}
- onChange={[Function]}
- placeholder="search.search_for_users"
- value=""
- />
- <FacetItemsList>
- <FacetItem
- active={true}
- disabled={false}
- halfWidth={false}
- loading={false}
- name="unassigned"
- onClick={[Function]}
- stat="5"
- tooltip="unassigned"
- value=""
- />
- <FacetItem
- active={false}
- disabled={false}
- halfWidth={false}
- key="foo"
- loading={false}
- name={
- <span>
- <Connect(Avatar)
- className="little-spacer-right"
- hash="avatart-foo"
- name="name-foo"
- size={16}
- />
- name-foo
- </span>
- }
- onClick={[Function]}
- stat="13"
- tooltip="name-foo"
- value="foo"
- />
- <FacetItem
- active={false}
- disabled={false}
- halfWidth={false}
- key="bar"
- loading={false}
- name="bar"
- onClick={[Function]}
- stat="7"
- tooltip="bar"
- value="bar"
- />
- <FacetItem
- active={false}
- disabled={false}
- halfWidth={false}
- key="baz"
- loading={false}
- name="baz"
- onClick={[Function]}
- stat="6"
- tooltip="baz"
- value="baz"
- />
- </FacetItemsList>
- <MultipleSelectionHint
- options={4}
- values={0}
- />
- </React.Fragment>
-</FacetBox>
-`;
-
-exports[`should select user 1`] = `
-<FacetBox
- property="assignees"
->
- <FacetHeader
- name="issues.facet.assignees"
- onClear={[Function]}
- onClick={[Function]}
- open={true}
- values={
- Array [
- "name-foo",
- ]
+ query={Object {}}
+ renderFacetItem={[Function]}
+ renderSearchResult={[Function]}
+ searchPlaceholder="search.search_for_users"
+ stats={
+ Object {
+ "": 5,
+ "bar": 7,
+ "baz": 6,
+ "foo": 13,
}
- />
- <DeferredSpinner
- loading={false}
- timeout={100}
- />
- <React.Fragment>
- <SearchBox
- autoFocus={true}
- className="little-spacer-top spacer-bottom"
- loading={false}
- minLength={2}
- onChange={[Function]}
- placeholder="search.search_for_users"
- value=""
- />
- <FacetItemsList>
- <FacetItem
- active={false}
- disabled={false}
- halfWidth={false}
- loading={false}
- name="unassigned"
- onClick={[Function]}
- stat="5"
- tooltip="unassigned"
- value=""
- />
- <FacetItem
- active={true}
- disabled={false}
- halfWidth={false}
- key="foo"
- loading={false}
- name={
- <span>
- <Connect(Avatar)
- className="little-spacer-right"
- hash="avatart-foo"
- name="name-foo"
- size={16}
- />
- name-foo
- </span>
- }
- onClick={[Function]}
- stat="13"
- tooltip="name-foo"
- value="foo"
- />
- <FacetItem
- active={false}
- disabled={false}
- halfWidth={false}
- key="bar"
- loading={false}
- name="bar"
- onClick={[Function]}
- stat="7"
- tooltip="bar"
- value="bar"
- />
- <FacetItem
- active={false}
- disabled={false}
- halfWidth={false}
- key="baz"
- loading={false}
- name="baz"
- onClick={[Function]}
- stat="6"
- tooltip="baz"
- value="baz"
- />
- </FacetItemsList>
- <MultipleSelectionHint
- options={4}
- values={1}
- />
- </React.Fragment>
-</FacetBox>
+ }
+ values={
+ Array [
+ "foo",
+ ]
+ }
+/>
`;
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/StandardFacet-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/StandardFacet-test.tsx.snap
index 939fb386fd6..363e37abfbe 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/StandardFacet-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/StandardFacet-test.tsx.snap
@@ -163,166 +163,37 @@ exports[`should render sub-facets 1`] = `
/>
</React.Fragment>
</FacetBox>
- <FacetBox
- className="is-inner"
- property="cwe"
- >
- <FacetHeader
- name="issues.facet.cwe"
- onClick={[Function]}
- open={true}
- values={
- Array [
- "CWE-42 - cwe-42 title",
- ]
- }
- />
- <DeferredSpinner
- loading={false}
- timeout={100}
- />
- <React.Fragment>
- <SearchBox
- autoFocus={true}
- className="little-spacer-top spacer-bottom"
- onChange={[Function]}
- placeholder="search.search_for_cwe"
- value=""
- />
- <FacetItemsList>
- <FacetItem
- active={true}
- disabled={false}
- halfWidth={false}
- key="42"
- loading={false}
- name="CWE-42 - cwe-42 title"
- onClick={[Function]}
- stat="5"
- tooltip="CWE-42 - cwe-42 title"
- value="42"
- />
- <FacetItem
- active={false}
- disabled={false}
- halfWidth={false}
- key="173"
- loading={false}
- name="CWE-173"
- onClick={[Function]}
- stat="3"
- tooltip="CWE-173"
- value="173"
- />
- </FacetItemsList>
- <MultipleSelectionHint
- options={2}
- values={1}
- />
- </React.Fragment>
- </FacetBox>
- </React.Fragment>
-</FacetBox>
-`;
-
-exports[`should search CWE 1`] = `
-<FacetBox
- property="standards"
->
- <FacetHeader
- name="issues.facet.standards"
- onClear={[Function]}
- onClick={[Function]}
- open={true}
- values={
- Array [
- "CWE-42 - cwe-42 title",
- ]
- }
- />
- <React.Fragment>
- <FacetBox
- className="is-inner"
- property="owaspTop10"
- >
- <FacetHeader
- name="issues.facet.owaspTop10"
- onClick={[Function]}
- open={false}
- values={Array []}
- />
- <DeferredSpinner
- loading={false}
- timeout={100}
- />
- </FacetBox>
- <FacetBox
- className="is-inner"
- property="sansTop25"
- >
- <FacetHeader
- name="issues.facet.sansTop25"
- onClick={[Function]}
- open={false}
- values={Array []}
- />
- <DeferredSpinner
- loading={false}
- timeout={100}
- />
- </FacetBox>
- <FacetBox
+ <ListStyleFacet
className="is-inner"
+ facetHeader="issues.facet.cwe"
+ fetching={false}
+ getFacetItemText={[Function]}
+ getSearchResultKey={[Function]}
+ getSearchResultText={[Function]}
+ loadSearchResultCount={[Function]}
+ maxInitialItems={15}
+ maxItems={100}
+ onChange={[MockFunction]}
+ onSearch={[Function]}
+ onToggle={[MockFunction]}
+ open={true}
property="cwe"
- >
- <FacetHeader
- name="issues.facet.cwe"
- onClick={[Function]}
- open={true}
- values={
- Array [
- "CWE-42 - cwe-42 title",
- ]
+ query={Object {}}
+ renderFacetItem={[Function]}
+ renderSearchResult={[Function]}
+ searchPlaceholder="search.search_for_cwe"
+ stats={
+ Object {
+ "173": 3,
+ "42": 5,
}
- />
- <DeferredSpinner
- loading={false}
- timeout={100}
- />
- <React.Fragment>
- <SearchBox
- autoFocus={true}
- className="little-spacer-top spacer-bottom"
- onChange={[Function]}
- placeholder="search.search_for_cwe"
- value="unkn"
- />
- <FacetItemsList>
- <FacetItem
- active={false}
- disabled={false}
- halfWidth={false}
- key="unknown"
- loading={false}
- name={
- <React.Fragment>
- <mark>
- Unkn
- </mark>
- own CWE
- </React.Fragment>
- }
- onClick={[Function]}
- tooltip="Unknown CWE"
- value="unknown"
- />
- </FacetItemsList>
- <MultipleSelectionHint
- options={0}
- values={1}
- />
- </React.Fragment>
- </FacetBox>
+ }
+ values={
+ Array [
+ "42",
+ ]
+ }
+ />
</React.Fragment>
</FacetBox>
`;