diff options
author | Pascal Mugnier <pascal.mugnier@sonarsource.com> | 2018-07-30 14:49:25 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2018-08-02 20:21:32 +0200 |
commit | d0242b05cdd2c419055a162d56fc6f95c930cca8 (patch) | |
tree | 118c1b5e9430c10018d63a8520cb6dd4aabb69d2 /server/sonar-web/src/main/js/apps/issues | |
parent | 2d10f95a8da89ea498eab5a7fe5a38ed9af23849 (diff) | |
download | sonarqube-d0242b05cdd2c419055a162d56fc6f95c930cca8.tar.gz sonarqube-d0242b05cdd2c419055a162d56fc6f95c930cca8.zip |
SONAR-9178 Make spinner noticeable on loading projects issues or measure
Diffstat (limited to 'server/sonar-web/src/main/js/apps/issues')
23 files changed, 142 insertions, 29 deletions
diff --git a/server/sonar-web/src/main/js/apps/issues/components/App.tsx b/server/sonar-web/src/main/js/apps/issues/components/App.tsx index 384faec119a..dd2288d8346 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/App.tsx @@ -20,7 +20,7 @@ import * as React from 'react'; import Helmet from 'react-helmet'; import * as key from 'keymaster'; -import { keyBy, union, without } from 'lodash'; +import { keyBy, omit, union, without } from 'lodash'; import * as PropTypes from 'prop-types'; import BulkChangeModal from './BulkChangeModal'; import ComponentBreadcrumbs from './ComponentBreadcrumbs'; @@ -75,6 +75,7 @@ import DropdownIcon from '../../../components/icons-components/DropdownIcon'; import { isSonarCloud } from '../../../helpers/system'; import '../../../components/search-navigator.css'; import '../styles.css'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; interface FetchIssuesPromise { components: ReferencedComponent[]; @@ -104,6 +105,7 @@ export interface State { issues: Issue[]; lastChecked?: string; loading: boolean; + loadingFacets: { [key: string]: boolean }; locationsNavigator: boolean; myIssues: boolean; openFacets: { [facet: string]: boolean }; @@ -136,6 +138,7 @@ export default class App extends React.PureComponent<Props, State> { facets: {}, issues: [], loading: true, + loadingFacets: {}, locationsNavigator: false, myIssues: props.myIssues || areMyIssuesSelected(props.location.query), openFacets: { severities: true, types: true }, @@ -571,16 +574,21 @@ export default class App extends React.PureComponent<Props, State> { fetchFacet = (facet: string) => { const requestOrganizations = facet === 'projects'; + this.setState(state => ({ loadingFacets: { ...state.loadingFacets, [facet]: true } })); return this.fetchIssues({ ps: 1, facets: mapFacet(facet) }, false, requestOrganizations).then( ({ facets, ...other }) => { if (this.mounted) { this.setState(state => ({ facets: { ...state.facets, ...parseFacets(facets) }, + loadingFacets: omit(state.loadingFacets, facet), referencedComponents: { ...state.referencedComponents, ...keyBy(other.components, 'uuid') }, - referencedLanguages: { ...state.referencedLanguages, ...keyBy(other.languages, 'key') }, + referencedLanguages: { + ...state.referencedLanguages, + ...keyBy(other.languages, 'key') + }, referencedRules: { ...state.referencedRules, ...keyBy(other.rules, 'key') }, referencedUsers: { ...state.referencedUsers, ...keyBy(other.users, 'login') } })); @@ -869,6 +877,7 @@ export default class App extends React.PureComponent<Props, State> { component={component} facets={this.state.facets} loading={this.state.loading} + loadingFacets={this.state.loadingFacets} myIssues={this.state.myIssues} onFacetToggle={this.handleFacetToggle} onFilterChange={this.handleFilterChange} @@ -1004,6 +1013,31 @@ export default class App extends React.PureComponent<Props, State> { ); } + renderPage() { + const { openIssue } = this.state; + return ( + <div className="layout-page-main-inner"> + <DeferredSpinner loading={this.state.loading}> + {openIssue ? ( + <IssuesSourceViewer + branchLike={fillBranchLike(openIssue.branch, openIssue.pullRequest)} + loadIssues={this.fetchIssuesForComponent} + locationsNavigator={this.state.locationsNavigator} + onIssueChange={this.handleIssueChange} + onIssueSelect={this.openIssue} + onLocationSelect={this.selectLocation} + openIssue={openIssue} + selectedFlowIndex={this.state.selectedFlowIndex} + selectedLocationIndex={this.state.selectedLocationIndex} + /> + ) : ( + this.renderList() + )} + </DeferredSpinner> + </div> + ); + } + render() { const { component } = this.props; const { openIssue, paging } = this.state; @@ -1038,7 +1072,6 @@ export default class App extends React.PureComponent<Props, State> { !this.props.component && (!isSonarCloud() || this.props.myIssues) )} - loading={this.state.loading} onReload={this.handleReload} paging={paging} selectedIndex={selectedIndex} @@ -1049,25 +1082,7 @@ export default class App extends React.PureComponent<Props, State> { </div> </div> - <div className="layout-page-main-inner"> - <div> - {openIssue ? ( - <IssuesSourceViewer - branchLike={fillBranchLike(openIssue.branch, openIssue.pullRequest)} - loadIssues={this.fetchIssuesForComponent} - locationsNavigator={this.state.locationsNavigator} - onIssueChange={this.handleIssueChange} - onIssueSelect={this.openIssue} - onLocationSelect={this.selectLocation} - openIssue={openIssue} - selectedFlowIndex={this.state.selectedFlowIndex} - selectedLocationIndex={this.state.selectedLocationIndex} - /> - ) : ( - this.renderList() - )} - </div> - </div> + {this.renderPage()} </div> </div> ); diff --git a/server/sonar-web/src/main/js/apps/issues/components/PageActions.tsx b/server/sonar-web/src/main/js/apps/issues/components/PageActions.tsx index 38e0821b0e9..330409caa95 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/PageActions.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/PageActions.tsx @@ -21,14 +21,12 @@ import * as React from 'react'; import IssuesCounter from './IssuesCounter'; import ReloadButton from './ReloadButton'; import { HomePageType, Paging } from '../../../app/types'; -import DeferredSpinner from '../../../components/common/DeferredSpinner'; import HomePageSelect from '../../../components/controls/HomePageSelect'; import { translate } from '../../../helpers/l10n'; import { isSonarCloud } from '../../../helpers/system'; interface Props { canSetHome: boolean; - loading: boolean; onReload: () => void; paging: Paging | undefined; selectedIndex: number | undefined; @@ -61,9 +59,7 @@ export default class PageActions extends React.PureComponent<Props> { {this.renderShortcuts()} <div className="issues-page-actions"> - <DeferredSpinner className="issues-main-header-spinner" loading={this.props.loading}> - <ReloadButton onClick={this.props.onReload} /> - </DeferredSpinner> + <ReloadButton onClick={this.props.onReload} /> {paging != null && ( <IssuesCounter className="spacer-left" current={selectedIndex} total={paging.total} /> )} 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 7e671e03616..73c22e1498e 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 @@ -28,11 +28,13 @@ import FacetItem from '../../../components/facet/FacetItem'; import FacetItemsList from '../../../components/facet/FacetItemsList'; import Avatar from '../../../components/ui/Avatar'; import { translate } from '../../../helpers/l10n'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; export interface Props { assigned: boolean; assignees: string[]; component: Component | undefined; + fetching: boolean; loading?: boolean; onChange: (changes: Partial<Query>) => void; onToggle: (property: string) => void; @@ -201,6 +203,7 @@ export default class AssigneeFacet extends React.PureComponent<Props> { values={this.getValues()} /> + <DeferredSpinner loading={this.props.fetching} /> {this.props.open && this.renderList()} {this.props.open && this.renderFooter()} </FacetBox> 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 9ea5200d8d5..a163c43c363 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 @@ -25,8 +25,10 @@ import FacetHeader from '../../../components/facet/FacetHeader'; import FacetItem from '../../../components/facet/FacetItem'; import FacetItemsList from '../../../components/facet/FacetItemsList'; import { translate } from '../../../helpers/l10n'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; interface Props { + fetching: boolean; loading?: boolean; onChange: (changes: Partial<Query>) => void; onToggle: (property: string) => void; @@ -107,6 +109,7 @@ export default class AuthorFacet extends React.PureComponent<Props> { values={this.props.authors} /> + <DeferredSpinner loading={this.props.fetching} /> {this.props.open && this.renderList()} </FacetBox> ); 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 7d242ebf048..db6e7f6eff1 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 @@ -33,6 +33,7 @@ import DateRangeInput from '../../../components/controls/DateRangeInput'; import { isSameDay, parseDate } from '../../../helpers/dates'; import { translate } from '../../../helpers/l10n'; import { formatMeasure } from '../../../helpers/measures'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; interface Props { component: Component | undefined; @@ -40,6 +41,7 @@ interface Props { createdAt: string; createdBefore: Date | undefined; createdInLast: string; + fetching: boolean; loading?: boolean; onChange: (changes: Partial<Query>) => void; onToggle: (property: string) => void; @@ -290,6 +292,7 @@ export default class CreationDateFacet extends React.PureComponent<Props> { values={this.getValues()} /> + <DeferredSpinner loading={this.props.fetching} /> {this.props.open && this.renderInner()} </FacetBox> ); 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 248418cee17..b2aa50d02f5 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 @@ -27,8 +27,10 @@ import FacetItemsList from '../../../components/facet/FacetItemsList'; import QualifierIcon from '../../../components/icons-components/QualifierIcon'; import { translate } from '../../../helpers/l10n'; import { collapsePath } from '../../../helpers/path'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; interface Props { + fetching: boolean; directories: string[]; loading?: boolean; onChange: (changes: Partial<Query>) => void; @@ -125,6 +127,7 @@ export default class DirectoryFacet extends React.PureComponent<Props> { values={values} /> + <DeferredSpinner loading={this.props.fetching} /> {this.props.open && this.renderList()} </FacetBox> ); 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 28a798a2429..86063f1be60 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 @@ -27,8 +27,10 @@ import FacetItemsList from '../../../components/facet/FacetItemsList'; import QualifierIcon from '../../../components/icons-components/QualifierIcon'; import { translate } from '../../../helpers/l10n'; import { collapsePath } from '../../../helpers/path'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; interface Props { + fetching: boolean; files: string[]; loading?: boolean; onChange: (changes: Partial<Query>) => void; @@ -126,6 +128,7 @@ export default class FileFacet extends React.PureComponent<Props> { values={values} /> + <DeferredSpinner loading={this.props.fetching} /> {this.props.open && this.renderList()} </FacetBox> ); 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 2c63c463044..81585ed2f97 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 @@ -26,8 +26,10 @@ import FacetHeader from '../../../components/facet/FacetHeader'; import FacetItem from '../../../components/facet/FacetItem'; import FacetItemsList from '../../../components/facet/FacetItemsList'; import { translate } from '../../../helpers/l10n'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; interface Props { + fetching: boolean; languages: string[]; loading?: boolean; onChange: (changes: Partial<Query>) => void; @@ -130,6 +132,7 @@ export default class LanguageFacet extends React.PureComponent<Props> { values={values} /> + <DeferredSpinner loading={this.props.fetching} /> {this.props.open && this.renderList()} {this.props.open && this.renderFooter()} </FacetBox> 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 faf97f7d5ae..3f4ec1fb9a4 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 @@ -26,8 +26,10 @@ import FacetItem from '../../../components/facet/FacetItem'; import FacetItemsList from '../../../components/facet/FacetItemsList'; import QualifierIcon from '../../../components/icons-components/QualifierIcon'; import { translate } from '../../../helpers/l10n'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; interface Props { + fetching: boolean; loading?: boolean; modules: string[]; onChange: (changes: Partial<Query>) => void; @@ -124,6 +126,7 @@ export default class ModuleFacet extends React.PureComponent<Props> { values={values} /> + <DeferredSpinner loading={this.props.fetching} /> {this.props.open && this.renderList()} </FacetBox> ); 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 c684af5b1cd..5163f9683f0 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 @@ -30,10 +30,12 @@ import FacetFooter from '../../../components/facet/FacetFooter'; import Organization from '../../../components/shared/Organization'; import QualifierIcon from '../../../components/icons-components/QualifierIcon'; import { translate } from '../../../helpers/l10n'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; interface Props { component: Component | undefined; loading?: boolean; + fetching: boolean; onChange: (changes: Partial<Query>) => void; onToggle: (property: string) => void; open: boolean; @@ -192,7 +194,7 @@ export default class ProjectFacet extends React.PureComponent<Props> { open={this.props.open} values={values} /> - + <DeferredSpinner loading={this.props.fetching} /> {this.props.open && this.renderList()} {this.props.open && this.renderFooter()} </FacetBox> 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 1a6163d9124..1ad489a6e29 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 @@ -25,8 +25,10 @@ import FacetHeader from '../../../components/facet/FacetHeader'; import FacetItem from '../../../components/facet/FacetItem'; import FacetItemsList from '../../../components/facet/FacetItemsList'; import { translate } from '../../../helpers/l10n'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; interface Props { + fetching: boolean; loading?: boolean; onChange: (changes: Partial<Query>) => void; onToggle: (property: string) => void; @@ -124,6 +126,7 @@ export default class ResolutionFacet extends React.PureComponent<Props> { values={values} /> + <DeferredSpinner loading={this.props.fetching} /> {this.props.open && <FacetItemsList>{resolutions.map(this.renderItem)}</FacetItemsList>} </FacetBox> ); 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 e2db449dc3c..46f58bd9e4d 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 @@ -27,8 +27,10 @@ import FacetItem from '../../../components/facet/FacetItem'; import FacetItemsList from '../../../components/facet/FacetItemsList'; import FacetFooter from '../../../components/facet/FacetFooter'; import { translate } from '../../../helpers/l10n'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; interface Props { + fetching: boolean; languages: string[]; loading?: boolean; onChange: (changes: Partial<Query>) => void; @@ -144,6 +146,7 @@ export default class RuleFacet extends React.PureComponent<Props> { values={values} /> + <DeferredSpinner loading={this.props.fetching} /> {this.props.open && this.renderList()} {this.props.open && this.renderFooter()} </FacetBox> 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 5d2e61b5e58..dba8a5366bf 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 @@ -26,8 +26,10 @@ import FacetItem from '../../../components/facet/FacetItem'; import FacetItemsList from '../../../components/facet/FacetItemsList'; import SeverityHelper from '../../../components/shared/SeverityHelper'; import { translate } from '../../../helpers/l10n'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; interface Props { + fetching: boolean; loading?: boolean; onChange: (changes: Partial<Query>) => void; onToggle: (property: string) => void; @@ -104,6 +106,7 @@ export default class SeverityFacet extends React.PureComponent<Props> { values={values} /> + <DeferredSpinner loading={this.props.fetching} /> {this.props.open && <FacetItemsList>{severities.map(this.renderItem)}</FacetItemsList>} </FacetBox> ); 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 906e9245842..ace5779f36b 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 @@ -47,6 +47,7 @@ export interface Props { component: Component | undefined; facets: { [facet: string]: Facet }; loading?: boolean; + loadingFacets: { [key: string]: boolean }; myIssues: boolean; onFacetToggle: (property: string) => void; onFilterChange: (changes: Partial<Query>) => void; @@ -77,6 +78,7 @@ export default class Sidebar extends React.PureComponent<Props> { return ( <div className="search-navigator-facets-list"> <TypeFacet + fetching={this.props.loadingFacets.types === true} loading={this.props.loading} onChange={this.props.onFilterChange} onToggle={this.props.onFacetToggle} @@ -85,6 +87,7 @@ export default class Sidebar extends React.PureComponent<Props> { types={query.types} /> <SeverityFacet + fetching={this.props.loadingFacets.severities === true} loading={this.props.loading} onChange={this.props.onFilterChange} onToggle={this.props.onFacetToggle} @@ -93,6 +96,7 @@ export default class Sidebar extends React.PureComponent<Props> { stats={facets.severities} /> <ResolutionFacet + fetching={this.props.loadingFacets.resolutions === true} loading={this.props.loading} onChange={this.props.onFilterChange} onToggle={this.props.onFacetToggle} @@ -102,6 +106,7 @@ export default class Sidebar extends React.PureComponent<Props> { stats={facets.resolutions} /> <StatusFacet + fetching={this.props.loadingFacets.statuses === true} loading={this.props.loading} onChange={this.props.onFilterChange} onToggle={this.props.onFacetToggle} @@ -115,6 +120,7 @@ export default class Sidebar extends React.PureComponent<Props> { createdAt={query.createdAt} createdBefore={query.createdBefore} createdInLast={query.createdInLast} + fetching={this.props.loadingFacets.createdAt === true} loading={this.props.loading} onChange={this.props.onFilterChange} onToggle={this.props.onFacetToggle} @@ -123,6 +129,7 @@ export default class Sidebar extends React.PureComponent<Props> { stats={facets.createdAt} /> <LanguageFacet + fetching={this.props.loadingFacets.languages === true} languages={query.languages} loading={this.props.loading} onChange={this.props.onFilterChange} @@ -132,6 +139,7 @@ export default class Sidebar extends React.PureComponent<Props> { stats={facets.languages} /> <RuleFacet + fetching={this.props.loadingFacets.rules === true} languages={query.languages} loading={this.props.loading} onChange={this.props.onFilterChange} @@ -146,6 +154,9 @@ export default class Sidebar extends React.PureComponent<Props> { cwe={query.cwe} cweOpen={!!openFacets.cwe} cweStats={facets.cwe} + fetchingCwe={this.props.loadingFacets.cwe === true} + fetchingOwaspTop10={this.props.loadingFacets.owaspTop10 === true} + fetchingSansTop25={this.props.loadingFacets.sansTop25 === true} loading={this.props.loading} onChange={this.props.onFilterChange} onToggle={this.props.onFacetToggle} @@ -159,6 +170,7 @@ export default class Sidebar extends React.PureComponent<Props> { /> <TagFacet component={component} + fetching={this.props.loadingFacets.tags === true} loading={this.props.loading} onChange={this.props.onFilterChange} onToggle={this.props.onFacetToggle} @@ -170,6 +182,7 @@ export default class Sidebar extends React.PureComponent<Props> { {displayProjectsFacet && ( <ProjectFacet component={component} + fetching={this.props.loadingFacets.projects === true} loading={this.props.loading} onChange={this.props.onFilterChange} onToggle={this.props.onFacetToggle} @@ -182,6 +195,7 @@ export default class Sidebar extends React.PureComponent<Props> { )} {displayModulesFacet && ( <ModuleFacet + fetching={this.props.loadingFacets.modules === true} loading={this.props.loading} modules={query.modules} onChange={this.props.onFilterChange} @@ -194,6 +208,7 @@ export default class Sidebar extends React.PureComponent<Props> { {displayDirectoriesFacet && ( <DirectoryFacet directories={query.directories} + fetching={this.props.loadingFacets.directories === true} loading={this.props.loading} onChange={this.props.onFilterChange} onToggle={this.props.onFacetToggle} @@ -204,6 +219,7 @@ export default class Sidebar extends React.PureComponent<Props> { )} {displayFilesFacet && ( <FileFacet + fetching={this.props.loadingFacets.files === true} files={query.files} loading={this.props.loading} onChange={this.props.onFilterChange} @@ -218,6 +234,7 @@ export default class Sidebar extends React.PureComponent<Props> { assigned={query.assigned} assignees={query.assignees} component={component} + fetching={this.props.loadingFacets.assignees === true} loading={this.props.loading} onChange={this.props.onFilterChange} onToggle={this.props.onFacetToggle} @@ -230,6 +247,7 @@ export default class Sidebar extends React.PureComponent<Props> { {displayAuthorFacet && ( <AuthorFacet authors={query.authors} + fetching={this.props.loadingFacets.authors === true} loading={this.props.loading} onChange={this.props.onFilterChange} onToggle={this.props.onFacetToggle} 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 febd7e1cbdd..9670921d9e1 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 @@ -32,11 +32,15 @@ import { renderCWECategory, Standards } from '../../securityReports/utils'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; export interface Props { cwe: string[]; cweOpen: boolean; cweStats: { [x: string]: number } | undefined; + fetchingOwaspTop10: boolean; + fetchingSansTop25: boolean; + fetchingCwe: boolean; loading?: boolean; onChange: (changes: Partial<Query>) => void; onToggle: (property: string) => void; @@ -260,6 +264,7 @@ export default class StandardFacet extends React.PureComponent<Props, State> { renderOwaspTop10Category(this.state.standards, item) )} /> + <DeferredSpinner loading={this.props.fetchingOwaspTop10} /> {this.props.owaspTop10Open && this.renderOwaspTop10List()} </FacetBox> <FacetBox className="is-inner" property="sansTop25"> @@ -271,6 +276,7 @@ export default class StandardFacet extends React.PureComponent<Props, State> { renderSansTop25Category(this.state.standards, item) )} /> + <DeferredSpinner loading={this.props.fetchingSansTop25} /> {this.props.sansTop25Open && this.renderSansTop25List()} </FacetBox> <FacetBox className="is-inner" property="cwe"> @@ -280,6 +286,7 @@ export default class StandardFacet extends React.PureComponent<Props, State> { open={this.props.cweOpen} values={this.props.cwe.map(item => renderCWECategory(this.state.standards, item))} /> + <DeferredSpinner loading={this.props.fetchingCwe} /> {this.props.cweOpen && this.renderCWEList()} {this.props.cweOpen && this.renderCWESearch()} </FacetBox> 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 dea89105567..1eb4e060fc2 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 @@ -26,8 +26,10 @@ import FacetItem from '../../../components/facet/FacetItem'; import FacetItemsList from '../../../components/facet/FacetItemsList'; import StatusHelper from '../../../components/shared/StatusHelper'; import { translate } from '../../../helpers/l10n'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; interface Props { + fetching: boolean; loading?: boolean; onChange: (changes: Partial<Query>) => void; onToggle: (property: string) => void; @@ -104,6 +106,7 @@ export default class StatusFacet extends React.PureComponent<Props> { values={values} /> + <DeferredSpinner loading={this.props.fetching} /> {this.props.open && <FacetItemsList>{statuses.map(this.renderItem)}</FacetItemsList>} </FacetBox> ); 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 2db0f5c9118..c4a804d0e69 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 @@ -30,9 +30,11 @@ import FacetItem from '../../../components/facet/FacetItem'; import FacetItemsList from '../../../components/facet/FacetItemsList'; import TagsIcon from '../../../components/icons-components/TagsIcon'; import { translate } from '../../../helpers/l10n'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; interface Props { component: Component | undefined; + fetching: boolean; loading?: boolean; onChange: (changes: Partial<Query>) => void; onToggle: (property: string) => void; @@ -143,8 +145,8 @@ export default class TagFacet extends React.PureComponent<Props> { values={this.props.tags} /> + <DeferredSpinner loading={this.props.fetching} /> {this.props.open && this.renderList()} - {this.props.open && this.renderFooter()} </FacetBox> ); 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 62900a529a3..6246b462078 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 @@ -26,8 +26,10 @@ import FacetItem from '../../../components/facet/FacetItem'; import FacetItemsList from '../../../components/facet/FacetItemsList'; import IssueTypeIcon from '../../../components/ui/IssueTypeIcon'; import { translate } from '../../../helpers/l10n'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; interface Props { + fetching: boolean; loading?: boolean; onChange: (changes: Partial<Query>) => void; onToggle: (property: string) => void; @@ -118,6 +120,7 @@ export default class TypeFacet extends React.PureComponent<Props> { values={values} /> + <DeferredSpinner loading={this.props.fetching} /> {this.props.open && <FacetItemsList>{types.map(this.renderItem)}</FacetItemsList>} </FacetBox> ); 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 efed16ca848..64217b4de0b 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 @@ -29,6 +29,7 @@ const renderAssigneeFacet = (props?: Partial<Props>) => assigned={true} assignees={[]} component={undefined} + fetching={false} onChange={jest.fn()} onToggle={jest.fn()} open={true} 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 cbffca5d94c..7b658ae3db0 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={{}} + loadingFacets={{}} myIssues={false} onFacetToggle={jest.fn()} onFilterChange={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 f36d3c29176..815d6b03f93 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 @@ -153,6 +153,9 @@ function shallowRender(props: Partial<Props> = {}) { cwe={[]} cweOpen={false} cweStats={{}} + fetchingCwe={false} + fetchingOwaspTop10={false} + fetchingSansTop25={false} onChange={jest.fn()} onToggle={jest.fn()} open={false} 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 e28401150f4..406c6d77188 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 @@ -11,6 +11,10 @@ exports[`should render 1`] = ` open={true} values={Array []} /> + <DeferredSpinner + loading={false} + timeout={100} + /> <FacetItemsList> <FacetItem active={false} @@ -101,6 +105,10 @@ exports[`should render without stats 1`] = ` open={true} values={Array []} /> + <DeferredSpinner + loading={false} + timeout={100} + /> </FacetBox> `; @@ -119,6 +127,10 @@ exports[`should select unassigned 1`] = ` ] } /> + <DeferredSpinner + loading={false} + timeout={100} + /> <FacetItemsList> <FacetItem active={true} @@ -201,6 +213,10 @@ exports[`should select user 1`] = ` ] } /> + <DeferredSpinner + loading={false} + timeout={100} + /> <FacetItemsList> <FacetItem active={false} 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 1982863f389..42bf996ded1 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 @@ -25,6 +25,10 @@ exports[`should render empty sub-facet 1`] = ` open={true} values={Array []} /> + <DeferredSpinner + loading={false} + timeout={100} + /> <div className="search-navigator-facet-empty little-spacer-top" > @@ -65,6 +69,10 @@ exports[`should render sub-facets 1`] = ` ] } /> + <DeferredSpinner + loading={false} + timeout={100} + /> <FacetItemsList> <FacetItem active={false} @@ -106,6 +114,10 @@ exports[`should render sub-facets 1`] = ` ] } /> + <DeferredSpinner + loading={false} + timeout={100} + /> <FacetItemsList> <FacetItem active={false} @@ -147,6 +159,10 @@ exports[`should render sub-facets 1`] = ` ] } /> + <DeferredSpinner + loading={false} + timeout={100} + /> <FacetItemsList> <FacetItem active={true} |