]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19465 Remove deferred state from spinner
authorViktor Vorona <viktor.vorona@sonarsource.com>
Thu, 10 Aug 2023 10:25:28 +0000 (12:25 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 15 Aug 2023 20:02:41 +0000 (20:02 +0000)
142 files changed:
server/sonar-web/design-system/src/components/DeferredSpinner.tsx [deleted file]
server/sonar-web/design-system/src/components/FacetBox.tsx
server/sonar-web/design-system/src/components/Spinner.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/__tests__/ColorsLegend-test.tsx
server/sonar-web/design-system/src/components/__tests__/DeferredSpinner-test.tsx [deleted file]
server/sonar-web/design-system/src/components/__tests__/FacetBox-test.tsx
server/sonar-web/design-system/src/components/__tests__/Spinner-test.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/__tests__/Tags-test.tsx
server/sonar-web/design-system/src/components/index.ts
server/sonar-web/design-system/src/components/input/Checkbox.tsx
server/sonar-web/design-system/src/components/input/InputSearch.tsx
server/sonar-web/design-system/src/components/input/__tests__/InputSearch-test.tsx
server/sonar-web/design-system/src/components/modal/ModalFooter.tsx
server/sonar-web/design-system/src/components/modal/__tests__/ModalFooter-test.tsx
server/sonar-web/design-system/src/components/modal/__tests__/__snapshots__/ModalFooter-test.tsx.snap
server/sonar-web/eslint-local-rules/__tests__/no-conditional-rendering-of-deferredspinner-test.js
server/sonar-web/eslint-local-rules/no-conditional-rendering-of-deferredspinner.js
server/sonar-web/src/main/js/app/components/global-search/GlobalSearchShowMore.tsx
server/sonar-web/src/main/js/app/components/nav/component/AnalysisStatus.tsx
server/sonar-web/src/main/js/app/components/nav/component/AnalysisWarningsModal.tsx
server/sonar-web/src/main/js/app/styles/init/misc.css
server/sonar-web/src/main/js/apps/account/__tests__/Account-it.tsx
server/sonar-web/src/main/js/apps/account/notifications/Notifications.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/AnalysisWarningsModal.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.tsx
server/sonar-web/src/main/js/apps/code/components/CodeAppRenderer.tsx
server/sonar-web/src/main/js/apps/code/components/Search.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetails.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsIssues.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/StandardFacet.tsx
server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx
server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx
server/sonar-web/src/main/js/apps/create/project/Azure/AzurePersonalAccessTokenForm.tsx
server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectAccordion.tsx
server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectCreateRenderer.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketCloud/BitbucketCloudPersonalAccessTokenForm.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketCloud/BitbucketCloudProjectCreateRender.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketProjectCreateRenderer.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketSearchResults.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketServerPersonalAccessTokenForm.tsx
server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx
server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreateRenderer.tsx
server/sonar-web/src/main/js/apps/create/project/Gitlab/GItlabPersonalAccessTokenForm.tsx
server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectCreateRenderer.tsx
server/sonar-web/src/main/js/apps/create/project/__tests__/GitHub-it.tsx
server/sonar-web/src/main/js/apps/create/project/components/NewCodeDefinitionSelection.tsx
server/sonar-web/src/main/js/apps/groups/components/DeleteGroupForm.tsx
server/sonar-web/src/main/js/apps/groups/components/GroupForm.tsx
server/sonar-web/src/main/js/apps/groups/components/ViewMembersModal.tsx
server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx
server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.tsx
server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx
server/sonar-web/src/main/js/apps/issues/issues-subnavigation/SubnavigationIssuesListHeader.tsx
server/sonar-web/src/main/js/apps/marketplace/App.tsx
server/sonar-web/src/main/js/apps/overview/branches/ActivityPanel.tsx
server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanel.tsx
server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanel.tsx
server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx
server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx
server/sonar-web/src/main/js/apps/permission-templates/components/DeleteForm.tsx
server/sonar-web/src/main/js/apps/permission-templates/components/Form.tsx
server/sonar-web/src/main/js/apps/permission-templates/components/Header.tsx
server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.tsx
server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx
server/sonar-web/src/main/js/apps/permissions/project/components/PageHeader.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx
server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisListRenderer.tsx
server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx
server/sonar-web/src/main/js/apps/projectBaseline/components/BranchList.tsx
server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineApp.tsx
server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx
server/sonar-web/src/main/js/apps/projectBranches/components/BranchPurgeSetting.tsx
server/sonar-web/src/main/js/apps/projectBranches/components/LifetimeInformationRenderer.tsx
server/sonar-web/src/main/js/apps/projectInformation/badges/ProjectBadges.tsx
server/sonar-web/src/main/js/apps/projectInformation/notifications/ProjectNotifications.tsx
server/sonar-web/src/main/js/apps/projectLinks/CreationModal.tsx
server/sonar-web/src/main/js/apps/projectLinks/ProjectLinksApp.tsx
server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/App.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/Details.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatePermissionsRenderer.tsx
server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.tsx
server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx
server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsAppRenderer.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotOpenInIdeButton.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSidebarHeader.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerRenderer.tsx
server/sonar-web/src/main/js/apps/sessions/components/Login.tsx
server/sonar-web/src/main/js/apps/sessions/components/LoginForm.tsx
server/sonar-web/src/main/js/apps/settings/components/EmailForm.tsx
server/sonar-web/src/main/js/apps/settings/components/NewCodePeriod.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormRenderer.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTabRenderer.tsx
server/sonar-web/src/main/js/apps/settings/components/authentication/ConfigurationForm.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx
server/sonar-web/src/main/js/apps/settings/encryption/EncryptionApp.tsx
server/sonar-web/src/main/js/apps/settings/encryption/EncryptionForm.tsx
server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.tsx
server/sonar-web/src/main/js/apps/users/UsersApp.tsx
server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx
server/sonar-web/src/main/js/apps/users/components/TokensFormItem.tsx
server/sonar-web/src/main/js/apps/web-api-v2/WebApiApp.tsx
server/sonar-web/src/main/js/apps/webhooks/components/DeleteWebhookForm.tsx
server/sonar-web/src/main/js/apps/webhooks/components/DeliveriesForm.tsx
server/sonar-web/src/main/js/apps/webhooks/components/DeliveryItem.tsx
server/sonar-web/src/main/js/components/SourceViewer/SourceViewerCode.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsHistory.tsx
server/sonar-web/src/main/js/components/activity-graph/__tests__/ActivityGraph-it.tsx
server/sonar-web/src/main/js/components/controls/Checkbox.tsx
server/sonar-web/src/main/js/components/controls/ConfirmModal.tsx
server/sonar-web/src/main/js/components/controls/ListFooter.tsx
server/sonar-web/src/main/js/components/controls/SearchBox.css
server/sonar-web/src/main/js/components/controls/SearchBox.tsx
server/sonar-web/src/main/js/components/controls/Select.tsx
server/sonar-web/src/main/js/components/controls/SelectListListContainer.tsx
server/sonar-web/src/main/js/components/controls/ValidationModal.tsx
server/sonar-web/src/main/js/components/controls/__tests__/Checkbox-test.tsx
server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.tsx
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Checkbox-test.tsx.snap
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ConfirmModal-test.tsx.snap
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/SearchBox-test.tsx.snap
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Select-test.tsx.snap
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/SelectListListContainer-test.tsx.snap
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ValidationModal-test.tsx.snap
server/sonar-web/src/main/js/components/facet/FacetHeader.tsx
server/sonar-web/src/main/js/components/tutorials/components/EditTokenModal.tsx
server/sonar-web/src/main/js/components/tutorials/other/TokenStep.tsx
server/sonar-web/src/main/js/components/ui/Alert.tsx
server/sonar-web/src/main/js/components/ui/DeferredSpinner.css [deleted file]
server/sonar-web/src/main/js/components/ui/DeferredSpinner.tsx [deleted file]
server/sonar-web/src/main/js/components/ui/Spinner.css [new file with mode: 0644]
server/sonar-web/src/main/js/components/ui/Spinner.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/ui/__tests__/DeferredSpinner-test.tsx [deleted file]
server/sonar-web/src/main/js/components/ui/__tests__/Spinner-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/workspace/WorkspaceRuleDetails.tsx
server/sonar-web/src/main/js/components/workspace/__tests__/__snapshots__/WorkspaceRuleDetails-test.tsx.snap
server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx

diff --git a/server/sonar-web/design-system/src/components/DeferredSpinner.tsx b/server/sonar-web/design-system/src/components/DeferredSpinner.tsx
deleted file mode 100644 (file)
index ad03b0f..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { keyframes } from '@emotion/react';
-import styled from '@emotion/styled';
-import React, { DetailedHTMLProps, HTMLAttributes } from 'react';
-import { useIntl } from 'react-intl';
-import tw from 'twin.macro';
-import { themeColor } from '../helpers/theme';
-
-interface Props {
-  ariaLabel?: string;
-  children?: React.ReactNode;
-  className?: string;
-  customSpinner?: JSX.Element;
-  loading?: boolean;
-  placeholder?: boolean;
-  timeout?: number;
-}
-
-interface State {
-  showSpinner: boolean;
-}
-
-const DEFAULT_TIMEOUT = 100;
-
-export class DeferredSpinner extends React.PureComponent<Props, State> {
-  timer?: number;
-  static displayName = 'DeferredSpinner';
-  state: State = { showSpinner: false };
-
-  componentDidMount() {
-    if (this.props.loading == null || this.props.loading) {
-      this.startTimer();
-    }
-  }
-
-  componentDidUpdate(prevProps: Props) {
-    if (prevProps.loading === false && this.props.loading === true) {
-      this.stopTimer();
-      this.startTimer();
-    }
-    if (prevProps.loading === true && this.props.loading === false) {
-      this.stopTimer();
-      this.setState({ showSpinner: false });
-    }
-  }
-
-  componentWillUnmount() {
-    this.stopTimer();
-  }
-
-  startTimer = () => {
-    this.timer = window.setTimeout(() => {
-      this.setState({ showSpinner: true });
-    }, this.props.timeout ?? DEFAULT_TIMEOUT);
-  };
-
-  stopTimer = () => {
-    window.clearTimeout(this.timer);
-  };
-
-  render() {
-    const { showSpinner } = this.state;
-    const { customSpinner, className, children, placeholder, ariaLabel } = this.props;
-    if (showSpinner) {
-      if (customSpinner) {
-        return customSpinner;
-      }
-      // Overwrite aria-label only if defined
-      return <Spinner {...(ariaLabel ? { 'aria-label': ariaLabel } : {})} className={className} />;
-    }
-    if (children) {
-      return children;
-    }
-    if (placeholder) {
-      return <Placeholder className={className} />;
-    }
-    return null;
-  }
-}
-
-const spinAnimation = keyframes`
-  from {
-    transform: rotate(0deg);
-  }
-
-  to {
-    transform: rotate(-360deg);
-  }
-`;
-
-/* Exported to allow styles to be overridden */
-export const StyledSpinner = styled.div`
-  border: 2px solid transparent;
-  background: linear-gradient(0deg, ${themeColor('primary')} 50%, transparent 50% 100%) border-box,
-    linear-gradient(90deg, ${themeColor('primary')} 25%, transparent 75% 100%) border-box;
-  mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
-  -webkit-mask-composite: xor;
-  mask-composite: exclude;
-  animation: ${spinAnimation} 1s infinite linear;
-
-  ${tw`sw-h-4 sw-w-4`};
-  ${tw`sw-inline-block`};
-  ${tw`sw-box-border`};
-  ${tw`sw-rounded-pill`}
-`;
-
-export function Spinner(props: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>) {
-  const intl = useIntl();
-
-  return (
-    <StyledSpinner aria-label={intl.formatMessage({ id: 'loading' })} role="status" {...props} />
-  );
-}
-
-const Placeholder = styled.div`
-  position: relative;
-  visibility: hidden;
-
-  ${tw`sw-inline-flex sw-items-center sw-justify-center`};
-  ${tw`sw-h-4 sw-w-4`};
-`;
index 0366e6fe69ef51e217b42c4c9d3672098b453295..5736f58d0e13309f38a82f1f21adb35b567b392c 100644 (file)
@@ -25,8 +25,8 @@ import * as React from 'react';
 import tw from 'twin.macro';
 import { themeColor } from '../helpers';
 import { Badge } from './Badge';
-import { DeferredSpinner } from './DeferredSpinner';
 import { DestructiveIcon } from './InteractiveIcon';
+import { Spinner } from './Spinner';
 import Tooltip from './Tooltip';
 import { BareButton } from './buttons';
 import { OpenCloseIndicator } from './icons';
@@ -103,7 +103,7 @@ export function FacetBox(props: FacetBoxProps) {
           <HeaderTitle disabled={disabled}>{name}</HeaderTitle>
         </ChevronAndTitle>
 
-        {<DeferredSpinner loading={loading} />}
+        {<Spinner loading={loading} />}
 
         {counter > 0 && (
           <BadgeAndIcons>
diff --git a/server/sonar-web/design-system/src/components/Spinner.tsx b/server/sonar-web/design-system/src/components/Spinner.tsx
new file mode 100644 (file)
index 0000000..aa5db5e
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { keyframes } from '@emotion/react';
+import styled from '@emotion/styled';
+import classNames from 'classnames';
+import * as React from 'react';
+import { useIntl } from 'react-intl';
+import tw from 'twin.macro';
+import { themeColor } from '../helpers/theme';
+
+interface Props {
+  ariaLabel?: string;
+  className?: string;
+  customSpinner?: JSX.Element;
+  loading?: boolean;
+  placeholder?: boolean;
+}
+
+export function Spinner(props: React.PropsWithChildren<Props>) {
+  const intl = useIntl();
+  const {
+    customSpinner,
+    className,
+    children,
+    placeholder,
+    ariaLabel = intl.formatMessage({ id: 'loading' }),
+    loading = true,
+  } = props;
+
+  if (customSpinner) {
+    return <>{loading ? customSpinner : children}</>;
+  }
+
+  return (
+    <>
+      <div className="sw-overflow-hidden">
+        <StyledSpinner
+          aria-live="polite"
+          className={classNames(className, { 'a11y-hidden': !loading })}
+          role="status"
+        >
+          {loading && <span className="a11y-hidden">{ariaLabel}</span>}
+        </StyledSpinner>
+      </div>
+      {!loading && (children ?? (placeholder && <Placeholder className={className} />) ?? null)}
+    </>
+  );
+}
+
+const spinAnimation = keyframes`
+  from {
+    transform: rotate(0deg);
+  }
+
+  to {
+    transform: rotate(-360deg);
+  }
+`;
+
+export const StyledSpinner = styled.div`
+  border: 2px solid transparent;
+  background: linear-gradient(0deg, ${themeColor('primary')} 50%, transparent 50% 100%) border-box,
+    linear-gradient(90deg, ${themeColor('primary')} 25%, transparent 75% 100%) border-box;
+  mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
+  -webkit-mask-composite: xor;
+  mask-composite: exclude;
+  animation: ${spinAnimation} 1s infinite linear;
+
+  ${tw`sw-h-4 sw-w-4`};
+  ${tw`sw-inline-block`};
+  ${tw`sw-box-border`};
+  ${tw`sw-rounded-pill`}
+`;
+
+const Placeholder = styled.div`
+  position: relative;
+  visibility: hidden;
+
+  ${tw`sw-inline-flex sw-items-center sw-justify-center`};
+  ${tw`sw-h-4 sw-w-4`};
+`;
index cb70e5fe76085275d11fc1e87e2aac4308b8470f..ca4b3a4e332e50f4763718a1e733e9ccd8c75d9c 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { screen } from '@testing-library/react';
 
-import { render } from '../../helpers/testUtils';
+import { renderWithContext } from '../../helpers/testUtils';
 import { FCProps } from '../../types/misc';
 import { ColorsLegend } from '../ColorsLegend';
 
@@ -53,5 +53,5 @@ it('should react when a rating is clicked', () => {
 });
 
 function renderColorLegend(props: Partial<FCProps<typeof ColorsLegend>> = {}) {
-  return render(<ColorsLegend colors={colors} onColorClick={jest.fn()} {...props} />);
+  return renderWithContext(<ColorsLegend colors={colors} onColorClick={jest.fn()} {...props} />);
 }
diff --git a/server/sonar-web/design-system/src/components/__tests__/DeferredSpinner-test.tsx b/server/sonar-web/design-system/src/components/__tests__/DeferredSpinner-test.tsx
deleted file mode 100644 (file)
index d8dd6ab..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { render, screen } from '@testing-library/react';
-import { IntlWrapper } from '../../helpers/testUtils';
-import { DeferredSpinner } from '../DeferredSpinner';
-
-beforeEach(() => {
-  jest.useFakeTimers();
-});
-
-afterEach(() => {
-  jest.runOnlyPendingTimers();
-  jest.useRealTimers();
-});
-
-it('renders children before timeout', () => {
-  renderDeferredSpinner({ children: <a href="#">foo</a> });
-  expect(screen.getByRole('link')).toBeInTheDocument();
-  jest.runAllTimers();
-  expect(screen.queryByRole('link')).not.toBeInTheDocument();
-});
-
-it('renders spinner after timeout', () => {
-  renderDeferredSpinner();
-  expect(screen.queryByLabelText('loading')).not.toBeInTheDocument();
-  jest.runAllTimers();
-  expect(screen.getByLabelText('loading')).toBeInTheDocument();
-});
-
-it('allows setting a custom class name', () => {
-  renderDeferredSpinner({ className: 'foo' });
-  jest.runAllTimers();
-  expect(screen.getByLabelText('loading')).toHaveClass('foo');
-});
-
-it('can be controlled by the loading prop', () => {
-  const { rerender } = renderDeferredSpinner({ loading: true });
-  jest.runAllTimers();
-  expect(screen.getByLabelText('loading')).toBeInTheDocument();
-
-  rerender(prepareDeferredSpinner({ loading: false }));
-  expect(screen.queryByLabelText('loading')).not.toBeInTheDocument();
-});
-
-function renderDeferredSpinner(props: Partial<DeferredSpinner['props']> = {}) {
-  // We don't use our renderComponent() helper here, as we have some tests that
-  // require changes in props.
-  return render(prepareDeferredSpinner(props));
-}
-
-function prepareDeferredSpinner(props: Partial<DeferredSpinner['props']> = {}) {
-  return (
-    <IntlWrapper>
-      <DeferredSpinner {...props} />
-    </IntlWrapper>
-  );
-}
index 434a6147e5ca65f4b284b6393c64a684505a4679..a7146855abf25d6cfcb1ee94e6d89c407390b7a4 100644 (file)
@@ -20,7 +20,7 @@
 
 import { screen } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
-import { render } from '../../helpers/testUtils';
+import { renderWithContext } from '../../helpers/testUtils';
 import { FacetBox, FacetBoxProps } from '../FacetBox';
 
 it('should render an empty disabled facet box', async () => {
@@ -68,7 +68,7 @@ it('should render an inner expanded facet box with count', async () => {
 });
 
 function renderComponent({ children, ...props }: Partial<FacetBoxProps> = {}) {
-  return render(
+  return renderWithContext(
     <FacetBox name="Test FacetBox" {...props}>
       {children}
     </FacetBox>
diff --git a/server/sonar-web/design-system/src/components/__tests__/Spinner-test.tsx b/server/sonar-web/design-system/src/components/__tests__/Spinner-test.tsx
new file mode 100644 (file)
index 0000000..b92b41a
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { render, screen } from '@testing-library/react';
+import { IntlWrapper } from '../../helpers/testUtils';
+import { Spinner } from '../Spinner';
+
+it('allows setting a custom class name', () => {
+  renderSpinner({ className: 'foo' });
+  expect(screen.getByRole('status')).toHaveClass('foo');
+});
+
+it('can be controlled by the loading prop', () => {
+  const { rerender } = renderSpinner({ loading: true });
+  expect(screen.getByText('loading')).toBeInTheDocument();
+
+  rerender(prepareSpinner({ loading: false }));
+  expect(screen.queryByText('loading')).not.toBeInTheDocument();
+});
+
+function renderSpinner(props: Partial<Parameters<typeof Spinner>[0]> = {}) {
+  // We don't use our renderComponent() helper here, as we have some tests that
+  // require changes in props.
+  return render(prepareSpinner(props));
+}
+
+function prepareSpinner(props: Partial<Parameters<typeof Spinner>[0]> = {}) {
+  return (
+    <IntlWrapper>
+      <Spinner {...props} />
+    </IntlWrapper>
+  );
+}
index 7192c8d91e07b6a484a28e2c0e9118be2e54d6af..2974d4898030b588d3645cd28c698972ee99a2dd 100644 (file)
 
 /* eslint-disable import/no-extraneous-dependencies */
 
-import { render, screen } from '@testing-library/react';
+import { screen } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import { useState } from 'react';
+import { renderWithContext } from '../../helpers/testUtils';
 import { FCProps } from '../../types/misc';
 import { Tags } from '../Tags';
 import { TagsSelector } from '../TagsSelector';
@@ -75,7 +76,7 @@ it('should allow editing tags', async () => {
 });
 
 function renderTags(overrides: Partial<FCProps<typeof Tags>> = {}) {
-  render(<Wrapper {...overrides} />);
+  renderWithContext(<Wrapper {...overrides} />);
 }
 
 function Wrapper(overrides: Partial<FCProps<typeof Tags>> = {}) {
index 484ae86999d4edd02421e92193f86c5d5b447ac8..bc774d6885a6cc71c784249df1e7a865bd040c3b 100644 (file)
@@ -31,7 +31,6 @@ export * from './CodeSnippet';
 export * from './CodeSyntaxHighlighter';
 export * from './ColorsLegend';
 export * from './CoverageIndicator';
-export { DeferredSpinner, Spinner } from './DeferredSpinner';
 export { ActionsDropdown, Dropdown } from './Dropdown';
 export * from './DropdownMenu';
 export { DropdownToggler } from './DropdownToggler';
@@ -69,6 +68,7 @@ export * from './Separator';
 export * from './SizeIndicator';
 export * from './SonarCodeColorizer';
 export * from './SonarQubeLogo';
+export { Spinner } from './Spinner';
 export * from './Table';
 export * from './Tags';
 export * from './TagsSelector';
index 707cae6aa65e49a5fa110513966fafa564207bb8..9266b88cd603fccf7b6bdbc77830e28f0b45c6d1 100644 (file)
  */
 
 import styled from '@emotion/styled';
+import { Spinner } from 'design-system/src/components/Spinner';
 import React from 'react';
 import tw from 'twin.macro';
 import { themeBorder, themeColor, themeContrast } from '../../helpers/theme';
-import { DeferredSpinner } from '../DeferredSpinner';
 import { CheckIcon } from '../icons/CheckIcon';
 import { CustomIcon } from '../icons/Icon';
 
@@ -76,11 +76,12 @@ export function Checkbox({
         onFocus={onFocus}
         type="checkbox"
       />
-      <DeferredSpinner loading={loading}>
+      {!loading && (
         <StyledCheckbox aria-hidden data-clickable="true" title={title}>
           <CheckboxIcon checked={checked} thirdState={thirdState} />
         </StyledCheckbox>
-      </DeferredSpinner>
+      )}
+      <Spinner loading={loading} />
       {!right && children}
     </CheckboxContainer>
   );
@@ -99,7 +100,7 @@ function CheckboxIcon({ checked, thirdState }: CheckIconProps) {
       </CustomIcon>
     );
   } else if (checked) {
-    return <CheckIcon fill="currentColor" />;
+    return <CheckIcon fill="buttonSecondary" />;
   }
   return null;
 }
index cb3eed1cbeba4c088aeb08dda3264465a1449007..b4f72d2f8deb844f32367c7c2d218da8d9bda731 100644 (file)
@@ -28,8 +28,8 @@ import { Key } from '../../helpers/keyboard';
 import { themeBorder, themeColor, themeContrast } from '../../helpers/theme';
 import { isDefined } from '../../helpers/types';
 import { InputSizeKeys } from '../../types/theme';
-import { DeferredSpinner, StyledSpinner } from '../DeferredSpinner';
 import { InteractiveIcon } from '../InteractiveIcon';
+import { Spinner, StyledSpinner } from '../Spinner';
 import { CloseIcon } from '../icons/CloseIcon';
 import { SearchIcon } from '../icons/SearchIcon';
 
@@ -160,9 +160,9 @@ export function InputSearch({
             value={value}
           />
         )}
-        <DeferredSpinner className="sw-z-normal" loading={loading ?? false}>
+        <Spinner className="sw-z-normal" loading={loading ?? false}>
           <StyledSearchIcon />
-        </DeferredSpinner>
+        </Spinner>
         {value && (
           <StyledInteractiveIcon
             Icon={CloseIcon}
index fa5f5c71b7369a27534a93e1a27900f4b38aee39..55901ed7cecaeae2aa8caf6864d29aed3b438b27 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import { screen, waitFor } from '@testing-library/react';
-import { render } from '../../../helpers/testUtils';
+import { renderWithContext } from '../../../helpers/testUtils';
 import { FCProps } from '../../../types/misc';
 import { InputSearch } from '../InputSearch';
 
@@ -74,7 +74,7 @@ it('should clear input using escape', async () => {
 });
 
 function setupWithProps(props: Partial<FCProps<typeof InputSearch>> = {}) {
-  return render(
+  return renderWithContext(
     <InputSearch
       clearIconAriaLabel=""
       maxLength={150}
index c196f895f666a3609c2c753801ccd497d61a2f13..d75afcb8f67b4c3d56a0bc50c81852b6e3bca910 100644 (file)
@@ -20,7 +20,7 @@
 
 import styled from '@emotion/styled';
 import tw from 'twin.macro';
-import { DeferredSpinner } from '../DeferredSpinner';
+import { Spinner } from '../Spinner';
 
 interface Props {
   loading?: boolean;
@@ -31,7 +31,7 @@ interface Props {
 export function ModalFooter({ loading = false, primaryButton, secondaryButton }: Props) {
   return (
     <StyledFooter>
-      <DeferredSpinner loading={loading} />
+      <Spinner loading={loading} />
       {primaryButton}
       {secondaryButton}
     </StyledFooter>
index ab2c19c133dc3b53462554e6e8bd41fe82299b5e..e9d1256828fe2f929fc436132b91100bc2507c93 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-import { render } from '../../../helpers/testUtils';
+import { renderWithContext } from '../../../helpers/testUtils';
 import { FCProps } from '../../../types/misc';
 import { ModalFooter } from '../ModalFooter';
 
@@ -52,5 +52,5 @@ it('should render with primary and secondary buttons', () => {
 });
 
 function setupWithProps(props: Partial<FCProps<typeof ModalFooter>> = {}) {
-  return render(<ModalFooter secondaryButton={<div />} {...props} />);
+  return renderWithContext(<ModalFooter secondaryButton={<div />} {...props} />);
 }
index ace0eba51cfca2cfc12ff01dd87bfa806c3a5072..13e1e90ab13a9b437ada216473f111752da4896d 100644 (file)
@@ -1,6 +1,22 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should render with primary and secondary buttons 1`] = `
+@keyframes animation-0 {
+  from {
+    -webkit-transform: rotate(0deg);
+    -moz-transform: rotate(0deg);
+    -ms-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+
+  to {
+    -webkit-transform: rotate(-360deg);
+    -moz-transform: rotate(-360deg);
+    -ms-transform: rotate(-360deg);
+    transform: rotate(-360deg);
+  }
+}
+
 .emotion-0 {
   display: -webkit-box;
   display: -webkit-flex;
@@ -17,10 +33,36 @@ exports[`should render with primary and secondary buttons 1`] = `
   gap: 0.75rem;
 }
 
+.emotion-2 {
+  border: 2px solid transparent;
+  background: linear-gradient(0deg, rgb(93,108,208) 50%, transparent 50% 100%) border-box,linear-gradient(90deg, rgb(93,108,208) 25%, transparent 75% 100%) border-box;
+  -webkit-mask: linear-gradient(#fff 0 0) padding-box,linear-gradient(#fff 0 0);
+  mask: linear-gradient(#fff 0 0) padding-box,linear-gradient(#fff 0 0);
+  -webkit-mask-composite: xor;
+  -webkit-mask-composite: exclude;
+  mask-composite: exclude;
+  -webkit-animation: animation-0 1s infinite linear;
+  animation: animation-0 1s infinite linear;
+  height: 1rem;
+  width: 1rem;
+  display: inline-block;
+  box-sizing: border-box;
+  border-radius: 625rem;
+}
+
 <div>
   <div
     class="emotion-0 emotion-1"
   >
+    <div
+      class="sw-overflow-hidden"
+    >
+      <div
+        aria-live="polite"
+        class="a11y-hidden emotion-2 emotion-3"
+        role="status"
+      />
+    </div>
     <button
       type="button"
     >
@@ -36,6 +78,22 @@ exports[`should render with primary and secondary buttons 1`] = `
 `;
 
 exports[`should render with secondary button 1`] = `
+@keyframes animation-0 {
+  from {
+    -webkit-transform: rotate(0deg);
+    -moz-transform: rotate(0deg);
+    -ms-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+
+  to {
+    -webkit-transform: rotate(-360deg);
+    -moz-transform: rotate(-360deg);
+    -ms-transform: rotate(-360deg);
+    transform: rotate(-360deg);
+  }
+}
+
 .emotion-0 {
   display: -webkit-box;
   display: -webkit-flex;
@@ -52,10 +110,36 @@ exports[`should render with secondary button 1`] = `
   gap: 0.75rem;
 }
 
+.emotion-2 {
+  border: 2px solid transparent;
+  background: linear-gradient(0deg, rgb(93,108,208) 50%, transparent 50% 100%) border-box,linear-gradient(90deg, rgb(93,108,208) 25%, transparent 75% 100%) border-box;
+  -webkit-mask: linear-gradient(#fff 0 0) padding-box,linear-gradient(#fff 0 0);
+  mask: linear-gradient(#fff 0 0) padding-box,linear-gradient(#fff 0 0);
+  -webkit-mask-composite: xor;
+  -webkit-mask-composite: exclude;
+  mask-composite: exclude;
+  -webkit-animation: animation-0 1s infinite linear;
+  animation: animation-0 1s infinite linear;
+  height: 1rem;
+  width: 1rem;
+  display: inline-block;
+  box-sizing: border-box;
+  border-radius: 625rem;
+}
+
 <div>
   <div
     class="emotion-0 emotion-1"
   >
+    <div
+      class="sw-overflow-hidden"
+    >
+      <div
+        aria-live="polite"
+        class="a11y-hidden emotion-2 emotion-3"
+        role="status"
+      />
+    </div>
     <button
       type="button"
     >
index 9d9f9c695ca6aa4ac5c16e4018bae0f58485e370..30e4f05eee1e4949661f99f4ce091a97041034d1 100644 (file)
@@ -37,7 +37,7 @@ ruleTester.run(
       {
         code: `function MyCompontent({ loading }) {
   return <>
-    <DeferredSpinner loading={loading} />
+    <Spinner loading={loading} />
   </>
 }`,
       },
@@ -46,7 +46,7 @@ ruleTester.run(
       {
         code: `function MyCompontent({ loading }) {
   return <>
-    {loading && <DeferredSpinner />}
+    {loading && <Spinner />}
   </>
 }`,
         errors: [{ messageId: 'noConditionalRenderingOfDeferredSpinner' }],
@@ -54,7 +54,7 @@ ruleTester.run(
       {
         code: `function MyComponent({ loading }) {
   return <>
-    {loading ? <DeferredSpinner /> : <div />}
+    {loading ? <Spinner /> : <div />}
   </>
 }`,
         errors: [{ messageId: 'noConditionalRenderingOfDeferredSpinner' }],
@@ -62,7 +62,7 @@ ruleTester.run(
       {
         code: `function MyCompontent({ loaded }) {
   return <>
-    {loaded ? <div /> : <DeferredSpinner />}
+    {loaded ? <div /> : <Spinner />}
   </>
 }`,
         errors: [{ messageId: 'noConditionalRenderingOfDeferredSpinner' }],
index 03d16a266529a44b3cfa933664ad4e39afe323a3..c4347d43f191304933468f9a45038eeb151ed4b6 100644 (file)
@@ -21,7 +21,7 @@ module.exports = {
   meta: {
     messages: {
       noConditionalRenderingOfDeferredSpinner:
-        'For accessibility reasons, you should not conditionally render a <DeferredSpinner />. Always render it, and pass a loading prop instead.',
+        'For accessibility reasons, you should not conditionally render a <Spinner />. Always render it, and pass a loading prop instead.',
     },
   },
   create(context) {
@@ -54,6 +54,6 @@ function isDeferredSpinnerComponent(element) {
   return (
     element.type === 'JSXElement' &&
     element.openingElement &&
-    element.openingElement.name.name === 'DeferredSpinner'
+    element.openingElement.name.name === 'Spinner'
   );
 }
index ded1f4142560be58085d6d584dd5467c3fb28385..eb63b27a859ba13d67179fb369a126f540b62f9d 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import classNames from 'classnames';
-import { DeferredSpinner, ItemButton } from 'design-system';
+import { ItemButton, Spinner } from 'design-system';
 import * as React from 'react';
 import { translate } from '../../../helpers/l10n';
 
@@ -61,9 +61,7 @@ export default class GlobalSearchShowMore extends React.PureComponent<Props> {
           this.handleMouseEnter(qualifier);
         }}
       >
-        <DeferredSpinner loading={loadingMore === qualifier}>
-          {translate('show_more')}
-        </DeferredSpinner>
+        <Spinner loading={loadingMore === qualifier}>{translate('show_more')}</Spinner>
       </ItemButton>
     );
   }
index a082485aa6578e7a7a370fb45e9bf69e6a0f76aa..b76fbe499de26a2616e2d62de6f1fe30681b0dfd 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { DeferredSpinner, FlagMessage, Link } from 'design-system';
+import { FlagMessage, Link, Spinner } from 'design-system';
 import * as React from 'react';
 import { translate } from '../../../../helpers/l10n';
 import { useBranchWarrningQuery } from '../../../../queries/branch';
@@ -48,7 +48,7 @@ export function AnalysisStatus(props: HeaderMetaProps) {
   if (isInProgress || isPending) {
     return (
       <div className="sw-flex sw-items-center">
-        <DeferredSpinner timeout={0} />
+        <Spinner />
         <span className="sw-ml-1">
           {isInProgress
             ? translate('project_navigation.analysis_status.in_progress')
index 90f7da976090d1ca536e8e622960e135c2f91985..cd599a72a75dda1532adf9fb126f78027683ac51 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import {
-  DangerButtonSecondary,
-  DeferredSpinner,
-  FlagMessage,
-  HtmlFormatter,
-  Modal,
-} from 'design-system';
+import { DangerButtonSecondary, FlagMessage, HtmlFormatter, Modal, Spinner } from 'design-system';
 import * as React from 'react';
 import { translate } from '../../../../helpers/l10n';
 import { sanitizeStringRestricted } from '../../../../helpers/sanitize';
@@ -77,10 +71,7 @@ export function AnalysisWarningsModal(props: Props) {
                   {translate('dismiss_permanently')}
                 </DangerButtonSecondary>
 
-                <DeferredSpinner
-                  className="sw-ml-2"
-                  loading={isLoading && variables?.key === key}
-                />
+                <Spinner className="sw-ml-2" loading={isLoading && variables?.key === key} />
               </div>
             )}
           </div>
index 526a667115de9bd50953a24a459616034122ce3b..6ac611f45a6f0d3df85ec008186919e2e3c2a680 100644 (file)
@@ -42,7 +42,7 @@ th.hide-overflow {
 .a11y-hidden {
   position: absolute !important;
   left: -10000px !important;
-  top: auto !important;
+  top: 0 !important;
   width: 1px !important;
   height: 1px !important;
   overflow: hidden !important;
index 4fbc29fdf32cbfc2e300b14876e191bb45cd56a9..e050c593a6634ac7da270b5ba39b56b21d8ae952 100644 (file)
@@ -559,7 +559,7 @@ describe('notifications page', () => {
     renderAccountApp(mockLoggedInUser(), notificationsPagePath);
 
     await user.click(
-      screen.getByRole('button', { name: 'my_profile.per_project_notifications.add' })
+      await screen.findByRole('button', { name: 'my_profile.per_project_notifications.add' })
     );
     expect(screen.getByLabelText('search.placeholder', { selector: 'input' })).toBeInTheDocument();
     await user.keyboard('sonarqube');
index 1321037e356321dd6a67d84496446a48d77df9f2..8e1ba6aaf3eb77e8f2d3b6865cbed91f5ebc72ae 100644 (file)
@@ -25,7 +25,7 @@ import {
   WithNotificationsProps,
 } from '../../../components/hoc/withNotifications';
 import { Alert } from '../../../components/ui/Alert';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 import GlobalNotifications from './GlobalNotifications';
 import Projects from './Projects';
@@ -47,7 +47,7 @@ export function Notifications(props: WithNotificationsProps) {
     <div className="account-body account-container">
       <Helmet defer={false} title={translate('my_account.notifications')} />
       <Alert variant="info">{translate('notification.dispatcher.information')}</Alert>
-      <DeferredSpinner loading={loading}>
+      <Spinner loading={loading}>
         {notifications && (
           <>
             <GlobalNotifications
@@ -66,7 +66,7 @@ export function Notifications(props: WithNotificationsProps) {
             />
           </>
         )}
-      </DeferredSpinner>
+      </Spinner>
     </div>
   );
 }
index c585b9a8da3a287fe36d2fa1de0e6f620cb922a4..a53b2200515faefe3a02bfc86734cb586feedead 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import {
-  DangerButtonSecondary,
-  DeferredSpinner,
-  FlagMessage,
-  HtmlFormatter,
-  Modal,
-} from 'design-system';
+import { DangerButtonSecondary, FlagMessage, HtmlFormatter, Modal, Spinner } from 'design-system';
 import * as React from 'react';
 import { dismissAnalysisWarning, getTask } from '../../../api/ce';
 import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
@@ -116,7 +110,7 @@ export class AnalysisWarningsModal extends React.PureComponent<Props, State> {
     const header = translate('warnings');
 
     const body = (
-      <DeferredSpinner loading={loading}>
+      <Spinner loading={loading}>
         {warnings.map(({ dismissable, key, message }) => (
           <React.Fragment key={key}>
             <div className="sw-flex sw-items-center sw-mt-2">
@@ -143,13 +137,13 @@ export class AnalysisWarningsModal extends React.PureComponent<Props, State> {
                     {translate('dismiss_permanently')}
                   </DangerButtonSecondary>
 
-                  <DeferredSpinner className="sw-ml-2" loading={dismissedWarning === key} />
+                  <Spinner className="sw-ml-2" loading={dismissedWarning === key} />
                 </div>
               )}
             </div>
           </React.Fragment>
         ))}
-      </DeferredSpinner>
+      </Spinner>
     );
 
     return (
index 3d5aa5659308c87112560253b17cb3acd5ad3920..62946b6765eb2c5a4bc6d60eefbeb7c1e9d6d4b4 100644 (file)
@@ -31,7 +31,7 @@ import withComponentContext from '../../../app/components/componentContext/withC
 import ListFooter from '../../../components/controls/ListFooter';
 import Suggestions from '../../../components/embed-docs-modal/Suggestions';
 import { Location, Router, withRouter } from '../../../components/hoc/withRouter';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { toShortISO8601String } from '../../../helpers/dates';
 import { translate } from '../../../helpers/l10n';
 import { parseAsDate } from '../../../helpers/query';
@@ -218,7 +218,7 @@ export class BackgroundTasksApp extends React.PureComponent<Props, State> {
       return (
         <div className="page page-limited">
           <Helmet defer={false} title={translate('background_tasks.page')} />
-          <DeferredSpinner loading />
+          <Spinner />
         </div>
       );
     }
index d7ea12754eb261cb60df8a925d477ec66c1b19c2..d420d15d43beb82f0854ad85115a037eebb015ad 100644 (file)
  */
 import {
   Card,
-  DeferredSpinner,
   FlagMessage,
   HelperHintIcon,
   KeyboardHint,
   LargeCenteredLayout,
   LightLabel,
+  Spinner,
 } from 'design-system';
 import { intersection } from 'lodash';
 import * as React from 'react';
@@ -182,7 +182,7 @@ export default function CodeAppRenderer(props: Props) {
 
       {(showComponentList || showSearch) && (
         <Card className="sw-mt-2">
-          <DeferredSpinner loading={loading}>
+          <Spinner loading={loading}>
             {showComponentList && (
               <Components
                 baseComponent={baseComponent}
@@ -212,7 +212,7 @@ export default function CodeAppRenderer(props: Props) {
                 selected={highlighted}
               />
             )}
-          </DeferredSpinner>
+          </Spinner>
         </Card>
       )}
 
index 3c1323fe81b204093be4a30b8dc83796e8d8dd7e..f7d8252248a7f8e25da1995dfd0944c7360f3dec 100644 (file)
@@ -23,7 +23,7 @@ import { isEmpty, omit } from 'lodash';
 import * as React from 'react';
 import { getTree } from '../../../api/components';
 import { Location, Router, withRouter } from '../../../components/hoc/withRouter';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { getBranchLikeQuery } from '../../../helpers/branch-like';
 import { KeyboardKeys } from '../../../helpers/keycodes';
 import { translate } from '../../../helpers/l10n';
@@ -176,7 +176,7 @@ class Search extends React.PureComponent<Props, State> {
           size="large"
           value={this.state.query}
         />
-        <DeferredSpinner className="sw-ml-2" loading={loading} />
+        <Spinner className="sw-ml-2" loading={loading} />
       </div>
     );
   }
index 4b59c2c0b60f6a69714c512ed381fd8857e3609e..27233ef7bbae2e9bcda4cc4ccaad3a69d3cb453b 100644 (file)
@@ -23,7 +23,7 @@ import { deleteRule, getRuleDetails, updateRule } from '../../../api/rules';
 import ConfirmButton from '../../../components/controls/ConfirmButton';
 import HelpTooltip from '../../../components/controls/HelpTooltip';
 import { Button } from '../../../components/controls/buttons';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { Dict, RuleActivation, RuleDetails as TypeRuleDetails } from '../../../types/types';
 import { Activation, Query } from '../query';
@@ -163,7 +163,7 @@ export default class RuleDetails extends React.PureComponent<Props, State> {
 
     return (
       <div className="coding-rule-details">
-        <DeferredSpinner loading={this.state.loading}>
+        <Spinner loading={this.state.loading}>
           <RuleDetailsMeta
             canWrite={canWrite}
             onFilterChange={this.props.onFilterChange}
@@ -252,7 +252,7 @@ export default class RuleDetails extends React.PureComponent<Props, State> {
           {!ruleDetails.isTemplate && ruleDetails.type !== 'SECURITY_HOTSPOT' && (
             <RuleDetailsIssues ruleDetails={ruleDetails} />
           )}
-        </DeferredSpinner>
+        </Spinner>
       </div>
     );
   }
index cd1d53047d510a56fcb75c99ec73168de87249a5..de10ac4a3a81d156f697ff09697dac5ebd1c92f0 100644 (file)
@@ -24,7 +24,7 @@ import Link from '../../../components/common/Link';
 import ConfirmButton from '../../../components/controls/ConfirmButton';
 import { Button } from '../../../components/controls/buttons';
 import SeverityHelper from '../../../components/shared/SeverityHelper';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { getRuleUrl } from '../../../helpers/urls';
 import { Rule, RuleDetails } from '../../../types/types';
@@ -164,13 +164,13 @@ export default class RuleDetailsCustomRules extends React.PureComponent<Props, S
             </CustomRuleButton>
           )}
 
-          <DeferredSpinner className="spacer-left" loading={loading}>
+          <Spinner className="spacer-left" loading={loading}>
             {rules.length > 0 && (
               <table className="coding-rules-detail-list" id="coding-rules-detail-custom-rules">
                 <tbody>{sortBy(rules, (rule) => rule.name).map(this.renderRule)}</tbody>
               </table>
             )}
-          </DeferredSpinner>
+          </Spinner>
         </div>
       </div>
     );
index 344e90a0bec87af8e549dadd2cee603973f16a50..d57d9a000060d2f4223d7ebebc6bc1152b1d571e 100644 (file)
@@ -24,7 +24,7 @@ import withAvailableFeatures, {
 } from '../../../app/components/available-features/withAvailableFeatures';
 import Link from '../../../components/common/Link';
 import Tooltip from '../../../components/controls/Tooltip';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 import { formatMeasure } from '../../../helpers/measures';
 import { getIssuesUrl } from '../../../helpers/urls';
@@ -149,7 +149,7 @@ export class RuleDetailsIssues extends React.PureComponent<Props, State> {
 
     return (
       <div className="js-rule-issues coding-rule-section">
-        <DeferredSpinner loading={loading}>
+        <Spinner loading={loading}>
           <h2 className="coding-rules-detail-title">
             {translate('coding_rules.issues')}
             {this.renderTotal()}
@@ -171,7 +171,7 @@ export class RuleDetailsIssues extends React.PureComponent<Props, State> {
               {translate('coding_rules.no_issue_detected_for_projects')}
             </div>
           )}
-        </DeferredSpinner>
+        </Spinner>
       </div>
     );
   }
index 3b6382f99ea786f914a640b7bdddd88f8a572f82..41495c4995318cfc61c6cd62f3d10fa707b1f8c6 100644 (file)
@@ -28,7 +28,7 @@ import FacetItemsList from '../../../components/facet/FacetItemsList';
 import ListStyleFacet from '../../../components/facet/ListStyleFacet';
 import ListStyleFacetFooter from '../../../components/facet/ListStyleFacetFooter';
 import MultipleSelectionHint from '../../../components/facet/MultipleSelectionHint';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 import { highlightTerm } from '../../../helpers/search';
 import {
@@ -236,7 +236,7 @@ export class StandardFacet extends React.PureComponent<Props, State> {
     const values = this.props[valuesProp];
 
     if (!stats) {
-      return <DeferredSpinner className="sw-ml-4" />;
+      return <Spinner className="sw-ml-4" />;
     }
 
     const categories = sortBy(Object.keys(stats), (key) => -stats[key]);
@@ -321,7 +321,7 @@ export class StandardFacet extends React.PureComponent<Props, State> {
     const values = this.props.sonarsourceSecurity;
 
     if (!stats) {
-      return <DeferredSpinner className="sw-ml-4" />;
+      return <Spinner className="sw-ml-4" />;
     }
 
     const sortedItems = sortBy(
index 33a9263ee3cc3e85b05ca61cbaefd86518135e7d..1d58466994b3a083ca6ff434d0e21a5b354c9348 100644 (file)
 import { withTheme } from '@emotion/react';
 import styled from '@emotion/styled';
 import {
-  DeferredSpinner,
   LargeCenteredLayout,
   Note,
   PageContentFontWrapper,
+  Spinner,
   themeBorder,
   themeColor,
 } from 'design-system';
@@ -266,10 +266,7 @@ class ComponentMeasuresApp extends React.PureComponent<Props, State> {
         <Suggestions suggestions="component_measures" />
         <Helmet defer={false} title={translate('layout.measures')} />
         <PageContentFontWrapper>
-          <DeferredSpinner
-            className="my-10 sw-flex sw-content-center"
-            loading={this.state.loading}
-          />
+          <Spinner className="my-10 sw-flex sw-content-center" loading={this.state.loading} />
 
           {measures.length > 0 ? (
             <div className="sw-grid sw-grid-cols-12 sw-w-full">
index 95a8382f216b7de899e4e5ef79c4f5f3fea36da3..4fb34c5cfa34e2ddf552e5bdbe559704b1d43b57 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { DeferredSpinner } from 'design-system';
+import { Spinner } from 'design-system';
 import * as React from 'react';
 import { getComponentLeaves } from '../../../api/components';
 import SourceViewer from '../../../components/SourceViewer/SourceViewer';
@@ -169,7 +169,7 @@ export default class MeasureOverview extends React.PureComponent<Props, State> {
         />
 
         <div className="sw-p-6">
-          <DeferredSpinner loading={loading} />
+          <Spinner loading={loading} />
           {!loading && this.renderContent(isFileComponent)}
         </div>
       </div>
index 55a12b730d419925c0da3e57f2db54c92a491de6..c7a681f82f1d9dc3a45c059d802a98ce9cf5637f 100644 (file)
@@ -19,7 +19,7 @@
  */
 import {
   ButtonPrimary,
-  DeferredSpinner,
+  Spinner,
   FlagErrorIcon,
   FlagMessage,
   FormField,
@@ -61,7 +61,7 @@ export default function AzurePersonalAccessTokenForm({
   } = usePersonalAccessToken(almSetting, resetPat, onPersonalAccessTokenCreate);
 
   if (checkingPat) {
-    return <DeferredSpinner className="sw-ml-2" loading />;
+    return <Spinner className="sw-ml-2" loading />;
   }
 
   const isInvalid = (validationFailed && !touched) || (touched && !password);
@@ -147,7 +147,7 @@ export default function AzurePersonalAccessTokenForm({
         <ButtonPrimary type="submit" disabled={isInvalid || submitting || !touched}>
           {translate('save')}
         </ButtonPrimary>
-        <DeferredSpinner className="sw-ml-2" loading={submitting} />
+        <Spinner className="sw-ml-2" loading={submitting} />
       </div>
     </form>
   );
index 9bc4aaa19c86ad5a71dad4155258167829f7717b..5d4001510d68d528aee090b570ce3b90231782fc 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { Accordion, DeferredSpinner, FlagMessage, Link, SearchHighlighter } from 'design-system';
+import { Accordion, Spinner, FlagMessage, Link, SearchHighlighter } from 'design-system';
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import ListFooter from '../../../../components/controls/ListFooter';
@@ -66,9 +66,8 @@ export default function AzureProjectAccordion(props: AzureProjectAccordionProps)
     >
       {/* eslint-disable-next-line local-rules/no-conditional-rendering-of-deferredspinner*/}
       {open && (
-        <DeferredSpinner loading={loading}>
-          {/* The extra loading guard is to prevent the flash of the Alert */}
-          {!loading && repositories.length === 0 ? (
+        <Spinner loading={loading}>
+          {repositories.length === 0 ? (
             <FlagMessage variant="warning">
               <span>
                 <FormattedMessage
@@ -118,7 +117,7 @@ export default function AzureProjectAccordion(props: AzureProjectAccordionProps)
               />
             </>
           )}
-        </DeferredSpinner>
+        </Spinner>
       )}
     </Accordion>
   );
index c775999c4d6d2de58ed96b0381315365e2a6f43a..0dfb6b79fd8bb4f777b5cae01ead1af206851c46 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import {
-  DeferredSpinner,
   FlagMessage,
   InputSearch,
   LightPrimary,
   Link,
   PageContentFontWrapper,
+  Spinner,
   Title,
 } from 'design-system';
 import * as React from 'react';
@@ -94,7 +94,7 @@ export default function AzureProjectCreateRenderer(props: AzureProjectCreateRend
         onChangeConfig={props.onSelectedAlmInstanceChange}
       />
 
-      <DeferredSpinner loading={loading} />
+      <Spinner loading={loading} />
 
       {showUrlError && (
         <FlagMessage variant="error" className="sw-mb-2">
@@ -142,7 +142,7 @@ export default function AzureProjectCreateRenderer(props: AzureProjectCreateRend
                 size="full"
               />
             </div>
-            <DeferredSpinner loading={Boolean(searching)}>
+            <Spinner loading={Boolean(searching)}>
               <AzureProjectsList
                 loadingRepositories={loadingRepositories}
                 onOpenProject={props.onOpenProject}
@@ -152,7 +152,7 @@ export default function AzureProjectCreateRenderer(props: AzureProjectCreateRend
                 searchResults={searchResults}
                 searchQuery={searchQuery}
               />
-            </DeferredSpinner>
+            </Spinner>
           </>
         ))}
     </PageContentFontWrapper>
index e703e78bef6936dd38dc22396453831d3cbb50df..e33feb31cafc3d3770c2f100ad8951819d91d1a8 100644 (file)
 
 import {
   ButtonPrimary,
-  DeferredSpinner,
   FlagErrorIcon,
   FlagMessage,
   FormField,
   InputField,
   LightPrimary,
   Link,
+  Spinner,
 } from 'design-system';
 import React from 'react';
 import { FormattedMessage } from 'react-intl';
@@ -60,7 +60,7 @@ export default function BitbucketCloudPersonalAccessTokenForm({
   } = usePersonalAccessToken(almSetting, resetPat, onPersonalAccessTokenCreated);
 
   if (checkingPat) {
-    return <DeferredSpinner className="sw-ml-2" loading />;
+    return <Spinner className="sw-ml-2" loading />;
   }
 
   const isInvalid = validationFailed && !touched;
@@ -179,7 +179,7 @@ export default function BitbucketCloudPersonalAccessTokenForm({
       <ButtonPrimary type="submit" disabled={submitButtonDiabled} className="sw-mb-6">
         {translate('save')}
       </ButtonPrimary>
-      <DeferredSpinner className="sw-ml-2" loading={submitting} />
+      <Spinner className="sw-ml-2" loading={submitting} />
     </form>
   );
 }
index 8cd8154af3150e778ac365ad06fc43ef85d83543..0682393082bc44b4125361882bfdf393a865cd90 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { DeferredSpinner, LightPrimary, Title } from 'design-system';
+import { LightPrimary, Spinner, Title } from 'design-system';
 import * as React from 'react';
 import { translate } from '../../../../helpers/l10n';
 import { BitbucketCloudRepository } from '../../../../types/alm-integration';
@@ -81,7 +81,7 @@ export default function BitbucketCloudProjectCreateRenderer(
         onChangeConfig={props.onSelectedAlmInstanceChange}
       />
 
-      <DeferredSpinner loading={loading} />
+      <Spinner loading={loading} />
 
       {!loading && !selectedAlmInstance && (
         <WrongBindingCountAlert alm={AlmKeys.BitbucketCloud} canAdmin={!!canAdmin} />
index c42983bb13765117ef8ae6d2359b93368caaaa60..9a6654bb0ef0514455bb69dce5226d2c4f34efea 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { DeferredSpinner, LightPrimary, PageContentFontWrapper, Title } from 'design-system';
+import { LightPrimary, PageContentFontWrapper, Spinner, Title } from 'design-system';
 import * as React from 'react';
 import { translate } from '../../../../helpers/l10n';
 import {
@@ -81,7 +81,7 @@ export default function BitbucketProjectCreateRenderer(props: BitbucketProjectCr
         onChangeConfig={props.onSelectedAlmInstanceChange}
       />
 
-      <DeferredSpinner loading={loading}>
+      <Spinner loading={loading}>
         {!loading && !selectedAlmInstance && (
           <WrongBindingCountAlert alm={AlmKeys.BitbucketServer} canAdmin={!!canAdmin} />
         )}
@@ -104,7 +104,7 @@ export default function BitbucketProjectCreateRenderer(props: BitbucketProjectCr
               onImportRepository={props.onImportRepository}
             />
           ))}
-      </DeferredSpinner>
+      </Spinner>
     </PageContentFontWrapper>
   );
 }
index 4afc74f2460441df106b23ba64691006c7070ee0..479d75f929a622590c170102a9c19a9ce6f758f8 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { DeferredSpinner, FlagMessage } from 'design-system';
+import { FlagMessage, Spinner } from 'design-system';
 import * as React from 'react';
 import { translate } from '../../../../helpers/l10n';
 import { BitbucketProject, BitbucketRepository } from '../../../../types/alm-integration';
@@ -50,7 +50,7 @@ export default function BitbucketSearchResults(props: BitbucketSearchResultsProp
   );
 
   return (
-    <DeferredSpinner loading={searching}>
+    <Spinner loading={searching}>
       {filteredSearchResults.length > 0 && (
         <BitbucketProjectAccordion
           onImportRepository={props.onImportRepository}
@@ -74,6 +74,6 @@ export default function BitbucketSearchResults(props: BitbucketSearchResultsProp
           />
         );
       })}
-    </DeferredSpinner>
+    </Spinner>
   );
 }
index 914cee82adc7c43959bb26fe1e1a3572ddb11e96..885f69aa646e907cbb4698807ff89852894cb986 100644 (file)
 
 import {
   ButtonPrimary,
-  DeferredSpinner,
   FlagErrorIcon,
   FlagMessage,
   FormField,
   InputField,
   LightPrimary,
   Link,
+  Spinner,
 } from 'design-system';
 import React from 'react';
 import { FormattedMessage } from 'react-intl';
@@ -58,7 +58,7 @@ export default function BitbucketServerPersonalAccessTokenForm({
   } = usePersonalAccessToken(almSetting, resetPat, onPersonalAccessTokenCreated);
 
   if (checkingPat) {
-    return <DeferredSpinner className="sw-ml-2" loading />;
+    return <Spinner className="sw-ml-2" loading />;
   }
 
   const { url } = almSetting;
@@ -143,7 +143,7 @@ export default function BitbucketServerPersonalAccessTokenForm({
       <ButtonPrimary type="submit" disabled={submitButtonDiabled} className="sw-mb-6">
         {translate('save')}
       </ButtonPrimary>
-      <DeferredSpinner className="sw-ml-2" loading={submitting} />
+      <Spinner className="sw-ml-2" loading={submitting} />
     </form>
   );
 }
index ad3055c6b7ec53c85da4859ecf76df2c7e6215d2..1c0dd2de98ceb6269b68508677704b73412e5c2e 100644 (file)
 /* eslint-disable react/no-unused-prop-types */
 import {
   ButtonSecondary,
-  DeferredSpinner,
   GreyCard,
   HelperHintIcon,
   LightPrimary,
+  Spinner,
   StandoutLink,
   TextMuted,
   Title,
@@ -106,7 +106,7 @@ function renderAlmOption(
         )}
       </div>
 
-      <DeferredSpinner loading={loadingBindings}>
+      <Spinner loading={loadingBindings}>
         {!hasConfig &&
           (canAdmin ? (
             <ButtonSecondary onClick={() => props.onConfigMode(configMode)}>
@@ -117,7 +117,7 @@ function renderAlmOption(
               <HelperHintIcon aria-label="help-tooltip" />
             </HelpTooltip>
           ))}
-      </DeferredSpinner>
+      </Spinner>
     </GreyCard>
   );
 }
index 69de3118fd35ed8eebe15215b669a4d367f629a3..97ffd1f2755f5b076605cc64f3a3fd55ecfdcaec 100644 (file)
 
 import {
   DarkLabel,
-  DeferredSpinner,
   FlagMessage,
   InputSearch,
   InputSelect,
   LightPrimary,
   Link,
+  Spinner,
   Title,
 } from 'design-system';
 import * as React from 'react';
@@ -131,7 +131,7 @@ export default function GitHubProjectCreateRenderer(props: GitHubProjectCreateRe
   } = props;
 
   if (loadingBindings) {
-    return <DeferredSpinner />;
+    return <Spinner />;
   }
 
   return (
@@ -172,7 +172,7 @@ export default function GitHubProjectCreateRenderer(props: GitHubProjectCreateRe
         </FlagMessage>
       )}
 
-      <DeferredSpinner loading={loadingOrganizations && !error}>
+      <Spinner loading={loadingOrganizations && !error}>
         {!error && (
           <div className="sw-flex sw-flex-col">
             <DarkLabel htmlFor="github-choose-organization" className="sw-mb-2">
@@ -215,7 +215,7 @@ export default function GitHubProjectCreateRenderer(props: GitHubProjectCreateRe
             )}
           </div>
         )}
-      </DeferredSpinner>
+      </Spinner>
 
       {renderRepositoryList(props)}
     </>
index 2b12662b9dd62e2ba7c666cbce2e2ee0b7c8a14c..e2d3115b00cfb92b1150333b37ed510efbc7eac0 100644 (file)
 
 import {
   ButtonPrimary,
-  DeferredSpinner,
   FlagErrorIcon,
   FlagMessage,
   FormField,
   InputField,
   LightPrimary,
   Link,
+  Spinner,
 } from 'design-system';
 import React from 'react';
 import { FormattedMessage } from 'react-intl';
@@ -58,7 +58,7 @@ export default function GitlabPersonalAccessTokenForm({
   } = usePersonalAccessToken(almSetting, resetPat, onPersonalAccessTokenCreated);
 
   if (checkingPat) {
-    return <DeferredSpinner className="sw-ml-2" loading />;
+    return <Spinner className="sw-ml-2" loading />;
   }
 
   const isInvalid = validationFailed && !touched;
@@ -136,7 +136,7 @@ export default function GitlabPersonalAccessTokenForm({
       <ButtonPrimary type="submit" disabled={submitButtonDiabled} className="sw-mb-6">
         {translate('save')}
       </ButtonPrimary>
-      <DeferredSpinner className="sw-ml-2" loading={submitting} />
+      <Spinner className="sw-ml-2" loading={submitting} />
     </form>
   );
 }
index 0b843afff802a383f7f4f34336ea731fc019fb7a..655009755db670dce9baf8de9dc5fe90c92d7d71 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-import { DeferredSpinner, LightPrimary, Title } from 'design-system';
+import { LightPrimary, Spinner, Title } from 'design-system';
 import * as React from 'react';
 import { translate } from '../../../../helpers/l10n';
 import { GitlabProject } from '../../../../types/alm-integration';
@@ -79,7 +79,7 @@ export default function GitlabProjectCreateRenderer(props: GitlabProjectCreateRe
         onChangeConfig={props.onSelectedAlmInstanceChange}
       />
 
-      <DeferredSpinner loading={loading} />
+      <Spinner loading={loading} />
 
       {!loading && !selectedAlmInstance && (
         <WrongBindingCountAlert alm={AlmKeys.GitLab} canAdmin={!!canAdmin} />
index 7970bde1bf16e7c0521ab71f642f94e9f331f8a2..c9244c31a638a5d7b3787495639377ee4a9e2529 100644 (file)
@@ -76,9 +76,6 @@ it('should redirect to github authorization page when not already authorized', a
   await selectEvent.select(ui.instanceSelector.get(), [/conf-github-1/]);
 
   expect(window.location.replace).toHaveBeenCalled();
-  expect(
-    screen.getByText('onboarding.create_project.github.choose_organization')
-  ).toBeInTheDocument();
 });
 
 it('should not redirect to github when url is malformated', async () => {
index f331941bdc52b36feef5501f710c133e0165d6f8..003be840a138c341daab726e059b2ca8c01776c4 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { ButtonPrimary, DeferredSpinner, Link, Title } from 'design-system';
+import { ButtonPrimary, Link, Spinner, Title } from 'design-system';
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import { Router } from '../../../../components/hoc/withRouter';
@@ -84,7 +84,7 @@ export default function NewCodeDefinitionSelection(props: Props) {
           type="submit"
         >
           {translate('onboarding.create_project.new_code_definition.create_project')}
-          <DeferredSpinner className="sw-ml-2" loading={submitting} />
+          <Spinner className="sw-ml-2" loading={submitting} />
         </ButtonPrimary>
       </div>
     </div>
index fd716931b765eea858f94655310fec3b0183f0e9..3345f248d988dfdd08309ef77df4c687128968c9 100644 (file)
@@ -20,9 +20,9 @@
 import * as React from 'react';
 import { useCallback } from 'react';
 import { deleteGroup } from '../../../api/user_groups';
-import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons';
 import SimpleModal from '../../../components/controls/SimpleModal';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons';
+import Spinner from '../../../components/ui/Spinner';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { Group } from '../../../types/types';
 
@@ -55,7 +55,7 @@ export default function DeleteGroupForm(props: Props) {
           </div>
 
           <footer className="modal-foot">
-            <DeferredSpinner className="spacer-right" loading={submitting} />
+            <Spinner className="spacer-right" loading={submitting} />
             <SubmitButton className="button-red" disabled={submitting}>
               {translate('delete')}
             </SubmitButton>
index 958d8537ee797b5abc5bbf44351ae4d7163c5d12..b13b23bb7f2de48d1c5c28f44727ace365908976 100644 (file)
 import * as React from 'react';
 import { useCallback, useEffect, useState } from 'react';
 import { createGroup, updateGroup } from '../../../api/user_groups';
-import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons';
 import SimpleModal from '../../../components/controls/SimpleModal';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons';
 import MandatoryFieldMarker from '../../../components/ui/MandatoryFieldMarker';
 import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 import { omitNil } from '../../../helpers/request';
 import { Group } from '../../../types/types';
@@ -123,7 +123,7 @@ export default function GroupForm(props: Props) {
           </div>
 
           <footer className="modal-foot">
-            <DeferredSpinner className="spacer-right" loading={submitting} />
+            <Spinner className="spacer-right" loading={submitting} />
             <SubmitButton disabled={submitting}>
               {create ? translate('create') : translate('update_verb')}
             </SubmitButton>
index 76d326fad4950536dd0ef4974e0f49fa799ea3e4..664b6e4d79e4cd02345856722013caffaa190c59 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { DeferredSpinner } from 'design-system/lib';
+import { Spinner } from 'design-system/lib';
 import * as React from 'react';
 import { getUsersInGroup } from '../../../api/user_groups';
 import ListFooter from '../../../components/controls/ListFooter';
@@ -85,7 +85,7 @@ export default function ViewMembersModal(props: Props) {
           value={query}
         />
         <div className="select-list-list-container spacer-top">
-          <DeferredSpinner loading={loading}>
+          <Spinner loading={loading}>
             <ul className="menu">
               {users.map((user) => (
                 <li key={user.login} className="display-flex-center">
@@ -104,7 +104,7 @@ export default function ViewMembersModal(props: Props) {
                 </li>
               ))}
             </ul>
-          </DeferredSpinner>
+          </Spinner>
         </div>
         {total !== undefined && (
           <ListFooter count={users.length} loadMore={() => setPage((p) => p + 1)} total={total} />
index e191ebc3d8bea8c7f5d609549f56a198a4607b10..b9c4fb78f98cb622d1f774272d9840469e025774 100644 (file)
@@ -258,11 +258,10 @@ describe('issues app', () => {
       expect(screen.getByRole('button', { name: 'issues.bulk_change_X_issues.10' })).toHaveFocus();
       await user.click(screen.getByRole('checkbox', { name: 'issues.select_all_issues' }));
 
-      // Check that we bulk change the selected issue
-      const issueBoxFixThat = within(screen.getByRole('region', { name: 'Fix that' }));
-
       expect(
-        issueBoxFixThat.getByLabelText('issue.type.type_x_click_to_change.issue.type.CODE_SMELL')
+        within(screen.getByRole('region', { name: 'Fix that' })).getByLabelText(
+          'issue.type.type_x_click_to_change.issue.type.CODE_SMELL'
+        )
       ).toBeInTheDocument();
 
       await user.click(
@@ -280,7 +279,9 @@ describe('issues app', () => {
       await user.click(screen.getByRole('button', { name: 'apply' }));
 
       expect(
-        issueBoxFixThat.getByLabelText('issue.type.type_x_click_to_change.issue.type.BUG')
+        await within(screen.getByRole('region', { name: 'Fix that' })).findByLabelText(
+          'issue.type.type_x_click_to_change.issue.type.BUG'
+        )
       ).toBeInTheDocument();
     });
   });
index bbe8bcc8a46df6bcdeea6cf57c417344467deafc..b9ace10c84da003e9f8cf092688c21eba1b17a03 100644 (file)
@@ -21,7 +21,6 @@
 import {
   ButtonPrimary,
   Checkbox,
-  DeferredSpinner,
   FlagMessage,
   FormField,
   HelperHintIcon,
@@ -31,6 +30,7 @@ import {
   LightLabel,
   Modal,
   RadioButton,
+  Spinner,
 } from 'design-system';
 import { pickBy, sortBy } from 'lodash';
 import * as React from 'react';
@@ -449,7 +449,7 @@ export class BulkChangeModal extends React.PureComponent<Props, State> {
     const limitReached = paging && paging.total > MAX_PAGE_SIZE;
 
     return (
-      <DeferredSpinner loading={loading}>
+      <Spinner loading={loading}>
         <form id="bulk-change-form" onSubmit={this.handleSubmit}>
           {limitReached && (
             <FlagMessage className="sw-mb-4" variant="warning">
@@ -479,7 +479,7 @@ export class BulkChangeModal extends React.PureComponent<Props, State> {
             <FlagMessage variant="warning">{translate('issue_bulk_change.no_match')}</FlagMessage>
           )}
         </form>
-      </DeferredSpinner>
+      </Spinner>
     );
   };
 
index a10a9e735403f149cf488c6813107dff7de9b5a6..4c09972fd5af73eb3fa808ff3f4421cf547b1e9e 100644 (file)
@@ -51,7 +51,7 @@ import withIndexationGuard from '../../../components/hoc/withIndexationGuard';
 import { Location, Router, withRouter } from '../../../components/hoc/withRouter';
 import IssueTabViewer from '../../../components/rules/IssueTabViewer';
 import '../../../components/search-navigator.css';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { fillBranchLike, getBranchLikeQuery, isSameBranchLike } from '../../../helpers/branch-like';
 import handleRequiredAuthentication from '../../../helpers/handleRequiredAuthentication';
 import { parseIssueFromResponse } from '../../../helpers/issues';
@@ -1227,7 +1227,7 @@ export class App extends React.PureComponent<Props, State> {
           >
             {this.renderHeader({ openIssue, paging, selectedIndex })}
 
-            <DeferredSpinner loading={loadingRule}>
+            <Spinner loading={loadingRule}>
               {/* eslint-disable-next-line local-rules/no-conditional-rendering-of-deferredspinner */}
               {openIssue && openRuleDetails ? (
                 <IssueTabViewer
@@ -1262,7 +1262,7 @@ export class App extends React.PureComponent<Props, State> {
                   className="sw-px-6 sw-pb-6"
                   style={{ marginTop: `-${PSEUDO_SHADOW_HEIGHT}px` }}
                 >
-                  <DeferredSpinner
+                  <Spinner
                     ariaLabel={translate('issues.loading_issues')}
                     className="sw-mt-4"
                     loading={loading}
@@ -1291,10 +1291,10 @@ export class App extends React.PureComponent<Props, State> {
                     )}
 
                     {this.renderList()}
-                  </DeferredSpinner>
+                  </Spinner>
                 </div>
               )}
-            </DeferredSpinner>
+            </Spinner>
           </div>
         )}
       </ScreenPositionHelper>
index 8e5e8551a022c626e7b0b055bd4a672202604205..018c43287b7f7d6b24cafa776903441561e892f0 100644 (file)
@@ -33,7 +33,7 @@ import {
   issuesByComponentAndLine,
 } from '../../../components/SourceViewer/helpers/indexing';
 import { Alert } from '../../../components/ui/Alert';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { WorkspaceContext } from '../../../components/workspace/context';
 import { getBranchLikeQuery } from '../../../helpers/branch-like';
 import { throwGlobalError } from '../../../helpers/error';
@@ -192,7 +192,7 @@ export default class CrossComponentSourceViewer extends React.PureComponent<Prop
     if (loading) {
       return (
         <div>
-          <DeferredSpinner ariaLabel={translate('code_viewer.loading')} />
+          <Spinner ariaLabel={translate('code_viewer.loading')} />
         </div>
       );
     }
index 3c29418ac850163354f6ee72a0e2d9ff77488072..a9973b3484862ce7775ae942d2d2ee647833cec4 100644 (file)
@@ -23,11 +23,11 @@ import classNames from 'classnames';
 import {
   ChevronRightIcon,
   CopyIcon,
-  DeferredSpinner,
   HoverLink,
   InteractiveIcon,
   LightLabel,
   Link,
+  Spinner,
   ThemeProp,
   UnfoldIcon,
   themeColor,
@@ -156,7 +156,7 @@ function IssueSourceViewerHeader(props: Props & ThemeProp) {
         </div>
       )}
 
-      <DeferredSpinner className="sw-mr-1" loading={loading} />
+      <Spinner className="sw-mr-1" loading={loading} />
 
       {expandable && !loading && (
         <div className="sw-ml-4">
index bab12fa09231194c1196a72490b7d25561f44c9d..17c203ef2279db0f125bca94b932e559a7131571 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import styled from '@emotion/styled';
-import { DeferredSpinner, SubnavigationHeading, themeBorder } from 'design-system';
+import { Spinner, SubnavigationHeading, themeBorder } from 'design-system';
 import * as React from 'react';
 import { Paging } from '../../../types/types';
 import IssuesCounter from '../components/IssuesCounter';
@@ -34,9 +34,9 @@ export default function SubnavigationIssuesListHeader(props: Props) {
 
   return (
     <StyledHeader>
-      <DeferredSpinner loading={loading}>
+      <Spinner loading={loading}>
         {paging && <IssuesCounter current={selectedIndex} total={paging.total} />}
-      </DeferredSpinner>
+      </Spinner>
     </StyledHeader>
   );
 }
index 9b5afe83d71f69a6fce89f3356a03dd79b003383..cd302fabda35840f1f6dd13da791ddd48db9a49c 100644 (file)
@@ -27,7 +27,7 @@ import DocLink from '../../components/common/DocLink';
 import Suggestions from '../../components/embed-docs-modal/Suggestions';
 import { Location, Router, withRouter } from '../../components/hoc/withRouter';
 import { Alert } from '../../components/ui/Alert';
-import DeferredSpinner from '../../components/ui/DeferredSpinner';
+import Spinner from '../../components/ui/Spinner';
 import { translate } from '../../helpers/l10n';
 import { EditionKey } from '../../types/editions';
 import { PendingPluginResult, Plugin, RiskConsent } from '../../types/plugins';
@@ -196,7 +196,7 @@ class App extends React.PureComponent<Props, State> {
           updateCenterActive={this.props.updateCenterActive}
           updateQuery={this.updateQuery}
         />
-        <DeferredSpinner loading={loadingPlugins}>
+        <Spinner loading={loadingPlugins}>
           {filteredPlugins.length === 0 &&
             translate('marketplace.plugin_list.no_plugins', query.filter)}
           {filteredPlugins.length > 0 && (
@@ -210,7 +210,7 @@ class App extends React.PureComponent<Props, State> {
               <Footer total={filteredPlugins.length} />
             </>
           )}
-        </DeferredSpinner>
+        </Spinner>
       </main>
     );
   }
index 47ede69bd925166566d7d6a1db8b84cec80d6128..b1bec3ff132649fb7b82ab84b5775923bec77f22 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { BasicSeparator, Card, DeferredSpinner, PageTitle } from 'design-system';
+import { BasicSeparator, Card, PageTitle, Spinner } from 'design-system';
 import * as React from 'react';
 import GraphsHeader from '../../../components/activity-graph/GraphsHeader';
 import GraphsHistory from '../../../components/activity-graph/GraphsHistory';
@@ -115,7 +115,7 @@ export function ActivityPanel(props: ActivityPanelProps) {
         </div>
       </Card>
       <Card className="sw-mt-4" data-test="overview__activity-analyses">
-        <DeferredSpinner loading={loading}>
+        <Spinner loading={loading}>
           {filteredAnalyses.length === 0 ? (
             <p>{translate('no_results')}</p>
           ) : (
@@ -126,7 +126,7 @@ export function ActivityPanel(props: ActivityPanelProps) {
               </div>
             ))
           )}
-        </DeferredSpinner>
+        </Spinner>
       </Card>
     </div>
   );
index f6cb093a55981a0ec2f43b95e3593fc9d8c0e158..63367c2a4421d422f9b7605f2102df7affda0dc8 100644 (file)
 import {
   Card,
   CoverageIndicator,
-  DeferredSpinner,
   DuplicationsIndicator,
   FlagMessage,
   LightLabel,
   PageTitle,
+  Spinner,
   ToggleButton,
 } from 'design-system';
 import * as React from 'react';
@@ -128,7 +128,7 @@ export function MeasuresPanel(props: MeasuresPanelProps) {
 
       {loading ? (
         <div>
-          <DeferredSpinner loading={loading} />
+          <Spinner loading={loading} />
         </div>
       ) : (
         <>
index c31b5cc7f42764d64f408226cf778074a729d38b..3df69b32c76dd6dcce2dc2b926eecd095bde30ad 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-import { BasicSeparator, Card, DeferredSpinner } from 'design-system';
+import { BasicSeparator, Card, Spinner } from 'design-system';
 import { flatMap } from 'lodash';
 import * as React from 'react';
 import { ComponentQualifier, isApplication } from '../../../types/component';
@@ -82,7 +82,7 @@ export function QualityGatePanel(props: QualityGatePanelProps) {
         <div>
           {loading ? (
             <div className="sw-p-6">
-              <DeferredSpinner loading={loading} />
+              <Spinner loading={loading} />
             </div>
           ) : (
             <>
index 783b6ed1a89843da7f8b7d72a3f447e5248708e1..e7addbf5864540506f8b451c4110b532366214b5 100644 (file)
@@ -49,7 +49,7 @@ export function EmptyOverview(props: EmptyOverviewProps) {
       .length > 0;
 
   if (isLoading) {
-    return <Spinner />;
+    return <Spinner loading />;
   }
 
   if (component.qualifier === ComponentQualifier.Application) {
@@ -75,7 +75,7 @@ export function EmptyOverview(props: EmptyOverviewProps) {
         <PageContentFontWrapper>
           <FlagMessage variant="warning">
             {translate('provisioning.permission_synch_in_progress')}
-            <Spinner className="sw-ml-8 sw-hidden" aria-hidden />
+            <Spinner className="sw-ml-8 sw-hidden" aria-hidden loading />
           </FlagMessage>
         </PageContentFontWrapper>
       </LargeCenteredLayout>
index e01f332016f2baba1d4cbef3e7c85b1cc7367b2f..61389583269baea80e8dc13d134b8d797f4b2d2e 100644 (file)
@@ -21,12 +21,12 @@ import {
   BasicSeparator,
   Card,
   CoverageIndicator,
-  DeferredSpinner,
   DuplicationsIndicator,
   HelperHintIcon,
   LargeCenteredLayout,
   Link,
   PageTitle,
+  Spinner,
   TextMuted,
 } from 'design-system';
 import { uniq } from 'lodash';
@@ -95,7 +95,7 @@ export default function PullRequestOverview(props: Props) {
     return (
       <LargeCenteredLayout>
         <div className="sw-p-6">
-          <DeferredSpinner loading />
+          <Spinner loading />
         </div>
       </LargeCenteredLayout>
     );
index fbf1ffe6ca560388e65b38770458693c6db985ee..c29b65a05a12d8afd2958435252ea4420b40a2e1 100644 (file)
@@ -18,9 +18,9 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons';
 import SimpleModal from '../../../components/controls/SimpleModal';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons';
+import Spinner from '../../../components/ui/Spinner';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { PermissionTemplate } from '../../../types/types';
 
@@ -49,7 +49,7 @@ export default function DeleteForm({ onClose, onSubmit, permissionTemplate: t }:
           </div>
 
           <div className="modal-foot">
-            <DeferredSpinner className="spacer-right" loading={submitting} />
+            <Spinner className="spacer-right" loading={submitting} />
             <SubmitButton className="button-red" disabled={submitting}>
               {translate('delete')}
             </SubmitButton>
index c7927d28bb35a8c2d4394801edc2ea71c9fd6744..6f00ae2600095e5279d01176ccf7dd0d4573f82e 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons';
 import SimpleModal from '../../../components/controls/SimpleModal';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons';
 import MandatoryFieldMarker from '../../../components/ui/MandatoryFieldMarker';
 import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -141,7 +141,7 @@ export default class Form extends React.PureComponent<Props, State> {
             </div>
 
             <div className="modal-foot">
-              <DeferredSpinner className="spacer-right" loading={submitting} />
+              <Spinner className="spacer-right" loading={submitting} />
               <SubmitButton disabled={submitting} id="permission-template-submit">
                 {this.props.confirmButtonText}
               </SubmitButton>
index 9b19b8dd794a300aefad74984ee2c852533fb561..8ba5375730f6698e9a75fcbb643133a90dd60921 100644 (file)
@@ -22,7 +22,7 @@ import { createPermissionTemplate } from '../../../api/permissions';
 import { Button } from '../../../components/controls/buttons';
 import { Router, withRouter } from '../../../components/hoc/withRouter';
 import { Alert } from '../../../components/ui/Alert';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { throwGlobalError } from '../../../helpers/error';
 import { translate } from '../../../helpers/l10n';
 import { useGithubProvisioningEnabledQuery } from '../../../queries/identity-provider';
@@ -62,7 +62,7 @@ function Header(props: Props) {
       <header className="page-header" id="project-permissions-header">
         <h1 className="page-title">{translate('permission_templates.page')}</h1>
 
-        <DeferredSpinner loading={!ready} />
+        <Spinner loading={!ready} />
 
         <div className="page-actions">
           <Button onClick={() => setCreateModal(true)}>{translate('create')}</Button>
index b8396164e9a5d9844a1e219bfb3bac160ba1e113..3625d0be7a519f3cc139958346069f8cc5044a16 100644 (file)
@@ -19,7 +19,7 @@
  */
 import * as React from 'react';
 import Link from '../../../components/common/Link';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 import { PermissionTemplate } from '../../../types/types';
 import { PERMISSION_TEMPLATES_PATH } from '../utils';
@@ -42,7 +42,7 @@ export default function TemplateHeader(props: Props) {
 
       <h1 className="page-title">{template.name}</h1>
 
-      <DeferredSpinner loading={loading} />
+      <Spinner loading={loading} />
 
       <div className="pull-right">
         <ActionsCell
index bc1d118871c8986a324b6037ec0b703e99b8c242..54e8541bc9684c6563eac268c04ba6e419036f45 100644 (file)
  */
 import * as React from 'react';
 import { applyTemplateToProject, getPermissionTemplates } from '../../../../api/permissions';
-import { ResetButtonLink, SubmitButton } from '../../../../components/controls/buttons';
 import Select from '../../../../components/controls/Select';
 import SimpleModal from '../../../../components/controls/SimpleModal';
+import { ResetButtonLink, SubmitButton } from '../../../../components/controls/buttons';
 import { Alert } from '../../../../components/ui/Alert';
-import DeferredSpinner from '../../../../components/ui/DeferredSpinner';
 import MandatoryFieldMarker from '../../../../components/ui/MandatoryFieldMarker';
 import MandatoryFieldsExplanation from '../../../../components/ui/MandatoryFieldsExplanation';
+import Spinner from '../../../../components/ui/Spinner';
 import { translate, translateWithParameters } from '../../../../helpers/l10n';
 import { PermissionTemplate } from '../../../../types/types';
 
@@ -147,7 +147,7 @@ export default class ApplyTemplate extends React.PureComponent<Props, State> {
             </div>
 
             <footer className="modal-foot">
-              <DeferredSpinner className="spacer-right" loading={submitting} />
+              <Spinner className="spacer-right" loading={submitting} />
               {!this.state.done && (
                 <SubmitButton disabled={submitting || !this.state.permissionTemplate}>
                   {translate('apply')}
index 38d5012a19a5e60729edf81e0d1b14b3a14cade9..0b452177aca0b7d055f551c2748cbbe091593808 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import GitHubSynchronisationWarning from '../../../../app/components/GitHubSynchronisationWarning';
 import { Button } from '../../../../components/controls/buttons';
 import { Alert } from '../../../../components/ui/Alert';
-import DeferredSpinner from '../../../../components/ui/DeferredSpinner';
+import Spinner from '../../../../components/ui/Spinner';
 import { translate } from '../../../../helpers/l10n';
 import { getBaseUrl } from '../../../../helpers/system';
 import { useGithubProvisioningEnabledQuery } from '../../../../queries/identity-provider';
@@ -81,7 +81,7 @@ export default function PageHeader(props: Props) {
         )}
       </h1>
 
-      <DeferredSpinner className="spacer-left" loading={loading} />
+      <Spinner className="spacer-left" loading={loading} />
 
       {canApplyPermissionTemplate && (
         <div className="page-actions">
index 6ea2f6bf60a9bdf945c83efb9148485faaa66c48..b17cd760b5483da3d15b13e1a091eebc9b02a10c 100644 (file)
@@ -20,7 +20,7 @@
 import styled from '@emotion/styled';
 import classNames from 'classnames';
 import { isEqual } from 'date-fns';
-import { Badge, DeferredSpinner, LightLabel, themeColor } from 'design-system';
+import { Badge, LightLabel, Spinner, themeColor } from 'design-system';
 import * as React from 'react';
 import Tooltip from '../../../components/controls/Tooltip';
 import DateFormatter from '../../../components/intl/DateFormatter';
@@ -113,7 +113,7 @@ export default class ProjectActivityAnalysesList extends React.PureComponent<Pro
         <div>
           {this.props.initializing ? (
             <div className="sw-p-4 sw-body-sm">
-              <DeferredSpinner />
+              <Spinner />
             </div>
           ) : (
             <div className="sw-p-4 sw-body-sm">
@@ -183,7 +183,7 @@ export default class ProjectActivityAnalysesList extends React.PureComponent<Pro
         })}
         {this.props.analysesLoading && (
           <li className="sw-text-center">
-            <DeferredSpinner />
+            <Spinner />
           </li>
         )}
       </ul>
index f4aa054ff89a45438024b92606c32f57a230b7f7..420f7f758626c204c2b477d54250c54969fd42e4 100644 (file)
@@ -25,7 +25,7 @@ import Select from '../../../components/controls/Select';
 import Tooltip from '../../../components/controls/Tooltip';
 import DateFormatter, { longFormatterOption } from '../../../components/intl/DateFormatter';
 import TimeFormatter from '../../../components/intl/TimeFormatter';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { parseDate, toShortISO8601String } from '../../../helpers/dates';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { ParsedAnalysis } from '../../../types/project-activity';
@@ -138,7 +138,7 @@ function BranchAnalysisListRenderer(
       </div>
       <div className="branch-analysis-list-wrapper">
         <div className="bordered branch-analysis-list" onScroll={props.handleScroll}>
-          <DeferredSpinner className="big-spacer-top" loading={loading} />
+          <Spinner className="big-spacer-top" loading={loading} />
 
           {!loading && !hasFilteredData ? (
             <div className="big-spacer-top big-spacer-bottom strong">
index 5d14760a99bcecf296967e23d7d8540eef7e51c8..67fe858571a5a9c83ea3bdcac3e1ca2c458fa456 100644 (file)
@@ -25,7 +25,7 @@ import { ResetButtonLink, SubmitButton } from '../../../components/controls/butt
 import NewCodeDefinitionDaysOption from '../../../components/new-code-definition/NewCodeDefinitionDaysOption';
 import NewCodeDefinitionPreviousVersionOption from '../../../components/new-code-definition/NewCodeDefinitionPreviousVersionOption';
 import NewCodeDefinitionWarning from '../../../components/new-code-definition/NewCodeDefinitionWarning';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { toISO8601WithOffsetString } from '../../../helpers/dates';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { getNumberOfDaysDefaultValue } from '../../../helpers/new-code-definition';
@@ -218,7 +218,7 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop
             )}
           </div>
           <footer className="modal-foot">
-            <DeferredSpinner className="spacer-right" loading={saving} />
+            <Spinner className="spacer-right" loading={saving} />
             <SubmitButton disabled={!isChanged || saving || !isValid}>
               {translate('save')}
             </SubmitButton>
index 857895cb8dd47cf2500b8214746a26c92aee6c0c..ff1aa3a432958c3daf0fc211cafea7032feb1341 100644 (file)
@@ -19,7 +19,7 @@
  */
 import * as React from 'react';
 import { listBranchesNewCodePeriod, resetNewCodePeriod } from '../../../api/newCodePeriod';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { isBranch, sortBranches } from '../../../helpers/branch-like';
 import { translate } from '../../../helpers/l10n';
 import { DEFAULT_NEW_CODE_DEFINITION_TYPE } from '../../../helpers/new-code-definition';
@@ -141,7 +141,7 @@ export default class BranchList extends React.PureComponent<Props, State> {
     }
 
     if (loading) {
-      return <DeferredSpinner />;
+      return <Spinner />;
     }
 
     return (
index 3d73558be4dc99990ad4c2e867e133378dd6c70f..620c5d786539aeb496fff581c073339962fba36c 100644 (file)
@@ -29,7 +29,7 @@ import withAvailableFeatures, {
 import withComponentContext from '../../../app/components/componentContext/withComponentContext';
 import Suggestions from '../../../components/embed-docs-modal/Suggestions';
 import AlertSuccessIcon from '../../../components/icons/AlertSuccessIcon';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { isBranch, sortBranches } from '../../../helpers/branch-like';
 import { translate } from '../../../helpers/l10n';
 import {
@@ -280,7 +280,7 @@ class ProjectBaselineApp extends React.PureComponent<Props, State> {
         <Helmet defer={false} title={translate('project_baseline.page')} />
         <div className="page page-limited">
           <AppHeader canAdmin={!!appState.canAdmin} />
-          <DeferredSpinner loading={loading} />
+          <Spinner loading={loading} />
 
           {!loading && (
             <div className="panel-white project-baseline">
index 20756d3f5eb7431c1c4c2ce096cbb6e6856d25f6..affb46b2e5f131f8f7d3fa4f6587266da0d1eeb7 100644 (file)
@@ -28,7 +28,7 @@ import NewCodeDefinitionDaysOption from '../../../components/new-code-definition
 import NewCodeDefinitionPreviousVersionOption from '../../../components/new-code-definition/NewCodeDefinitionPreviousVersionOption';
 import NewCodeDefinitionWarning from '../../../components/new-code-definition/NewCodeDefinitionWarning';
 import { Alert } from '../../../components/ui/Alert';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 import { isNewCodeDefinitionCompliant } from '../../../helpers/new-code-definition';
 import { Branch } from '../../../types/branch-like';
@@ -196,7 +196,7 @@ export default function ProjectBaselineSelector(props: ProjectBaselineSelectorPr
         <Alert variant="info" className="spacer-bottom">
           {translate('baseline.next_analysis_notice')}
         </Alert>
-        <DeferredSpinner className="spacer-right" loading={saving} />
+        <Spinner className="spacer-right" loading={saving} />
         <SubmitButton disabled={saving || !isValid || !isChanged}>{translate('save')}</SubmitButton>
         <ResetButtonLink className="spacer-left" onClick={props.onCancel}>
           {translate('cancel')}
index d0fd998284e5630c8417521b5a3d4659fec7802d..06e16afec8a5c8f2c17297aa0a37b69583d8e424 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import { useEffect } from 'react';
 import HelpTooltip from '../../../components/controls/HelpTooltip';
 import Toggle from '../../../components/controls/Toggle';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { isMainBranch } from '../../../helpers/branch-like';
 import { translate } from '../../../helpers/l10n';
 import { useExcludeFromPurgeMutation } from '../../../queries/branch';
@@ -52,7 +52,7 @@ export default function BranchPurgeSetting(props: Props) {
     <>
       <Toggle disabled={disabled} onChange={handleOnChange} value={branch.excludedFromPurge} />
       <span className="spacer-left">
-        <DeferredSpinner loading={isLoading} />
+        <Spinner loading={isLoading} />
       </span>
       {isTheMainBranch && (
         <HelpTooltip
index edb949f5f5092577763f0829be15c6dd5fadd580..afa5d48ce77340402fd7e533bd8fa4abb69bc52a 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import Link from '../../../components/common/Link';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 import { formatMeasure } from '../../../helpers/measures';
 
@@ -34,7 +34,7 @@ function LifetimeInformationRenderer(props: LifetimeInformationRendererProps) {
   const { branchAndPullRequestLifeTimeInDays, canAdmin, loading } = props;
 
   return (
-    <DeferredSpinner loading={loading}>
+    <Spinner loading={loading}>
       {branchAndPullRequestLifeTimeInDays && (
         <p className="page-description">
           <FormattedMessage
@@ -53,7 +53,7 @@ function LifetimeInformationRenderer(props: LifetimeInformationRendererProps) {
           )}
         </p>
       )}
-    </DeferredSpinner>
+    </Spinner>
   );
 }
 
index d5ae521d4b815c4a58bd861929feac1ad3d6a207..cb9c461c5c3a9cf4a33cc967f2d7edaf2f3a8e54 100644 (file)
@@ -21,11 +21,11 @@ import {
   BasicSeparator,
   ButtonSecondary,
   CodeSnippet,
-  DeferredSpinner,
   FlagMessage,
   FormField,
   IllustratedSelectionCard,
   InputSelect,
+  Spinner,
   SubTitle,
   ToggleButton,
 } from 'design-system';
@@ -94,7 +94,7 @@ export default function ProjectBadges(props: ProjectBadgesProps) {
       <SubTitle>{translate('overview.badges.get_badge')}</SubTitle>
       <p className="big-spacer-bottom">{translate('overview.badges.description', qualifier)}</p>
 
-      <DeferredSpinner loading={isLoading || isEmpty(token)}>
+      <Spinner loading={isLoading || isEmpty(token)}>
         <div className="sw-flex sw-space-x-4 sw-mb-4">
           <IllustratedSelectionCard
             className="sw-w-abs-300 it__badge-button"
@@ -127,7 +127,7 @@ export default function ProjectBadges(props: ProjectBadgesProps) {
             )}
           />
         </div>
-      </DeferredSpinner>
+      </Spinner>
 
       {BadgeType.measure === selectedType && (
         <FormField htmlFor="badge-param-customize" label={translate('overview.badges.metric')}>
@@ -162,7 +162,7 @@ export default function ProjectBadges(props: ProjectBadgesProps) {
         </div>
       </FormField>
 
-      <DeferredSpinner className="spacer-top spacer-bottom" loading={isFetchingToken || isRenewing}>
+      <Spinner className="spacer-top spacer-bottom" loading={isFetchingToken || isRenewing}>
         {!isLoading && (
           <CodeSnippet
             language="plaintext"
@@ -171,7 +171,7 @@ export default function ProjectBadges(props: ProjectBadgesProps) {
             wrap
           />
         )}
-      </DeferredSpinner>
+      </Spinner>
 
       <FlagMessage className="sw-w-full" variant="warning">
         <p>
index 760cc32b7d2c24407d0d49b8fc34a94a588d875d..837b5ca4149952ec275acaea02666efaf3f01ad6 100644 (file)
@@ -23,7 +23,7 @@ import {
   WithNotificationsProps,
   withNotifications,
 } from '../../../components/hoc/withNotifications';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { hasMessage, translate, translateWithParameters } from '../../../helpers/l10n';
 import { NotificationProjectType } from '../../../types/notifications';
 import { Component } from '../../../types/types';
@@ -79,7 +79,7 @@ export function ProjectNotifications(props: WithNotificationsProps & Props) {
         {translate('notification.dispatcher.information')}
       </FlagMessage>
 
-      <DeferredSpinner loading={loading}>
+      <Spinner loading={loading}>
         <h3 id="notifications-update-title" className="sw-mt-6">
           {translate('project_information.project_notifications.title')}
         </h3>
@@ -102,7 +102,7 @@ export function ProjectNotifications(props: WithNotificationsProps & Props) {
             </li>
           ))}
         </ul>
-      </DeferredSpinner>
+      </Spinner>
     </form>
   );
 }
index a1ac7d9582e141a3d4968581ad5947b93da79ae6..30823e150ca74f16fe3b614490b0f30044e42725 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons';
 import SimpleModal from '../../components/controls/SimpleModal';
-import DeferredSpinner from '../../components/ui/DeferredSpinner';
+import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons';
 import MandatoryFieldMarker from '../../components/ui/MandatoryFieldMarker';
 import MandatoryFieldsExplanation from '../../components/ui/MandatoryFieldsExplanation';
+import Spinner from '../../components/ui/Spinner';
 import { translate } from '../../helpers/l10n';
 
 interface Props {
@@ -104,7 +104,7 @@ export default class CreationModal extends React.PureComponent<Props, State> {
             </div>
 
             <footer className="modal-foot">
-              <DeferredSpinner className="spacer-right" loading={submitting} />
+              <Spinner className="spacer-right" loading={submitting} />
               <SubmitButton disabled={submitting} id="create-link-confirm">
                 {translate('create')}
               </SubmitButton>
index 8da9634a0513a7c394d99ef03136342a76d84197..e9209cded54f564c13ea0ced2795ebbad236efc1 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
 import { createLink, deleteLink, getProjectLinks } from '../../api/projectLinks';
 import withComponentContext from '../../app/components/componentContext/withComponentContext';
-import DeferredSpinner from '../../components/ui/DeferredSpinner';
+import Spinner from '../../components/ui/Spinner';
 import { translate } from '../../helpers/l10n';
 import { Component, ProjectLink } from '../../types/types';
 import Header from './Header';
@@ -105,9 +105,9 @@ export class ProjectLinksApp extends React.PureComponent<Props, State> {
       <div className="page page-limited">
         <Helmet defer={false} title={translate('project_links.page')} />
         <Header onCreate={this.handleCreateLink} />
-        <DeferredSpinner loading={loading}>
+        <Spinner loading={loading}>
           {links && <Table links={links} onDelete={this.handleDeleteLink} />}
-        </DeferredSpinner>
+        </Spinner>
       </div>
     );
   }
index 8c30d1d2f83c2f0a2d79c8c5ccab6bd0876b8933..654ef2df955aa0c6f6930477b11eacaeee273cee 100644 (file)
@@ -36,7 +36,7 @@ import ScreenPositionHelper from '../../../components/common/ScreenPositionHelpe
 import Suggestions from '../../../components/embed-docs-modal/Suggestions';
 import { Location, Router, withRouter } from '../../../components/hoc/withRouter';
 import '../../../components/search-navigator.css';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import handleRequiredAuthentication from '../../../helpers/handleRequiredAuthentication';
 import { translate } from '../../../helpers/l10n';
 import { addSideBarClass, removeSideBarClass } from '../../../helpers/pages';
@@ -280,7 +280,7 @@ export class AllProjects extends React.PureComponent<Props, State> {
 
   renderMain = () => {
     if (this.state.loading && this.state.projects === undefined) {
-      return <DeferredSpinner />;
+      return <Spinner />;
     }
 
     return (
index 26d929bcc014c1fdb1aa355091bd932696e971cf..64a78adfdd5c1ac49a907175b4ce20d064784abc 100644 (file)
@@ -21,7 +21,7 @@ import React, { useState } from 'react';
 import { getComponentNavigation } from '../../api/navigation';
 import { Project } from '../../api/project-management';
 import ActionsDropdown, { ActionsDropdownItem } from '../../components/controls/ActionsDropdown';
-import DeferredSpinner from '../../components/ui/DeferredSpinner';
+import Spinner from '../../components/ui/Spinner';
 import { translate, translateWithParameters } from '../../helpers/l10n';
 import { getComponentPermissionsUrl } from '../../helpers/urls';
 import { useGithubProvisioningEnabledQuery } from '../../queries/identity-provider';
@@ -76,7 +76,7 @@ export default function ProjectRowActions({ currentUser, project }: Props) {
       >
         {loading ? (
           <ActionsDropdownItem>
-            <DeferredSpinner />
+            <Spinner />
           </ActionsDropdownItem>
         ) : (
           <>
index a0ce8d48ab9b5dedf246e9b95f4c261b198f5079..3f4d681d753886f948472766ce8b977786bd7917 100644 (file)
@@ -24,7 +24,7 @@ import { fetchQualityGates } from '../../../api/quality-gates';
 import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
 import Suggestions from '../../../components/embed-docs-modal/Suggestions';
 import '../../../components/search-navigator.css';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import {
   addSideBarClass,
@@ -135,9 +135,9 @@ class App extends React.PureComponent<Props, State> {
                       canCreate={canCreate}
                       refreshQualityGates={this.fetchQualityGates}
                     />
-                    <DeferredSpinner loading={this.state.loading}>
+                    <Spinner loading={this.state.loading}>
                       <List qualityGates={qualityGates} currentQualityGate={name} />
-                    </DeferredSpinner>
+                    </Spinner>
                   </div>
                 </div>
               </nav>
index 97f18dc80f0aaa24b00b53e4fcd2e35176d811ad..cf246948bbbdfdc41631c3b44a804789eb3fbcf4 100644 (file)
@@ -21,7 +21,7 @@ import { clone } from 'lodash';
 import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
 import { fetchQualityGate } from '../../../api/quality-gates';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { addGlobalSuccessMessage } from '../../../helpers/globalMessages';
 import { translate } from '../../../helpers/l10n';
 import { Condition, QualityGate } from '../../../types/types';
@@ -151,7 +151,7 @@ export default class Details extends React.PureComponent<Props, State> {
 
     return (
       <main className="layout-page-main">
-        <DeferredSpinner loading={loading} timeout={200}>
+        <Spinner loading={loading}>
           {qualityGate && (
             <>
               <Helmet defer={false} title={qualityGate.name} />
@@ -171,7 +171,7 @@ export default class Details extends React.PureComponent<Props, State> {
               />
             </>
           )}
-        </DeferredSpinner>
+        </Spinner>
       </main>
     );
   }
index 3daa2daec750119eb44cd6be469ba10e3b2957c6..97dd4a14deecb9dd372e93d2266ff53ce654dc53 100644 (file)
@@ -19,9 +19,9 @@
  */
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
-import { Button } from '../../../components/controls/buttons';
 import ConfirmModal from '../../../components/controls/ConfirmModal';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import { Button } from '../../../components/controls/buttons';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 import { Group, isUser } from '../../../types/quality-gates';
 import { QualityGate } from '../../../types/types';
@@ -54,7 +54,7 @@ export default function QualityGatePermissionsRenderer(props: QualityGatePermiss
       <h3 className="spacer-bottom">{translate('quality_gates.permissions')}</h3>
       <p className="spacer-bottom">{translate('quality_gates.permissions.help')}</p>
       <div>
-        <DeferredSpinner loading={loading}>
+        <Spinner loading={loading}>
           <ul>
             {users.map((user) => (
               <li key={user.login}>
@@ -67,7 +67,7 @@ export default function QualityGatePermissionsRenderer(props: QualityGatePermiss
               </li>
             ))}
           </ul>
-        </DeferredSpinner>
+        </Spinner>
       </div>
 
       <Button className="big-spacer-top" onClick={props.onClickAddPermission}>
index c2e38caeefd48dad0aa005ae2b446c3f7f859876..4d0507c2dea80ef444f9e35f4387379c391af52a 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import { ChangelogResponse, getProfileChangelog } from '../../../api/quality-profiles';
 import { Location, Router, withRouter } from '../../../components/hoc/withRouter';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { parseDate, toISO8601WithOffsetString } from '../../../helpers/dates';
 import { translate } from '../../../helpers/l10n';
 import { withQualityProfilesContext } from '../qualityProfilesContext';
@@ -147,7 +147,7 @@ class ChangelogContainer extends React.PureComponent<Props, State> {
             onDateRangeChange={this.handleDateRangeChange}
             onReset={this.handleReset}
           />
-          <DeferredSpinner loading={this.state.loading} className="spacer-left" />
+          <Spinner loading={this.state.loading} className="spacer-left" />
         </div>
 
         {this.state.events != null && this.state.events.length === 0 && <ChangelogEmpty />}
index 333e6aeff304f0e93a895641fcc6365dfb1eb04c..94fbb4e40534eb0e5bfc23fad832600a6fb86fae 100644 (file)
@@ -20,9 +20,9 @@
 import * as React from 'react';
 import { Profile } from '../../../api/quality-profiles';
 import { getRuleDetails } from '../../../api/rules';
-import { Button } from '../../../components/controls/buttons';
 import Tooltip from '../../../components/controls/Tooltip';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import { Button } from '../../../components/controls/buttons';
+import Spinner from '../../../components/ui/Spinner';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { RuleDetails } from '../../../types/types';
 import ActivationFormModal from '../../coding-rules/components/ActivationFormModal';
@@ -78,7 +78,7 @@ export default class ComparisonResultActivation extends React.PureComponent<Prop
     const { profile } = this.props;
 
     return (
-      <DeferredSpinner loading={this.state.state === 'opening'}>
+      <Spinner loading={this.state.state === 'opening'}>
         <Tooltip
           placement="bottom"
           overlay={translateWithParameters(
@@ -107,7 +107,7 @@ export default class ComparisonResultActivation extends React.PureComponent<Prop
             rule={this.state.rule}
           />
         )}
-      </DeferredSpinner>
+      </Spinner>
     );
   }
 }
index 8253691a9fb0a3b960911b80233fea17f6928a4c..d0c74ef0f435237d72f6bc9e1c49953620f080aa 100644 (file)
 import { withTheme } from '@emotion/react';
 import styled from '@emotion/styled';
 import {
-  DeferredSpinner,
   LAYOUT_FOOTER_HEIGHT,
   LAYOUT_GLOBAL_NAV_HEIGHT,
   LAYOUT_PROJECT_NAV_HEIGHT,
   LargeCenteredLayout,
   PageContentFontWrapper,
+  Spinner,
   themeBorder,
   themeColor,
 } from 'design-system';
@@ -159,13 +159,13 @@ export default function SecurityHotspotsAppRenderer(props: SecurityHotspotsAppRe
                   }px`,
                 }}
               >
-                <DeferredSpinner className="sw-mt-3" loading={loading}>
-                  <HotspotFilterByStatus
-                    filters={filters}
-                    isStaticListOfHotspots={isStaticListOfHotspots}
-                    onChangeFilters={onChangeFilters}
-                    onShowAllHotspots={onShowAllHotspots}
-                  />
+                <HotspotFilterByStatus
+                  filters={filters}
+                  isStaticListOfHotspots={isStaticListOfHotspots}
+                  onChangeFilters={onChangeFilters}
+                  onShowAllHotspots={onShowAllHotspots}
+                />
+                <Spinner className="sw-mt-3" loading={loading}>
                   {hotspots.length > 0 && selectedHotspot && (
                     <>
                       {filterByCategory || filterByCWE || filterByFile ? (
@@ -200,7 +200,7 @@ export default function SecurityHotspotsAppRenderer(props: SecurityHotspotsAppRe
                       )}
                     </>
                   )}
-                </DeferredSpinner>
+                </Spinner>
               </StyledSidebarContent>
             </StyledSidebar>
 
index 3d879f6ef32165a5d7982ecd30181ac6a57949d7..5229e07481ef55cc3131416c92df67360791ba31 100644 (file)
@@ -25,7 +25,7 @@ import {
   PopupPlacement,
 } from 'design-system';
 import * as React from 'react';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { addGlobalErrorMessage, addGlobalSuccessMessage } from '../../../helpers/globalMessages';
 import { translate } from '../../../helpers/l10n';
 import { openHotspot, probeSonarLintServers } from '../../../helpers/sonarlint';
@@ -122,7 +122,7 @@ export default class HotspotOpenInIdeButton extends React.PureComponent<Props, S
         >
           <ButtonSecondary onClick={this.handleOnClick}>
             {translate('hotspots.open_in_ide.open')}
-            <DeferredSpinner loading={loading} className="sw-ml-4" />
+            <Spinner loading={loading} className="sw-ml-4" />
           </ButtonSecondary>
         </DropdownToggler>
       </div>
index 56d26d44309440e36c45559b47e1351dd99d7416..c1751a3a336d74d7c9de310ef8c049628918b01e 100644 (file)
@@ -35,7 +35,7 @@ import withCurrentUserContext from '../../../app/components/current-user/withCur
 import HelpTooltip from '../../../components/controls/HelpTooltip';
 import Tooltip from '../../../components/controls/Tooltip';
 import Measure from '../../../components/measure/Measure';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { PopupPlacement } from '../../../components/ui/popups';
 import { isBranch } from '../../../helpers/branch-like';
 import { translate } from '../../../helpers/l10n';
@@ -76,7 +76,7 @@ function HotspotSidebarHeader(props: SecurityHotspotsAppRendererProps) {
 
   return (
     <div className="sw-flex sw-h-6 sw-items-center sw-px-4 sw-py-4">
-      <DeferredSpinner loading={loadingMeasure}>
+      <Spinner loading={loadingMeasure}>
         {hotspotsReviewedMeasure !== undefined && (
           <CoverageIndicator value={hotspotsReviewedMeasure} />
         )}
@@ -91,85 +91,85 @@ function HotspotSidebarHeader(props: SecurityHotspotsAppRendererProps) {
           metricType={MetricType.Percent}
           value={hotspotsReviewedMeasure}
         />
-
-        <span className="sw-body-sm sw-ml-1">
-          {translate('metric.security_hotspots_reviewed.name')}
-        </span>
-
-        <HelpTooltip className="sw-ml-1" overlay={translate('hotspots.reviewed.tooltip')}>
-          <HelperHintIcon aria-label="help-tooltip" />
-        </HelpTooltip>
-
-        {!isStaticListOfHotspots && (isBranch(branchLike) || userLoggedIn || isFiltered) && (
-          <div className="sw-flex sw-flex-grow sw-justify-end">
-            <Dropdown
-              allowResizing
-              closeOnClick={false}
-              id="filter-hotspots-menu"
-              isPortal
-              overlay={
-                <>
-                  <ItemHeader>{translate('hotspot.filters.title')}</ItemHeader>
-
-                  {isBranch(branchLike) && (
+      </Spinner>
+
+      <span className="sw-body-sm sw-ml-1">
+        {translate('metric.security_hotspots_reviewed.name')}
+      </span>
+
+      <HelpTooltip className="sw-ml-1" overlay={translate('hotspots.reviewed.tooltip')}>
+        <HelperHintIcon aria-label="help-tooltip" />
+      </HelpTooltip>
+
+      {!isStaticListOfHotspots && (isBranch(branchLike) || userLoggedIn || isFiltered) && (
+        <div className="sw-flex sw-flex-grow sw-justify-end">
+          <Dropdown
+            allowResizing
+            closeOnClick={false}
+            id="filter-hotspots-menu"
+            isPortal
+            overlay={
+              <>
+                <ItemHeader>{translate('hotspot.filters.title')}</ItemHeader>
+
+                {isBranch(branchLike) && (
+                  <ItemCheckbox
+                    checked={Boolean(filters.inNewCodePeriod)}
+                    onCheck={(inNewCodePeriod: boolean) =>
+                      props.onChangeFilters({ inNewCodePeriod })
+                    }
+                  >
+                    <span className="sw-mx-2">
+                      {translate('hotspot.filters.period.since_leak_period')}
+                    </span>
+                  </ItemCheckbox>
+                )}
+
+                {userLoggedIn && (
+                  <Tooltip
+                    classNameSpace={component?.needIssueSync ? 'tooltip' : 'sw-hidden'}
+                    overlay={<HotspotDisabledFilterTooltip />}
+                    placement="right"
+                  >
                     <ItemCheckbox
-                      checked={Boolean(filters.inNewCodePeriod)}
-                      onCheck={(inNewCodePeriod: boolean) =>
-                        props.onChangeFilters({ inNewCodePeriod })
-                      }
+                      checked={Boolean(filters.assignedToMe)}
+                      disabled={component?.needIssueSync}
+                      onCheck={(assignedToMe: boolean) => props.onChangeFilters({ assignedToMe })}
                     >
                       <span className="sw-mx-2">
-                        {translate('hotspot.filters.period.since_leak_period')}
+                        {translate('hotspot.filters.assignee.assigned_to_me')}
                       </span>
                     </ItemCheckbox>
-                  )}
-
-                  {userLoggedIn && (
-                    <Tooltip
-                      classNameSpace={component?.needIssueSync ? 'tooltip' : 'sw-hidden'}
-                      overlay={<HotspotDisabledFilterTooltip />}
-                      placement="right"
-                    >
-                      <ItemCheckbox
-                        checked={Boolean(filters.assignedToMe)}
-                        disabled={component?.needIssueSync}
-                        onCheck={(assignedToMe: boolean) => props.onChangeFilters({ assignedToMe })}
-                      >
-                        <span className="sw-mx-2">
-                          {translate('hotspot.filters.assignee.assigned_to_me')}
-                        </span>
-                      </ItemCheckbox>
-                    </Tooltip>
-                  )}
-
-                  {isFiltered && <ItemDivider />}
-
-                  {isFiltered && (
-                    <ItemDangerButton
-                      onClick={() =>
-                        props.onChangeFilters({
-                          assignedToMe: false,
-                          inNewCodePeriod: false,
-                        })
-                      }
-                    >
-                      {translate('hotspot.filters.clear')}
-                    </ItemDangerButton>
-                  )}
-                </>
-              }
-              placement={PopupPlacement.BottomRight}
+                  </Tooltip>
+                )}
+
+                {isFiltered && <ItemDivider />}
+
+                {isFiltered && (
+                  <ItemDangerButton
+                    onClick={() =>
+                      props.onChangeFilters({
+                        assignedToMe: false,
+                        inNewCodePeriod: false,
+                      })
+                    }
+                  >
+                    {translate('hotspot.filters.clear')}
+                  </ItemDangerButton>
+                )}
+              </>
+            }
+            placement={PopupPlacement.BottomRight}
+          >
+            <DiscreetInteractiveIcon
+              Icon={FilterIcon}
+              aria-label={translate('hotspot.filters.title')}
             >
-              <DiscreetInteractiveIcon
-                Icon={FilterIcon}
-                aria-label={translate('hotspot.filters.title')}
-              >
-                {isFiltered ? filtersCount : null}
-              </DiscreetInteractiveIcon>
-            </Dropdown>
-          </div>
-        )}
-      </DeferredSpinner>
+              {isFiltered ? filtersCount : null}
+            </DiscreetInteractiveIcon>
+          </Dropdown>
+        </div>
+      )}
     </div>
   );
 }
index 48433425bde6e6920ed03b83269cd6779fb725a5..92abb4afe39d5c056d06024e6d9f26e38c428987 100644 (file)
@@ -21,7 +21,7 @@ import { withTheme } from '@emotion/react';
 import styled from '@emotion/styled';
 import { themeBorder, themeColor } from 'design-system';
 import * as React from 'react';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 import { BranchLike } from '../../../types/branch-like';
 import { Hotspot } from '../../../types/security-hotspots';
@@ -155,7 +155,7 @@ export default function HotspotSnippetContainerRenderer(
       <SourceFileWrapper className="sw-box-border sw-w-full sw-rounded-1" ref={scrollableRef}>
         <HotspotSnippetHeader hotspot={hotspot} component={component} branchLike={branchLike} />
 
-        <DeferredSpinner className="big-spacer" loading={loading} />
+        <Spinner className="big-spacer" loading={loading} />
 
         {!loading && sourceLines.length > 0 && (
           <SnippetViewer
index 8aedbfe45d0355851fc2e45c748959c3f67e35f4..4edfc35fe77b003e097d53bd29278cd943c55a92 100644 (file)
@@ -20,7 +20,7 @@
 
 import * as React from 'react';
 import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { fillBranchLike } from '../../../helpers/branch-like';
 import { Standards } from '../../../types/security';
 import { Hotspot, HotspotStatusOption } from '../../../types/security-hotspots';
@@ -70,7 +70,7 @@ export function HotspotViewerRenderer(props: HotspotViewerRendererProps) {
 
   return (
     <>
-      <DeferredSpinner className="sw-ml-4 sw-mt-4" loading={loading} />
+      <Spinner className="sw-ml-4 sw-mt-4" loading={loading} />
 
       {showStatusUpdateSuccessModal && (
         <StatusUpdateSuccessModal
index abc039a55859effa43c7e816c400052435c02fd8..8af2013638ae0d1e3796ba9ce2c742ba2cdc26ca 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
 import { Location } from '../../../components/hoc/withRouter';
 import { Alert } from '../../../components/ui/Alert';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 import { sanitizeUserInput } from '../../../helpers/sanitize';
 import { getReturnUrl } from '../../../helpers/urls';
@@ -50,7 +50,7 @@ export default function Login(props: LoginProps) {
       </h1>
       <Helmet defer={false} title={translate('login.page')} />
       {loading ? (
-        <DeferredSpinner loading={loading} timeout={0} />
+        <Spinner loading={loading} />
       ) : (
         <>
           {displayError && (
index 962ac29d1115c36932cd05b2c40bd2f4ef14cb73..e1da85d5982fd178e7fa7754a9d21ab61b67c9dc 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import Link from '../../../components/common/Link';
 import { ButtonLink, SubmitButton } from '../../../components/controls/buttons';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 import './LoginForm.css';
 
@@ -121,7 +121,7 @@ export default class LoginForm extends React.PureComponent<Props, State> {
 
         <div>
           <div className="text-right overflow-hidden">
-            <DeferredSpinner className="spacer-right" loading={this.state.loading} />
+            <Spinner className="spacer-right" loading={this.state.loading} />
             <SubmitButton disabled={this.state.loading}>
               {translate('sessions.log_in')}
             </SubmitButton>
index 77b64a6bd9029b1e47753b365a118c3a993e9e2a..47dd8d91b1a40b40f91a097a095bd40a26d97625 100644 (file)
@@ -22,9 +22,9 @@ import { sendTestEmail } from '../../../api/settings';
 import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import { SubmitButton } from '../../../components/controls/buttons';
 import { Alert } from '../../../components/ui/Alert';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
 import MandatoryFieldMarker from '../../../components/ui/MandatoryFieldMarker';
 import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation';
+import Spinner from '../../../components/ui/Spinner';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { parseError } from '../../../helpers/request';
 import { LoggedInUser } from '../../../types/users';
@@ -168,7 +168,7 @@ export class EmailForm extends React.PureComponent<Props, State> {
           <SubmitButton disabled={loading}>
             {translate('email_configuration.test.send')}
           </SubmitButton>
-          {loading && <DeferredSpinner className="spacer-left" />}
+          {loading && <Spinner className="spacer-left" />}
         </form>
       </div>
     );
index b6495cbc1f7d8809ec74423d2585efce3fc7ba34..b1047cec1f205b00b2a35bdc9f370ee24ddd10fd 100644 (file)
@@ -26,7 +26,7 @@ import AlertSuccessIcon from '../../../components/icons/AlertSuccessIcon';
 import NewCodeDefinitionDaysOption from '../../../components/new-code-definition/NewCodeDefinitionDaysOption';
 import NewCodeDefinitionPreviousVersionOption from '../../../components/new-code-definition/NewCodeDefinitionPreviousVersionOption';
 import NewCodeDefinitionWarning from '../../../components/new-code-definition/NewCodeDefinitionWarning';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 import {
   getNumberOfDaysDefaultValue,
@@ -198,7 +198,7 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> {
                   </div>
 
                   <div className="settings-definition-right">
-                    <DeferredSpinner loading={loading} timeout={500}>
+                    <Spinner loading={loading}>
                       <form onSubmit={this.onSubmit}>
                         <NewCodeDefinitionPreviousVersionOption
                           isDefault
@@ -225,7 +225,7 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> {
                             <p className="spacer-bottom">
                               {translate('baseline.next_analysis_notice')}
                             </p>
-                            <DeferredSpinner className="spacer-right" loading={saving} />
+                            <Spinner className="spacer-right" loading={saving} />
                             <SubmitButton disabled={saving || !isValid}>
                               {translate('save')}
                             </SubmitButton>
@@ -243,7 +243,7 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> {
                           </div>
                         )}
                       </form>
-                    </DeferredSpinner>
+                    </Spinner>
                   </div>
                 </div>
               </li>
index 5e95c87d2c389259819345a9bcf219a9a2b40ce5..2f2f67442e446463b749efc7ec6c1c7a2477b55b 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import Modal from '../../../../components/controls/Modal';
 import { ResetButtonLink, SubmitButton } from '../../../../components/controls/buttons';
 import { Alert } from '../../../../components/ui/Alert';
-import DeferredSpinner from '../../../../components/ui/DeferredSpinner';
+import Spinner from '../../../../components/ui/Spinner';
 import { translate } from '../../../../helpers/l10n';
 import {
   AlmBindingDefinition,
@@ -134,7 +134,7 @@ export default class AlmBindingDefinitionFormRenderer extends React.PureComponen
           <div className="modal-foot">
             <SubmitButton disabled={!canSubmit || submitting}>
               {translate('settings.almintegration.form.save')}
-              <DeferredSpinner className="spacer-left" loading={submitting} />
+              <Spinner className="spacer-left" loading={submitting} />
             </SubmitButton>
             <ResetButtonLink onClick={this.props.onCancel}>{translate('cancel')}</ResetButtonLink>
           </div>
index 2d22762356f9a9651eea53c6383fea30facbaae3..5096a11a8fc6760cb32e1a794da12f7a8841da06 100644 (file)
@@ -23,7 +23,7 @@ import Link from '../../../../components/common/Link';
 import { getTabId, getTabPanelId } from '../../../../components/controls/BoxedTabs';
 import { Button } from '../../../../components/controls/buttons';
 import { Alert } from '../../../../components/ui/Alert';
-import DeferredSpinner from '../../../../components/ui/DeferredSpinner';
+import Spinner from '../../../../components/ui/Spinner';
 import { translate } from '../../../../helpers/l10n';
 import {
   AlmBindingDefinition,
@@ -85,7 +85,7 @@ export default function AlmTabRenderer(props: AlmTabRendererProps) {
       aria-labelledby={getTabId(almTab)}
     >
       <div className="big-padded">
-        <DeferredSpinner loading={loadingAlmDefinitions}>
+        <Spinner loading={loadingAlmDefinitions}>
           {definitions.length === 0 && (
             <p className="spacer-top">{translate('settings.almintegration.empty', almTab)}</p>
           )}
@@ -122,7 +122,7 @@ export default function AlmTabRenderer(props: AlmTabRendererProps) {
               afterSubmit={props.afterSubmit}
             />
           )}
-        </DeferredSpinner>
+        </Spinner>
       </div>
       {AUTHENTICATION_AVAILABLE_PLATFORMS.includes(almTab) && (
         <Alert variant="info" className="spacer">
index 4c1a3081db638b1414348849b748c3258d27a897..45370d107347c107a1b88a2c047755abb9cffe0e 100644 (file)
@@ -24,7 +24,7 @@ import DocLink from '../../../../components/common/DocLink';
 import Modal from '../../../../components/controls/Modal';
 import { ResetButtonLink, SubmitButton } from '../../../../components/controls/buttons';
 import { Alert } from '../../../../components/ui/Alert';
-import DeferredSpinner from '../../../../components/ui/DeferredSpinner';
+import Spinner from '../../../../components/ui/Spinner';
 import { translate } from '../../../../helpers/l10n';
 import { useSaveValuesMutation } from '../../../../queries/settings';
 import { Dict } from '../../../../types/types';
@@ -101,10 +101,7 @@ export default function ConfigurationForm(props: Props) {
           <h2>{headerLabel}</h2>
         </div>
         <div className="modal-body modal-container">
-          <DeferredSpinner
-            loading={loading}
-            ariaLabel={translate('settings.authentication.form.loading')}
-          >
+          <Spinner loading={loading} ariaLabel={translate('settings.authentication.form.loading')}>
             <Alert variant={hasLegacyConfiguration ? 'warning' : 'info'}>
               <FormattedMessage
                 id={`settings.authentication.${
@@ -145,13 +142,13 @@ export default function ConfigurationForm(props: Props) {
                 </div>
               );
             })}
-          </DeferredSpinner>
+          </Spinner>
         </div>
 
         <div className="modal-foot">
           <SubmitButton disabled={!canBeSave}>
             {translate('settings.almintegration.form.save')}
-            <DeferredSpinner className="spacer-left" loading={loading} />
+            <Spinner className="spacer-left" loading={loading} />
           </SubmitButton>
           <ResetButtonLink onClick={props.onClose}>{translate('cancel')}</ResetButtonLink>
         </div>
index 2c0c482303362c509d1d6f8ae3c37f2dc447705b..fbcb870481084d0fa3a97003faa1ffd67075c7af 100644 (file)
@@ -24,9 +24,9 @@ import { Button, SubmitButton } from '../../../../components/controls/buttons';
 import AlmSettingsInstanceSelector from '../../../../components/devops-platform/AlmSettingsInstanceSelector';
 import AlertSuccessIcon from '../../../../components/icons/AlertSuccessIcon';
 import { Alert } from '../../../../components/ui/Alert';
-import DeferredSpinner from '../../../../components/ui/DeferredSpinner';
 import MandatoryFieldMarker from '../../../../components/ui/MandatoryFieldMarker';
 import MandatoryFieldsExplanation from '../../../../components/ui/MandatoryFieldsExplanation';
+import Spinner from '../../../../components/ui/Spinner';
 import { translate } from '../../../../helpers/l10n';
 import { getGlobalSettingsUrl } from '../../../../helpers/urls';
 import {
@@ -72,7 +72,7 @@ export default function PRDecorationBindingRenderer(props: PRDecorationBindingRe
   } = props;
 
   if (loading) {
-    return <DeferredSpinner />;
+    return <Spinner />;
   }
 
   if (instances.length < 1) {
@@ -154,7 +154,7 @@ export default function PRDecorationBindingRenderer(props: PRDecorationBindingRe
           {isChanged && (
             <SubmitButton className="spacer-right button-success" disabled={updating || !isValid}>
               <span data-test="project-settings__alm-save">{translate('save')}</span>
-              <DeferredSpinner className="spacer-left" loading={updating} />
+              <Spinner className="spacer-left" loading={updating} />
             </SubmitButton>
           )}
           {!updating && successfullyUpdated && (
@@ -171,7 +171,7 @@ export default function PRDecorationBindingRenderer(props: PRDecorationBindingRe
               {!isChanged && (
                 <Button onClick={props.onCheckConfiguration} disabled={checkingConfiguration}>
                   {translate('settings.pr_decoration.binding.check_configuration')}
-                  <DeferredSpinner className="spacer-left" loading={checkingConfiguration} />
+                  <Spinner className="spacer-left" loading={checkingConfiguration} />
                 </Button>
               )}
             </>
index 6b56a14c61bc2db71692358038345e9a0215364c..5c9502161a9d0211e3b52ef654f5c9b8a2513416 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
 import { checkSecretKey, generateSecretKey } from '../../../api/settings';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 import EncryptionForm from './EncryptionForm';
 import GenerateSecretKeyForm from './GenerateSecretKeyForm';
@@ -74,7 +74,7 @@ export default class EncryptionApp extends React.PureComponent<{}, State> {
         <Helmet defer={false} title={translate('property.category.security.encryption')} />
         <header className="page-header">
           <h1 className="page-title">{translate('property.category.security.encryption')}</h1>
-          <DeferredSpinner loading={loading} />
+          <Spinner loading={loading} />
         </header>
 
         {!loading && !secretKeyAvailable && (
index 2498c8f417a47461f0956a06f59c45e9df964dc7..c2ba4907837c64515757c5c28195d827c7940978 100644 (file)
@@ -23,7 +23,7 @@ import { encryptValue } from '../../../api/settings';
 import DocLink from '../../../components/common/DocLink';
 import { SubmitButton } from '../../../components/controls/buttons';
 import { ClipboardButton } from '../../../components/controls/clipboard';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -101,7 +101,7 @@ export default class EncryptionForm extends React.PureComponent<Props, State> {
             <SubmitButton disabled={encrypting || generating}>
               {translate('encryption.encrypt')}
             </SubmitButton>
-            <DeferredSpinner className="spacer-left" loading={encrypting} />
+            <Spinner className="spacer-left" loading={encrypting} />
           </div>
         </form>
 
@@ -143,7 +143,7 @@ export default class EncryptionForm extends React.PureComponent<Props, State> {
           <SubmitButton disabled={generating || encrypting}>
             {translate('encryption.generate_new_secret_key')}{' '}
           </SubmitButton>
-          <DeferredSpinner className="spacer-left" loading={generating} />
+          <Spinner className="spacer-left" loading={generating} />
         </form>
       </div>
     );
index 6ef0b5d576c6b1ebcb3e1059674adf9d98dbf6d2..8d478192490e81fcf056ae6714cebf26378cb989 100644 (file)
@@ -22,7 +22,7 @@ import { FormattedMessage } from 'react-intl';
 import DocLink from '../../../components/common/DocLink';
 import { SubmitButton } from '../../../components/controls/buttons';
 import { ClipboardButton } from '../../../components/controls/clipboard';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
@@ -125,7 +125,7 @@ export default class GenerateSecretKeyForm extends React.PureComponent<Props, St
             <SubmitButton disabled={submitting}>
               {translate('encryption.generate_secret_key')}
             </SubmitButton>
-            <DeferredSpinner className="spacer-left" loading={submitting} />
+            <Spinner className="spacer-left" loading={submitting} />
           </form>
         )}
       </div>
index 7ba7969debb98c57454897b21e8f5963529fa986..91d942a63254ddb46abf2fbc7fbaeb047fb790b7 100644 (file)
@@ -29,7 +29,7 @@ import SearchBox from '../../components/controls/SearchBox';
 import Select, { LabelValueSelectOption } from '../../components/controls/Select';
 import Suggestions from '../../components/embed-docs-modal/Suggestions';
 import { Provider, useManageProvider } from '../../components/hooks/useManageProvider';
-import DeferredSpinner from '../../components/ui/DeferredSpinner';
+import Spinner from '../../components/ui/Spinner';
 import { now, toISO8601WithOffsetString } from '../../helpers/dates';
 import { translate } from '../../helpers/l10n';
 import { useUsersQueries } from '../../queries/users';
@@ -141,13 +141,13 @@ export default function UsersApp() {
           />
         </div>
       </div>
-      <DeferredSpinner loading={isLoading}>
+      <Spinner loading={isLoading}>
         <UsersList
           identityProviders={identityProviders}
           users={users}
           manageProvider={manageProvider}
         />
-      </DeferredSpinner>
+      </Spinner>
 
       <ListFooter
         count={users.length}
index 3fb253cb56dac2fccbb907adb106cb1802401325..079006f6469fbafeac42648ce81bd59824d55ef3 100644 (file)
@@ -24,7 +24,7 @@ import { generateToken, getTokens } from '../../../api/user-tokens';
 import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import Select, { LabelValueSelectOption } from '../../../components/controls/Select';
 import { SubmitButton } from '../../../components/controls/buttons';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 import {
   EXPIRATION_OPTIONS,
@@ -405,9 +405,9 @@ export class TokensForm extends React.PureComponent<Props, State> {
             </tr>
           </thead>
           <tbody>
-            <DeferredSpinner customSpinner={customSpinner} loading={loading && tokens.length <= 0}>
+            <Spinner customSpinner={customSpinner} loading={loading && tokens.length <= 0}>
               {this.renderItems()}
-            </DeferredSpinner>
+            </Spinner>
           </tbody>
         </table>
       </>
index 2a8980b81e257b33c70f298578f22c6b7c10b564..8e05a173ed8fbf7ef4898f4963952513433e204b 100644 (file)
@@ -26,7 +26,7 @@ import { Button } from '../../../components/controls/buttons';
 import WarningIcon from '../../../components/icons/WarningIcon';
 import DateFormatter from '../../../components/intl/DateFormatter';
 import DateFromNow from '../../../components/intl/DateFromNow';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { UserToken } from '../../../types/token';
 
@@ -117,9 +117,9 @@ export default class TokensFormItem extends React.PureComponent<Props, State> {
               onClick={this.handleRevoke}
               aria-label={translateWithParameters('users.tokens.remove_label', token.name)}
             >
-              <DeferredSpinner className="little-spacer-right" loading={loading}>
+              <Spinner className="little-spacer-right" loading={loading}>
                 {translate('remove')}
-              </DeferredSpinner>
+              </Spinner>
             </Button>
           )}
           {!token.isExpired && deleteConfirmation === 'modal' && (
@@ -159,7 +159,7 @@ export default class TokensFormItem extends React.PureComponent<Props, State> {
               }
               onClick={this.handleClick}
             >
-              <DeferredSpinner className="little-spacer-right" loading={loading} />
+              <Spinner className="little-spacer-right" loading={loading} />
               {showConfirmation ? translate('users.tokens.sure') : translate('users.tokens.revoke')}
             </Button>
           )}
index 4147cca391ace97ee3f70723efd08ad3cf55a08a..cce9a665dd3b476f8ee3d11f04e3c449a675a7b9 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import styled from '@emotion/styled';
-import { DeferredSpinner, LargeCenteredLayout, PageContentFontWrapper, Title } from 'design-system';
+import { LargeCenteredLayout, PageContentFontWrapper, Spinner, Title } from 'design-system';
 import { omit } from 'lodash';
 import React, { useMemo } from 'react';
 import { Helmet } from 'react-helmet-async';
@@ -57,7 +57,7 @@ export default function WebApiApp() {
     <LargeCenteredLayout>
       <PageContentFontWrapper className="sw-body-sm">
         <Helmet defer={false} title={translate('api_documentation.page')} />
-        <DeferredSpinner loading={isLoading}>
+        <Spinner loading={isLoading}>
           {data && (
             <div className="sw-w-full sw-flex">
               <NavContainer aria-label={translate('api_documentation.page')} className="sw--mx-2">
@@ -76,7 +76,7 @@ export default function WebApiApp() {
                 className="sw-relative sw-ml-12 sw-flex-1 sw-overflow-y-auto sw-py-6"
                 style={{ height: 'calc(100vh - 160px)' }}
               >
-                <DeferredSpinner loading={isLoading}>
+                <Spinner loading={isLoading}>
                   {!activeData && (
                     <>
                       <Title>{translate('about')}</Title>
@@ -91,11 +91,11 @@ export default function WebApiApp() {
                       method={activeData.method}
                     />
                   )}
-                </DeferredSpinner>
+                </Spinner>
               </main>
             </div>
           )}
-        </DeferredSpinner>
+        </Spinner>
       </PageContentFontWrapper>
     </LargeCenteredLayout>
   );
index 238ec10c8b902ed7c14cc2c128affe562e0059ad..2ad2f5e195ab84c61e1733d24321a8942056113b 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import SimpleModal from '../../../components/controls/SimpleModal';
 import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { WebhookResponse } from '../../../types/webhook';
 
@@ -46,7 +46,7 @@ export default function DeleteWebhookForm({ onClose, onSubmit, webhook }: Props)
           </div>
 
           <footer className="modal-foot">
-            <DeferredSpinner className="spacer-right" loading={submitting} />
+            <Spinner className="spacer-right" loading={submitting} />
             <SubmitButton className="button-red" disabled={submitting}>
               {translate('delete')}
             </SubmitButton>
index e3c3dd82e32b474a7cf2e09c2a9fcb8b2ca54417..26b461327c435fc8efa6bee9514b74aba7638457 100644 (file)
@@ -23,7 +23,7 @@ import { searchDeliveries } from '../../../api/webhooks';
 import ListFooter from '../../../components/controls/ListFooter';
 import Modal from '../../../components/controls/Modal';
 import { ResetButtonLink } from '../../../components/controls/buttons';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { Paging } from '../../../types/types';
 import { WebhookDelivery, WebhookResponse } from '../../../types/webhook';
@@ -87,7 +87,7 @@ export default function DeliveriesForm({ onClose, webhook }: Props) {
           <DeliveryAccordion delivery={delivery} key={delivery.id} />
         ))}
         <div className="text-center">
-          <DeferredSpinner loading={loading} />
+          <Spinner loading={loading} />
         </div>
         {paging !== undefined && (
           <ListFooter
index cd184e8f9a5dc67d9156e75960777b5cc3461ead..35ee6b3628b67b697f5b81c38e324fbe90c2d23d 100644 (file)
@@ -19,7 +19,7 @@
  */
 import * as React from 'react';
 import CodeSnippet from '../../../components/common/CodeSnippet';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Spinner from '../../../components/ui/Spinner';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { formatMeasure } from '../../../helpers/measures';
 import { WebhookDelivery } from '../../../types/webhook';
@@ -48,9 +48,9 @@ export default function DeliveryItem({ className, delivery, loading, payload }:
         )}
       </p>
       <p className="spacer-bottom">{translate('webhooks.delivery.payload')}</p>
-      <DeferredSpinner className="spacer-left spacer-top" loading={loading}>
+      <Spinner className="spacer-left spacer-top" loading={loading}>
         {payload && <CodeSnippet noCopy snippet={formatPayload(payload)} />}
-      </DeferredSpinner>
+      </Spinner>
     </div>
   );
 }
index 9f53f638091e08f92022fec59939983ff2e06ece..0f965bccc3e675c3a1126cb4f691226fe8505fc7 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-import { ButtonSecondary, DeferredSpinner, LightLabel, SonarCodeColorizer } from 'design-system';
+import { ButtonSecondary, LightLabel, SonarCodeColorizer, Spinner } from 'design-system';
 import * as React from 'react';
 import { decorateWithUnderlineFlags } from '../../helpers/code-viewer';
 import { translate } from '../../helpers/l10n';
@@ -287,7 +287,7 @@ export default class SourceViewerCode extends React.PureComponent<Props, State>
             <div className="sw-flex sw-justify-center sw-p-6">
               {loadingSourcesBefore ? (
                 <div className="sw-flex sw-items-center">
-                  <DeferredSpinner loading />
+                  <Spinner loading />
                   <LightLabel className="sw-ml-2">
                     {translate('source_viewer.loading_more_code')}
                   </LightLabel>
@@ -326,7 +326,7 @@ export default class SourceViewerCode extends React.PureComponent<Props, State>
             <div className="sw-flex sw-justify-center sw-p-6">
               {loadingSourcesAfter ? (
                 <div className="sw-flex sw-items-center">
-                  <DeferredSpinner loading />
+                  <Spinner loading />
                   <LightLabel className="sw-ml-2">
                     {translate('source_viewer.loading_more_code')}
                   </LightLabel>
index fe17ca2251c38724f15b583f440184875255c55a..1bc44e8fea6c8262963e1ff03d2f5b61295ecb3a 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { DeferredSpinner, LightLabel } from 'design-system';
+import { LightLabel, Spinner } from 'design-system';
 import { isEqual, uniqBy } from 'lodash';
 import * as React from 'react';
 import { translate, translateWithParameters } from '../../helpers/l10n';
@@ -73,7 +73,7 @@ export default class GraphsHistory extends React.PureComponent<Props, State> {
       return (
         <div className="sw-flex sw-justify-center sw-flex-col sw-items-stretch sw-grow">
           <div className="sw-text-center">
-            <DeferredSpinner ariaLabel={translate('loading')} loading={loading} />
+            <Spinner ariaLabel={translate('loading')} loading={loading} />
           </div>
         </div>
       );
index 8f1d75b645df1f5fd0ba1b3d93b91904c497f3f8..a75b385edc1862e54ec38dc2e14ccff053c61a4a 100644 (file)
@@ -42,7 +42,7 @@ const START_DATE = '2016-01-01T00:00:00+0200';
 describe('rendering', () => {
   it('should render correctly when loading', async () => {
     renderActivityGraph({ loading: true });
-    expect(await screen.findByLabelText('loading')).toBeInTheDocument();
+    expect(await screen.findByText('loading')).toBeInTheDocument();
   });
 
   it('should show the correct legend items', async () => {
index e105d7274a0436428421412cf4247563b6dfaa4b..63bdcef3cfeffbaae4b9ca574416b720530b48e1 100644 (file)
@@ -22,7 +22,7 @@
 
 import classNames from 'classnames';
 import * as React from 'react';
-import DeferredSpinner from '../ui/DeferredSpinner';
+import Spinner from '../ui/Spinner';
 import './Checkbox.css';
 
 interface Props {
@@ -77,16 +77,16 @@ export default class Checkbox extends React.PureComponent<Props> {
           title={title}
         >
           {right && children}
-          <DeferredSpinner loading={Boolean(loading)}>
+          <Spinner loading={Boolean(loading)}>
             <i className={className} />
-          </DeferredSpinner>
+          </Spinner>
           {!right && children}
         </a>
       );
     }
 
     if (loading) {
-      return <DeferredSpinner ariaLabel={label} />;
+      return <Spinner ariaLabel={label} />;
     }
 
     return (
index 19dcca498105e60125be2ef00818fef4a775aa0b..4705effa33999296d9b1acf3de9509e46417b622 100644 (file)
  */
 import * as React from 'react';
 import { translate } from '../../helpers/l10n';
-import DeferredSpinner from '../ui/DeferredSpinner';
-import { ResetButtonLink, SubmitButton } from './buttons';
+import Spinner from '../ui/Spinner';
 import ClickEventBoundary from './ClickEventBoundary';
 import { ModalProps } from './Modal';
 import SimpleModal, { ChildrenProps } from './SimpleModal';
+import { ResetButtonLink, SubmitButton } from './buttons';
 
 export interface ConfirmModalProps<T> extends ModalProps {
   cancelButtonText?: string;
@@ -88,7 +88,7 @@ export default class ConfirmModal<T = string> extends React.PureComponent<Props<
           </header>
           <div className="modal-body">{children}</div>
           <footer className="modal-foot">
-            <DeferredSpinner className="spacer-right" loading={submitting} />
+            <Spinner className="spacer-right" loading={submitting} />
             <SubmitButton
               autoFocus
               className={isDestructive ? 'button-red' : undefined}
index 4c8ca65397157e54a4fb2c6366370ec4fcffcee8..55a4284165991cadfc31fe7a939e60db3ab18c85 100644 (file)
  */
 import styled from '@emotion/styled';
 import classNames from 'classnames';
-import { ButtonSecondary, DeferredSpinner, themeColor } from 'design-system';
+import { ButtonSecondary, Spinner, themeColor } from 'design-system';
 import * as React from 'react';
 import { translate, translateWithParameters } from '../../helpers/l10n';
 import { formatMeasure } from '../../helpers/measures';
 import { MetricType } from '../../types/metrics';
-import LegacySpinner from '../ui/DeferredSpinner';
+import LegacySpinner from '../ui/Spinner';
 import { Button } from './buttons';
 
 export interface ListFooterProps {
@@ -123,7 +123,7 @@ export default function ListFooter(props: ListFooterProps) {
       {button}
       {/* eslint-disable local-rules/no-conditional-rendering-of-deferredspinner */}
       {useMIUIButtons ? (
-        <DeferredSpinner loading={loading} className="sw-ml-2" />
+        <Spinner loading={loading} className="sw-ml-2" />
       ) : (
         <LegacySpinner loading={loading} className="sw-ml-2" />
       )}
index 30c190d12da18aaf068ed6635fe2c755b2fef329..ad0ad0d9fa83b962c2971da1a4221dfcb8c6e0eb 100644 (file)
@@ -81,7 +81,7 @@
   transition: color 0.3s ease;
 }
 
-.search-box > .deferred-spinner {
+.search-box > .spinner {
   position: absolute;
   top: 4px;
   left: 5px;
index 3c5b8430a61e5b75fa7837b0d223f82c254d559c..4ae74ff0009600e5f661587a3e1674f095a0d422 100644 (file)
@@ -23,7 +23,7 @@ import * as React from 'react';
 import { KeyboardKeys } from '../../helpers/keycodes';
 import { translate, translateWithParameters } from '../../helpers/l10n';
 import SearchIcon from '../icons/SearchIcon';
-import DeferredSpinner from '../ui/DeferredSpinner';
+import Spinner from '../ui/Spinner';
 import { ClearButton } from './buttons';
 import './SearchBox.css';
 
@@ -158,9 +158,9 @@ export default class SearchBox extends React.PureComponent<Props, State> {
           value={value}
         />
 
-        <DeferredSpinner loading={loading !== undefined ? loading : false}>
+        <Spinner loading={loading !== undefined ? loading : false}>
           <SearchIcon className="search-box-magnifier" />
-        </DeferredSpinner>
+        </Spinner>
 
         {value && (
           <ClearButton
index 3d35a3d68065018bd2138c8b7b4298af78240f99..898b6f2d33c94a16064349a261d21faf935b853e 100644 (file)
@@ -82,9 +82,7 @@ export function loadingIndicator<
   IsMulti extends boolean = boolean,
   Group extends GroupBase<Option> = GroupBase<Option>
 >({ innerProps }: LoadingIndicatorProps<Option, IsMulti, Group>) {
-  return (
-    <i className={classNames('deferred-spinner spacer-left spacer-right', innerProps.className)} />
-  );
+  return <i className={classNames('spinner spacer-left spacer-right', innerProps.className)} />;
 }
 
 export function multiValueRemove<
index 3a745ddae1e7b92bf30d18b2c1120217e60ad5fb..0cf5c2657f4213e54aada1f71bf927b98675f110 100644 (file)
@@ -21,7 +21,7 @@ import classNames from 'classnames';
 import { uniqueId } from 'lodash';
 import * as React from 'react';
 import { translate } from '../../helpers/l10n';
-import DeferredSpinner from '../ui/DeferredSpinner';
+import Spinner from '../ui/Spinner';
 import Checkbox from './Checkbox';
 import { SelectListFilter } from './SelectList';
 import SelectListListElement from './SelectListListElement';
@@ -94,7 +94,7 @@ export default class SelectListListContainer extends React.PureComponent<Props,
           >
             <span className="big-spacer-left">
               {translate('bulk_change')}
-              <DeferredSpinner className="spacer-left" loading={this.state.loading} timeout={10} />
+              <Spinner className="spacer-left" loading={this.state.loading} />
             </span>
           </Checkbox>
         </li>
index 0babe37d0553df7aa0312f7c084103011e5e13e4..a88956818ddfdc6f7074c0bb5e44ad4ac13fdba1 100644 (file)
 import { FormikValues } from 'formik';
 import * as React from 'react';
 import { translate } from '../../helpers/l10n';
-import DeferredSpinner from '../ui/DeferredSpinner';
-import { ResetButtonLink, SubmitButton } from './buttons';
+import Spinner from '../ui/Spinner';
 import Modal, { ModalProps } from './Modal';
 import ValidationForm, { ChildrenProps } from './ValidationForm';
+import { ResetButtonLink, SubmitButton } from './buttons';
 
 interface Props<V> extends ModalProps {
   children: (props: ChildrenProps<V>) => React.ReactNode;
@@ -65,7 +65,7 @@ export default class ValidationModal<V extends FormikValues> extends React.PureC
               <div className="modal-body">{this.props.children(props)}</div>
 
               <footer className="modal-foot">
-                <DeferredSpinner className="spacer-right" loading={props.isSubmitting} />
+                <Spinner className="spacer-right" loading={props.isSubmitting} />
                 <SubmitButton
                   className={this.props.isDestructive ? 'button-red' : undefined}
                   disabled={props.isSubmitting || !props.isValid || !props.dirty}
index 51c6f35ad58d19be2c733c2124b4264ec33f42cf..6603a1deff171dce6b172d0fbd85bc6a009927ae 100644 (file)
@@ -52,11 +52,8 @@ describe.each([
   });
 
   it('should render loading state', () => {
-    jest.useFakeTimers();
     renderCheckbox({ label: 'me', children, loading: true });
-    jest.runAllTimers();
-    expect(screen.getByTestId('deferred-spinner')).toMatchSnapshot();
-    jest.useRealTimers();
+    expect(screen.getByTestId('spinner')).toMatchSnapshot();
   });
 });
 
index c40b95436253a0316f2ea35aa541e4443aa107b5..709658d78f9881174431c51adb041c538270996e 100644 (file)
@@ -86,7 +86,7 @@ describe('ListFooter using MIUI buttons', () => {
   describe('rendering', () => {
     it('should render correctly when loading', async () => {
       renderListFooter({ loading: true });
-      expect(await screen.findByLabelText('loading')).toBeInTheDocument();
+      expect(await screen.findByText('loading')).toBeInTheDocument();
     });
 
     it('should not render if there are neither loadmore nor reload props', () => {
index ae3c407b7bd2e5f30098b1960f8f121456c5ddef..ccceff9af232297cc1ac285b451922d191036bef 100644 (file)
@@ -3,8 +3,8 @@
 exports[`Checkbox with children should render loading state 1`] = `
 <i
   aria-live="polite"
-  class="deferred-spinner is-loading"
-  data-testid="deferred-spinner"
+  class="spinner is-loading"
+  data-testid="spinner"
 >
   <span
     class="a11y-hidden"
@@ -17,8 +17,8 @@ exports[`Checkbox with children should render loading state 1`] = `
 exports[`Checkbox with no children should render loading state 1`] = `
 <i
   aria-live="polite"
-  class="deferred-spinner is-loading"
-  data-testid="deferred-spinner"
+  class="spinner is-loading"
+  data-testid="spinner"
 >
   <span
     class="a11y-hidden"
@@ -44,8 +44,8 @@ exports[`should render the checkbox on the right 1`] = `
   >
     <i
       aria-live="polite"
-      class="deferred-spinner a11y-hidden"
-      data-testid="deferred-spinner"
+      class="spinner a11y-hidden"
+      data-testid="spinner"
     />
   </div>
   <i
index f1367ae048a39967bbb51cba6fcec397bf0a5950..ea4ee4afd8c797f0d211a1c1da1fd802331f1c6d 100644 (file)
@@ -4,7 +4,7 @@ exports[`should confirm and close after confirm 1`] = `
 <footer
   className="modal-foot"
 >
-  <DeferredSpinner
+  <Spinner
     className="spacer-right"
     loading={true}
   />
@@ -59,7 +59,7 @@ exports[`should render correctly 2`] = `
       <footer
         className="modal-foot"
       >
-        <DeferredSpinner
+        <Spinner
           className="spacer-right"
           loading={false}
         />
index 95e8fea7598c9c1db701edf81524a206b1f6cda7..ce284960f01c0d85f773d6e38e10f30b30a91ca4 100644 (file)
@@ -16,13 +16,13 @@ exports[`renders 1`] = `
     type="search"
     value="foo"
   />
-  <DeferredSpinner
+  <Spinner
     loading={false}
   >
     <SearchIcon
       className="search-box-magnifier"
     />
-  </DeferredSpinner>
+  </Spinner>
   <ClearButton
     aria-label="clear"
     className="button-tiny search-box-clear"
index 49cb58d74e34523d27aba88ea6f8720c9a02c665..0136a28365e71135e7efa15831a20a9c24ddfad1 100644 (file)
@@ -93,7 +93,7 @@ exports[`Select should render dropdownIndicator correctly 1`] = `<Styled(span) /
 
 exports[`Select should render loadingIndicator correctly 1`] = `
 <i
-  className="deferred-spinner spacer-left spacer-right additional-class"
+  className="spinner spacer-left spacer-right additional-class"
 />
 `;
 
index 450d9144161f92db50e638a81e5ff0a6734f7a21..ecbe4c96647b77a8e484392849b35e9ab02d58cf 100644 (file)
@@ -18,10 +18,9 @@ exports[`should render correctly 1`] = `
           className="big-spacer-left"
         >
           bulk_change
-          <DeferredSpinner
+          <Spinner
             className="spacer-left"
             loading={false}
-            timeout={10}
           />
         </span>
       </Checkbox>
index bf6335287a50f4c87fae14eacb24ceaac13943bd..ee17dc8e5ce3279f3a835a74c417bd1398f6cb89 100644 (file)
@@ -92,7 +92,7 @@ exports[`should render correctly 2`] = `
     <footer
       className="modal-foot"
     >
-      <DeferredSpinner
+      <Spinner
         className="spacer-right"
         loading={false}
       />
index 1339669273c41a7274bc5def285dd9d71d7259fd..e741e3084ee728e5f3eb88bf90e70dcab675cb68 100644 (file)
@@ -22,9 +22,9 @@ import * as React from 'react';
 import HelpTooltip from '../../components/controls/HelpTooltip';
 import { Button, ButtonLink } from '../../components/controls/buttons';
 import OpenCloseIcon from '../../components/icons/OpenCloseIcon';
-import DeferredSpinner from '../../components/ui/DeferredSpinner';
 import { translate, translateWithParameters } from '../../helpers/l10n';
 import Tooltip from '../controls/Tooltip';
+import Spinner from '../ui/Spinner';
 
 interface Props {
   children?: React.ReactNode;
@@ -118,7 +118,7 @@ export default class FacetHeader extends React.PureComponent<Props> {
 
         {fetching && (
           <span className="little-spacer-right">
-            <DeferredSpinner />
+            <Spinner />
           </span>
         )}
 
index e1d62e9745de8199a2e159ae30c13a0664f509ac..466d63dc05d04b91d9ea8be2dfa288b83b61288f 100644 (file)
@@ -20,7 +20,6 @@
 import {
   ButtonSecondary,
   ClipboardIconButton,
-  DeferredSpinner,
   DestructiveIcon,
   FlagMessage,
   InputField,
@@ -28,6 +27,7 @@ import {
   LabelValueSelectOption,
   Link,
   Modal,
+  Spinner,
   TrashIcon,
 } from 'design-system';
 import * as React from 'react';
@@ -205,7 +205,7 @@ export default class EditTokenModal extends React.PureComponent<Props, State> {
         ) : (
           <>
             <div className="sw-flex sw-pt-4">
-              <DeferredSpinner loading={loading}>
+              <Spinner loading={loading}>
                 <div className="sw-flex-col sw-mr-2">
                   <label className="sw-block" htmlFor="token-name">
                     {translate('onboarding.token.name.label')}
@@ -243,7 +243,7 @@ export default class EditTokenModal extends React.PureComponent<Props, State> {
                     </ButtonSecondary>
                   </div>
                 </div>
-              </DeferredSpinner>
+              </Spinner>
             </div>
             {type === TokenType.Project && <ProjectTokenScopeInfo />}
           </>
index 6766b7bc2790cd3b56c8e134fd5262f1ab4e34ac..1e0a5e4ffa6f8854ae8f56aaf03ef2fcdb0fe565 100644 (file)
@@ -20,7 +20,6 @@
 import {
   ButtonPrimary,
   ButtonSecondary,
-  DeferredSpinner,
   DestructiveIcon,
   FlagMessage,
   FlagSuccessIcon,
@@ -32,6 +31,7 @@ import {
   Link,
   Note,
   RadioButton,
+  Spinner,
   TrashIcon,
 } from 'design-system';
 import * as React from 'react';
@@ -266,7 +266,7 @@ export default class TokenStep extends React.PureComponent<Props, State> {
                   <ButtonSecondary
                     type="submit"
                     disabled={!tokenName || loading}
-                    icon={<DeferredSpinner className="sw-mr-1" loading={loading} />}
+                    icon={<Spinner className="sw-mr-1" loading={loading} />}
                   >
                     {translate('onboarding.token.generate')}
                   </ButtonSecondary>
@@ -345,14 +345,14 @@ export default class TokenStep extends React.PureComponent<Props, State> {
               <strong className="sw-font-semibold">{token}</strong>
             </span>
 
-            <DeferredSpinner className="sw-ml-3 sw-my-2" loading={loading}>
+            <Spinner className="sw-ml-3 sw-my-2" loading={loading}>
               <DestructiveIcon
                 className="sw-ml-1"
                 Icon={TrashIcon}
                 aria-label={translate('onboarding.token.delete')}
                 onClick={this.handleTokenRevoke}
               />
-            </DeferredSpinner>
+            </Spinner>
           </form>
         ) : (
           <div>
index 88f91e671fa648948bf0bd8d2fb3674555774d79..eb18521582fce7dd20f003ba39b13b7a073876d7 100644 (file)
@@ -27,7 +27,7 @@ import AlertErrorIcon from '../icons/AlertErrorIcon';
 import AlertSuccessIcon from '../icons/AlertSuccessIcon';
 import AlertWarnIcon from '../icons/AlertWarnIcon';
 import InfoIcon from '../icons/InfoIcon';
-import DeferredSpinner from './DeferredSpinner';
+import Spinner from './Spinner';
 
 type AlertDisplay = 'banner' | 'inline' | 'block';
 export type AlertVariant = 'error' | 'warning' | 'success' | 'info' | 'loading';
@@ -140,7 +140,7 @@ function getAlertVariantInfo(variant: AlertVariant) {
       backGroundColor: colors.alertBackgroundInfo,
     },
     loading: {
-      icon: <DeferredSpinner timeout={0} />,
+      icon: <Spinner />,
       color: colors.alertTextInfo,
       borderColor: colors.alertBorderInfo,
       backGroundColor: colors.alertBackgroundInfo,
diff --git a/server/sonar-web/src/main/js/components/ui/DeferredSpinner.css b/server/sonar-web/src/main/js/components/ui/DeferredSpinner.css
deleted file mode 100644 (file)
index dc25eec..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-.deferred-spinner {
-  position: relative;
-  vertical-align: middle;
-  width: 16px;
-  height: 16px;
-  border: 2px solid var(--blue);
-  border-radius: 50%;
-  animation: spin 0.75s infinite linear;
-}
-
-.deferred-spinner:before,
-.deferred-spinner:after {
-  left: -2px;
-  top: -2px;
-  display: none;
-  position: absolute;
-  content: '';
-  width: inherit;
-  height: inherit;
-  border: inherit;
-  border-radius: inherit;
-}
-
-.deferred-spinner,
-.deferred-spinner:before,
-.deferred-spinner:after {
-  display: inline-block;
-  box-sizing: border-box;
-  border-color: transparent;
-  border-top-color: var(--info500);
-  animation-duration: 1.2s;
-}
-
-.deferred-spinner:before {
-  transform: rotate(120deg);
-}
-
-.deferred-spinner:after {
-  transform: rotate(240deg);
-}
-
-.deferred-spinner-placeholder {
-  position: relative;
-  display: inline-block;
-  vertical-align: middle;
-  width: 16px;
-  height: 16px;
-  visibility: hidden;
-}
-
-@keyframes spin {
-  from {
-    transform: rotate(0deg);
-  }
-
-  to {
-    transform: rotate(360deg);
-  }
-}
diff --git a/server/sonar-web/src/main/js/components/ui/DeferredSpinner.tsx b/server/sonar-web/src/main/js/components/ui/DeferredSpinner.tsx
deleted file mode 100644 (file)
index e619906..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import classNames from 'classnames';
-import * as React from 'react';
-import { translate } from '../../helpers/l10n';
-import './DeferredSpinner.css';
-
-interface Props {
-  ariaLabel?: string;
-  children?: React.ReactNode;
-  className?: string;
-  customSpinner?: JSX.Element;
-  loading?: boolean;
-  timeout?: number;
-}
-
-interface State {
-  showSpinner: boolean;
-}
-
-const DEFAULT_TIMEOUT = 100;
-
-/**
- * Recommendation: do not render this component conditionally based on any loading state:
- *   // Don't do this:
- *   {loading && <DeferredSpinner />}
- * Instead, pass the loading state as a prop:
- *   // Do this:
- *   <DeferredSpinner loading={loading} />
- */
-export default class DeferredSpinner extends React.PureComponent<Props, State> {
-  timer?: number;
-
-  state: State = { showSpinner: false };
-
-  componentDidMount() {
-    if (this.props.loading == null || this.props.loading === true) {
-      this.startTimer();
-    }
-  }
-
-  componentDidUpdate(prevProps: Props) {
-    if (prevProps.loading === false && this.props.loading === true) {
-      this.stopTimer();
-      this.startTimer();
-    }
-    if (prevProps.loading === true && this.props.loading === false) {
-      this.stopTimer();
-      this.setState({ showSpinner: false });
-    }
-  }
-
-  componentWillUnmount() {
-    this.stopTimer();
-  }
-
-  startTimer = () => {
-    this.timer = window.setTimeout(
-      () => this.setState({ showSpinner: true }),
-      this.props.timeout || DEFAULT_TIMEOUT
-    );
-  };
-
-  stopTimer = () => {
-    window.clearTimeout(this.timer);
-  };
-
-  render() {
-    const { ariaLabel = translate('loading'), children, className, customSpinner } = this.props;
-    const { showSpinner } = this.state;
-
-    if (customSpinner) {
-      return showSpinner ? customSpinner : children;
-    }
-
-    return (
-      <>
-        <div className="sw-overflow-hidden">
-          <i
-            aria-live="polite"
-            data-testid="deferred-spinner"
-            className={classNames('deferred-spinner', className, {
-              'a11y-hidden': !showSpinner,
-              'is-loading': showSpinner,
-            })}
-          >
-            {showSpinner && <span className="a11y-hidden">{ariaLabel}</span>}
-          </i>
-        </div>
-        {!showSpinner && children}
-      </>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/components/ui/Spinner.css b/server/sonar-web/src/main/js/components/ui/Spinner.css
new file mode 100644 (file)
index 0000000..3d185b6
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+.spinner {
+  position: relative;
+  vertical-align: middle;
+  width: 16px;
+  height: 16px;
+  border: 2px solid var(--blue);
+  border-radius: 50%;
+  animation: spin 0.75s infinite linear;
+}
+
+.spinner:before,
+.spinner:after {
+  left: -2px;
+  top: -2px;
+  display: none;
+  position: absolute;
+  content: '';
+  width: inherit;
+  height: inherit;
+  border: inherit;
+  border-radius: inherit;
+}
+
+.spinner,
+.spinner:before,
+.spinner:after {
+  display: inline-block;
+  box-sizing: border-box;
+  border-color: transparent;
+  border-top-color: var(--info500);
+  animation-duration: 1.2s;
+}
+
+.spinner:before {
+  transform: rotate(120deg);
+}
+
+.spinner:after {
+  transform: rotate(240deg);
+}
+
+.spinner-placeholder {
+  position: relative;
+  display: inline-block;
+  vertical-align: middle;
+  width: 16px;
+  height: 16px;
+  visibility: hidden;
+}
+
+@keyframes spin {
+  from {
+    transform: rotate(0deg);
+  }
+
+  to {
+    transform: rotate(360deg);
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/ui/Spinner.tsx b/server/sonar-web/src/main/js/components/ui/Spinner.tsx
new file mode 100644 (file)
index 0000000..8224074
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import classNames from 'classnames';
+import * as React from 'react';
+import { translate } from '../../helpers/l10n';
+import './Spinner.css';
+
+interface Props {
+  ariaLabel?: string;
+  children?: React.ReactNode;
+  className?: string;
+  customSpinner?: JSX.Element;
+  loading?: boolean;
+}
+
+export default function Spinner(props: Props) {
+  const {
+    ariaLabel = translate('loading'),
+    children,
+    className,
+    customSpinner,
+    loading = true,
+  } = props;
+
+  if (customSpinner) {
+    return <>{loading ? customSpinner : children}</>;
+  }
+
+  return (
+    <>
+      <div className="sw-overflow-hidden">
+        <i
+          aria-live="polite"
+          data-testid="spinner"
+          className={classNames('spinner', className, {
+            'a11y-hidden': !loading,
+            'is-loading': loading,
+          })}
+        >
+          {loading && <span className="a11y-hidden">{ariaLabel}</span>}
+        </i>
+      </div>
+      {!loading && children}
+    </>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/DeferredSpinner-test.tsx b/server/sonar-web/src/main/js/components/ui/__tests__/DeferredSpinner-test.tsx
deleted file mode 100644 (file)
index 0237097..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { render, screen } from '@testing-library/react';
-import * as React from 'react';
-import DeferredSpinner from '../DeferredSpinner';
-
-beforeEach(() => {
-  jest.useFakeTimers();
-});
-
-afterEach(() => {
-  jest.runOnlyPendingTimers();
-  jest.useRealTimers();
-});
-
-it('renders children before timeout', () => {
-  renderDeferredSpinner({ children: <a href="#">foo</a> });
-  expect(screen.getByRole('link')).toBeInTheDocument();
-  jest.runAllTimers();
-  expect(screen.queryByRole('link')).not.toBeInTheDocument();
-});
-
-it('renders spinner after timeout', () => {
-  renderDeferredSpinner();
-  expect(screen.queryByText('loading')).not.toBeInTheDocument();
-  jest.runAllTimers();
-  expect(screen.getByText('loading')).toBeInTheDocument();
-});
-
-it('allows setting a custom class name', () => {
-  renderDeferredSpinner({ className: 'foo' });
-  jest.runAllTimers();
-  expect(screen.getByTestId('deferred-spinner')).toHaveClass('foo');
-});
-
-it('can be controlled by the loading prop', () => {
-  const { rerender } = renderDeferredSpinner({ loading: true });
-  jest.runAllTimers();
-  expect(screen.getByText('loading')).toBeInTheDocument();
-
-  rerender(prepareDeferredSpinner({ loading: false }));
-  expect(screen.queryByText('loading')).not.toBeInTheDocument();
-});
-
-function renderDeferredSpinner(props: Partial<DeferredSpinner['props']> = {}) {
-  // We don't use our renderComponent() helper here, as we have some tests that
-  // require changes in props.
-  return render(prepareDeferredSpinner(props));
-}
-
-function prepareDeferredSpinner(props: Partial<DeferredSpinner['props']> = {}) {
-  return <DeferredSpinner ariaLabel="loading" {...props} />;
-}
diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/Spinner-test.tsx b/server/sonar-web/src/main/js/components/ui/__tests__/Spinner-test.tsx
new file mode 100644 (file)
index 0000000..fbc8dbd
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { render, screen } from '@testing-library/react';
+import * as React from 'react';
+import Spinner from '../Spinner';
+
+it('can be controlled by the loading prop', () => {
+  const { rerender } = renderSpinner({ loading: true });
+  expect(screen.getByText('loading')).toBeInTheDocument();
+
+  rerender(prepareSpinner({ loading: false }));
+  expect(screen.queryByText('loading')).not.toBeInTheDocument();
+});
+
+function renderSpinner(props: Partial<Parameters<typeof Spinner>[0]> = {}) {
+  // We don't use our renderComponent() helper here, as we have some tests that
+  // require changes in props.
+  return render(prepareSpinner(props));
+}
+
+function prepareSpinner(props: Partial<Parameters<typeof Spinner>[0]> = {}) {
+  return <Spinner ariaLabel="loading" {...props} />;
+}
index 3d21f1f48f50b2816410df68356111ea7b9877ca..8ade13db05c845a14b6cdbbaafdd8973fe26c99d 100644 (file)
@@ -23,8 +23,8 @@ import { getRuleDetails, getRulesApp } from '../../api/rules';
 import RuleDetailsDescription from '../../apps/coding-rules/components/RuleDetailsDescription';
 import RuleDetailsMeta from '../../apps/coding-rules/components/RuleDetailsMeta';
 import '../../apps/coding-rules/styles.css';
-import DeferredSpinner from '../../components/ui/DeferredSpinner';
 import { Dict, RuleDetails } from '../../types/types';
+import Spinner from '../ui/Spinner';
 
 interface Props {
   onLoad: (details: { name: string }) => void;
@@ -81,7 +81,7 @@ export default class WorkspaceRuleDetails extends React.PureComponent<Props, Sta
 
   render() {
     return (
-      <DeferredSpinner loading={this.state.loading}>
+      <Spinner loading={this.state.loading}>
         {this.state.ruleDetails && (
           <>
             <RuleDetailsMeta
@@ -99,7 +99,7 @@ export default class WorkspaceRuleDetails extends React.PureComponent<Props, Sta
             />
           </>
         )}
-      </DeferredSpinner>
+      </Spinner>
     );
   }
 }
index 08866c568d3a7c52f2a9c37355c870ff11e4c11f..3e8578a0921349093b8475e4423d4137b6fc9734 100644 (file)
@@ -1,13 +1,13 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should render 1`] = `
-<DeferredSpinner
+<Spinner
   loading={true}
 />
 `;
 
 exports[`should render 2`] = `
-<DeferredSpinner
+<Spinner
   loading={false}
 >
   <RuleDetailsMeta
@@ -41,5 +41,5 @@ exports[`should render 2`] = `
       }
     }
   />
-</DeferredSpinner>
+</Spinner>
 `;
index 5a47af23c3e20122e67c9cd208d295916f48de91..ad497152d7694b6857ef1aed434242311950e7cf 100644 (file)
@@ -23,7 +23,7 @@ import { UserEvent } from '@testing-library/user-event/dist/types/setup/setup';
 import { omit } from 'lodash';
 import * as React from 'react';
 import { HelmetProvider } from 'react-helmet-async';
-import { IntlProvider } from 'react-intl';
+import { IntlProvider, ReactIntlErrorCode } from 'react-intl';
 import { MemoryRouter, Outlet, Route, Routes, parsePath } from 'react-router-dom';
 import AdminContext from '../app/components/AdminContext';
 import GlobalMessagesContainer from '../app/components/GlobalMessagesContainer';
@@ -100,7 +100,7 @@ export function renderComponent(
     const queryClient = new QueryClient();
 
     return (
-      <IntlProvider defaultLocale="en" locale="en">
+      <IntlWrapper>
         <QueryClientProvider client={queryClient}>
           <HelmetProvider>
             <AvailableFeaturesContext.Provider value={featureList}>
@@ -114,7 +114,7 @@ export function renderComponent(
             </AvailableFeaturesContext.Provider>
           </HelmetProvider>
         </QueryClientProvider>
-      </IntlProvider>
+      </IntlWrapper>
     );
   }
 
@@ -194,7 +194,7 @@ function renderRoutedApp(
 
   return render(
     <HelmetProvider context={{}}>
-      <IntlProvider defaultLocale="en" locale="en">
+      <IntlWrapper>
         <MetricsContext.Provider value={metrics}>
           <LanguagesContext.Provider value={languages}>
             <AvailableFeaturesContext.Provider value={featureList}>
@@ -216,7 +216,7 @@ function renderRoutedApp(
             </AvailableFeaturesContext.Provider>
           </LanguagesContext.Provider>
         </MetricsContext.Provider>
-      </IntlProvider>
+      </IntlWrapper>
     </HelmetProvider>
   );
 }
@@ -259,3 +259,28 @@ Example:
     ? within(target).getByText(text, { selector })
     : screen.getByText(text, { selector });
 }
+
+export function IntlWrapper({
+  children,
+  messages = {},
+}: {
+  children: React.ReactNode;
+  messages?: Record<string, string>;
+}) {
+  return (
+    <IntlProvider
+      defaultLocale="en"
+      locale="en"
+      messages={messages}
+      onError={(e) => {
+        // ignore missing translations, there are none!
+        if (e.code !== ReactIntlErrorCode.MISSING_TRANSLATION) {
+          // eslint-disable-next-line no-console
+          console.error(e);
+        }
+      }}
+    >
+      {children}
+    </IntlProvider>
+  );
+}