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 | |
parent | 2d10f95a8da89ea498eab5a7fe5a38ed9af23849 (diff) | |
download | sonarqube-d0242b05cdd2c419055a162d56fc6f95c930cca8.tar.gz sonarqube-d0242b05cdd2c419055a162d56fc6f95c930cca8.zip |
SONAR-9178 Make spinner noticeable on loading projects issues or measure
33 files changed, 276 insertions, 204 deletions
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.js index a7cd534be4c..e3843c8bcd0 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.js @@ -35,6 +35,7 @@ import { enhanceComponent, isFileType, isViewType } from '../utils'; import { getProjectUrl } from '../../../helpers/urls'; import { isDiffMetric } from '../../../helpers/measures'; import { isSameBranchLike, getBranchLikeQuery } from '../../../helpers/branches'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; /*:: import type { Component, ComponentEnhanced, Paging, Period } from '../types'; */ /*:: import type { MeasureEnhanced } from '../../../components/measure/types'; */ /*:: import type { Metric } from '../../../store/metrics/actions'; */ @@ -319,7 +320,6 @@ export default class MeasureContent extends React.PureComponent { )} <PageActions current={selectedIdx != null && view !== 'treemap' ? selectedIdx + 1 : null} - loading={this.props.loading} isFile={isFile} paging={this.state.paging} totalLoadedComponents={this.state.components.length} @@ -343,7 +343,9 @@ export default class MeasureContent extends React.PureComponent { metric={metric} secondaryMeasure={this.props.secondaryMeasure} /> - {isFileType(component) ? this.renderCode() : this.renderMeasure()} + <DeferredSpinner loading={this.props.loading}> + {isFileType(component) ? this.renderCode() : this.renderMeasure()} + </DeferredSpinner> </div> )} </div> diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.js index 77cb425f7a9..83e59016ccb 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.js @@ -28,6 +28,7 @@ import SourceViewer from '../../../components/SourceViewer/SourceViewer'; import { getComponentLeaves } from '../../../api/components'; import { enhanceComponent, getBubbleMetrics, isFileType } from '../utils'; import { getBranchLikeQuery } from '../../../helpers/branches'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; /*:: import type { Component, ComponentEnhanced, Paging, Period } from '../types'; */ /*:: import type { Metric } from '../../../store/metrics/actions'; */ @@ -163,7 +164,6 @@ export default class MeasureOverview extends React.PureComponent { <PageActions current={this.state.components.length} isFile={isFile} - loading={this.props.loading} paging={this.state.paging} /> </div> @@ -175,6 +175,7 @@ export default class MeasureOverview extends React.PureComponent { <LeakPeriodLegend className="pull-right" component={component} period={leakPeriod} /> )} </div> + <DeferredSpinner loading={this.props.loading} /> {!this.props.loading && this.renderContent()} </div> </div> diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/PageActions.js b/server/sonar-web/src/main/js/apps/component-measures/components/PageActions.js index c68d7090f13..4b26994fa81 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/PageActions.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/PageActions.js @@ -20,13 +20,11 @@ // @flow import React from 'react'; import FilesCounter from './FilesCounter'; -import DeferredSpinner from '../../../components/common/DeferredSpinner'; import { translate } from '../../../helpers/l10n'; /*:: import type { Paging } from '../types'; */ /*:: type Props = {| current: ?number, - loading: boolean, isFile: ?boolean, paging: ?Paging, totalLoadedComponents?: number, @@ -41,9 +39,6 @@ export default function PageActions(props /*: Props */) { {!isFile && showShortcuts && renderShortcuts()} {isFile && paging && renderFileShortcuts()} <div className="measure-details-page-actions"> - <DeferredSpinner loading={props.loading}> - <i className="spinner-placeholder" /> - </DeferredSpinner> {paging != null && ( <FilesCounter className="spacer-left" diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/PageActions-test.js b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/PageActions-test.js index 36a0b3eb152..067ecb4d78d 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/PageActions-test.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/PageActions-test.js @@ -23,14 +23,12 @@ import PageActions from '../PageActions'; it('should display correctly for a project', () => { expect( - shallow(<PageActions loading={true} isFile={false} view="list" totalLoadedComponents={20} />) + shallow(<PageActions isFile={false} totalLoadedComponents={20} view="list" />) ).toMatchSnapshot(); }); it('should display correctly for a file', () => { - const wrapper = shallow( - <PageActions loading={false} isFile={true} view="tree" totalLoadedComponents={10} /> - ); + const wrapper = shallow(<PageActions isFile={true} totalLoadedComponents={10} view="tree" />); expect(wrapper).toMatchSnapshot(); wrapper.setProps({ paging: { total: 100 } }); expect(wrapper).toMatchSnapshot(); @@ -38,7 +36,7 @@ it('should display correctly for a file', () => { it('should not display shortcuts for treemap', () => { expect( - shallow(<PageActions loading={true} isFile={false} view="treemap" totalLoadedComponents={20} />) + shallow(<PageActions isFile={false} totalLoadedComponents={20} view="treemap" />) ).toMatchSnapshot(); }); @@ -47,11 +45,10 @@ it('should display the total of files', () => { shallow( <PageActions current={12} - loading={true} isFile={false} - view="treemap" - totalLoadedComponents={20} paging={{ total: 120 }} + totalLoadedComponents={20} + view="treemap" /> ) ).toMatchSnapshot(); @@ -59,11 +56,10 @@ it('should display the total of files', () => { shallow( <PageActions current={12} - loading={false} isFile={true} - view="list" - totalLoadedComponents={20} paging={{ total: 120 }} + totalLoadedComponents={20} + view="list" /> ) ).toMatchSnapshot(); diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/PageActions-test.js.snap b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/PageActions-test.js.snap index c33278f92af..9849310b4c2 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/PageActions-test.js.snap +++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/PageActions-test.js.snap @@ -6,16 +6,7 @@ exports[`should display correctly for a file 1`] = ` > <div className="measure-details-page-actions" - > - <DeferredSpinner - loading={false} - timeout={100} - > - <i - className="spinner-placeholder" - /> - </DeferredSpinner> - </div> + /> </div> `; @@ -43,14 +34,6 @@ exports[`should display correctly for a file 2`] = ` <div className="measure-details-page-actions" > - <DeferredSpinner - loading={false} - timeout={100} - > - <i - className="spinner-placeholder" - /> - </DeferredSpinner> <FilesCounter className="spacer-left" total={10} @@ -97,16 +80,7 @@ exports[`should display correctly for a project 1`] = ` </span> <div className="measure-details-page-actions" - > - <DeferredSpinner - loading={true} - timeout={100} - > - <i - className="spinner-placeholder" - /> - </DeferredSpinner> - </div> + /> </div> `; @@ -117,14 +91,6 @@ exports[`should display the total of files 1`] = ` <div className="measure-details-page-actions" > - <DeferredSpinner - loading={true} - timeout={100} - > - <i - className="spinner-placeholder" - /> - </DeferredSpinner> <FilesCounter className="spacer-left" current={12} @@ -158,14 +124,6 @@ exports[`should display the total of files 2`] = ` <div className="measure-details-page-actions" > - <DeferredSpinner - loading={false} - timeout={100} - > - <i - className="spinner-placeholder" - /> - </DeferredSpinner> <FilesCounter className="spacer-left" current={12} @@ -181,15 +139,6 @@ exports[`should not display shortcuts for treemap 1`] = ` > <div className="measure-details-page-actions" - > - <DeferredSpinner - loading={true} - timeout={100} - > - <i - className="spinner-placeholder" - /> - </DeferredSpinner> - </div> + /> </div> `; 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} diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx index a97b447e3a1..e0b234e235d 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx @@ -39,6 +39,7 @@ import { parseUrlQuery, Query, hasFilterParams, hasVisualizationParams } from '. import { isSonarCloud } from '../../../helpers/system'; import '../../../components/search-navigator.css'; import '../styles.css'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; export interface Props { currentUser: CurrentUser; @@ -277,40 +278,45 @@ export default class AllProjects extends React.PureComponent<Props, State> { </div> ); - renderMain = () => - this.getView() === 'visualizations' ? ( - <div className="layout-page-main-inner"> - {this.state.projects && ( - <Visualizations - displayOrganizations={!this.props.organization && this.props.organizationsEnabled} - projects={this.state.projects} - sort={this.state.query.sort} - total={this.state.total} - visualization={this.getVisualization()} - /> - )} - </div> - ) : ( - <div className="layout-page-main-inner"> - {this.state.projects && ( - <ProjectsList - cardType={this.getView()} - currentUser={this.props.currentUser} - isFavorite={this.props.isFavorite} - isFiltered={hasFilterParams(this.state.query)} - organization={this.props.organization} - projects={this.state.projects} - query={this.state.query} - /> + renderMain = () => { + return ( + <DeferredSpinner loading={this.state.loading}> + {this.getView() === 'visualizations' ? ( + <div className="layout-page-main-inner"> + {this.state.projects && ( + <Visualizations + displayOrganizations={!this.props.organization && this.props.organizationsEnabled} + projects={this.state.projects} + sort={this.state.query.sort} + total={this.state.total} + visualization={this.getVisualization()} + /> + )} + </div> + ) : ( + <div className="layout-page-main-inner"> + {this.state.projects && ( + <ProjectsList + cardType={this.getView()} + currentUser={this.props.currentUser} + isFavorite={this.props.isFavorite} + isFiltered={hasFilterParams(this.state.query)} + organization={this.props.organization} + projects={this.state.projects} + query={this.state.query} + /> + )} + <ListFooter + count={this.state.projects !== undefined ? this.state.projects.length : 0} + loadMore={this.fetchMoreProjects} + ready={!this.state.loading} + total={this.state.total !== undefined ? this.state.total : 0} + /> + </div> )} - <ListFooter - count={this.state.projects !== undefined ? this.state.projects.length : 0} - loadMore={this.fetchMoreProjects} - ready={!this.state.loading} - total={this.state.total !== undefined ? this.state.total : 0} - /> - </div> + </DeferredSpinner> ); + }; render() { return ( diff --git a/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx b/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx index 17c584fdc52..c29cc32f56c 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx @@ -93,8 +93,6 @@ export default function PageHeader(props: Props) { className={classNames('projects-topbar-item', 'is-last', { 'is-loading': loading })}> - {loading && <i className="spinner spacer-right" />} - {total != null && ( <span> <strong id="projects-total">{total}</strong> {translate('projects._projects')} diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap index e2dca712a6e..c4ba6c754a1 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap @@ -79,58 +79,63 @@ exports[`renders 1`] = ` </div> </div> </div> - <div - className="layout-page-main-inner" + <DeferredSpinner + loading={false} + timeout={100} > - <ProjectsList - cardType="overall" - currentUser={ - Object { - "isLoggedIn": true, + <div + className="layout-page-main-inner" + > + <ProjectsList + cardType="overall" + currentUser={ + Object { + "isLoggedIn": true, + } } - } - isFavorite={false} - isFiltered={false} - projects={ - Array [ + isFavorite={false} + isFiltered={false} + projects={ + Array [ + Object { + "key": "foo", + "measures": Object {}, + "name": "Foo", + }, + ] + } + query={ Object { - "key": "foo", - "measures": Object {}, - "name": "Foo", - }, - ] - } - query={ - Object { - "coverage": undefined, - "duplications": undefined, - "gate": undefined, - "languages": undefined, - "maintainability": undefined, - "new_coverage": undefined, - "new_duplications": undefined, - "new_lines": undefined, - "new_maintainability": undefined, - "new_reliability": undefined, - "new_security": undefined, - "reliability": undefined, - "search": undefined, - "security": undefined, - "size": undefined, - "sort": undefined, - "tags": undefined, - "view": undefined, - "visualization": undefined, + "coverage": undefined, + "duplications": undefined, + "gate": undefined, + "languages": undefined, + "maintainability": undefined, + "new_coverage": undefined, + "new_duplications": undefined, + "new_lines": undefined, + "new_maintainability": undefined, + "new_reliability": undefined, + "new_security": undefined, + "reliability": undefined, + "search": undefined, + "security": undefined, + "size": undefined, + "sort": undefined, + "tags": undefined, + "view": undefined, + "visualization": undefined, + } } - } - /> - <ListFooter - count={1} - loadMore={[Function]} - ready={true} - total={0} - /> - </div> + /> + <ListFooter + count={1} + loadMore={[Function]} + ready={true} + total={0} + /> + </div> + </DeferredSpinner> </div> </div> `; @@ -196,24 +201,29 @@ exports[`renders 2`] = ` </div> </div> </div> - <div - className="layout-page-main-inner" + <DeferredSpinner + loading={false} + timeout={100} > - <Visualizations - displayOrganizations={false} - projects={ - Array [ - Object { - "key": "foo", - "measures": Object {}, - "name": "Foo", - }, - ] - } - total={0} - visualization="risk" - /> - </div> + <div + className="layout-page-main-inner" + > + <Visualizations + displayOrganizations={false} + projects={ + Array [ + Object { + "key": "foo", + "measures": Object {}, + "name": "Foo", + }, + ] + } + total={0} + visualization="risk" + /> + </div> + </DeferredSpinner> </div> </div> `; diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap index 5b8486ad3ec..057c6048368 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap @@ -75,9 +75,6 @@ exports[`should render correctly while loading 1`] = ` <div className="projects-topbar-item is-last is-loading" > - <i - className="spinner spacer-right" - /> <span> <strong id="projects-total" diff --git a/server/sonar-web/src/main/js/components/search-navigator.css b/server/sonar-web/src/main/js/components/search-navigator.css index c9ab518bf00..a8205a593ba 100644 --- a/server/sonar-web/src/main/js/components/search-navigator.css +++ b/server/sonar-web/src/main/js/components/search-navigator.css @@ -85,6 +85,11 @@ color: var(--secondFontColor); } +.search-navigator-facet-box > .spinner { + float: right; + margin-top: -24px; +} + .search-navigator-facet { position: relative; display: inline-block; |