aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main
diff options
context:
space:
mode:
authorJeremy Davis <jeremy.davis@sonarsource.com>2022-03-08 15:21:03 +0100
committersonartech <sonartech@sonarsource.com>2022-03-11 20:02:49 +0000
commitaccf42f839ee635c43c683de657de95ff249603e (patch)
treea168628c7c3e373c3b7bf43788532f799fce7368 /server/sonar-web/src/main
parentd05858841336b062e4bef229998fe721f0f43a85 (diff)
downloadsonarqube-accf42f839ee635c43c683de657de95ff249603e.tar.gz
sonarqube-accf42f839ee635c43c683de657de95ff249603e.zip
SONAR-15992 Remove Global Settings from redux
Diffstat (limited to 'server/sonar-web/src/main')
-rw-r--r--server/sonar-web/src/main/js/app/components/AdminContainer.tsx3
-rw-r--r--server/sonar-web/src/main/js/app/components/App.tsx34
-rw-r--r--server/sonar-web/src/main/js/app/components/ComponentContainer.tsx3
-rw-r--r--server/sonar-web/src/main/js/app/components/GlobalFooter.tsx2
-rw-r--r--server/sonar-web/src/main/js/app/components/PageTracker.tsx33
-rw-r--r--server/sonar-web/src/main/js/app/components/StartupModal.tsx3
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/App-test.tsx44
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/PageTracker-test.tsx19
-rw-r--r--server/sonar-web/src/main/js/app/components/app-state/AppStateContext.tsx2
-rw-r--r--server/sonar-web/src/main/js/app/components/app-state/AppStateContextProvider.tsx17
-rw-r--r--server/sonar-web/src/main/js/app/components/app-state/__tests__/AppStateContextProvider-test.tsx14
-rw-r--r--server/sonar-web/src/main/js/app/components/app-state/__tests__/__snapshots__/AppStateContextProvider-test.tsx.snap19
-rw-r--r--server/sonar-web/src/main/js/app/components/app-state/__tests__/withAppStateContext-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx2
-rw-r--r--server/sonar-web/src/main/js/app/components/extensions/Extension.tsx3
-rw-r--r--server/sonar-web/src/main/js/app/components/extensions/GlobalPageExtension.tsx2
-rw-r--r--server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts2
-rw-r--r--server/sonar-web/src/main/js/app/components/indexation/IndexationContextProvider.tsx2
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNavLicenseNotif.tsx2
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx3
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchLikeNavigation.tsx3
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/GlobalNavBranding.tsx26
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx3
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavBranding-test.tsx50
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNav-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavBranding-test.tsx.snap52
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx3
-rw-r--r--server/sonar-web/src/main/js/app/index.ts2
-rw-r--r--server/sonar-web/src/main/js/app/utils/startReactApp.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/audit-logs/components/AuditApp.tsx68
-rw-r--r--server/sonar-web/src/main/js/apps/audit-logs/components/__tests__/AuditApp-test.tsx42
-rw-r--r--server/sonar-web/src/main/js/apps/background-tasks/components/StatPendingCount.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/change-admin-password/ChangeAdminPasswordApp.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsIssues.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesApp-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx27
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-test.tsx28
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.tsx.snap8
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/MarketplaceAppContainer.tsx (renamed from server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx)39
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/__tests__/AppContainer-test.tsx52
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/__tests__/MarketplaceAppContainer-test.tsx48
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/MarketplaceAppContainer-test.tsx.snap54
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/routes.ts2
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/App.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap42
-rw-r--r--server/sonar-web/src/main/js/apps/permission-templates/components/App.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/permissions/shared/components/__tests__/__snapshots__/UserHolder-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/LifetimeInformation.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projectDump/ProjectDumpApp.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ApplicationCreation.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/PermissionItem-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissionsAddModalRenderer-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsUser-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap26
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/__snapshots__/AssigneeSelectionRenderer-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegration.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/almIntegration/CreationTooltip.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/settings/store/__tests__/actions-test.ts55
-rw-r--r--server/sonar-web/src/main/js/apps/settings/store/actions.ts39
-rw-r--r--server/sonar-web/src/main/js/apps/settings/store/values.ts78
-rw-r--r--server/sonar-web/src/main/js/apps/system/components/PageHeader.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItem-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UsersSelectSearch-test.tsx.snap8
-rw-r--r--server/sonar-web/src/main/js/components/controls/ComponentReportActions.tsx3
-rw-r--r--server/sonar-web/src/main/js/components/docs/DocLink.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.tsx.snap6
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.tsx.snap8
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetAssigneePopup-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SimilarIssuesPopup-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/components/measure/Measure.tsx5
-rw-r--r--server/sonar-web/src/main/js/components/measure/RatingTooltipContent.tsx94
-rw-r--r--server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx14
-rw-r--r--server/sonar-web/src/main/js/components/measure/__tests__/RatingTooltipContent-test.tsx62
-rw-r--r--server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap15
-rw-r--r--server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/RatingTooltipContent-test.tsx.snap37
-rw-r--r--server/sonar-web/src/main/js/components/measure/utils.ts11
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/PublishSteps.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/AnalysisCommand.tsx3
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx3
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/gitlabci/YmlFileStep.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx3
-rw-r--r--server/sonar-web/src/main/js/components/ui/Avatar.tsx56
-rw-r--r--server/sonar-web/src/main/js/components/ui/__tests__/Avatar-test.tsx16
-rw-r--r--server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx2
-rw-r--r--server/sonar-web/src/main/js/helpers/analytics.js26
-rw-r--r--server/sonar-web/src/main/js/helpers/measures.ts62
-rw-r--r--server/sonar-web/src/main/js/helpers/testMocks.ts14
-rw-r--r--server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx3
-rw-r--r--server/sonar-web/src/main/js/store/rootReducer.ts13
-rw-r--r--server/sonar-web/src/main/js/types/appstate.ts (renamed from server/sonar-web/src/main/js/apps/settings/store/rootReducer.ts)29
-rw-r--r--server/sonar-web/src/main/js/types/extension.ts3
-rw-r--r--server/sonar-web/src/main/js/types/settings.ts11
-rw-r--r--server/sonar-web/src/main/js/types/types.ts19
106 files changed, 830 insertions, 764 deletions
diff --git a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx
index 8a690681ac7..0f05906ec49 100644
--- a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx
@@ -24,8 +24,9 @@ import { getPendingPlugins } from '../../api/plugins';
import { getSystemStatus, waitSystemUPStatus } from '../../api/system';
import handleRequiredAuthorization from '../../app/utils/handleRequiredAuthorization';
import { translate } from '../../helpers/l10n';
+import { AppState } from '../../types/appstate';
import { PendingPluginResult } from '../../types/plugins';
-import { AppState, Extension, SysStatus } from '../../types/types';
+import { Extension, SysStatus } from '../../types/types';
import AdminContext, { defaultPendingPlugins, defaultSystemStatus } from './AdminContext';
import withAppStateContext from './app-state/withAppStateContext';
import SettingsNav from './nav/settings/SettingsNav';
diff --git a/server/sonar-web/src/main/js/app/components/App.tsx b/server/sonar-web/src/main/js/app/components/App.tsx
index b7e22a74638..e7dcd7d0654 100644
--- a/server/sonar-web/src/main/js/app/components/App.tsx
+++ b/server/sonar-web/src/main/js/app/components/App.tsx
@@ -18,16 +18,16 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { connect } from 'react-redux';
import { lazyLoadComponent } from '../../components/lazyLoadComponent';
-import { getGlobalSettingValue, Store } from '../../store/rootReducer';
+import { AppState } from '../../types/appstate';
+import { GlobalSettingKeys } from '../../types/settings';
+import withAppStateContext from './app-state/withAppStateContext';
import KeyboardShortcutsModal from './KeyboardShortcutsModal';
const PageTracker = lazyLoadComponent(() => import('./PageTracker'));
interface Props {
- enableGravatar: boolean;
- gravatarServerUrl: string;
+ appState: AppState;
}
export class App extends React.PureComponent<Props> {
@@ -68,8 +68,19 @@ export class App extends React.PureComponent<Props> {
};
renderPreconnectLink = () => {
+ const {
+ appState: { settings }
+ } = this.props;
+
+ const enableGravatar = settings[GlobalSettingKeys.EnableGravatar] === 'true';
+ const gravatarServerUrl = settings[GlobalSettingKeys.GravatarServerUrl];
+
+ if (!enableGravatar || !gravatarServerUrl) {
+ return null;
+ }
+
const parser = document.createElement('a');
- parser.href = this.props.gravatarServerUrl;
+ parser.href = gravatarServerUrl;
if (parser.hostname !== window.location.hostname) {
return <link href={parser.origin} rel="preconnect" />;
}
@@ -79,7 +90,7 @@ export class App extends React.PureComponent<Props> {
render() {
return (
<>
- <PageTracker>{this.props.enableGravatar && this.renderPreconnectLink()}</PageTracker>
+ <PageTracker>{this.renderPreconnectLink()}</PageTracker>
{this.props.children}
<KeyboardShortcutsModal />
</>
@@ -87,13 +98,4 @@ export class App extends React.PureComponent<Props> {
}
}
-const mapStateToProps = (state: Store) => {
- const enableGravatar = getGlobalSettingValue(state, 'sonar.lf.enableGravatar');
- const gravatarServerUrl = getGlobalSettingValue(state, 'sonar.lf.gravatarServerUrl');
- return {
- enableGravatar: Boolean(enableGravatar && enableGravatar.value === 'true'),
- gravatarServerUrl: (gravatarServerUrl && gravatarServerUrl.value) || ''
- };
-};
-
-export default connect(mapStateToProps)(App);
+export default withAppStateContext(App);
diff --git a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
index 61aafd19ee9..e663080820a 100644
--- a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
@@ -39,10 +39,11 @@ import {
ProjectAlmBindingConfigurationErrors,
ProjectAlmBindingResponse
} from '../../types/alm-settings';
+import { AppState } from '../../types/appstate';
import { BranchLike } from '../../types/branch-like';
import { ComponentQualifier, isPortfolioLike } from '../../types/component';
import { Task, TaskStatuses, TaskTypes, TaskWarning } from '../../types/tasks';
-import { AppState, Component, Status } from '../../types/types';
+import { Component, Status } from '../../types/types';
import handleRequiredAuthorization from '../utils/handleRequiredAuthorization';
import withAppStateContext from './app-state/withAppStateContext';
import ComponentContainerNotFound from './ComponentContainerNotFound';
diff --git a/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx b/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx
index da00469d33c..fae270252e2 100644
--- a/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx
+++ b/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx
@@ -23,8 +23,8 @@ import InstanceMessage from '../../components/common/InstanceMessage';
import { Alert } from '../../components/ui/Alert';
import { getEdition } from '../../helpers/editions';
import { translate, translateWithParameters } from '../../helpers/l10n';
+import { AppState } from '../../types/appstate';
import { EditionKey } from '../../types/editions';
-import { AppState } from '../../types/types';
import withAppStateContext from './app-state/withAppStateContext';
import GlobalFooterBranding from './GlobalFooterBranding';
diff --git a/server/sonar-web/src/main/js/app/components/PageTracker.tsx b/server/sonar-web/src/main/js/app/components/PageTracker.tsx
index 02c47e467fb..7ae1adbf0a9 100644
--- a/server/sonar-web/src/main/js/app/components/PageTracker.tsx
+++ b/server/sonar-web/src/main/js/app/components/PageTracker.tsx
@@ -19,19 +19,15 @@
*/
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
-import { connect } from 'react-redux';
import { Location, withRouter } from '../../components/hoc/withRouter';
-import { gtm } from '../../helpers/analytics';
import { installScript } from '../../helpers/extensions';
import { getWebAnalyticsPageHandlerFromCache } from '../../helpers/extensionsHandler';
import { getInstance } from '../../helpers/system';
-import { getGlobalSettingValue, Store } from '../../store/rootReducer';
-import { AppState } from '../../types/types';
+import { AppState } from '../../types/appstate';
import withAppStateContext from './app-state/withAppStateContext';
interface Props {
location: Location;
- trackingIdGTM?: string;
appState: AppState;
}
@@ -43,54 +39,37 @@ export class PageTracker extends React.Component<Props, State> {
state: State = {};
componentDidMount() {
- const { trackingIdGTM, appState } = this.props;
+ const { appState } = this.props;
if (appState.webAnalyticsJsPath && !getWebAnalyticsPageHandlerFromCache()) {
installScript(appState.webAnalyticsJsPath, 'head');
}
-
- if (trackingIdGTM) {
- gtm(trackingIdGTM);
- }
}
trackPage = () => {
- const { location, trackingIdGTM } = this.props;
+ const { location } = this.props;
const { lastLocation } = this.state;
- const { dataLayer } = window as any;
const locationChanged = location.pathname !== lastLocation;
const webAnalyticsPageChange = getWebAnalyticsPageHandlerFromCache();
if (webAnalyticsPageChange && locationChanged) {
this.setState({ lastLocation: location.pathname });
setTimeout(() => webAnalyticsPageChange(location.pathname), 500);
- } else if (dataLayer && dataLayer.push && trackingIdGTM && location.pathname !== '/') {
- this.setState({ lastLocation: location.pathname });
- setTimeout(() => dataLayer.push({ event: 'render-end' }), 500);
}
};
render() {
- const { trackingIdGTM, appState } = this.props;
+ const { appState } = this.props;
return (
<Helmet
defaultTitle={getInstance()}
defer={false}
- onChangeClientState={
- trackingIdGTM || appState.webAnalyticsJsPath ? this.trackPage : undefined
- }>
+ onChangeClientState={appState.webAnalyticsJsPath ? this.trackPage : undefined}>
{this.props.children}
</Helmet>
);
}
}
-const mapStateToProps = (state: Store) => {
- const trackingIdGTM = getGlobalSettingValue(state, 'sonar.analytics.gtm.trackingId');
- return {
- trackingIdGTM: trackingIdGTM && trackingIdGTM.value
- };
-};
-
-export default withRouter(connect(mapStateToProps)(withAppStateContext(PageTracker)));
+export default withRouter(withAppStateContext(PageTracker));
diff --git a/server/sonar-web/src/main/js/app/components/StartupModal.tsx b/server/sonar-web/src/main/js/app/components/StartupModal.tsx
index ff8216a49d1..5b7c3915672 100644
--- a/server/sonar-web/src/main/js/app/components/StartupModal.tsx
+++ b/server/sonar-web/src/main/js/app/components/StartupModal.tsx
@@ -28,8 +28,9 @@ import { hasMessage } from '../../helpers/l10n';
import { get, save } from '../../helpers/storage';
import { isLoggedIn } from '../../helpers/users';
import { getCurrentUser, Store } from '../../store/rootReducer';
+import { AppState } from '../../types/appstate';
import { EditionKey } from '../../types/editions';
-import { AppState, CurrentUser } from '../../types/types';
+import { CurrentUser } from '../../types/types';
import withAppStateContext from './app-state/withAppStateContext';
const LicensePromptModal = lazyLoadComponent(
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/App-test.tsx
index 75b9d805b88..842155e2977 100644
--- a/server/sonar-web/src/main/js/app/components/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/__tests__/App-test.tsx
@@ -19,23 +19,20 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { connect } from 'react-redux';
+import { mockAppState } from '../../../helpers/testMocks';
import { App } from '../App';
-jest.mock('react-redux', () => ({
- connect: jest.fn(() => (a: any) => a)
-}));
-
-jest.mock('../../../store/rootReducer', () => ({
- getGlobalSettingValue: jest.fn((_, key: string) => ({
- value: key === 'sonar.lf.enableGravatar' ? 'true' : 'http://gravatar.com'
- }))
-}));
-
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot('default');
expect(
- shallowRender({ enableGravatar: true, gravatarServerUrl: 'http://example.com' })
+ shallowRender({
+ appState: mockAppState({
+ settings: {
+ 'sonar.lf.enableGravatar': 'true',
+ 'sonar.lf.gravatarServerUrl': 'http://example.com'
+ }
+ })
+ })
).toMatchSnapshot('with gravatar');
});
@@ -44,17 +41,16 @@ it('should correctly set the scrollbar width as a custom property', () => {
expect(document.body.style.getPropertyValue('--sbw')).toBe('0px');
});
-describe('redux', () => {
- it('should correctly map state props', () => {
- const [mapStateToProps] = (connect as jest.Mock).mock.calls[0];
-
- expect(mapStateToProps({})).toEqual({
- enableGravatar: true,
- gravatarServerUrl: 'http://gravatar.com'
- });
- });
-});
-
function shallowRender(props: Partial<App['props']> = {}) {
- return shallow<App>(<App enableGravatar={false} gravatarServerUrl="" {...props} />);
+ return shallow<App>(
+ <App
+ appState={mockAppState({
+ settings: {
+ 'sonar.lf.enableGravatar': 'false',
+ 'sonar.lf.gravatarServerUrl': ''
+ }
+ })}
+ {...props}
+ />
+ );
}
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/PageTracker-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/PageTracker-test.tsx
index 5da29b31463..5e7d3dfde6c 100644
--- a/server/sonar-web/src/main/js/app/components/__tests__/PageTracker-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/__tests__/PageTracker-test.tsx
@@ -19,7 +19,6 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { gtm } from '../../../helpers/analytics';
import { installScript } from '../../../helpers/extensions';
import { getWebAnalyticsPageHandlerFromCache } from '../../../helpers/extensionsHandler';
import { mockAppState, mockLocation } from '../../../helpers/testMocks';
@@ -33,7 +32,6 @@ jest.mock('../../../helpers/extensionsHandler', () => ({
getWebAnalyticsPageHandlerFromCache: jest.fn().mockReturnValue(undefined)
}));
-jest.mock('../../../helpers/analytics', () => ({ gtm: jest.fn() }));
beforeAll(() => {
jest.useFakeTimers();
});
@@ -52,7 +50,6 @@ it('should not trigger if no analytics system is given', () => {
const wrapper = shallowRender();
expect(wrapper).toMatchSnapshot();
expect(installScript).not.toHaveBeenCalled();
- expect(gtm).not.toHaveBeenCalled();
});
it('should work for WebAnalytics plugin', () => {
@@ -70,22 +67,6 @@ it('should work for WebAnalytics plugin', () => {
expect(pageChange).toHaveBeenCalledWith('/path');
});
-it('should work for Google Tag Manager', () => {
- (window as any).dataLayer = [];
- const { dataLayer } = window as any;
- const push = jest.spyOn(dataLayer, 'push');
- const wrapper = shallowRender({ trackingIdGTM: '123' });
-
- expect(wrapper.find('Helmet').prop('onChangeClientState')).toBe(wrapper.instance().trackPage);
- expect(gtm).toBeCalled();
- expect(dataLayer).toHaveLength(0);
-
- wrapper.instance().trackPage();
- jest.runAllTimers();
- expect(push).toBeCalledWith({ event: 'render-end' });
- expect(dataLayer).toHaveLength(1);
-});
-
function shallowRender(props: Partial<PageTracker['props']> = {}) {
return shallow<PageTracker>(
<PageTracker appState={mockAppState()} location={mockLocation()} {...props} />
diff --git a/server/sonar-web/src/main/js/app/components/app-state/AppStateContext.tsx b/server/sonar-web/src/main/js/app/components/app-state/AppStateContext.tsx
index 1e2dcf47d4f..a8e130c84d2 100644
--- a/server/sonar-web/src/main/js/app/components/app-state/AppStateContext.tsx
+++ b/server/sonar-web/src/main/js/app/components/app-state/AppStateContext.tsx
@@ -19,7 +19,7 @@
*/
import * as React from 'react';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
const defaultAppState = {
authenticationError: false,
diff --git a/server/sonar-web/src/main/js/app/components/app-state/AppStateContextProvider.tsx b/server/sonar-web/src/main/js/app/components/app-state/AppStateContextProvider.tsx
index 1d48066c47a..43c937b651f 100644
--- a/server/sonar-web/src/main/js/app/components/app-state/AppStateContextProvider.tsx
+++ b/server/sonar-web/src/main/js/app/components/app-state/AppStateContextProvider.tsx
@@ -20,29 +20,16 @@
*/
import * as React from 'react';
-import { connect } from 'react-redux';
-import { setValues } from '../../../apps/settings/store/actions';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
import { AppStateContext } from './AppStateContext';
export interface AppStateContextProviderProps {
- setValues: typeof setValues;
appState: AppState;
}
-export function AppStateContextProvider({
- setValues,
+export default function AppStateContextProvider({
appState,
children
}: React.PropsWithChildren<AppStateContextProviderProps>) {
- React.useEffect(() => {
- setValues(
- Object.keys(appState.settings),
- Object.entries(appState.settings).map(([key, value]) => ({ key, value }))
- );
- });
-
return <AppStateContext.Provider value={appState}>{children}</AppStateContext.Provider>;
}
-const mapDispatchToProps = { setValues };
-export default connect(null, mapDispatchToProps)(AppStateContextProvider);
diff --git a/server/sonar-web/src/main/js/app/components/app-state/__tests__/AppStateContextProvider-test.tsx b/server/sonar-web/src/main/js/app/components/app-state/__tests__/AppStateContextProvider-test.tsx
index 05d5fe55e21..57e3f0a1e0a 100644
--- a/server/sonar-web/src/main/js/app/components/app-state/__tests__/AppStateContextProvider-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/app-state/__tests__/AppStateContextProvider-test.tsx
@@ -21,20 +21,18 @@ import { mount } from 'enzyme';
import * as React from 'react';
import { mockAppState } from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';
-import { AppStateContextProvider, AppStateContextProviderProps } from '../AppStateContextProvider';
+import AppStateContextProvider, { AppStateContextProviderProps } from '../AppStateContextProvider';
it('should set value correctly', async () => {
- const setValues = jest.fn();
+ const appState = mockAppState({ settings: { 'sonar.lf.logoUrl': 'whatevs/' } });
const wrapper = render({
- appState: mockAppState({ settings: { foo: 'bar' } }),
- setValues
+ appState
});
await waitAndUpdate(wrapper);
- expect(setValues).toHaveBeenCalledWith(['foo'], [{ key: 'foo', value: 'bar' }]);
+
+ expect(wrapper).toMatchSnapshot();
});
function render(override?: Partial<AppStateContextProviderProps>) {
- return mount(
- <AppStateContextProvider appState={mockAppState()} setValues={jest.fn()} {...override} />
- );
+ return mount(<AppStateContextProvider appState={mockAppState()} {...override} />);
}
diff --git a/server/sonar-web/src/main/js/app/components/app-state/__tests__/__snapshots__/AppStateContextProvider-test.tsx.snap b/server/sonar-web/src/main/js/app/components/app-state/__tests__/__snapshots__/AppStateContextProvider-test.tsx.snap
new file mode 100644
index 00000000000..1e81e16bd33
--- /dev/null
+++ b/server/sonar-web/src/main/js/app/components/app-state/__tests__/__snapshots__/AppStateContextProvider-test.tsx.snap
@@ -0,0 +1,19 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should set value correctly 1`] = `
+<AppStateContextProvider
+ appState={
+ Object {
+ "edition": "community",
+ "productionDatabase": true,
+ "qualifiers": Array [
+ "TRK",
+ ],
+ "settings": Object {
+ "sonar.lf.logoUrl": "whatevs/",
+ },
+ "version": "1.0",
+ }
+ }
+/>
+`;
diff --git a/server/sonar-web/src/main/js/app/components/app-state/__tests__/withAppStateContext-test.tsx b/server/sonar-web/src/main/js/app/components/app-state/__tests__/withAppStateContext-test.tsx
index d6323fb0f81..78e009131c2 100644
--- a/server/sonar-web/src/main/js/app/components/app-state/__tests__/withAppStateContext-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/app-state/__tests__/withAppStateContext-test.tsx
@@ -20,7 +20,7 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockAppState } from '../../../../helpers/testMocks';
-import { AppState } from '../../../../types/types';
+import { AppState } from '../../../../types/appstate';
import withAppStateContext from '../withAppStateContext';
const appState = mockAppState();
diff --git a/server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx b/server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx
index 64107510cf1..fa432377d1e 100644
--- a/server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx
+++ b/server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx
@@ -20,7 +20,7 @@
import * as React from 'react';
import { getWrappedDisplayName } from '../../../components/hoc/utils';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
import { AppStateContext } from './AppStateContext';
export interface WithAppStateContextProps {
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 aeb29e3d72a..db90d3e0342 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
@@ -27,8 +27,9 @@ import { getCurrentL10nBundle, translate } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
import { addGlobalErrorMessage } from '../../../store/globalMessages';
import { getCurrentUser, Store } from '../../../store/rootReducer';
+import { AppState } from '../../../types/appstate';
import { ExtensionStartMethod } from '../../../types/extension';
-import { AppState, CurrentUser, Dict, Extension as TypeExtension } from '../../../types/types';
+import { CurrentUser, Dict, Extension as TypeExtension } from '../../../types/types';
import * as theme from '../../theme';
import getStore from '../../utils/getStore';
import withAppStateContext from '../app-state/withAppStateContext';
diff --git a/server/sonar-web/src/main/js/app/components/extensions/GlobalPageExtension.tsx b/server/sonar-web/src/main/js/app/components/extensions/GlobalPageExtension.tsx
index d3b0559d35c..0c4206497c2 100644
--- a/server/sonar-web/src/main/js/app/components/extensions/GlobalPageExtension.tsx
+++ b/server/sonar-web/src/main/js/app/components/extensions/GlobalPageExtension.tsx
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
import withAppStateContext from '../app-state/withAppStateContext';
import NotFound from '../NotFound';
import Extension from './Extension';
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 e86c3868d16..68a380c179a 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
@@ -70,6 +70,7 @@ import DateFormatter from '../../../components/intl/DateFormatter';
import DateFromNow from '../../../components/intl/DateFromNow';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import Measure from '../../../components/measure/Measure';
+import RatingTooltipContent from '../../../components/measure/RatingTooltipContent';
import { Alert } from '../../../components/ui/Alert';
import CoverageRating from '../../../components/ui/CoverageRating';
import DeferredSpinner from '../../../components/ui/DeferredSpinner';
@@ -237,6 +238,7 @@ const exposeLibraries = () => {
Radio,
RadioToggle,
Rating,
+ RatingTooltipContent,
ReloadButton,
ResetButtonLink,
SearchBox,
diff --git a/server/sonar-web/src/main/js/app/components/indexation/IndexationContextProvider.tsx b/server/sonar-web/src/main/js/app/components/indexation/IndexationContextProvider.tsx
index 091fc03024d..e2b4e58a538 100644
--- a/server/sonar-web/src/main/js/app/components/indexation/IndexationContextProvider.tsx
+++ b/server/sonar-web/src/main/js/app/components/indexation/IndexationContextProvider.tsx
@@ -19,8 +19,8 @@
*/
/* eslint-disable react/no-unused-state */
import * as React from 'react';
+import { AppState } from '../../../types/appstate';
import { IndexationContextInterface, IndexationStatus } from '../../../types/indexation';
-import { AppState } from '../../../types/types';
import withAppStateContext from '../app-state/withAppStateContext';
import { IndexationContext } from './IndexationContext';
import IndexationNotificationHelper from './IndexationNotificationHelper';
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavLicenseNotif.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavLicenseNotif.tsx
index 37770276d4f..7c9e217e1ab 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavLicenseNotif.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavLicenseNotif.tsx
@@ -22,9 +22,9 @@ import { Link } from 'react-router';
import { isValidLicense } from '../../../../api/marketplace';
import { Alert } from '../../../../components/ui/Alert';
import { translate, translateWithParameters } from '../../../../helpers/l10n';
+import { AppState } from '../../../../types/appstate';
import { ComponentQualifier } from '../../../../types/component';
import { Task } from '../../../../types/tasks';
-import { AppState } from '../../../../types/types';
import withAppStateContext from '../../app-state/withAppStateContext';
interface Props {
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx
index 7f7d3c32843..7b33626b62a 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx
@@ -30,9 +30,10 @@ import NavBarTabs from '../../../../components/ui/NavBarTabs';
import { getBranchLikeQuery, isPullRequest } from '../../../../helpers/branch-like';
import { hasMessage, translate, translateWithParameters } from '../../../../helpers/l10n';
import { getPortfolioUrl, getProjectQueryUrl } from '../../../../helpers/urls';
+import { AppState } from '../../../../types/appstate';
import { BranchLike, BranchParameters } from '../../../../types/branch-like';
import { ComponentQualifier, isPortfolioLike } from '../../../../types/component';
-import { AppState, Component, Extension } from '../../../../types/types';
+import { Component, Extension } from '../../../../types/types';
import withAppStateContext from '../../app-state/withAppStateContext';
import './Menu.css';
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchLikeNavigation.tsx b/server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchLikeNavigation.tsx
index 2e619a4e049..3688efac0b2 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchLikeNavigation.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchLikeNavigation.tsx
@@ -21,8 +21,9 @@ import classNames from 'classnames';
import * as React from 'react';
import Toggler from '../../../../../components/controls/Toggler';
import { ProjectAlmBindingResponse } from '../../../../../types/alm-settings';
+import { AppState } from '../../../../../types/appstate';
import { BranchLike } from '../../../../../types/branch-like';
-import { AppState, Component } from '../../../../../types/types';
+import { Component } from '../../../../../types/types';
import withAppStateContext from '../../../app-state/withAppStateContext';
import './BranchLikeNavigation.css';
import CurrentBranchLike from './CurrentBranchLike';
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavBranding.tsx b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavBranding.tsx
index 2706aa5cffb..6a952f3cc4b 100644
--- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavBranding.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavBranding.tsx
@@ -18,18 +18,21 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { connect } from 'react-redux';
import { Link } from 'react-router';
import { translate } from '../../../../helpers/l10n';
import { getBaseUrl } from '../../../../helpers/system';
-import { getGlobalSettingValue, Store } from '../../../../store/rootReducer';
+import { AppState } from '../../../../types/appstate';
+import { GlobalSettingKeys } from '../../../../types/settings';
+import withAppStateContext from '../../app-state/withAppStateContext';
-interface StateProps {
- customLogoUrl?: string;
- customLogoWidth?: string | number;
+export interface GlobalNavBrandingProps {
+ appState: AppState;
}
-export function GlobalNavBranding({ customLogoUrl, customLogoWidth }: StateProps) {
+export function GlobalNavBranding({ appState: { settings } }: GlobalNavBrandingProps) {
+ const customLogoUrl = settings[GlobalSettingKeys.LogoUrl];
+ const customLogoWidth = settings[GlobalSettingKeys.LogoWidth];
+
const title = translate('layout.sonar.slogan');
const url = customLogoUrl || `${getBaseUrl()}/images/logo.svg?v=6.6`;
const width = customLogoUrl ? customLogoWidth || 100 : 83;
@@ -41,13 +44,4 @@ export function GlobalNavBranding({ customLogoUrl, customLogoWidth }: StateProps
);
}
-const mapStateToProps = (state: Store): StateProps => {
- const customLogoUrl = getGlobalSettingValue(state, 'sonar.lf.logoUrl');
- const customLogoWidth = getGlobalSettingValue(state, 'sonar.lf.logoWidthPx');
- return {
- customLogoUrl: customLogoUrl && customLogoUrl.value,
- customLogoWidth: customLogoWidth && customLogoWidth.value
- };
-};
-
-export default connect(mapStateToProps)(GlobalNavBranding);
+export default withAppStateContext(GlobalNavBranding);
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx
index daf8af77711..230bdffe274 100644
--- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx
@@ -25,8 +25,9 @@ import Dropdown from '../../../../components/controls/Dropdown';
import DropdownIcon from '../../../../components/icons/DropdownIcon';
import { translate } from '../../../../helpers/l10n';
import { getQualityGatesUrl } from '../../../../helpers/urls';
+import { AppState } from '../../../../types/appstate';
import { ComponentQualifier } from '../../../../types/component';
-import { AppState, CurrentUser, Extension } from '../../../../types/types';
+import { CurrentUser, Extension } from '../../../../types/types';
import withAppStateContext from '../../app-state/withAppStateContext';
interface Props {
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavBranding-test.tsx b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavBranding-test.tsx
new file mode 100644
index 00000000000..f182f4d43e7
--- /dev/null
+++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavBranding-test.tsx
@@ -0,0 +1,50 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockAppState } from '../../../../../helpers/testMocks';
+import { GlobalNavBranding, GlobalNavBrandingProps } from '../GlobalNavBranding';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot('default');
+ expect(
+ shallowRender({
+ appState: mockAppState({
+ settings: {
+ 'sonar.lf.logoUrl': 'http://sonarsource.com/custom-logo.svg'
+ }
+ })
+ })
+ ).toMatchSnapshot('with logo');
+ expect(
+ shallowRender({
+ appState: mockAppState({
+ settings: {
+ 'sonar.lf.logoUrl': 'http://sonarsource.com/custom-logo.svg',
+ 'sonar.lf.logoWidthPx': '200'
+ }
+ })
+ })
+ ).toMatchSnapshot('with logo and width');
+});
+
+function shallowRender(overrides: Partial<GlobalNavBrandingProps> = {}) {
+ return shallow(<GlobalNavBranding appState={mockAppState()} {...overrides} />);
+}
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNav-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNav-test.tsx.snap
index cc530594fb3..964e19a9cdb 100644
--- a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNav-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNav-test.tsx.snap
@@ -6,7 +6,7 @@ exports[`should render correctly: anonymous users 1`] = `
height={48}
id="global-navigation"
>
- <Connect(GlobalNavBranding) />
+ <withAppStateContext(GlobalNavBranding) />
<withAppStateContext(GlobalNavMenu)
currentUser={
Object {
@@ -47,7 +47,7 @@ exports[`should render correctly: logged in users 1`] = `
height={48}
id="global-navigation"
>
- <Connect(GlobalNavBranding) />
+ <withAppStateContext(GlobalNavBranding) />
<withAppStateContext(GlobalNavMenu)
currentUser={
Object {
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavBranding-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavBranding-test.tsx.snap
new file mode 100644
index 00000000000..5419fd45cbb
--- /dev/null
+++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavBranding-test.tsx.snap
@@ -0,0 +1,52 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: default 1`] = `
+<Link
+ className="navbar-brand"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/"
+>
+ <img
+ alt="layout.sonar.slogan"
+ height={30}
+ src="/images/logo.svg?v=6.6"
+ title="layout.sonar.slogan"
+ width={83}
+ />
+</Link>
+`;
+
+exports[`should render correctly: with logo 1`] = `
+<Link
+ className="navbar-brand"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/"
+>
+ <img
+ alt="layout.sonar.slogan"
+ height={30}
+ src="http://sonarsource.com/custom-logo.svg"
+ title="layout.sonar.slogan"
+ width={100}
+ />
+</Link>
+`;
+
+exports[`should render correctly: with logo and width 1`] = `
+<Link
+ className="navbar-brand"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/"
+>
+ <img
+ alt="layout.sonar.slogan"
+ height={30}
+ src="http://sonarsource.com/custom-logo.svg"
+ title="layout.sonar.slogan"
+ width="200"
+ />
+</Link>
+`;
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.tsx.snap
index b509fb25cd2..f131dfc9555 100644
--- a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.tsx.snap
@@ -60,7 +60,7 @@ exports[`should render the right interface for logged in user 1`] = `
href="#"
title="Skywalker"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
name="Skywalker"
size={32}
/>
diff --git a/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx b/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx
index 8958a1c02d6..b399276ddf0 100644
--- a/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx
+++ b/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx
@@ -27,9 +27,10 @@ import SystemUpgradeButton from '../../../components/upgrade/SystemUpgradeButton
import { sortUpgrades, UpdateUseCase } from '../../../components/upgrade/utils';
import { translate } from '../../../helpers/l10n';
import { hasGlobalPermission, isLoggedIn } from '../../../helpers/users';
+import { AppState } from '../../../types/appstate';
import { Permissions } from '../../../types/permissions';
import { SystemUpgrade } from '../../../types/system';
-import { AppState, CurrentUser, Dict } from '../../../types/types';
+import { CurrentUser, Dict } from '../../../types/types';
import withAppStateContext from '../app-state/withAppStateContext';
import './UpdateNotification.css';
diff --git a/server/sonar-web/src/main/js/app/index.ts b/server/sonar-web/src/main/js/app/index.ts
index 88081787e94..f63328930f4 100644
--- a/server/sonar-web/src/main/js/app/index.ts
+++ b/server/sonar-web/src/main/js/app/index.ts
@@ -21,7 +21,7 @@ import { installExtensionsHandler, installWebAnalyticsHandler } from '../helpers
import { loadL10nBundle } from '../helpers/l10n';
import { parseJSON, request } from '../helpers/request';
import { getBaseUrl, getSystemStatus } from '../helpers/system';
-import { AppState } from '../types/types';
+import { AppState } from '../types/appstate';
import './styles/sonar.ts';
installWebAnalyticsHandler();
diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
index 0fa4217844b..6854fa5fbb7 100644
--- a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
+++ b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
@@ -60,7 +60,8 @@ import webhooksRoutes from '../../apps/webhooks/routes';
import withIndexationGuard from '../../components/hoc/withIndexationGuard';
import { lazyLoadComponent } from '../../components/lazyLoadComponent';
import getHistory from '../../helpers/getHistory';
-import { AppState, CurrentUser } from '../../types/types';
+import { AppState } from '../../types/appstate';
+import { CurrentUser } from '../../types/types';
import App from '../components/App';
import AppStateContextProvider from '../components/app-state/AppStateContextProvider';
import GlobalContainer from '../components/GlobalContainer';
diff --git a/server/sonar-web/src/main/js/apps/audit-logs/components/AuditApp.tsx b/server/sonar-web/src/main/js/apps/audit-logs/components/AuditApp.tsx
index df64cb4c24b..da2e3196a93 100644
--- a/server/sonar-web/src/main/js/apps/audit-logs/components/AuditApp.tsx
+++ b/server/sonar-web/src/main/js/apps/audit-logs/components/AuditApp.tsx
@@ -18,60 +18,63 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { connect } from 'react-redux';
-import { getGlobalSettingValue, Store } from '../../../store/rootReducer';
+import { getValues } from '../../../api/settings';
import { AdminPageExtension } from '../../../types/extension';
+import { SettingsKey } from '../../../types/settings';
import { Extension } from '../../../types/types';
-import { fetchValues } from '../../settings/store/actions';
import '../style.css';
import { HousekeepingPolicy, RangeOption } from '../utils';
import AuditAppRenderer from './AuditAppRenderer';
interface Props {
- auditHousekeepingPolicy: HousekeepingPolicy;
- fetchValues: typeof fetchValues;
adminPages: Extension[];
}
interface State {
dateRange?: { from?: Date; to?: Date };
- hasGovernanceExtension?: boolean;
downloadStarted: boolean;
+ housekeepingPolicy: HousekeepingPolicy;
selection: RangeOption;
}
-export class AuditApp extends React.PureComponent<Props, State> {
+export default class AuditApp extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props);
- const hasGovernanceExtension = Boolean(
- props.adminPages?.find(e => e.key === AdminPageExtension.GovernanceConsole)
- );
+
this.state = {
downloadStarted: false,
- selection: RangeOption.Today,
- hasGovernanceExtension
+ housekeepingPolicy: HousekeepingPolicy.Monthly,
+ selection: RangeOption.Today
};
}
componentDidMount() {
- const { hasGovernanceExtension } = this.state;
-
- if (hasGovernanceExtension) {
- this.props.fetchValues(['sonar.dbcleaner.auditHousekeeping']);
+ if (this.hasGovernanceExtension()) {
+ this.fetchHouseKeepingPolicy();
}
}
componentDidUpdate(prevProps: Props) {
- if (prevProps.adminPages !== this.props.adminPages) {
- const hasGovernanceExtension = Boolean(
- this.props.adminPages?.find(e => e.key === AdminPageExtension.GovernanceConsole)
- );
- this.setState({
- hasGovernanceExtension
- });
+ if (prevProps.adminPages !== this.props.adminPages && this.hasGovernanceExtension()) {
+ this.fetchHouseKeepingPolicy();
}
}
+ fetchHouseKeepingPolicy = async () => {
+ const results = await getValues({ keys: SettingsKey.AuditHouseKeeping });
+
+ this.setState({
+ housekeepingPolicy:
+ (results[0]?.value as HousekeepingPolicy | undefined) ?? HousekeepingPolicy.Monthly
+ });
+ };
+
+ hasGovernanceExtension = () => {
+ return Boolean(
+ this.props.adminPages?.find(e => e.key === AdminPageExtension.GovernanceConsole)
+ );
+ };
+
handleDateSelection = (dateRange: { from?: Date; to?: Date }) =>
this.setState({ dateRange, downloadStarted: false, selection: RangeOption.Custom });
@@ -85,10 +88,7 @@ export class AuditApp extends React.PureComponent<Props, State> {
};
render() {
- const { hasGovernanceExtension, ...auditAppRendererProps } = this.state;
- const { auditHousekeepingPolicy } = this.props;
-
- if (!hasGovernanceExtension) {
+ if (!this.hasGovernanceExtension()) {
return null;
}
@@ -97,20 +97,8 @@ export class AuditApp extends React.PureComponent<Props, State> {
handleDateSelection={this.handleDateSelection}
handleOptionSelection={this.handleOptionSelection}
handleStartDownload={this.handleStartDownload}
- housekeepingPolicy={auditHousekeepingPolicy || HousekeepingPolicy.Monthly}
- {...auditAppRendererProps}
+ {...this.state}
/>
);
}
}
-
-const mapDispatchToProps = { fetchValues };
-
-const mapStateToProps = (state: Store) => {
- const settingValue = getGlobalSettingValue(state, 'sonar.dbcleaner.auditHousekeeping');
- return {
- auditHousekeepingPolicy: settingValue?.value as HousekeepingPolicy
- };
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(AuditApp);
diff --git a/server/sonar-web/src/main/js/apps/audit-logs/components/__tests__/AuditApp-test.tsx b/server/sonar-web/src/main/js/apps/audit-logs/components/__tests__/AuditApp-test.tsx
index 1b9740af760..baac6e88cda 100644
--- a/server/sonar-web/src/main/js/apps/audit-logs/components/__tests__/AuditApp-test.tsx
+++ b/server/sonar-web/src/main/js/apps/audit-logs/components/__tests__/AuditApp-test.tsx
@@ -20,30 +20,41 @@
import { subDays } from 'date-fns';
import { shallow } from 'enzyme';
import * as React from 'react';
+import { getValues } from '../../../../api/settings';
import { waitAndUpdate } from '../../../../helpers/testUtils';
import { AdminPageExtension } from '../../../../types/extension';
import { HousekeepingPolicy, RangeOption } from '../../utils';
-import { AuditApp } from '../AuditApp';
+import AuditApp from '../AuditApp';
import AuditAppRenderer from '../AuditAppRenderer';
+jest.mock('../../../../api/settings', () => ({
+ getValues: jest.fn().mockResolvedValue([])
+}));
+
+beforeEach(() => {
+ jest.clearAllMocks();
+});
+
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});
it('should do nothing if governance is not available', async () => {
- const fetchValues = jest.fn();
- const wrapper = shallowRender({ fetchValues, adminPages: [] });
+ const wrapper = shallowRender({ adminPages: [] });
await waitAndUpdate(wrapper);
expect(wrapper.type()).toBeNull();
- expect(fetchValues).not.toBeCalled();
+ expect(getValues).not.toBeCalled();
});
-it('should fetch houskeeping policy on mount', async () => {
- const fetchValues = jest.fn();
- const wrapper = shallowRender({ fetchValues });
+it('should handle housekeeping policy', async () => {
+ (getValues as jest.Mock).mockResolvedValueOnce([{ value: HousekeepingPolicy.Weekly }]);
+
+ const wrapper = shallowRender();
+
await waitAndUpdate(wrapper);
- expect(fetchValues).toBeCalled();
+
+ expect(wrapper.find(AuditAppRenderer).props().housekeepingPolicy).toBe(HousekeepingPolicy.Weekly);
});
it('should handle date selection', () => {
@@ -76,11 +87,22 @@ it('should handle predefined selection', () => {
expect(wrapper.state().dateRange).toBeUndefined();
});
+it('should handle update to admin pages', async () => {
+ const wrapper = shallowRender({ adminPages: [] });
+ await waitAndUpdate(wrapper);
+
+ expect(wrapper.type()).toBeNull();
+ expect(getValues).not.toBeCalled();
+
+ wrapper.setProps({ adminPages: [{ key: AdminPageExtension.GovernanceConsole, name: 'name' }] });
+ await waitAndUpdate(wrapper);
+
+ expect(getValues).toBeCalled();
+});
+
function shallowRender(props: Partial<AuditApp['props']> = {}) {
return shallow<AuditApp>(
<AuditApp
- auditHousekeepingPolicy={HousekeepingPolicy.Monthly}
- fetchValues={jest.fn()}
adminPages={[{ key: AdminPageExtension.GovernanceConsole, name: 'name' }]}
{...props}
/>
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/StatPendingCount.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/StatPendingCount.tsx
index d1cc1e097c7..de54ad439fb 100644
--- a/server/sonar-web/src/main/js/apps/background-tasks/components/StatPendingCount.tsx
+++ b/server/sonar-web/src/main/js/apps/background-tasks/components/StatPendingCount.tsx
@@ -24,7 +24,7 @@ import { ClearButton } from '../../../components/controls/buttons';
import ConfirmButton from '../../../components/controls/ConfirmButton';
import Tooltip from '../../../components/controls/Tooltip';
import { translate } from '../../../helpers/l10n';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
export interface Props {
appState: AppState;
diff --git a/server/sonar-web/src/main/js/apps/change-admin-password/ChangeAdminPasswordApp.tsx b/server/sonar-web/src/main/js/apps/change-admin-password/ChangeAdminPasswordApp.tsx
index 5bb5d8b601b..3582400a715 100644
--- a/server/sonar-web/src/main/js/apps/change-admin-password/ChangeAdminPasswordApp.tsx
+++ b/server/sonar-web/src/main/js/apps/change-admin-password/ChangeAdminPasswordApp.tsx
@@ -21,7 +21,7 @@ import * as React from 'react';
import { changePassword } from '../../api/users';
import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import { Location, withRouter } from '../../components/hoc/withRouter';
-import { AppState } from '../../types/types';
+import { AppState } from '../../types/appstate';
import ChangeAdminPasswordAppRenderer from './ChangeAdminPasswordAppRenderer';
import { DEFAULT_ADMIN_LOGIN, DEFAULT_ADMIN_PASSWORD } from './constants';
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsIssues.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsIssues.tsx
index fa64d3cee56..5c282e3ee5f 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsIssues.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsIssues.tsx
@@ -26,7 +26,8 @@ import DeferredSpinner from '../../../components/ui/DeferredSpinner';
import { translate } from '../../../helpers/l10n';
import { formatMeasure } from '../../../helpers/measures';
import { getIssuesUrl } from '../../../helpers/urls';
-import { AppState, RuleDetails } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
+import { RuleDetails } from '../../../types/types';
interface Props {
appState: AppState;
diff --git a/server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx b/server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx
index 3cd2701916e..4471a7923b0 100644
--- a/server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx
@@ -24,7 +24,7 @@ import ChevronsIcon from '../../../components/icons/ChevronsIcon';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
import { AlmKeys } from '../../../types/alm-settings';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
import { CreateProjectModes } from './types';
export interface CreateProjectModeSelectionProps {
diff --git a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx
index 6f250980373..4c4a3d7bbb6 100644
--- a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx
@@ -27,7 +27,8 @@ import { whenLoggedIn } from '../../../components/hoc/whenLoggedIn';
import { translate } from '../../../helpers/l10n';
import { getProjectUrl } from '../../../helpers/urls';
import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings';
-import { AppState, LoggedInUser } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
+import { LoggedInUser } from '../../../types/types';
import AlmBindingDefinitionForm from '../../settings/components/almIntegration/AlmBindingDefinitionForm';
import AzureProjectCreate from './AzureProjectCreate';
import BitbucketCloudProjectCreate from './BitbucketCloudProjectCreate';
diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesApp-test.tsx.snap
index f5a71c751a2..073c704f38a 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesApp-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesApp-test.tsx.snap
@@ -65,7 +65,7 @@ exports[`should show warnning when not all projects are accessible 1`] = `
displayReset={true}
onReset={[Function]}
/>
- <Connect(Sidebar)
+ <withAppStateContext(Sidebar)
component={
Object {
"breadcrumbs": Array [],
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx
index fd455980dc0..4a80bd6dd96 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx
@@ -18,9 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { connect } from 'react-redux';
+import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import { isBranch, isPullRequest } from '../../../helpers/branch-like';
-import { getGlobalSettingValue, Store } from '../../../store/rootReducer';
+import { AppState } from '../../../types/appstate';
import { BranchLike } from '../../../types/branch-like';
import { ComponentQualifier, isApplication, isPortfolioLike } from '../../../types/component';
import {
@@ -29,6 +29,7 @@ import {
ReferencedLanguage,
ReferencedRule
} from '../../../types/issues';
+import { GlobalSettingKeys } from '../../../types/settings';
import { Component, Dict, UserBase } from '../../../types/types';
import { Query } from '../utils';
import AssigneeFacet from './AssigneeFacet';
@@ -48,6 +49,7 @@ import TagFacet from './TagFacet';
import TypeFacet from './TypeFacet';
export interface Props {
+ appState: AppState;
branchLike?: BranchLike;
component: Component | undefined;
createdAfterIncludesTime: boolean;
@@ -64,7 +66,6 @@ export interface Props {
referencedLanguages: Dict<ReferencedLanguage>;
referencedRules: Dict<ReferencedRule>;
referencedUsers: Dict<UserBase>;
- disableDeveloperAggregatedInfo: boolean;
}
export class Sidebar extends React.PureComponent<Props> {
@@ -108,6 +109,7 @@ export class Sidebar extends React.PureComponent<Props> {
render() {
const {
+ appState: { settings },
component,
createdAfterIncludesTime,
facets,
@@ -116,6 +118,9 @@ export class Sidebar extends React.PureComponent<Props> {
branchLike
} = this.props;
+ const disableDeveloperAggregatedInfo =
+ settings[GlobalSettingKeys.DeveloperAggregatedInfoDisabled] === 'true';
+
const branch =
(isBranch(branchLike) && branchLike.name) ||
(isPullRequest(branchLike) && branchLike.branch) ||
@@ -255,7 +260,7 @@ export class Sidebar extends React.PureComponent<Props> {
/>
)}
{this.renderComponentFacets()}
- {!this.props.myIssues && !this.props.disableDeveloperAggregatedInfo && (
+ {!this.props.myIssues && !disableDeveloperAggregatedInfo && (
<AssigneeFacet
assigned={query.assigned}
assignees={query.assignees}
@@ -269,7 +274,7 @@ export class Sidebar extends React.PureComponent<Props> {
stats={facets.assignees}
/>
)}
- {displayAuthorFacet && !this.props.disableDeveloperAggregatedInfo && (
+ {displayAuthorFacet && !disableDeveloperAggregatedInfo && (
<AuthorFacet
author={query.author}
component={component}
@@ -287,14 +292,4 @@ export class Sidebar extends React.PureComponent<Props> {
}
}
-export const mapStateToProps = (state: Store) => {
- const disableDeveloperAggregatedInfo = getGlobalSettingValue(
- state,
- 'sonar.developerAggregatedInfo.disabled'
- );
- return {
- disableDeveloperAggregatedInfo: disableDeveloperAggregatedInfo?.value === true.toString()
- };
-};
-
-export default connect(mapStateToProps)(Sidebar);
+export default withAppStateContext(Sidebar);
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-test.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-test.tsx
index 8e7b29ca2af..916e17efba1 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-test.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-test.tsx
@@ -21,12 +21,11 @@ import { shallow, ShallowWrapper } from 'enzyme';
import { flatten } from 'lodash';
import * as React from 'react';
import { mockComponent } from '../../../../helpers/mocks/component';
-import { getGlobalSettingValue } from '../../../../store/rootReducer';
+import { mockAppState } from '../../../../helpers/testMocks';
+import { GlobalSettingKeys } from '../../../../types/settings';
import { ComponentQualifier } from '../../../../types/component';
import { Query } from '../../utils';
-import { mapStateToProps, Sidebar } from '../Sidebar';
-
-jest.mock('../../../../store/rootReducer', () => ({ getGlobalSettingValue: jest.fn() }));
+import { Sidebar } from '../Sidebar';
it('should render facets for global page', () => {
expect(renderSidebar()).toMatchSnapshot();
@@ -52,16 +51,13 @@ it('should render facets when my issues are selected', () => {
});
it('should not render developer nominative facets when asked not to', () => {
- expect(renderSidebar({ disableDeveloperAggregatedInfo: true })).toMatchSnapshot();
-});
-
-it('should init the component with the proper store value', () => {
- mapStateToProps({} as any);
-
- expect(getGlobalSettingValue).toHaveBeenCalledWith(
- expect.any(Object),
- 'sonar.developerAggregatedInfo.disabled'
- );
+ expect(
+ renderSidebar({
+ appState: mockAppState({
+ settings: { [GlobalSettingKeys.DeveloperAggregatedInfoDisabled]: 'true' }
+ })
+ })
+ ).toMatchSnapshot();
});
const renderSidebar = (props?: Partial<Sidebar['props']>) => {
@@ -69,6 +65,9 @@ const renderSidebar = (props?: Partial<Sidebar['props']>) => {
mapChildren(
shallow<Sidebar>(
<Sidebar
+ appState={mockAppState({
+ settings: { [GlobalSettingKeys.DeveloperAggregatedInfoDisabled]: 'false' }
+ })}
component={undefined}
createdAfterIncludesTime={false}
facets={{}}
@@ -84,7 +83,6 @@ const renderSidebar = (props?: Partial<Sidebar['props']>) => {
referencedLanguages={{}}
referencedRules={{}}
referencedUsers={{}}
- disableDeveloperAggregatedInfo={false}
{...props}
/>
)
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.tsx.snap
index 6e5f27e4e47..ee6aa25506d 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.tsx.snap
@@ -41,7 +41,7 @@ exports[`should render 1`] = `
exports[`test behavior should correctly render facet item 1`] = `
<React.Fragment>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="Name Baz"
size={16}
@@ -52,7 +52,7 @@ exports[`test behavior should correctly render facet item 1`] = `
exports[`test behavior should correctly render facet item 2`] = `
<React.Fragment>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="foo"
size={16}
@@ -63,7 +63,7 @@ exports[`test behavior should correctly render facet item 2`] = `
exports[`test behavior should correctly render search result correctly 1`] = `
<React.Fragment>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="Name Bar"
size={16}
@@ -80,7 +80,7 @@ exports[`test behavior should correctly render search result correctly 1`] = `
exports[`test behavior should correctly render search result correctly 2`] = `
<React.Fragment>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="foo"
size={16}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx b/server/sonar-web/src/main/js/apps/marketplace/MarketplaceAppContainer.tsx
index 501b4f90072..77050b3ac91 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx
+++ b/server/sonar-web/src/main/js/apps/marketplace/MarketplaceAppContainer.tsx
@@ -18,35 +18,27 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { connect } from 'react-redux';
import AdminContext from '../../app/components/AdminContext';
import withAppStateContext from '../../app/components/app-state/withAppStateContext';
-import { getGlobalSettingValue, Store } from '../../store/rootReducer';
+import { AppState } from '../../types/appstate';
import { EditionKey } from '../../types/editions';
-import { AppState, RawQuery } from '../../types/types';
-import { fetchValues } from '../settings/store/actions';
+import { GlobalSettingKeys } from '../../types/settings';
+import { RawQuery } from '../../types/types';
import App from './App';
-interface OwnProps {
+export interface MarketplaceAppContainerProps {
location: { pathname: string; query: RawQuery };
appState: AppState;
}
-interface StateToProps {
- fetchValues: typeof fetchValues;
- updateCenterActive: boolean;
-}
-
-function WithAdminContext(props: StateToProps & OwnProps) {
- React.useEffect(() => {
- props.fetchValues(['sonar.updatecenter.activate']);
- });
+export function MarketplaceAppContainer(props: MarketplaceAppContainerProps) {
+ const { appState, location } = props;
const propsToPass = {
- location: props.location,
- updateCenterActive: props.updateCenterActive,
- currentEdition: props.appState.edition as EditionKey,
- standaloneMode: props.appState.standalone
+ location,
+ updateCenterActive: appState.settings[GlobalSettingKeys.UpdatecenterActivated] === 'true',
+ currentEdition: appState.edition as EditionKey,
+ standaloneMode: appState.standalone
};
return (
@@ -62,13 +54,4 @@ function WithAdminContext(props: StateToProps & OwnProps) {
);
}
-const mapDispatchToProps = { fetchValues };
-
-const mapStateToProps = (state: Store) => {
- const updateCenterActive = getGlobalSettingValue(state, 'sonar.updatecenter.activate');
- return {
- updateCenterActive: Boolean(updateCenterActive && updateCenterActive.value === 'true')
- };
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(withAppStateContext(WithAdminContext));
+export default withAppStateContext(MarketplaceAppContainer);
diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/AppContainer-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/AppContainer-test.tsx
deleted file mode 100644
index d306400526f..00000000000
--- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/AppContainer-test.tsx
+++ /dev/null
@@ -1,52 +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 { connect } from 'react-redux';
-import { mockStore } from '../../../helpers/testMocks';
-import { getGlobalSettingValue } from '../../../store/rootReducer';
-import '../AppContainer';
-
-jest.mock('react-redux', () => ({
- connect: jest.fn(() => (a: any) => a)
-}));
-
-jest.mock('../../../store/rootReducer', () => {
- return {
- getGlobalSettingValue: jest.fn()
- };
-});
-
-describe('redux', () => {
- it('should correctly map state and dispatch props', () => {
- const store = mockStore();
- const updateCenterActive = true;
- (getGlobalSettingValue as jest.Mock).mockReturnValueOnce({
- value: `${updateCenterActive}`
- });
-
- const [mapStateToProps] = (connect as jest.Mock).mock.calls[0];
-
- const props = mapStateToProps(store);
- expect(props).toEqual({
- updateCenterActive
- });
-
- expect(getGlobalSettingValue).toHaveBeenCalledWith(store, 'sonar.updatecenter.activate');
- });
-});
diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/MarketplaceAppContainer-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/MarketplaceAppContainer-test.tsx
new file mode 100644
index 00000000000..476b4035805
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/MarketplaceAppContainer-test.tsx
@@ -0,0 +1,48 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import React from 'react';
+import { mockAppState, mockLocation } from '../../../helpers/testMocks';
+import { GlobalSettingKeys } from '../../../types/settings';
+import { EditionKey } from '../../../types/editions';
+import { MarketplaceAppContainer, MarketplaceAppContainerProps } from '../MarketplaceAppContainer';
+
+it('should render correctly', () => {
+ expect(shallowRender().dive()).toMatchSnapshot('default');
+ expect(
+ shallowRender({
+ appState: mockAppState({
+ settings: {
+ [GlobalSettingKeys.UpdatecenterActivated]: 'true'
+ }
+ })
+ }).dive()
+ ).toMatchSnapshot('update center active');
+});
+
+function shallowRender(overrides: Partial<MarketplaceAppContainerProps> = {}) {
+ return shallow<MarketplaceAppContainerProps>(
+ <MarketplaceAppContainer
+ appState={mockAppState({ edition: EditionKey.community, standalone: true })}
+ location={mockLocation()}
+ {...overrides}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/MarketplaceAppContainer-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/MarketplaceAppContainer-test.tsx.snap
new file mode 100644
index 00000000000..58d697aede5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/MarketplaceAppContainer-test.tsx.snap
@@ -0,0 +1,54 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: default 1`] = `
+<withRouter(App)
+ currentEdition="community"
+ fetchPendingPlugins={[Function]}
+ location={
+ Object {
+ "action": "PUSH",
+ "hash": "",
+ "key": "key",
+ "pathname": "/path",
+ "query": Object {},
+ "search": "",
+ "state": Object {},
+ }
+ }
+ pendingPlugins={
+ Object {
+ "installing": Array [],
+ "removing": Array [],
+ "updating": Array [],
+ }
+ }
+ standaloneMode={true}
+ updateCenterActive={false}
+/>
+`;
+
+exports[`should render correctly: update center active 1`] = `
+<withRouter(App)
+ currentEdition="community"
+ fetchPendingPlugins={[Function]}
+ location={
+ Object {
+ "action": "PUSH",
+ "hash": "",
+ "key": "key",
+ "pathname": "/path",
+ "query": Object {},
+ "search": "",
+ "state": Object {},
+ }
+ }
+ pendingPlugins={
+ Object {
+ "installing": Array [],
+ "removing": Array [],
+ "updating": Array [],
+ }
+ }
+ updateCenterActive={true}
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/marketplace/routes.ts b/server/sonar-web/src/main/js/apps/marketplace/routes.ts
index c4d59b1b922..211f08c5a06 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/routes.ts
+++ b/server/sonar-web/src/main/js/apps/marketplace/routes.ts
@@ -21,7 +21,7 @@ import { lazyLoadComponent } from '../../components/lazyLoadComponent';
const routes = [
{
- indexRoute: { component: lazyLoadComponent(() => import('./AppContainer')) }
+ indexRoute: { component: lazyLoadComponent(() => import('./MarketplaceAppContainer')) }
}
];
diff --git a/server/sonar-web/src/main/js/apps/overview/components/App.tsx b/server/sonar-web/src/main/js/apps/overview/components/App.tsx
index 665b63e6ddc..0477fff99e9 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/App.tsx
@@ -24,9 +24,10 @@ import { Router, withRouter } from '../../../components/hoc/withRouter';
import { lazyLoadComponent } from '../../../components/lazyLoadComponent';
import { isPullRequest } from '../../../helpers/branch-like';
import { ProjectAlmBindingResponse } from '../../../types/alm-settings';
+import { AppState } from '../../../types/appstate';
import { BranchLike } from '../../../types/branch-like';
import { isPortfolioLike } from '../../../types/component';
-import { AppState, Component } from '../../../types/types';
+import { Component } from '../../../types/types';
import BranchOverview from '../branches/BranchOverview';
const EmptyOverview = lazyLoadComponent(() => import('./EmptyOverview'));
diff --git a/server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx b/server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx
index ec83f1ab978..2eee2295c08 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx
@@ -19,7 +19,8 @@
*/
import * as React from 'react';
import Tooltip from '../../../components/controls/Tooltip';
-import { getLeakValue, getRatingTooltip } from '../../../components/measure/utils';
+import RatingTooltipContent from '../../../components/measure/RatingTooltipContent';
+import { getLeakValue } from '../../../components/measure/utils';
import DrilldownLink from '../../../components/shared/DrilldownLink';
import Rating from '../../../components/ui/Rating';
import { findMeasure } from '../../../helpers/measures';
@@ -50,10 +51,9 @@ function renderRatingLink(props: IssueRatingProps) {
}
const value = measure && (useDiffMetric ? getLeakValue(measure) : measure.value);
- const tooltip = value && getRatingTooltip(rating, Number(value));
return (
- <Tooltip overlay={tooltip}>
+ <Tooltip overlay={value && <RatingTooltipContent metricKey={rating} value={value} />}>
<span>
<DrilldownLink
branchLike={branchLike}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap
index f8f24334651..6bc60fbd24e 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap
@@ -8,7 +8,12 @@ exports[`should render correctly for bugs 1`] = `
metric_domain.Reliability
</span>
<Tooltip
- overlay="metric.reliability_rating.tooltip.A"
+ overlay={
+ <withAppStateContext(RatingTooltipContent)
+ metricKey="reliability_rating"
+ value="1.0"
+ />
+ }
>
<span>
<DrilldownLink
@@ -43,7 +48,12 @@ exports[`should render correctly for bugs 2`] = `
metric_domain.Reliability
</span>
<Tooltip
- overlay="metric.reliability_rating.tooltip.A"
+ overlay={
+ <withAppStateContext(RatingTooltipContent)
+ metricKey="new_reliability_rating"
+ value="1.0"
+ />
+ }
>
<span>
<DrilldownLink
@@ -78,7 +88,12 @@ exports[`should render correctly for code smells 1`] = `
metric_domain.Maintainability
</span>
<Tooltip
- overlay="metric.sqale_rating.tooltip.A.0.0%"
+ overlay={
+ <withAppStateContext(RatingTooltipContent)
+ metricKey="sqale_rating"
+ value="1.0"
+ />
+ }
>
<span>
<DrilldownLink
@@ -113,7 +128,12 @@ exports[`should render correctly for code smells 2`] = `
metric_domain.Maintainability
</span>
<Tooltip
- overlay="metric.sqale_rating.tooltip.A.0.0%"
+ overlay={
+ <withAppStateContext(RatingTooltipContent)
+ metricKey="new_maintainability_rating"
+ value="1.0"
+ />
+ }
>
<span>
<DrilldownLink
@@ -148,7 +168,12 @@ exports[`should render correctly for vulnerabilities 1`] = `
metric_domain.Security
</span>
<Tooltip
- overlay="metric.security_rating.tooltip.A"
+ overlay={
+ <withAppStateContext(RatingTooltipContent)
+ metricKey="security_rating"
+ value="1.0"
+ />
+ }
>
<span>
<DrilldownLink
@@ -183,7 +208,12 @@ exports[`should render correctly for vulnerabilities 2`] = `
metric_domain.Security
</span>
<Tooltip
- overlay="metric.security_rating.tooltip.A"
+ overlay={
+ <withAppStateContext(RatingTooltipContent)
+ metricKey="new_security_rating"
+ value="1.0"
+ />
+ }
>
<span>
<DrilldownLink
diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/App.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/App.tsx
index f5fea8b899a..178579337eb 100644
--- a/server/sonar-web/src/main/js/apps/permission-templates/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/permission-templates/components/App.tsx
@@ -24,7 +24,8 @@ import { getPermissionTemplates } from '../../../api/permissions';
import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
import { translate } from '../../../helpers/l10n';
-import { AppState, Permission, PermissionTemplate } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
+import { Permission, PermissionTemplate } from '../../../types/types';
import '../../permissions/styles.css';
import { mergeDefaultsToTemplates, mergePermissionsToTemplates, sortPermissions } from '../utils';
import Home from './Home';
diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.tsx b/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.tsx
index 0f2d5f98748..8c4a4eb19f0 100644
--- a/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.tsx
+++ b/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.tsx
@@ -20,8 +20,9 @@
import * as React from 'react';
import withAppStateContext from '../../../../app/components/app-state/withAppStateContext';
import ListFooter from '../../../../components/controls/ListFooter';
+import { AppState } from '../../../../types/appstate';
import { ComponentQualifier } from '../../../../types/component';
-import { AppState, Paging, PermissionGroup, PermissionUser } from '../../../../types/types';
+import { Paging, PermissionGroup, PermissionUser } from '../../../../types/types';
import HoldersList from '../../shared/components/HoldersList';
import SearchForm from '../../shared/components/SearchForm';
import {
diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/components/__tests__/__snapshots__/UserHolder-test.tsx.snap b/server/sonar-web/src/main/js/apps/permissions/shared/components/__tests__/__snapshots__/UserHolder-test.tsx.snap
index a132c867d35..10307a9b053 100644
--- a/server/sonar-web/src/main/js/apps/permissions/shared/components/__tests__/__snapshots__/UserHolder-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/permissions/shared/components/__tests__/__snapshots__/UserHolder-test.tsx.snap
@@ -90,7 +90,7 @@ exports[`should render correctly: default 1`] = `
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="text-middle big-spacer-right flex-0"
name="John Doe"
size={36}
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx
index d515f2294e7..4c123c72f03 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx
@@ -27,9 +27,9 @@ import AlertSuccessIcon from '../../../components/icons/AlertSuccessIcon';
import DeferredSpinner from '../../../components/ui/DeferredSpinner';
import { isBranch, sortBranches } from '../../../helpers/branch-like';
import { translate } from '../../../helpers/l10n';
+import { AppState } from '../../../types/appstate';
import { Branch, BranchLike } from '../../../types/branch-like';
import {
- AppState,
Component,
NewCodePeriod,
NewCodePeriodSettingType,
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/LifetimeInformation.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/LifetimeInformation.tsx
index 3e855acdfa6..aabaf35c730 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/LifetimeInformation.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/LifetimeInformation.tsx
@@ -20,8 +20,8 @@
import * as React from 'react';
import { getValues } from '../../../api/settings';
import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
+import { AppState } from '../../../types/appstate';
import { SettingsKey } from '../../../types/settings';
-import { AppState } from '../../../types/types';
import LifetimeInformationRenderer from './LifetimeInformationRenderer';
interface Props {
diff --git a/server/sonar-web/src/main/js/apps/projectDump/ProjectDumpApp.tsx b/server/sonar-web/src/main/js/apps/projectDump/ProjectDumpApp.tsx
index cd067423adf..29043ef9a85 100644
--- a/server/sonar-web/src/main/js/apps/projectDump/ProjectDumpApp.tsx
+++ b/server/sonar-web/src/main/js/apps/projectDump/ProjectDumpApp.tsx
@@ -23,9 +23,10 @@ import { getStatus } from '../../api/project-dump';
import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import throwGlobalError from '../../app/utils/throwGlobalError';
import { translate } from '../../helpers/l10n';
+import { AppState } from '../../types/appstate';
import { DumpStatus, DumpTask } from '../../types/project-dump';
import { TaskStatuses, TaskTypes } from '../../types/tasks';
-import { AppState, Component } from '../../types/types';
+import { Component } from '../../types/types';
import Export from './components/Export';
import Import from './components/Import';
import './styles.css';
diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
index 38fa2a56eaf..ed14cc84902 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
@@ -33,8 +33,9 @@ import { translate } from '../../../helpers/l10n';
import { addSideBarClass, removeSideBarClass } from '../../../helpers/pages';
import { get, save } from '../../../helpers/storage';
import { isLoggedIn } from '../../../helpers/users';
+import { AppState } from '../../../types/appstate';
import { ComponentQualifier } from '../../../types/component';
-import { AppState, CurrentUser, RawQuery } from '../../../types/types';
+import { CurrentUser, RawQuery } from '../../../types/types';
import { hasFilterParams, hasViewParams, parseUrlQuery, Query } from '../query';
import '../styles.css';
import { Facets, Project } from '../types';
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ApplicationCreation.tsx b/server/sonar-web/src/main/js/apps/projects/components/ApplicationCreation.tsx
index 4fe00dc2785..dac6645952c 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ApplicationCreation.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/ApplicationCreation.tsx
@@ -27,9 +27,10 @@ import { Router, withRouter } from '../../../components/hoc/withRouter';
import { translate } from '../../../helpers/l10n';
import { getComponentAdminUrl, getComponentOverviewUrl } from '../../../helpers/urls';
import { hasGlobalPermission } from '../../../helpers/users';
+import { AppState } from '../../../types/appstate';
import { ComponentQualifier } from '../../../types/component';
import { Permissions } from '../../../types/permissions';
-import { AppState, LoggedInUser } from '../../../types/types';
+import { LoggedInUser } from '../../../types/types';
export interface ApplicationCreationProps {
appState: AppState;
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx
index 4d19a37e27a..81640c8c164 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx
@@ -29,7 +29,8 @@ import SearchBox from '../../components/controls/SearchBox';
import SelectLegacy from '../../components/controls/SelectLegacy';
import QualifierIcon from '../../components/icons/QualifierIcon';
import { translate } from '../../helpers/l10n';
-import { AppState, Visibility } from '../../types/types';
+import { AppState } from '../../types/appstate';
+import { Visibility } from '../../types/types';
import BulkApplyTemplateModal from './BulkApplyTemplateModal';
import DeleteModal from './DeleteModal';
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx
index ef92de4334b..14f98297bf7 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx
@@ -27,14 +27,9 @@ import ModalButton from '../../../components/controls/ModalButton';
import { Alert } from '../../../components/ui/Alert';
import { getLocalizedMetricName, translate } from '../../../helpers/l10n';
import { isDiffMetric } from '../../../helpers/measures';
+import { AppState } from '../../../types/appstate';
import { MetricKey } from '../../../types/metrics';
-import {
- AppState,
- Condition as ConditionType,
- Dict,
- Metric,
- QualityGate
-} from '../../../types/types';
+import { Condition as ConditionType, Dict, Metric, QualityGate } from '../../../types/types';
import Condition from './Condition';
import ConditionModal from './ConditionModal';
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/PermissionItem-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/PermissionItem-test.tsx.snap
index 5ddfe0ab0bc..096ddf281a6 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/PermissionItem-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/PermissionItem-test.tsx.snap
@@ -25,7 +25,7 @@ exports[`should render correctly: user 1`] = `
<div
className="display-flex-center permission-list-item padded"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="spacer-right"
name="John Doe"
size={32}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissionsAddModalRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissionsAddModalRenderer-test.tsx.snap
index d479967b848..5994dbc36f5 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissionsAddModalRenderer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissionsAddModalRenderer-test.tsx.snap
@@ -334,7 +334,7 @@ exports[`should render options correctly: group 1`] = `
exports[`should render options correctly: user 1`] = `
<React.Fragment>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
hash="A"
name="name"
size={16}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsUser-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsUser-test.tsx.snap
index 305d0624f07..fc1970f5c72 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsUser-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsUser-test.tsx.snap
@@ -8,7 +8,7 @@ exports[`renders 1`] = `
className="pull-right spacer-top spacer-left spacer-right button-small"
onClick={[Function]}
/>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="pull-left spacer-right"
name="Luke Skywalker"
size={32}
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap
index 2475a3723b9..125a1fba9b4 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap
@@ -10,7 +10,7 @@ exports[`should render correctly: default 1`] = `
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="Luke Skywalker"
size={20}
@@ -49,7 +49,7 @@ exports[`should render correctly: default 1`] = `
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="Luke Skywalker"
size={20}
@@ -88,7 +88,7 @@ exports[`should render correctly: default 1`] = `
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
@@ -130,7 +130,7 @@ exports[`should render correctly: default 1`] = `
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
@@ -172,7 +172,7 @@ exports[`should render correctly: default 1`] = `
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
@@ -255,7 +255,7 @@ exports[`should render correctly: show full list 1`] = `
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="Luke Skywalker"
size={20}
@@ -294,7 +294,7 @@ exports[`should render correctly: show full list 1`] = `
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="Luke Skywalker"
size={20}
@@ -333,7 +333,7 @@ exports[`should render correctly: show full list 1`] = `
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
@@ -375,7 +375,7 @@ exports[`should render correctly: show full list 1`] = `
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
@@ -417,7 +417,7 @@ exports[`should render correctly: show full list 1`] = `
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
@@ -459,7 +459,7 @@ exports[`should render correctly: show full list 1`] = `
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="john.doe"
size={20}
@@ -501,7 +501,7 @@ exports[`should render correctly: show full list 1`] = `
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
@@ -592,7 +592,7 @@ exports[`should render correctly: show full list 1`] = `
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/__snapshots__/AssigneeSelectionRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/__snapshots__/AssigneeSelectionRenderer-test.tsx.snap
index 087a963a423..7d05daab361 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/__snapshots__/AssigneeSelectionRenderer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/__snapshots__/AssigneeSelectionRenderer-test.tsx.snap
@@ -71,7 +71,7 @@ exports[`should render correctly: open with results 1`] = `
key="john.doe"
onClick={[Function]}
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="spacer-right"
name="John Doe"
size={16}
@@ -83,7 +83,7 @@ exports[`should render correctly: open with results 1`] = `
key="highlighted"
onClick={[Function]}
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="spacer-right"
name="John Doe"
size={16}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx
index 69722c98a05..bfc388ce5b2 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx
@@ -23,7 +23,8 @@ import * as React from 'react';
import { IndexLink } from 'react-router';
import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import { getGlobalSettingsUrl, getProjectSettingsUrl } from '../../../helpers/urls';
-import { AppState, Component } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
+import { Component } from '../../../types/types';
import { getCategoryName } from '../utils';
import { ADDITIONAL_CATEGORIES } from './AdditionalCategories';
import CATEGORY_OVERRIDES from './CategoryOverrides';
diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegration.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegration.tsx
index 7a54c0bc12b..dd57a2c7464 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegration.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegration.tsx
@@ -34,8 +34,9 @@ import {
AlmSettingsBindingStatus,
AlmSettingsBindingStatusType
} from '../../../../types/alm-settings';
+import { AppState } from '../../../../types/appstate';
import { ExtendedSettingDefinition } from '../../../../types/settings';
-import { AppState, Dict } from '../../../../types/types';
+import { Dict } from '../../../../types/types';
import AlmIntegrationRenderer from './AlmIntegrationRenderer';
interface Props extends Pick<WithRouterProps, 'location' | 'router'> {
diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/CreationTooltip.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/CreationTooltip.tsx
index 9f53a41396f..eaca7a893d2 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/CreationTooltip.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/CreationTooltip.tsx
@@ -24,8 +24,8 @@ import Tooltip from '../../../../components/controls/Tooltip';
import { getEdition, getEditionUrl } from '../../../../helpers/editions';
import { translate } from '../../../../helpers/l10n';
import { AlmKeys } from '../../../../types/alm-settings';
+import { AppState } from '../../../../types/appstate';
import { EditionKey } from '../../../../types/editions';
-import { AppState } from '../../../../types/types';
export interface CreationTooltipProps {
alm: AlmKeys;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx
index cd60e263b9d..2da3d4765a7 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx
@@ -32,8 +32,9 @@ import {
AlmSettingsInstance,
ProjectAlmBindingResponse
} from '../../../../types/alm-settings';
+import { AppState } from '../../../../types/appstate';
import { EditionKey } from '../../../../types/editions';
-import { AppState, Dict } from '../../../../types/types';
+import { Dict } from '../../../../types/types';
export interface AlmSpecificFormProps {
alm: AlmKeys;
diff --git a/server/sonar-web/src/main/js/apps/settings/store/__tests__/actions-test.ts b/server/sonar-web/src/main/js/apps/settings/store/__tests__/actions-test.ts
deleted file mode 100644
index 5fc7dd885cf..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/store/__tests__/actions-test.ts
+++ /dev/null
@@ -1,55 +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 { fetchValues, setValues } from '../actions';
-
-jest.mock('../../../../api/settings', () => {
- const { mockSettingValue } = jest.requireActual('../../../../helpers/mocks/settings');
- return {
- getValues: jest.fn().mockResolvedValue([mockSettingValue()])
- };
-});
-
-it('should setValues correctly', () => {
- const dispatch = jest.fn();
- setValues(['test'], [{ key: 'test', value: 'foo' }])(dispatch);
- expect(dispatch).toHaveBeenCalledWith({
- component: undefined,
- settings: [
- {
- key: 'test',
- value: 'foo'
- }
- ],
- type: 'RECEIVE_VALUES',
- updateKeys: ['test']
- });
-});
-
-it('should fetchValue correclty', async () => {
- const dispatch = jest.fn();
- await fetchValues(['test'], 'foo')(dispatch);
- expect(dispatch).toHaveBeenCalledWith({
- component: 'foo',
- settings: [{ key: 'test' }],
- type: 'RECEIVE_VALUES',
- updateKeys: ['test']
- });
- expect(dispatch).toHaveBeenCalledWith({ type: 'CLOSE_ALL_GLOBAL_MESSAGES' });
-});
diff --git a/server/sonar-web/src/main/js/apps/settings/store/actions.ts b/server/sonar-web/src/main/js/apps/settings/store/actions.ts
deleted file mode 100644
index 2c5430e48e7..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/store/actions.ts
+++ /dev/null
@@ -1,39 +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 { Dispatch } from 'redux';
-import { getValues } from '../../../api/settings';
-import { closeAllGlobalMessages } from '../../../store/globalMessages';
-import { receiveValues } from './values';
-
-export function fetchValues(keys: string[], component?: string) {
- return (dispatch: Dispatch) =>
- getValues({ keys: keys.join(), component }).then(settings => {
- dispatch(receiveValues(keys, settings, component));
- dispatch(closeAllGlobalMessages());
- });
-}
-
-export function setValues(
- keys: string[],
- settings: Array<{ key: string; value?: string }>,
- component?: string
-) {
- return (dispatch: Dispatch) => dispatch(receiveValues(keys, settings, component));
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/store/values.ts b/server/sonar-web/src/main/js/apps/settings/store/values.ts
deleted file mode 100644
index 478a873c02d..00000000000
--- a/server/sonar-web/src/main/js/apps/settings/store/values.ts
+++ /dev/null
@@ -1,78 +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 { keyBy, omit } from 'lodash';
-import { combineReducers } from 'redux';
-import { ActionType } from '../../../store/utils/actions';
-import { SettingValue } from '../../../types/settings';
-import { Dict } from '../../../types/types';
-
-enum Actions {
- receiveValues = 'RECEIVE_VALUES'
-}
-
-type Action = ActionType<typeof receiveValues, Actions.receiveValues>;
-
-type SettingsState = Dict<SettingValue>;
-
-export interface State {
- components: Dict<SettingsState>;
- global: SettingsState;
-}
-
-export function receiveValues(
- updateKeys: string[],
- settings: Array<{ key: string; value?: string }>,
- component?: string
-) {
- return { type: Actions.receiveValues, updateKeys, settings, component };
-}
-
-function components(state: State['components'] = {}, action: Action) {
- const { component: key } = action;
- if (!key) {
- return state;
- }
- if (action.type === Actions.receiveValues) {
- const settingsByKey = keyBy(action.settings, 'key');
- return { ...state, [key]: { ...omit(state[key] || {}, action.updateKeys), ...settingsByKey } };
- }
- return state;
-}
-
-function global(state: State['components'] = {}, action: Action) {
- if (action.type === Actions.receiveValues) {
- if (action.component) {
- return state;
- }
- const settingsByKey = keyBy(action.settings, 'key');
- return { ...omit(state, action.updateKeys), ...settingsByKey };
- }
-
- return state;
-}
-
-export default combineReducers({ components, global });
-
-export function getValue(state: State, key: string, component?: string): SettingValue | undefined {
- if (component) {
- return state.components[component] && state.components[component][key];
- }
- return state.global[key];
-}
diff --git a/server/sonar-web/src/main/js/apps/system/components/PageHeader.tsx b/server/sonar-web/src/main/js/apps/system/components/PageHeader.tsx
index 486c80de4c3..b36b03b3c9f 100644
--- a/server/sonar-web/src/main/js/apps/system/components/PageHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/system/components/PageHeader.tsx
@@ -23,7 +23,7 @@ import { ClipboardButton } from '../../../components/controls/clipboard';
import { Alert } from '../../../components/ui/Alert';
import { toShortNotSoISOString } from '../../../helpers/dates';
import { translate } from '../../../helpers/l10n';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
import PageActions from './PageActions';
export interface Props {
diff --git a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItem-test.tsx.snap b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItem-test.tsx.snap
index 5e34a0c7b61..9d62cbde539 100644
--- a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItem-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItem-test.tsx.snap
@@ -5,7 +5,7 @@ exports[`should render correctly 1`] = `
<td
className="thin nowrap text-middle"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
name="One"
size={36}
/>
@@ -92,7 +92,7 @@ exports[`should render correctly without last connection date 1`] = `
<td
className="thin nowrap text-middle"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
name="One"
size={36}
/>
diff --git a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UsersSelectSearch-test.tsx.snap b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UsersSelectSearch-test.tsx.snap
index c3e1c99564e..0d6493812cf 100644
--- a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UsersSelectSearch-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UsersSelectSearch-test.tsx.snap
@@ -88,7 +88,7 @@ exports[`UsersSelectSearchOption should render correctly with email instead of h
role="listitem"
title="Administrator"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
name="Administrator"
size={16}
/>
@@ -113,7 +113,7 @@ exports[`UsersSelectSearchOption should render correctly without all parameters
role="listitem"
title="Administrator"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
hash="7daf6c79d4802916d83f6266e24850af"
name="Administrator"
size={16}
@@ -139,7 +139,7 @@ exports[`UsersSelectSearchValue should render correctly with a user 1`] = `
<div
className="Select-value-label"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
hash="7daf6c79d4802916d83f6266e24850af"
name="Administrator"
size={16}
@@ -166,7 +166,7 @@ exports[`UsersSelectSearchValue should render correctly with email instead of ha
<div
className="Select-value-label"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
name="Administrator"
size={16}
/>
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 d4403291c37..88c25f5d75d 100644
--- a/server/sonar-web/src/main/js/components/controls/ComponentReportActions.tsx
+++ b/server/sonar-web/src/main/js/components/controls/ComponentReportActions.tsx
@@ -27,10 +27,11 @@ import withAppStateContext from '../../app/components/app-state/withAppStateCont
import addGlobalSuccessMessage from '../../app/utils/addGlobalSuccessMessage';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { isLoggedIn } from '../../helpers/users';
+import { AppState } from '../../types/appstate';
import { Branch } from '../../types/branch-like';
import { ComponentQualifier } from '../../types/component';
import { ComponentReportStatus } from '../../types/component-report';
-import { AppState, Component, CurrentUser } from '../../types/types';
+import { Component, CurrentUser } from '../../types/types';
import { withCurrentUser } from '../hoc/withCurrentUser';
import ComponentReportActionsRenderer from './ComponentReportActionsRenderer';
diff --git a/server/sonar-web/src/main/js/components/docs/DocLink.tsx b/server/sonar-web/src/main/js/components/docs/DocLink.tsx
index 1263177bf8f..77e6cbd833c 100644
--- a/server/sonar-web/src/main/js/components/docs/DocLink.tsx
+++ b/server/sonar-web/src/main/js/components/docs/DocLink.tsx
@@ -22,7 +22,7 @@ import { Link } from 'react-router';
import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import DetachIcon from '../../components/icons/DetachIcon';
import { isSonarCloud } from '../../helpers/system';
-import { AppState } from '../../types/types';
+import { AppState } from '../../types/appstate';
interface OwnProps {
appState: AppState;
diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.tsx.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.tsx.snap
index 50e8aacb158..69b5f81e7f5 100644
--- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.tsx.snap
@@ -32,7 +32,7 @@ exports[`should open the popup when the button is clicked 2`] = `
<span
className="text-top"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
hash="gravatarhash"
name=""
@@ -108,7 +108,7 @@ exports[`should render with the action 1`] = `
<span
className="text-top"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
hash="gravatarhash"
name=""
@@ -133,7 +133,7 @@ exports[`should render without the action when the correct rights are missing 1`
<span
className="text-top"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
hash="gravatarhash"
name=""
diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.tsx.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.tsx.snap
index 1a6849c827e..a6864ce05b5 100644
--- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.tsx.snap
@@ -20,7 +20,7 @@ exports[`should open the right popups when the buttons are clicked 3`] = `
className="issue-comment-author"
title="John Doe"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
hash="gravatarhash"
name="John Doe"
@@ -118,7 +118,7 @@ exports[`should render correctly a comment that is not updatable 1`] = `
className="issue-comment-author"
title="John Doe"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
hash="gravatarhash"
name="John Doe"
@@ -160,7 +160,7 @@ exports[`should render correctly a comment that is updatable 1`] = `
className="issue-comment-author"
title="John Doe"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
hash="gravatarhash"
name="John Doe"
@@ -255,7 +255,7 @@ exports[`should render correctly a comment with a deleted author 1`] = `
className="issue-comment-author"
title="user.x_deleted.john.doe"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
hash="gravatarhash"
name="john.doe"
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.tsx.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.tsx.snap
index 2bf516e280b..1de31a413cc 100644
--- a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.tsx.snap
@@ -39,7 +39,7 @@ exports[`should render the changelog popup correctly 1`] = `
className="text-left text-top"
>
<p>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
hash="gravatarhash"
name="John Doe"
@@ -104,7 +104,7 @@ exports[`should render the changelog popup when we have a deleted user 1`] = `
className="text-left text-top"
>
<p>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="john.doe"
size={16}
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetAssigneePopup-test.tsx.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetAssigneePopup-test.tsx.snap
index b539ed1acf0..88c0d0ba1cf 100644
--- a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetAssigneePopup-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetAssigneePopup-test.tsx.snap
@@ -33,7 +33,7 @@ exports[`should render correctly 1`] = `
item="luke"
key="luke"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="spacer-right"
name="Skywalker"
size={16}
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SimilarIssuesPopup-test.tsx.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SimilarIssuesPopup-test.tsx.snap
index 595727bceb6..aabffd0885e 100644
--- a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SimilarIssuesPopup-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SimilarIssuesPopup-test.tsx.snap
@@ -127,7 +127,7 @@ exports[`should render correctly when assigned 1`] = `
>
<span>
assigned_to
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-left little-spacer-right"
name="Luke Skywalker"
size={16}
@@ -143,7 +143,7 @@ exports[`should render correctly when assigned 2`] = `
>
<span>
assigned_to
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-left little-spacer-right"
name="luke"
size={16}
diff --git a/server/sonar-web/src/main/js/components/measure/Measure.tsx b/server/sonar-web/src/main/js/components/measure/Measure.tsx
index 9ed89f92189..8abc4e62781 100644
--- a/server/sonar-web/src/main/js/components/measure/Measure.tsx
+++ b/server/sonar-web/src/main/js/components/measure/Measure.tsx
@@ -22,7 +22,7 @@ import Tooltip from '../../components/controls/Tooltip';
import Level from '../../components/ui/Level';
import Rating from '../../components/ui/Rating';
import { formatMeasure } from '../../helpers/measures';
-import { getRatingTooltip } from './utils';
+import RatingTooltipContent from './RatingTooltipContent';
interface Props {
className?: string;
@@ -57,8 +57,9 @@ export default function Measure({
return <span className={className}>{formattedValue != null ? formattedValue : '–'}</span>;
}
- const tooltip = getRatingTooltip(metricKey, Number(value));
+ const tooltip = <RatingTooltipContent metricKey={metricKey} value={value} />;
const rating = <Rating small={small} value={value} />;
+
if (tooltip) {
return (
<Tooltip overlay={tooltip}>
diff --git a/server/sonar-web/src/main/js/components/measure/RatingTooltipContent.tsx b/server/sonar-web/src/main/js/components/measure/RatingTooltipContent.tsx
new file mode 100644
index 00000000000..855449adc3a
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/measure/RatingTooltipContent.tsx
@@ -0,0 +1,94 @@
+/*
+ * 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 * as React from 'react';
+import withAppStateContext from '../../app/components/app-state/withAppStateContext';
+import { translate, translateWithParameters } from '../../helpers/l10n';
+import { formatMeasure, isDiffMetric } from '../../helpers/measures';
+import { AppState } from '../../types/appstate';
+import { MetricKey } from '../../types/metrics';
+import { GlobalSettingKeys } from '../../types/settings';
+import { KNOWN_RATINGS } from './utils';
+
+const RATING_GRID_SIZE = 4;
+const DIFF_METRIC_PREFIX_LENGTH = 4;
+const PERCENT_MULTIPLIER = 100;
+const GRID_INDEX_OFFSET = 2; // Rating of 2 should get index 0 (threshold between 1 and 2)
+
+export interface RatingTooltipContentProps {
+ appState: AppState;
+ metricKey: MetricKey | string;
+ value: number | string;
+}
+
+function getMaintainabilityGrid(ratingGridSetting: string) {
+ const numbers = ratingGridSetting
+ .split(',')
+ .map(s => parseFloat(s))
+ .filter(n => !isNaN(n));
+
+ return numbers.length === RATING_GRID_SIZE ? numbers : [0, 0, 0, 0];
+}
+
+export function RatingTooltipContent(props: RatingTooltipContentProps) {
+ const {
+ appState: { settings },
+ metricKey,
+ value
+ } = props;
+
+ const finalMetricKey = isDiffMetric(metricKey)
+ ? metricKey.slice(DIFF_METRIC_PREFIX_LENGTH)
+ : metricKey;
+
+ if (!KNOWN_RATINGS.includes(finalMetricKey)) {
+ return null;
+ }
+
+ const rating = Number(value);
+ const ratingLetter = formatMeasure(value, 'RATING');
+
+ if (finalMetricKey !== 'sqale_rating' && finalMetricKey !== 'maintainability_rating') {
+ return <>{translate('metric', finalMetricKey, 'tooltip', ratingLetter)}</>;
+ }
+
+ const maintainabilityGrid = getMaintainabilityGrid(settings[GlobalSettingKeys.RatingGrid] ?? '');
+ const maintainabilityRatingThreshold =
+ maintainabilityGrid[Math.floor(rating) - GRID_INDEX_OFFSET];
+
+ return (
+ // Required to correctly satisfy the context typing
+ // eslint-disable-next-line react/jsx-no-useless-fragment
+ <>
+ {rating === 1
+ ? translateWithParameters(
+ 'metric.sqale_rating.tooltip.A',
+ formatMeasure(maintainabilityGrid[0] * PERCENT_MULTIPLIER, 'PERCENT')
+ )
+ : translateWithParameters(
+ 'metric.sqale_rating.tooltip',
+ ratingLetter,
+ formatMeasure(maintainabilityRatingThreshold * PERCENT_MULTIPLIER, 'PERCENT')
+ )}
+ </>
+ );
+}
+
+export default withAppStateContext(RatingTooltipContent);
diff --git a/server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx b/server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx
index 5bbf237365c..5fbc431a0b7 100644
--- a/server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx
+++ b/server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx
@@ -21,12 +21,6 @@ import { shallow } from 'enzyme';
import * as React from 'react';
import Measure from '../Measure';
-jest.mock('../../../helpers/measures', () => {
- const measures = jest.requireActual('../../../helpers/measures');
- measures.getRatingTooltip = jest.fn(() => 'tooltip');
- return measures;
-});
-
it('renders trivial measure', () => {
expect(
shallow(<Measure metricKey="coverage" metricType="PERCENT" value="73.0" />)
@@ -45,18 +39,12 @@ it('renders LEVEL', () => {
).toMatchSnapshot();
});
-it('renders known RATING', () => {
+it('renders RATING', () => {
expect(
shallow(<Measure metricKey="sqale_rating" metricType="RATING" value="3" />)
).toMatchSnapshot();
});
-it('renders unknown RATING', () => {
- expect(
- shallow(<Measure metricKey="foo_rating" metricType="RATING" value="4" />)
- ).toMatchSnapshot();
-});
-
it('renders undefined measure', () => {
expect(
shallow(<Measure metricKey="foo" metricType="PERCENT" value={undefined} />)
diff --git a/server/sonar-web/src/main/js/components/measure/__tests__/RatingTooltipContent-test.tsx b/server/sonar-web/src/main/js/components/measure/__tests__/RatingTooltipContent-test.tsx
new file mode 100644
index 00000000000..4a490d6481f
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/measure/__tests__/RatingTooltipContent-test.tsx
@@ -0,0 +1,62 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockAppState } from '../../../helpers/testMocks';
+import { GlobalSettingKeys } from '../../../types/settings';
+import { MetricKey } from '../../../types/metrics';
+import { RatingTooltipContent, RatingTooltipContentProps } from '../RatingTooltipContent';
+
+it('should render maintainability correctly', () => {
+ expect(shallowRender()).toMatchSnapshot('sqale rating');
+ expect(shallowRender({ value: 1 })).toMatchSnapshot('sqale rating A');
+ expect(shallowRender({ appState: mockAppState({ settings: {} }) })).toMatchSnapshot(
+ 'sqale rating default grid'
+ );
+ expect(
+ shallowRender({
+ appState: mockAppState({ settings: { [GlobalSettingKeys.RatingGrid]: '0,0.1' } })
+ })
+ ).toMatchSnapshot('sqale rating wrong grid');
+});
+
+it('should render other ratings correctly', () => {
+ expect(shallowRender({ metricKey: MetricKey.security_rating })).toMatchSnapshot(
+ 'security rating'
+ );
+ expect(shallowRender({ metricKey: MetricKey.new_security_rating })).toMatchSnapshot(
+ 'new security rating'
+ );
+});
+
+it('should ignore non-rating metrics', () => {
+ expect(shallowRender({ metricKey: MetricKey.code_smells }).type()).toBeNull();
+});
+
+function shallowRender(overrides: Partial<RatingTooltipContentProps> = {}) {
+ return shallow(
+ <RatingTooltipContent
+ appState={mockAppState({ settings: { [GlobalSettingKeys.RatingGrid]: '0.05,0.1,0.2,0.4' } })}
+ metricKey={MetricKey.sqale_rating}
+ value={2}
+ {...overrides}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap b/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap
index 437ca28a87b..29e685efb6b 100644
--- a/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap
@@ -6,9 +6,14 @@ exports[`renders LEVEL 1`] = `
/>
`;
-exports[`renders known RATING 1`] = `
+exports[`renders RATING 1`] = `
<Tooltip
- overlay="tooltip"
+ overlay={
+ <withAppStateContext(RatingTooltipContent)
+ metricKey="sqale_rating"
+ value="3"
+ />
+ }
>
<span>
<Rating
@@ -35,9 +40,3 @@ exports[`renders undefined measure 1`] = `
</span>
`;
-
-exports[`renders unknown RATING 1`] = `
-<Rating
- value="4"
-/>
-`;
diff --git a/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/RatingTooltipContent-test.tsx.snap b/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/RatingTooltipContent-test.tsx.snap
new file mode 100644
index 00000000000..2ce6e0cef37
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/RatingTooltipContent-test.tsx.snap
@@ -0,0 +1,37 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render maintainability correctly: sqale rating 1`] = `
+<Fragment>
+ metric.sqale_rating.tooltip.B.5.0%
+</Fragment>
+`;
+
+exports[`should render maintainability correctly: sqale rating A 1`] = `
+<Fragment>
+ metric.sqale_rating.tooltip.A.5.0%
+</Fragment>
+`;
+
+exports[`should render maintainability correctly: sqale rating default grid 1`] = `
+<Fragment>
+ metric.sqale_rating.tooltip.B.0.0%
+</Fragment>
+`;
+
+exports[`should render maintainability correctly: sqale rating wrong grid 1`] = `
+<Fragment>
+ metric.sqale_rating.tooltip.B.0.0%
+</Fragment>
+`;
+
+exports[`should render other ratings correctly: new security rating 1`] = `
+<Fragment>
+ metric.security_rating.tooltip.B
+</Fragment>
+`;
+
+exports[`should render other ratings correctly: security rating 1`] = `
+<Fragment>
+ metric.security_rating.tooltip.B
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/components/measure/utils.ts b/server/sonar-web/src/main/js/components/measure/utils.ts
index aea8911ff2e..8fb375bc9a0 100644
--- a/server/sonar-web/src/main/js/components/measure/utils.ts
+++ b/server/sonar-web/src/main/js/components/measure/utils.ts
@@ -17,10 +17,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { getRatingTooltip as nextGetRatingTooltip, isDiffMetric } from '../../helpers/measures';
import { Dict, Measure, MeasureEnhanced, MeasureIntern, Metric } from '../../types/types';
-const KNOWN_RATINGS = [
+export const KNOWN_RATINGS = [
'sqale_rating',
'maintainability_rating', // Needed to provide the label for "new_maintainability_rating"
'reliability_rating',
@@ -39,11 +38,3 @@ export function enhanceMeasure(measure: Measure, metrics: Dict<Metric>): Measure
export function getLeakValue(measure: MeasureIntern | undefined): string | undefined {
return measure?.period?.value;
}
-
-export function getRatingTooltip(metricKey: string, value: number): string | undefined {
- const finalMetricKey = isDiffMetric(metricKey) ? metricKey.substr(4) : metricKey;
- if (KNOWN_RATINGS.includes(finalMetricKey)) {
- return nextGetRatingTooltip(finalMetricKey, value);
- }
- return undefined;
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/PublishSteps.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/PublishSteps.tsx
index 3d8c44c888b..13d10fb06ec 100644
--- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/PublishSteps.tsx
+++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/PublishSteps.tsx
@@ -25,7 +25,7 @@ import { Alert } from '../../../../components/ui/Alert';
import { ALM_DOCUMENTATION_PATHS } from '../../../../helpers/constants';
import { translate } from '../../../../helpers/l10n';
import { AlmKeys } from '../../../../types/alm-settings';
-import { AppState } from '../../../../types/types';
+import { AppState } from '../../../../types/appstate';
import SentenceWithHighlights from '../../components/SentenceWithHighlights';
export interface PublishStepsProps {
diff --git a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/AnalysisCommand.tsx b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/AnalysisCommand.tsx
index 31c332fa14f..b0e372c194f 100644
--- a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/AnalysisCommand.tsx
+++ b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/AnalysisCommand.tsx
@@ -20,7 +20,8 @@
import { Dictionary } from 'lodash';
import * as React from 'react';
import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
-import { AppState, Component } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
+import { Component } from '../../../types/types';
import { CompilationInfo } from '../components/CompilationInfo';
import CreateYmlFile from '../components/CreateYmlFile';
import { BuildTools } from '../types';
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx b/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx
index 20b11cbb19d..1a8b2bcf243 100644
--- a/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx
+++ b/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx
@@ -22,7 +22,7 @@ import withAppStateContext from '../../../app/components/app-state/withAppStateC
import { translate } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
import { AlmKeys } from '../../../types/alm-settings';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
import SentenceWithHighlights from './SentenceWithHighlights';
export interface AllSetProps {
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx
index 4038f37237e..d0bd6b5c0b2 100644
--- a/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx
+++ b/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx
@@ -19,7 +19,8 @@
*/
import * as React from 'react';
import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
-import { AppState, Component } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
+import { Component } from '../../../types/types';
import { BuildTools } from '../types';
import CFamily from './commands/CFamily';
import DotNet from './commands/DotNet';
diff --git a/server/sonar-web/src/main/js/components/tutorials/gitlabci/YmlFileStep.tsx b/server/sonar-web/src/main/js/components/tutorials/gitlabci/YmlFileStep.tsx
index bc6ec8c0039..7b83e9de998 100644
--- a/server/sonar-web/src/main/js/components/tutorials/gitlabci/YmlFileStep.tsx
+++ b/server/sonar-web/src/main/js/components/tutorials/gitlabci/YmlFileStep.tsx
@@ -22,7 +22,7 @@ import { FormattedMessage } from 'react-intl';
import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import { ClipboardIconButton } from '../../../components/controls/clipboard';
import { translate } from '../../../helpers/l10n';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
import FinishButton from '../components/FinishButton';
import GithubCFamilyExampleRepositories from '../components/GithubCFamilyExampleRepositories';
import Step from '../components/Step';
diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx
index 15ddfb7fa86..bb65db801d6 100644
--- a/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx
+++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx
@@ -28,7 +28,8 @@ import {
AlmSettingsInstance,
ProjectAlmBindingResponse
} from '../../../types/alm-settings';
-import { AppState, Component, CurrentUserSetting } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
+import { Component, CurrentUserSetting } from '../../../types/types';
import AllSetStep from '../components/AllSetStep';
import JenkinsfileStep from './JenkinsfileStep';
import MultiBranchPipelineStep from './MultiBranchPipelineStep';
diff --git a/server/sonar-web/src/main/js/components/ui/Avatar.tsx b/server/sonar-web/src/main/js/components/ui/Avatar.tsx
index b9c60bcceef..82eb5fe5b56 100644
--- a/server/sonar-web/src/main/js/components/ui/Avatar.tsx
+++ b/server/sonar-web/src/main/js/components/ui/Avatar.tsx
@@ -19,51 +19,53 @@
*/
import classNames from 'classnames';
import * as React from 'react';
-import { connect } from 'react-redux';
+import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import GenericAvatar from '../../components/ui/GenericAvatar';
-import { getGlobalSettingValue, Store } from '../../store/rootReducer';
+import { AppState } from '../../types/appstate';
+import { GlobalSettingKeys } from '../../types/settings';
+
+const GRAVATAR_SIZE_MULTIPLIER = 2;
interface Props {
+ appState: AppState;
className?: string;
- enableGravatar: boolean;
- gravatarServerUrl: string;
hash?: string;
name?: string;
size: number;
}
-function Avatar(props: Props) {
- if (!props.enableGravatar || !props.hash) {
- if (!props.name) {
+export function Avatar(props: Props) {
+ const {
+ appState: { settings },
+ className,
+ hash,
+ name,
+ size
+ } = props;
+
+ const enableGravatar = settings[GlobalSettingKeys.EnableGravatar] === 'true';
+
+ if (!enableGravatar || !hash) {
+ if (!name) {
return null;
}
- return <GenericAvatar className={props.className} name={props.name} size={props.size} />;
+ return <GenericAvatar className={className} name={name} size={size} />;
}
- const url = props.gravatarServerUrl
- .replace('{EMAIL_MD5}', props.hash)
- .replace('{SIZE}', String(props.size * 2));
+ const gravatarServerUrl = settings[GlobalSettingKeys.GravatarServerUrl] ?? '';
+ const url = gravatarServerUrl
+ .replace('{EMAIL_MD5}', hash)
+ .replace('{SIZE}', String(size * GRAVATAR_SIZE_MULTIPLIER));
return (
<img
- alt={props.name}
- className={classNames(props.className, 'rounded')}
- height={props.size}
+ alt={name}
+ className={classNames(className, 'rounded')}
+ height={size}
src={url}
- width={props.size}
+ width={size}
/>
);
}
-const mapStateToProps = (state: Store) => {
- const enableGravatar = getGlobalSettingValue(state, 'sonar.lf.enableGravatar');
- const gravatarServerUrl = getGlobalSettingValue(state, 'sonar.lf.gravatarServerUrl');
- return {
- enableGravatar: Boolean(enableGravatar && enableGravatar.value === 'true'),
- gravatarServerUrl: (gravatarServerUrl && gravatarServerUrl.value) || ''
- };
-};
-
-export default connect(mapStateToProps)(Avatar);
-
-export const unconnectedAvatar = Avatar;
+export default withAppStateContext(Avatar);
diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/Avatar-test.tsx b/server/sonar-web/src/main/js/components/ui/__tests__/Avatar-test.tsx
index b5d138f2e25..b9bb9fec681 100644
--- a/server/sonar-web/src/main/js/components/ui/__tests__/Avatar-test.tsx
+++ b/server/sonar-web/src/main/js/components/ui/__tests__/Avatar-test.tsx
@@ -19,15 +19,21 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { unconnectedAvatar as Avatar } from '../Avatar';
+import { mockAppState } from '../../../helpers/testMocks';
+import { GlobalSettingKeys } from '../../../types/settings';
+import { Avatar } from '../Avatar';
const gravatarServerUrl = 'http://example.com/{EMAIL_MD5}.jpg?s={SIZE}';
it('should be able to render with hash only', () => {
const avatar = shallow(
<Avatar
- enableGravatar={true}
- gravatarServerUrl={gravatarServerUrl}
+ appState={mockAppState({
+ settings: {
+ [GlobalSettingKeys.EnableGravatar]: 'true',
+ [GlobalSettingKeys.GravatarServerUrl]: gravatarServerUrl
+ }
+ })}
hash="7daf6c79d4802916d83f6266e24850af"
name="Foo"
size={30}
@@ -38,14 +44,14 @@ it('should be able to render with hash only', () => {
it('falls back to dummy avatar', () => {
const avatar = shallow(
- <Avatar enableGravatar={false} gravatarServerUrl="" name="Foo Bar" size={30} />
+ <Avatar appState={mockAppState({ settings: {} })} name="Foo Bar" size={30} />
);
expect(avatar).toMatchSnapshot();
});
it('do not fail when name is missing', () => {
const avatar = shallow(
- <Avatar enableGravatar={false} gravatarServerUrl="" name={undefined} size={30} />
+ <Avatar appState={mockAppState({ settings: {} })} name={undefined} size={30} />
);
expect(avatar.getElement()).toBeNull();
});
diff --git a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx
index 6826d56c336..d78e057f2f6 100644
--- a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx
+++ b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx
@@ -21,9 +21,9 @@ import { filter, flatMap, isEmpty, negate } from 'lodash';
import * as React from 'react';
import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import { translate } from '../../helpers/l10n';
+import { AppState } from '../../types/appstate';
import { EditionKey } from '../../types/editions';
import { SystemUpgrade } from '../../types/system';
-import { AppState } from '../../types/types';
import { ResetButtonLink } from '../controls/buttons';
import Modal from '../controls/Modal';
import { Alert, AlertVariant } from '../ui/Alert';
diff --git a/server/sonar-web/src/main/js/helpers/analytics.js b/server/sonar-web/src/main/js/helpers/analytics.js
deleted file mode 100644
index 317c675157a..00000000000
--- a/server/sonar-web/src/main/js/helpers/analytics.js
+++ /dev/null
@@ -1,26 +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.
- */
-// The body of the `gtm` function comes from Google Tag Manager docs; let's keep it like it was written.
-// @ts-ignore
-// prettier-ignore
-// eslint-disable-next-line
-const gtm = id => (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});const f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);}(window,document,'script','dataLayer',id));
-
-module.exports = { gtm };
diff --git a/server/sonar-web/src/main/js/helpers/measures.ts b/server/sonar-web/src/main/js/helpers/measures.ts
index 3a6f15040a8..cedf304914a 100644
--- a/server/sonar-web/src/main/js/helpers/measures.ts
+++ b/server/sonar-web/src/main/js/helpers/measures.ts
@@ -63,68 +63,6 @@ export function isDiffMetric(metricKey: MetricKey | string): boolean {
return metricKey.indexOf('new_') === 0;
}
-function getRatingGrid(): string {
- // workaround cyclic dependencies
- const getStore = require('../app/utils/getStore').default;
- const { getGlobalSettingValue } = require('../store/rootReducer');
-
- const store = getStore();
- const settingValue = getGlobalSettingValue(store.getState(), 'sonar.technicalDebt.ratingGrid');
- return settingValue ? settingValue.value : '';
-}
-
-let maintainabilityRatingGrid: number[];
-
-function getMaintainabilityRatingGrid(): number[] {
- if (maintainabilityRatingGrid) {
- return maintainabilityRatingGrid;
- }
-
- const str = getRatingGrid();
- const numbers = str
- .split(',')
- .map(s => parseFloat(s))
- .filter(n => !isNaN(n));
-
- if (numbers.length === 4) {
- maintainabilityRatingGrid = numbers;
- } else {
- maintainabilityRatingGrid = [0, 0, 0, 0];
- }
-
- return maintainabilityRatingGrid;
-}
-
-function getMaintainabilityRatingTooltip(rating: number): string {
- const maintainabilityGrid = getMaintainabilityRatingGrid();
- const maintainabilityRatingThreshold = maintainabilityGrid[Math.floor(rating) - 2];
-
- if (rating < 2) {
- return translateWithParameters(
- 'metric.sqale_rating.tooltip.A',
- formatMeasure(maintainabilityGrid[0] * 100, 'PERCENT')
- );
- }
-
- const ratingLetter = formatMeasure(rating, 'RATING');
-
- return translateWithParameters(
- 'metric.sqale_rating.tooltip',
- ratingLetter,
- formatMeasure(maintainabilityRatingThreshold * 100, 'PERCENT')
- );
-}
-
-export function getRatingTooltip(metricKey: MetricKey | string, value: number | string): string {
- const ratingLetter = formatMeasure(value, 'RATING');
-
- const finalMetricKey = isDiffMetric(metricKey) ? metricKey.substr(4) : metricKey;
-
- return finalMetricKey === 'sqale_rating' || finalMetricKey === 'maintainability_rating'
- ? getMaintainabilityRatingTooltip(Number(value))
- : translate('metric', finalMetricKey, 'tooltip', ratingLetter);
-}
-
export function getDisplayMetrics(metrics: Metric[]) {
return metrics.filter(metric => !metric.hidden && !['DATA', 'DISTRIB'].includes(metric.type));
}
diff --git a/server/sonar-web/src/main/js/helpers/testMocks.ts b/server/sonar-web/src/main/js/helpers/testMocks.ts
index fde809bb117..d94d804787a 100644
--- a/server/sonar-web/src/main/js/helpers/testMocks.ts
+++ b/server/sonar-web/src/main/js/helpers/testMocks.ts
@@ -17,11 +17,13 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Location } from 'history';
+import { Location, LocationDescriptor } from 'history';
import { InjectedRouter } from 'react-router';
import { createStore, Store } from 'redux';
import { DocumentationEntry } from '../apps/documentation/utils';
import { Exporter, Profile } from '../apps/quality-profiles/types';
+import { AppState } from '../types/appstate';
+import { EditionKey } from '../types/editions';
import { Language } from '../types/languages';
import { DumpStatus, DumpTask } from '../types/project-dump';
import { TaskStatuses } from '../types/tasks';
@@ -29,7 +31,6 @@ import {
AlmApplication,
Analysis,
AnalysisEvent,
- AppState,
Condition,
CurrentUser,
FlowLocation,
@@ -117,7 +118,7 @@ export function mockAnalysisEvent(overrides: Partial<AnalysisEvent> = {}): Analy
export function mockAppState(overrides: Partial<AppState> = {}): AppState {
return {
- edition: 'community',
+ edition: EditionKey.community,
productionDatabase: true,
qualifiers: ['TRK'],
settings: {},
@@ -526,7 +527,12 @@ export function mockQualityProfileExporter(override?: Partial<Exporter>): Export
};
}
-export function mockRouter(overrides: { push?: Function; replace?: Function } = {}) {
+export function mockRouter(
+ overrides: {
+ push?: (loc: LocationDescriptor) => void;
+ replace?: (loc: LocationDescriptor) => void;
+ } = {}
+) {
return {
createHref: jest.fn(),
createPath: jest.fn(),
diff --git a/server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx b/server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx
index fbc4f8ace63..c1a3ec926e3 100644
--- a/server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx
+++ b/server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx
@@ -30,7 +30,8 @@ import { MetricsContext } from '../app/components/metrics/MetricsContext';
import getStore from '../app/utils/getStore';
import { RouteWithChildRoutes } from '../app/utils/startReactApp';
import { Store as State } from '../store/rootReducer';
-import { AppState, Dict, Metric } from '../types/types';
+import { AppState } from '../types/appstate';
+import { Dict, Metric } from '../types/types';
import { DEFAULT_METRICS } from './mocks/metrics';
import { mockAppState } from './testMocks';
diff --git a/server/sonar-web/src/main/js/store/rootReducer.ts b/server/sonar-web/src/main/js/store/rootReducer.ts
index b7ba18c3020..074d20e2a59 100644
--- a/server/sonar-web/src/main/js/store/rootReducer.ts
+++ b/server/sonar-web/src/main/js/store/rootReducer.ts
@@ -18,7 +18,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { combineReducers } from 'redux';
-import settingsApp, * as fromSettingsApp from '../apps/settings/store/rootReducer';
import { BranchLike } from '../types/branch-like';
import { CurrentUserSettingNames } from '../types/types';
import branches, * as fromBranches from './branches';
@@ -29,18 +28,12 @@ export type Store = {
branches: fromBranches.State;
globalMessages: fromGlobalMessages.State;
users: fromUsers.State;
-
- // apps
- settingsApp: any;
};
export default combineReducers<Store>({
branches,
globalMessages,
- users,
-
- // apps
- settingsApp
+ users
});
export function getGlobalMessages(state: Store) {
@@ -55,10 +48,6 @@ export function getCurrentUser(state: Store) {
return fromUsers.getCurrentUser(state.users);
}
-export function getGlobalSettingValue(state: Store, key: string) {
- return fromSettingsApp.getValue(state.settingsApp, key);
-}
-
export function getBranchStatusByBranchLike(
state: Store,
component: string,
diff --git a/server/sonar-web/src/main/js/apps/settings/store/rootReducer.ts b/server/sonar-web/src/main/js/types/appstate.ts
index 4bc466337b5..62555148764 100644
--- a/server/sonar-web/src/main/js/apps/settings/store/rootReducer.ts
+++ b/server/sonar-web/src/main/js/types/appstate.ts
@@ -17,15 +17,26 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { combineReducers } from 'redux';
-import values, * as fromValues from './values';
-interface State {
- values: fromValues.State;
-}
-
-export default combineReducers({ values });
+import { EditionKey } from './editions';
+import { GlobalSettingKeys } from './settings';
+import { Extension } from './types';
-export function getValue(state: State, key: string, component?: string) {
- return fromValues.getValue(state.values, key, component);
+export interface AppState {
+ authenticationError?: boolean;
+ authorizationError?: boolean;
+ branchesEnabled?: boolean;
+ canAdmin?: boolean;
+ edition?: EditionKey;
+ globalPages?: Extension[];
+ projectImportFeatureEnabled?: boolean;
+ instanceUsesDefaultAdminCredentials?: boolean;
+ multipleAlmEnabled?: boolean;
+ needIssueSync?: boolean;
+ productionDatabase: boolean;
+ qualifiers: string[];
+ settings: { [key in GlobalSettingKeys]?: string };
+ standalone?: boolean;
+ version: string;
+ webAnalyticsJsPath?: string;
}
diff --git a/server/sonar-web/src/main/js/types/extension.ts b/server/sonar-web/src/main/js/types/extension.ts
index a142cf56861..b444b100e69 100644
--- a/server/sonar-web/src/main/js/types/extension.ts
+++ b/server/sonar-web/src/main/js/types/extension.ts
@@ -21,8 +21,9 @@ import { IntlShape } from 'react-intl';
import { Store as ReduxStore } from 'redux';
import { Location, Router } from '../components/hoc/withRouter';
import { Store } from '../store/rootReducer';
+import { AppState } from './appstate';
import { L10nBundle } from './l10n';
-import { AppState, CurrentUser, Dict } from './types';
+import { CurrentUser, Dict } from './types';
export enum AdminPageExtension {
GovernanceConsole = 'governance/views_console'
diff --git a/server/sonar-web/src/main/js/types/settings.ts b/server/sonar-web/src/main/js/types/settings.ts
index 909e801bfa6..bf95ca0336b 100644
--- a/server/sonar-web/src/main/js/types/settings.ts
+++ b/server/sonar-web/src/main/js/types/settings.ts
@@ -20,12 +20,23 @@
import { Dict } from './types';
export const enum SettingsKey {
+ AuditHouseKeeping = 'sonar.dbcleaner.auditHousekeeping',
DaysBeforeDeletingInactiveBranchesAndPRs = 'sonar.dbcleaner.daysBeforeDeletingInactiveBranchesAndPRs',
DefaultProjectVisibility = 'projects.default.visibility',
ServerBaseUrl = 'sonar.core.serverBaseURL',
PluginRiskConsent = 'sonar.plugins.risk.consent'
}
+export enum GlobalSettingKeys {
+ LogoUrl = 'sonar.lf.logoUrl',
+ LogoWidth = 'sonar.lf.logoWidthPx',
+ EnableGravatar = 'sonar.lf.enableGravatar',
+ GravatarServerUrl = 'sonar.lf.gravatarServerUrl',
+ RatingGrid = 'sonar.technicalDebt.ratingGrid',
+ DeveloperAggregatedInfoDisabled = 'sonar.developerAggregatedInfo.disabled',
+ UpdatecenterActivated = 'sonar.updatecenter.activate'
+}
+
export type SettingDefinitionAndValue = {
definition: ExtendedSettingDefinition;
settingValue?: SettingValue;
diff --git a/server/sonar-web/src/main/js/types/types.ts b/server/sonar-web/src/main/js/types/types.ts
index cbff79bdbc7..2c6342be3dd 100644
--- a/server/sonar-web/src/main/js/types/types.ts
+++ b/server/sonar-web/src/main/js/types/types.ts
@@ -83,25 +83,6 @@ export interface AnalysisEvent {
};
}
-export interface AppState {
- authenticationError?: boolean;
- authorizationError?: boolean;
- branchesEnabled?: boolean;
- canAdmin?: boolean;
- edition: 'community' | 'developer' | 'enterprise' | 'datacenter' | undefined;
- globalPages?: Extension[];
- projectImportFeatureEnabled?: boolean;
- instanceUsesDefaultAdminCredentials?: boolean;
- multipleAlmEnabled?: boolean;
- needIssueSync?: boolean;
- productionDatabase: boolean;
- qualifiers: string[];
- settings: Dict<string>;
- standalone?: boolean;
- version: string;
- webAnalyticsJsPath?: string;
-}
-
export interface Breadcrumb {
key: string;
name: string;