From: Philippe Perrin Date: Fri, 29 Apr 2022 16:25:13 +0000 (+0200) Subject: SONAR-16205 Improve code sharing with the developer extension X-Git-Tag: 9.5.0.56709~179 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=f674607f777e3951a02b28334fffa6840edad44d;p=sonarqube.git SONAR-16205 Improve code sharing with the developer extension --- diff --git a/server/sonar-web/src/main/js/app/components/GlobalMessagesContainer.tsx b/server/sonar-web/src/main/js/app/components/GlobalMessagesContainer.tsx index 742d449ac58..0618a6befa0 100644 --- a/server/sonar-web/src/main/js/app/components/GlobalMessagesContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/GlobalMessagesContainer.tsx @@ -19,9 +19,9 @@ */ import styled from '@emotion/styled'; import React from 'react'; +import { registerListener, unregisterListener } from '../../helpers/globalMessages'; import { Message } from '../../types/globalMessages'; import { zIndexes } from '../theme'; -import { registerListener, unregisterListener } from '../utils/globalMessagesService'; import GlobalMessage from './GlobalMessage'; const MESSAGE_DISPLAY_TIME = 5000; diff --git a/server/sonar-web/src/main/js/app/components/__tests__/GlobalMessagesContainer-it.ts b/server/sonar-web/src/main/js/app/components/__tests__/GlobalMessagesContainer-it.ts index 9a6cc31ecb8..cc705e4d3b8 100644 --- a/server/sonar-web/src/main/js/app/components/__tests__/GlobalMessagesContainer-it.ts +++ b/server/sonar-web/src/main/js/app/components/__tests__/GlobalMessagesContainer-it.ts @@ -18,8 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { screen } from '@testing-library/react'; +import { addGlobalErrorMessage, addGlobalSuccessMessage } from '../../../helpers/globalMessages'; import { renderComponentApp } from '../../../helpers/testReactTestingUtils'; -import { addGlobalErrorMessage, addGlobalSuccessMessage } from '../../utils/globalMessagesService'; it('should display messages', () => { jest.useFakeTimers(); diff --git a/server/sonar-web/src/main/js/app/components/extensions/Extension.tsx b/server/sonar-web/src/main/js/app/components/extensions/Extension.tsx index 563c1c1efcf..e13e596fd83 100644 --- a/server/sonar-web/src/main/js/app/components/extensions/Extension.tsx +++ b/server/sonar-web/src/main/js/app/components/extensions/Extension.tsx @@ -22,6 +22,7 @@ import { Helmet } from 'react-helmet-async'; import { injectIntl, WrappedComponentProps } from 'react-intl'; import { Location, Router, withRouter } from '../../../components/hoc/withRouter'; import { getExtensionStart } from '../../../helpers/extensions'; +import { addGlobalErrorMessage } from '../../../helpers/globalMessages'; import { translate } from '../../../helpers/l10n'; import { getCurrentL10nBundle } from '../../../helpers/l10nBundle'; import { getBaseUrl } from '../../../helpers/system'; @@ -30,7 +31,6 @@ import { ExtensionStartMethod } from '../../../types/extension'; import { Dict, Extension as TypeExtension } from '../../../types/types'; import { CurrentUser } from '../../../types/users'; import * as theme from '../../theme'; -import { addGlobalErrorMessage } from '../../utils/globalMessagesService'; import withAppStateContext from '../app-state/withAppStateContext'; import withCurrentUserContext from '../current-user/withCurrentUserContext'; diff --git a/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts b/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts index 154fb38d9ff..b534527ce4f 100644 --- a/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts +++ b/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts @@ -86,6 +86,7 @@ import { sortBranches } from '../../../helpers/branch-like'; import { throwGlobalError } from '../../../helpers/error'; +import { addGlobalSuccessMessage } from '../../../helpers/globalMessages'; import handleRequiredAuthentication from '../../../helpers/handleRequiredAuthentication'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import * as measures from '../../../helpers/measures'; @@ -117,7 +118,6 @@ import { getMeasureHistoryUrl, getRulesUrl } from '../../../helpers/urls'; -import { addGlobalSuccessMessage } from '../../utils/globalMessagesService'; import A11ySkipTarget from '../a11y/A11ySkipTarget'; import Suggestions from '../embed-docs-modal/Suggestions'; diff --git a/server/sonar-web/src/main/js/app/utils/__tests__/globalMessagesService-test.ts b/server/sonar-web/src/main/js/app/utils/__tests__/globalMessagesService-test.ts deleted file mode 100644 index 0d402470d43..00000000000 --- a/server/sonar-web/src/main/js/app/utils/__tests__/globalMessagesService-test.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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 { MessageLevel } from '../../../types/globalMessages'; -import { - addGlobalErrorMessage, - addGlobalSuccessMessage, - registerListener -} from '../globalMessagesService'; - -it('should work as expected', () => { - const listener1 = jest.fn(); - registerListener(listener1); - - addGlobalErrorMessage('test'); - - expect(listener1).toBeCalledWith( - expect.objectContaining({ text: 'test', level: MessageLevel.Error }) - ); - - listener1.mockClear(); - const listener2 = jest.fn(); - registerListener(listener2); - - addGlobalSuccessMessage('test'); - - expect(listener1).toBeCalledWith( - expect.objectContaining({ text: 'test', level: MessageLevel.Success }) - ); - expect(listener2).toBeCalledWith( - expect.objectContaining({ text: 'test', level: MessageLevel.Success }) - ); -}); diff --git a/server/sonar-web/src/main/js/app/utils/globalMessagesService.ts b/server/sonar-web/src/main/js/app/utils/globalMessagesService.ts deleted file mode 100644 index 127cf1cea94..00000000000 --- a/server/sonar-web/src/main/js/app/utils/globalMessagesService.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 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 { uniqueId } from 'lodash'; -import { parseError } from '../../helpers/request'; -import { Message, MessageLevel } from '../../types/globalMessages'; - -const listeners: Array<(message: Message) => void> = []; - -export function registerListener(callback: (message: Message) => void) { - listeners.push(callback); -} - -export function unregisterListener(callback: (message: Message) => void) { - const index = listeners.indexOf(callback); - - if (index > -1) { - listeners.splice(index, 1); - } -} - -function addMessage(text: string, level: MessageLevel) { - listeners.forEach(listener => - listener({ - id: uniqueId('global-message-'), - level, - text - }) - ); -} - -export function addGlobalErrorMessage(text: string) { - addMessage(text, MessageLevel.Error); -} - -export function addGlobalErrorMessageFromAPI(param: any) { - if (param instanceof Response) { - return parseError(param).then(addGlobalErrorMessage, () => { - /* ignore parsing errors */ - }); - } - if (typeof param === 'string') { - return Promise.resolve(param).then(addGlobalErrorMessage); - } - - return Promise.resolve(); -} - -export function addGlobalSuccessMessage(text: string) { - addMessage(text, MessageLevel.Success); -} diff --git a/server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx b/server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx index aa519367723..6f2de530e76 100644 --- a/server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx +++ b/server/sonar-web/src/main/js/apps/projectDeletion/Form.tsx @@ -20,10 +20,10 @@ import * as React from 'react'; import { deleteApplication } from '../../api/application'; import { deletePortfolio, deleteProject } from '../../api/components'; -import { addGlobalSuccessMessage } from '../../app/utils/globalMessagesService'; import { Button } from '../../components/controls/buttons'; import ConfirmButton from '../../components/controls/ConfirmButton'; import { Router, withRouter } from '../../components/hoc/withRouter'; +import { addGlobalSuccessMessage } from '../../helpers/globalMessages'; import { translate, translateWithParameters } from '../../helpers/l10n'; import { isApplication, isPortfolioLike } from '../../types/component'; import { Component } from '../../types/types'; diff --git a/server/sonar-web/src/main/js/apps/projectQualityGate/ProjectQualityGateApp.tsx b/server/sonar-web/src/main/js/apps/projectQualityGate/ProjectQualityGateApp.tsx index e86d860f149..8f5df7ab9a1 100644 --- a/server/sonar-web/src/main/js/apps/projectQualityGate/ProjectQualityGateApp.tsx +++ b/server/sonar-web/src/main/js/apps/projectQualityGate/ProjectQualityGateApp.tsx @@ -26,8 +26,8 @@ import { getGateForProject, searchProjects } from '../../api/quality-gates'; -import { addGlobalSuccessMessage } from '../../app/utils/globalMessagesService'; import handleRequiredAuthorization from '../../app/utils/handleRequiredAuthorization'; +import { addGlobalSuccessMessage } from '../../helpers/globalMessages'; import { translate } from '../../helpers/l10n'; import { Component, QualityGate } from '../../types/types'; import { USE_SYSTEM_DEFAULT } from './constants'; diff --git a/server/sonar-web/src/main/js/apps/projectQualityGate/__tests__/ProjectQualityGateApp-test.tsx b/server/sonar-web/src/main/js/apps/projectQualityGate/__tests__/ProjectQualityGateApp-test.tsx index 0d22641ab3b..a45b3468083 100644 --- a/server/sonar-web/src/main/js/apps/projectQualityGate/__tests__/ProjectQualityGateApp-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectQualityGate/__tests__/ProjectQualityGateApp-test.tsx @@ -62,7 +62,7 @@ jest.mock('../../../api/quality-gates', () => { }; }); -jest.mock('../../../app/utils/globalMessagesService', () => ({ +jest.mock('../../../helpers/globalMessages', () => ({ addGlobalSuccessMessage: jest.fn() })); diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/ProjectQualityProfilesApp.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/ProjectQualityProfilesApp.tsx index 930558eef26..25455df1466 100644 --- a/server/sonar-web/src/main/js/apps/projectQualityProfiles/ProjectQualityProfilesApp.tsx +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/ProjectQualityProfilesApp.tsx @@ -26,8 +26,8 @@ import { Profile, searchQualityProfiles } from '../../api/quality-profiles'; -import { addGlobalSuccessMessage } from '../../app/utils/globalMessagesService'; import handleRequiredAuthorization from '../../app/utils/handleRequiredAuthorization'; +import { addGlobalSuccessMessage } from '../../helpers/globalMessages'; import { translateWithParameters } from '../../helpers/l10n'; import { isDefined } from '../../helpers/types'; import { Component } from '../../types/types'; diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/ProjectQualityProfilesApp-test.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/ProjectQualityProfilesApp-test.tsx index df5c7cf9553..90777d416e7 100644 --- a/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/ProjectQualityProfilesApp-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/ProjectQualityProfilesApp-test.tsx @@ -71,7 +71,7 @@ jest.mock('../../../api/quality-profiles', () => { }; }); -jest.mock('../../../app/utils/globalMessagesService', () => ({ +jest.mock('../../../helpers/globalMessages', () => ({ addGlobalSuccessMessage: jest.fn() })); diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx index 8a4c7da23b0..00a6e848cd7 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx @@ -19,7 +19,6 @@ */ import * as React from 'react'; import { bulkApplyTemplate, getPermissionTemplates } from '../../api/permissions'; -import { addGlobalErrorMessageFromAPI } from '../../app/utils/globalMessagesService'; import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons'; import Modal from '../../components/controls/Modal'; import Select from '../../components/controls/Select'; @@ -27,6 +26,7 @@ import { Alert } from '../../components/ui/Alert'; import MandatoryFieldMarker from '../../components/ui/MandatoryFieldMarker'; import MandatoryFieldsExplanation from '../../components/ui/MandatoryFieldsExplanation'; import { toNotSoISOString } from '../../helpers/dates'; +import { addGlobalErrorMessageFromAPI } from '../../helpers/globalMessages'; import { translate, translateWithParameters } from '../../helpers/l10n'; import { PermissionTemplate } from '../../types/types'; diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Details.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/Details.tsx index 3767d0df7b9..ca292d3d870 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/Details.tsx +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Details.tsx @@ -20,8 +20,8 @@ import * as React from 'react'; import { Helmet } from 'react-helmet-async'; import { fetchQualityGate } from '../../../api/quality-gates'; -import { addGlobalSuccessMessage } from '../../../app/utils/globalMessagesService'; import DeferredSpinner from '../../../components/ui/DeferredSpinner'; +import { addGlobalSuccessMessage } from '../../../helpers/globalMessages'; import { translate } from '../../../helpers/l10n'; import { Condition, QualityGate } from '../../../types/types'; import { addCondition, checkIfDefault, deleteCondition, replaceCondition } from '../utils'; diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotOpenInIdeButton.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotOpenInIdeButton.tsx index 8f6fa5f60a8..c7f5bfbe1b5 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotOpenInIdeButton.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotOpenInIdeButton.tsx @@ -18,14 +18,11 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { - addGlobalErrorMessage, - addGlobalSuccessMessage -} from '../../../app/utils/globalMessagesService'; import { Button } from '../../../components/controls/buttons'; import { DropdownOverlay } from '../../../components/controls/Dropdown'; import Toggler from '../../../components/controls/Toggler'; import DeferredSpinner from '../../../components/ui/DeferredSpinner'; +import { addGlobalErrorMessage, addGlobalSuccessMessage } from '../../../helpers/globalMessages'; import { translate } from '../../../helpers/l10n'; import { openHotspot, probeSonarLintServers } from '../../../helpers/sonarlint'; import { Ide } from '../../../types/sonarlint'; diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/Assignee.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/Assignee.tsx index e0e44703080..642e7a0ca2f 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/Assignee.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/Assignee.tsx @@ -20,7 +20,7 @@ import * as React from 'react'; import { assignSecurityHotspot } from '../../../../api/security-hotspots'; import withCurrentUserContext from '../../../../app/components/current-user/withCurrentUserContext'; -import { addGlobalSuccessMessage } from '../../../../app/utils/globalMessagesService'; +import { addGlobalSuccessMessage } from '../../../../helpers/globalMessages'; import { translate, translateWithParameters } from '../../../../helpers/l10n'; import { Hotspot, HotspotResolution, HotspotStatus } from '../../../../types/security-hotspots'; import { CurrentUser, isLoggedIn, UserActive } from '../../../../types/users'; diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/Assignee-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/Assignee-test.tsx index b95aad482a0..cc9839ef45d 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/Assignee-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/Assignee-test.tsx @@ -20,7 +20,7 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { assignSecurityHotspot } from '../../../../../api/security-hotspots'; -import { addGlobalSuccessMessage } from '../../../../../app/utils/globalMessagesService'; +import { addGlobalSuccessMessage } from '../../../../../helpers/globalMessages'; import { mockHotspot } from '../../../../../helpers/mocks/security-hotspots'; import { mockCurrentUser, mockUser } from '../../../../../helpers/testMocks'; import { waitAndUpdate } from '../../../../../helpers/testUtils'; @@ -33,7 +33,7 @@ jest.mock('../../../../../api/security-hotspots', () => ({ assignSecurityHotspot: jest.fn() })); -jest.mock('../../../../../app/utils/globalMessagesService', () => ({ +jest.mock('../../../../../helpers/globalMessages', () => ({ addGlobalSuccessMessage: jest.fn() })); diff --git a/server/sonar-web/src/main/js/apps/sessions/components/LoginContainer.tsx b/server/sonar-web/src/main/js/apps/sessions/components/LoginContainer.tsx index e4a2a2a46e5..bb7fcf719c3 100644 --- a/server/sonar-web/src/main/js/apps/sessions/components/LoginContainer.tsx +++ b/server/sonar-web/src/main/js/apps/sessions/components/LoginContainer.tsx @@ -21,7 +21,7 @@ import { Location } from 'history'; import * as React from 'react'; import { logIn } from '../../../api/auth'; import { getIdentityProviders } from '../../../api/users'; -import { addGlobalErrorMessage } from '../../../app/utils/globalMessagesService'; +import { addGlobalErrorMessage } from '../../../helpers/globalMessages'; import { translate } from '../../../helpers/l10n'; import { getReturnUrl } from '../../../helpers/urls'; import { IdentityProvider } from '../../../types/types'; diff --git a/server/sonar-web/src/main/js/apps/sessions/components/Logout.tsx b/server/sonar-web/src/main/js/apps/sessions/components/Logout.tsx index 4064b879c8c..95bc68c8896 100644 --- a/server/sonar-web/src/main/js/apps/sessions/components/Logout.tsx +++ b/server/sonar-web/src/main/js/apps/sessions/components/Logout.tsx @@ -20,7 +20,7 @@ import * as React from 'react'; import { logOut } from '../../../api/auth'; import RecentHistory from '../../../app/components/RecentHistory'; -import { addGlobalErrorMessage } from '../../../app/utils/globalMessagesService'; +import { addGlobalErrorMessage } from '../../../helpers/globalMessages'; import { translate } from '../../../helpers/l10n'; import { getBaseUrl } from '../../../helpers/system'; diff --git a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/Logout-test.tsx b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/Logout-test.tsx index 01e81b89e13..9ff8fa85c6f 100644 --- a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/Logout-test.tsx +++ b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/Logout-test.tsx @@ -20,7 +20,7 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { logOut } from '../../../../api/auth'; -import { addGlobalErrorMessage } from '../../../../app/utils/globalMessagesService'; +import { addGlobalErrorMessage } from '../../../../helpers/globalMessages'; import { waitAndUpdate } from '../../../../helpers/testUtils'; import Logout from '../Logout'; @@ -28,7 +28,7 @@ jest.mock('../../../../api/auth', () => ({ logOut: jest.fn().mockResolvedValue(true) })); -jest.mock('../../../../app/utils/globalMessagesService', () => ({ +jest.mock('../../../../helpers/globalMessages', () => ({ addGlobalErrorMessage: jest.fn() })); diff --git a/server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx b/server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx index 75f32f7c300..6aec452fe45 100644 --- a/server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx @@ -19,13 +19,13 @@ */ import * as React from 'react'; import { changePassword } from '../../../api/users'; -import { addGlobalSuccessMessage } from '../../../app/utils/globalMessagesService'; import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; import Modal from '../../../components/controls/Modal'; import { Alert } from '../../../components/ui/Alert'; import MandatoryFieldMarker from '../../../components/ui/MandatoryFieldMarker'; import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation'; import { throwGlobalError } from '../../../helpers/error'; +import { addGlobalSuccessMessage } from '../../../helpers/globalMessages'; import { translate } from '../../../helpers/l10n'; import { parseError } from '../../../helpers/request'; import { User } from '../../../types/users'; diff --git a/server/sonar-web/src/main/js/components/controls/ComponentReportActions.tsx b/server/sonar-web/src/main/js/components/controls/ComponentReportActions.tsx index 5cb685c46e9..38b294b2670 100644 --- a/server/sonar-web/src/main/js/components/controls/ComponentReportActions.tsx +++ b/server/sonar-web/src/main/js/components/controls/ComponentReportActions.tsx @@ -25,7 +25,7 @@ import { } from '../../api/component-report'; import withAppStateContext from '../../app/components/app-state/withAppStateContext'; import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext'; -import { addGlobalSuccessMessage } from '../../app/utils/globalMessagesService'; +import { addGlobalSuccessMessage } from '../../helpers/globalMessages'; import { translate, translateWithParameters } from '../../helpers/l10n'; import { AppState } from '../../types/appstate'; import { Branch } from '../../types/branch-like'; diff --git a/server/sonar-web/src/main/js/components/controls/Select.tsx b/server/sonar-web/src/main/js/components/controls/Select.tsx index 87cf5d50d15..d6de185293f 100644 --- a/server/sonar-web/src/main/js/components/controls/Select.tsx +++ b/server/sonar-web/src/main/js/components/controls/Select.tsx @@ -22,9 +22,11 @@ import classNames from 'classnames'; import { omit } from 'lodash'; import * as React from 'react'; import ReactSelect, { + components, GroupTypeBase, IndicatorProps, NamedProps, + OptionProps, OptionTypeBase, StylesConfig } from 'react-select'; @@ -94,6 +96,9 @@ export function multiValueRemove< return
×
; } +export type SelectOptionProps = OptionProps; +export const SelectOption = components.Option; + /* Keeping it as a class to simplify a dozen tests */ export default class Select< Option, diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/ComponentReportActions-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/ComponentReportActions-test.tsx index c0c419e1ea6..6bade668517 100644 --- a/server/sonar-web/src/main/js/components/controls/__tests__/ComponentReportActions-test.tsx +++ b/server/sonar-web/src/main/js/components/controls/__tests__/ComponentReportActions-test.tsx @@ -24,7 +24,7 @@ import { subscribeToEmailReport, unsubscribeFromEmailReport } from '../../../api/component-report'; -import { addGlobalSuccessMessage } from '../../../app/utils/globalMessagesService'; +import { addGlobalSuccessMessage } from '../../../helpers/globalMessages'; import { mockBranch } from '../../../helpers/mocks/branch-like'; import { mockComponent } from '../../../helpers/mocks/component'; import { mockComponentReportStatus } from '../../../helpers/mocks/component-report'; @@ -49,7 +49,7 @@ jest.mock('../../../helpers/system', () => ({ getBaseUrl: jest.fn().mockReturnValue('baseUrl') })); -jest.mock('../../../app/utils/globalMessagesService', () => ({ +jest.mock('../../../helpers/globalMessages', () => ({ addGlobalSuccessMessage: jest.fn() })); diff --git a/server/sonar-web/src/main/js/helpers/__tests__/error-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/error-test.ts index cf126933261..8615870ad6d 100644 --- a/server/sonar-web/src/main/js/helpers/__tests__/error-test.ts +++ b/server/sonar-web/src/main/js/helpers/__tests__/error-test.ts @@ -17,10 +17,10 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { addGlobalErrorMessage } from '../../app/utils/globalMessagesService'; import { throwGlobalError } from '../error'; +import { addGlobalErrorMessage } from '../globalMessages'; -jest.mock('../../app/utils/globalMessagesService', () => ({ +jest.mock('../../helpers/globalMessages', () => ({ addGlobalErrorMessage: jest.fn() })); diff --git a/server/sonar-web/src/main/js/helpers/__tests__/globalMessages-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/globalMessages-test.ts new file mode 100644 index 00000000000..c6842d9b7a8 --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/__tests__/globalMessages-test.ts @@ -0,0 +1,49 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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 { MessageLevel } from '../../types/globalMessages'; +import { + addGlobalErrorMessage, + addGlobalSuccessMessage, + registerListener +} from '../globalMessages'; + +it('should work as expected', () => { + const listener1 = jest.fn(); + registerListener(listener1); + + addGlobalErrorMessage('test'); + + expect(listener1).toBeCalledWith( + expect.objectContaining({ text: 'test', level: MessageLevel.Error }) + ); + + listener1.mockClear(); + const listener2 = jest.fn(); + registerListener(listener2); + + addGlobalSuccessMessage('test'); + + expect(listener1).toBeCalledWith( + expect.objectContaining({ text: 'test', level: MessageLevel.Success }) + ); + expect(listener2).toBeCalledWith( + expect.objectContaining({ text: 'test', level: MessageLevel.Success }) + ); +}); diff --git a/server/sonar-web/src/main/js/helpers/error.ts b/server/sonar-web/src/main/js/helpers/error.ts index 70ffdcc1795..7356741dea7 100644 --- a/server/sonar-web/src/main/js/helpers/error.ts +++ b/server/sonar-web/src/main/js/helpers/error.ts @@ -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 { addGlobalErrorMessage } from '../app/utils/globalMessagesService'; +import { addGlobalErrorMessage } from './globalMessages'; import { parseError } from './request'; export function throwGlobalError(param: Response | any): Promise { diff --git a/server/sonar-web/src/main/js/helpers/globalMessages.ts b/server/sonar-web/src/main/js/helpers/globalMessages.ts new file mode 100644 index 00000000000..dc5f2f02ca5 --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/globalMessages.ts @@ -0,0 +1,68 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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 { uniqueId } from 'lodash'; +import { Message, MessageLevel } from '../types/globalMessages'; +import { parseError } from './request'; + +const listeners: Array<(message: Message) => void> = []; + +export function registerListener(callback: (message: Message) => void) { + listeners.push(callback); +} + +export function unregisterListener(callback: (message: Message) => void) { + const index = listeners.indexOf(callback); + + if (index > -1) { + listeners.splice(index, 1); + } +} + +function addMessage(text: string, level: MessageLevel) { + listeners.forEach(listener => + listener({ + id: uniqueId('global-message-'), + level, + text + }) + ); +} + +export function addGlobalErrorMessage(text: string) { + addMessage(text, MessageLevel.Error); +} + +export function addGlobalErrorMessageFromAPI(param: Response | string) { + if (param instanceof Response) { + return parseError(param).then(addGlobalErrorMessage, () => { + /* ignore parsing errors */ + }); + } + if (typeof param === 'string') { + return Promise.resolve(param).then(addGlobalErrorMessage); + } + + return Promise.resolve(); +} + +export function addGlobalSuccessMessage(text: string) { + addMessage(text, MessageLevel.Success); +} diff --git a/server/sonar-web/src/main/js/types/browser.ts b/server/sonar-web/src/main/js/types/browser.ts index b2adcb34d6d..ce0d3ea65c7 100644 --- a/server/sonar-web/src/main/js/types/browser.ts +++ b/server/sonar-web/src/main/js/types/browser.ts @@ -29,4 +29,6 @@ export interface EnhancedWindow extends Window { registerExtension: (key: string, start: ExtensionStartMethod, providesCSSFile?: boolean) => void; setWebAnalyticsPageChangeHandler: (pageHandler: (pathname: string) => void) => void; + t: (...keys: string[]) => string; + tp: (messageKey: string, ...parameters: Array) => string; } diff --git a/server/sonar-web/src/main/js/types/extension.ts b/server/sonar-web/src/main/js/types/extension.ts index 2540bc44cfe..3defc91892d 100644 --- a/server/sonar-web/src/main/js/types/extension.ts +++ b/server/sonar-web/src/main/js/types/extension.ts @@ -21,7 +21,7 @@ import { IntlShape } from 'react-intl'; import { Location, Router } from '../components/hoc/withRouter'; import { AppState } from './appstate'; import { L10nBundle } from './l10nBundle'; -import { Dict } from './types'; +import { Component, Dict } from './types'; import { CurrentUser } from './users'; export enum AdminPageExtension { @@ -40,6 +40,7 @@ export interface ExtensionStartMethod { export interface ExtensionStartMethodParameter { appState: AppState; el: HTMLElement | undefined | null; + component?: Component; currentUser: CurrentUser; intl: IntlShape; location: Location;