aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuillaume Peoc'h <guillaume.peoch@sonarsource.com>2022-02-02 15:36:37 +0100
committersonartech <sonartech@sonarsource.com>2022-02-09 20:02:55 +0000
commit2b13f016dee62cc813d0374e9f835dee5f4cda28 (patch)
treea14a39e129bb993ab041c8b29af4f9963bdf0002
parent2d48cb3c1eb4f6cd68bd091c4eb9d62fa71deff8 (diff)
downloadsonarqube-2b13f016dee62cc813d0374e9f835dee5f4cda28.tar.gz
sonarqube-2b13f016dee62cc813d0374e9f835dee5f4cda28.zip
SONAR-15909 Extract AppState Redux
-rw-r--r--server/sonar-web/src/main/js/api/nav.ts7
-rw-r--r--server/sonar-web/src/main/js/app/components/AdminContainer.tsx30
-rw-r--r--server/sonar-web/src/main/js/app/components/App.tsx3
-rw-r--r--server/sonar-web/src/main/js/app/components/ComponentContainer.tsx13
-rw-r--r--server/sonar-web/src/main/js/app/components/GlobalContainer.tsx5
-rw-r--r--server/sonar-web/src/main/js/app/components/GlobalFooter.tsx25
-rw-r--r--server/sonar-web/src/main/js/app/components/PageTracker.tsx23
-rw-r--r--server/sonar-web/src/main/js/app/components/SimpleContainer.tsx4
-rw-r--r--server/sonar-web/src/main/js/app/components/SimpleSessionsContainer.tsx4
-rw-r--r--server/sonar-web/src/main/js/app/components/StartupModal.tsx29
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/AdminContainer-test.tsx17
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx25
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/PageTracker-test.tsx12
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx12
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/AdminContainer-test.tsx.snap15
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/GlobalContainer-test.tsx.snap12
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/GlobalFooter-test.tsx.snap5
-rw-r--r--server/sonar-web/src/main/js/app/components/app-state/AppStateContext.tsx (renamed from server/sonar-web/src/main/js/apps/projectBaseline/components/AppContainer.ts)20
-rw-r--r--server/sonar-web/src/main/js/app/components/app-state/AppStateContextProvider.tsx (renamed from server/sonar-web/src/main/js/app/components/GlobalFooterContainer.tsx)28
-rw-r--r--server/sonar-web/src/main/js/app/components/app-state/__tests__/withAppStateContext-test.tsx50
-rw-r--r--server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx (renamed from server/sonar-web/src/main/js/components/hoc/withAppState.tsx)36
-rw-r--r--server/sonar-web/src/main/js/app/components/extensions/GlobalAdminPageExtension.tsx17
-rw-r--r--server/sonar-web/src/main/js/app/components/extensions/GlobalPageExtension.tsx20
-rw-r--r--server/sonar-web/src/main/js/app/components/indexation/IndexationContextProvider.tsx10
-rw-r--r--server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationContextProvider-test.tsx13
-rw-r--r--server/sonar-web/src/main/js/app/components/indexation/__tests__/__snapshots__/IndexationContextProvider-test.tsx.snap7
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNavLicenseNotif.tsx10
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx6
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavLicenseNotif-test.tsx5
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/Menu-test.tsx3
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap12
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBgTaskNotif-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Header-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchLikeNavigation.tsx6
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.tsx12
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx7
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNav-test.tsx14
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavMenu-test.tsx11
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNav-test.tsx.snap18
-rw-r--r--server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx6
-rw-r--r--server/sonar-web/src/main/js/app/index.ts16
-rw-r--r--server/sonar-web/src/main/js/app/utils/startReactApp.tsx153
-rw-r--r--server/sonar-web/src/main/js/apps/audit-logs/components/AuditApp.tsx44
-rw-r--r--server/sonar-web/src/main/js/apps/audit-logs/components/__tests__/AuditApp-test.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/background-tasks/components/StatPendingCount.tsx16
-rw-r--r--server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/StatPendingCount-test.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Stats-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/apps/change-admin-password/ChangeAdminPasswordApp.tsx21
-rw-r--r--server/sonar-web/src/main/js/apps/change-admin-password/__tests__/ChangeAdminPasswordApp-test.tsx37
-rw-r--r--server/sonar-web/src/main/js/apps/change-admin-password/__tests__/__snapshots__/ChangeAdminPasswordApp-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsIssues.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsIssues-test.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectModeSelection-test.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPage-test.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx25
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/__tests__/AppContainer-test.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/App.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/permission-templates/components/App.tsx15
-rw-r--r--server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/App-test.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.tsx42
-rw-r--r--server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/AllHoldersList-test.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/__snapshots__/App-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx23
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/App-test.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/projectBaseline/routes.ts2
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/LifetimeInformation.tsx16
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/LifetimeInformation-test.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/projectDump/ProjectDumpApp.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ApplicationCreation.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/App.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/App-test.tsx21
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Search-test.tsx14
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Conditions-test.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsContent-test.tsx.snap8
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx17
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsAppRenderer-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegration.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/almIntegration/CreationTooltip.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegration-test.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap48
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx17
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx21
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/AlmSpecificForm-test.tsx10
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBinding-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap6
-rw-r--r--server/sonar-web/src/main/js/apps/system/components/PageHeader.tsx24
-rw-r--r--server/sonar-web/src/main/js/apps/system/components/__tests__/PageHeader-test.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/system/components/__tests__/__snapshots__/App-test.tsx.snap10
-rw-r--r--server/sonar-web/src/main/js/components/docs/DocLink.tsx50
-rw-r--r--server/sonar-web/src/main/js/components/docs/__tests__/DocLink-test.tsx21
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/PublishSteps.tsx4
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap6
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/DotNet-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/Other-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/AnalysisCommand.tsx4
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx4
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/AllSetStep-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx4
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/gitlabci/YmlFileStep.tsx4
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/__snapshots__/GitLabCITutorial-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx22
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-test.tsx7
-rw-r--r--server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx6
-rw-r--r--server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeForm-test.tsx3
-rw-r--r--server/sonar-web/src/main/js/store/appState.ts11
-rw-r--r--server/sonar-web/src/main/js/types/types.ts1
125 files changed, 791 insertions, 758 deletions
diff --git a/server/sonar-web/src/main/js/api/nav.ts b/server/sonar-web/src/main/js/api/nav.ts
index 61fa7d6e3cb..974fb5fc1b3 100644
--- a/server/sonar-web/src/main/js/api/nav.ts
+++ b/server/sonar-web/src/main/js/api/nav.ts
@@ -20,7 +20,7 @@
import throwGlobalError from '../app/utils/throwGlobalError';
import { getJSON } from '../helpers/request';
import { BranchParameters } from '../types/branch-like';
-import { Component } from '../types/types';
+import { Component, Extension } from '../types/types';
type NavComponent = Omit<Component, 'alm' | 'qualifier' | 'leakPeriodDate' | 'path' | 'tags'>;
@@ -34,6 +34,9 @@ export function getMarketplaceNavigation(): Promise<{ serverId: string; ncloc: n
return getJSON('/api/navigation/marketplace').catch(throwGlobalError);
}
-export function getSettingsNavigation(): Promise<any> {
+export function getSettingsNavigation(): Promise<{
+ extensions: Extension[];
+ showUpdateCenter: boolean;
+}> {
return getJSON('/api/navigation/settings').catch(throwGlobalError);
}
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 c66a0678b35..8a690681ac7 100644
--- a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx
@@ -19,35 +19,35 @@
*/
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
-import { connect } from 'react-redux';
import { getSettingsNavigation } from '../../api/nav';
import { getPendingPlugins } from '../../api/plugins';
import { getSystemStatus, waitSystemUPStatus } from '../../api/system';
import handleRequiredAuthorization from '../../app/utils/handleRequiredAuthorization';
import { translate } from '../../helpers/l10n';
-import { setAdminPages } from '../../store/appState';
-import { getAppState, Store } from '../../store/rootReducer';
import { PendingPluginResult } from '../../types/plugins';
import { AppState, Extension, SysStatus } from '../../types/types';
import AdminContext, { defaultPendingPlugins, defaultSystemStatus } from './AdminContext';
+import withAppStateContext from './app-state/withAppStateContext';
import SettingsNav from './nav/settings/SettingsNav';
-interface Props {
- appState: Pick<AppState, 'adminPages' | 'canAdmin'>;
+export interface AdminContainerProps {
+ appState: AppState;
location: {};
- setAdminPages: (adminPages: Extension[]) => void;
+ children: React.ReactElement;
}
interface State {
pendingPlugins: PendingPluginResult;
systemStatus: SysStatus;
+ adminPages: Extension[];
}
-export class AdminContainer extends React.PureComponent<Props, State> {
+export class AdminContainer extends React.PureComponent<AdminContainerProps, State> {
mounted = false;
state: State = {
pendingPlugins: defaultPendingPlugins,
- systemStatus: defaultSystemStatus
+ systemStatus: defaultSystemStatus,
+ adminPages: []
};
componentDidMount() {
@@ -67,7 +67,7 @@ export class AdminContainer extends React.PureComponent<Props, State> {
fetchNavigationSettings = () => {
getSettingsNavigation().then(
- r => this.props.setAdminPages(r.extensions),
+ r => this.setState({ adminPages: r.extensions }),
() => {}
);
};
@@ -110,7 +110,7 @@ export class AdminContainer extends React.PureComponent<Props, State> {
};
render() {
- const { adminPages } = this.props.appState;
+ const { adminPages } = this.state;
// Check that the adminPages are loaded
if (!adminPages) {
@@ -138,15 +138,13 @@ export class AdminContainer extends React.PureComponent<Props, State> {
pendingPlugins,
systemStatus
}}>
- {this.props.children}
+ {React.cloneElement(this.props.children, {
+ adminPages
+ })}
</AdminContext.Provider>
</div>
);
}
}
-const mapStateToProps = (state: Store) => ({ appState: getAppState(state) });
-
-const mapDispatchToProps = { setAdminPages };
-
-export default connect(mapStateToProps, mapDispatchToProps)(AdminContainer);
+export default withAppStateContext(AdminContainer);
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 e1ba399c605..b7e22a74638 100644
--- a/server/sonar-web/src/main/js/app/components/App.tsx
+++ b/server/sonar-web/src/main/js/app/components/App.tsx
@@ -72,9 +72,8 @@ export class App extends React.PureComponent<Props> {
parser.href = this.props.gravatarServerUrl;
if (parser.hostname !== window.location.hostname) {
return <link href={parser.origin} rel="preconnect" />;
- } else {
- return null;
}
+ return null;
};
render() {
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 e979ec1979d..7a362ba5da7 100644
--- a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
@@ -25,7 +25,6 @@ import { getBranches, getPullRequests } from '../../api/branches';
import { getAnalysisStatus, getTasksForComponent } from '../../api/ce';
import { getComponentData } from '../../api/components';
import { getComponentNavigation } from '../../api/nav';
-import { withAppState } from '../../components/hoc/withAppState';
import { Location, Router, withRouter } from '../../components/hoc/withRouter';
import {
getBranchLikeQuery,
@@ -44,13 +43,14 @@ 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 withAppStateContext from './app-state/withAppStateContext';
import ComponentContainerNotFound from './ComponentContainerNotFound';
import { ComponentContext } from './ComponentContext';
import PageUnavailableDueToIndexation from './indexation/PageUnavailableDueToIndexation';
import ComponentNav from './nav/component/ComponentNav';
interface Props {
- appState: Pick<AppState, 'branchesEnabled'>;
+ appState: AppState;
children: React.ReactElement;
location: Pick<Location, 'query' | 'pathname'>;
registerBranchStatus: (branchLike: BranchLike, component: string, status: Status) => void;
@@ -417,7 +417,8 @@ export class ComponentContainer extends React.PureComponent<Props, State> {
isPending,
projectBinding,
projectBindingErrors,
- tasksInProgress
+ tasksInProgress,
+ warnings
} = this.state;
const isInProgress = tasksInProgress && tasksInProgress.length > 0;
@@ -439,7 +440,7 @@ export class ComponentContainer extends React.PureComponent<Props, State> {
onWarningDismiss={this.handleWarningDismiss}
projectBinding={projectBinding}
projectBindingErrors={projectBindingErrors}
- warnings={this.state.warnings}
+ warnings={warnings}
/>
)}
{loading ? (
@@ -467,4 +468,6 @@ export class ComponentContainer extends React.PureComponent<Props, State> {
const mapDispatchToProps = { registerBranchStatus, requireAuthorization };
-export default withAppState(withRouter(connect(null, mapDispatchToProps)(ComponentContainer)));
+export default withRouter(
+ connect(null, mapDispatchToProps)(withAppStateContext(ComponentContainer))
+);
diff --git a/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx b/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx
index c9423bff331..d00fbc0cf73 100644
--- a/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx
@@ -22,7 +22,7 @@ import Workspace from '../../components/workspace/Workspace';
import A11yProvider from './a11y/A11yProvider';
import A11ySkipLinks from './a11y/A11ySkipLinks';
import SuggestionsProvider from './embed-docs-modal/SuggestionsProvider';
-import GlobalFooterContainer from './GlobalFooterContainer';
+import GlobalFooter from './GlobalFooter';
import GlobalMessagesContainer from './GlobalMessagesContainer';
import IndexationContextProvider from './indexation/IndexationContextProvider';
import IndexationNotification from './indexation/IndexationNotification';
@@ -41,13 +41,12 @@ export interface Props {
export default function GlobalContainer(props: Props) {
// it is important to pass `location` down to `GlobalNav` to trigger render on url change
- const { footer = <GlobalFooterContainer /> } = props;
+ const { footer = <GlobalFooter /> } = props;
return (
<SuggestionsProvider>
<A11yProvider>
<StartupModal>
<A11ySkipLinks />
-
<div className="global-container">
<div className="page-wrapper" id="container">
<div className="page-container">
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 e52b89d107a..da00469d33c 100644
--- a/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx
+++ b/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx
@@ -24,26 +24,21 @@ import { Alert } from '../../components/ui/Alert';
import { getEdition } from '../../helpers/editions';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { EditionKey } from '../../types/editions';
+import { AppState } from '../../types/types';
+import withAppStateContext from './app-state/withAppStateContext';
import GlobalFooterBranding from './GlobalFooterBranding';
-interface Props {
+export interface GlobalFooterProps {
hideLoggedInInfo?: boolean;
- productionDatabase: boolean;
- sonarqubeEdition?: EditionKey;
- sonarqubeVersion?: string;
+ appState?: AppState;
}
-export default function GlobalFooter({
- hideLoggedInInfo,
- productionDatabase,
- sonarqubeEdition,
- sonarqubeVersion
-}: Props) {
- const currentEdition = sonarqubeEdition && getEdition(sonarqubeEdition);
+export function GlobalFooter({ hideLoggedInInfo, appState }: GlobalFooterProps) {
+ const currentEdition = appState?.edition && getEdition(appState.edition as EditionKey);
return (
<div className="page-footer page-container" id="footer">
- {productionDatabase === false && (
+ {appState?.productionDatabase === false && (
<Alert display="inline" id="evaluation_warning" variant="warning">
<p className="big">{translate('footer.production_database_warning')}</p>
<p>
@@ -58,9 +53,9 @@ export default function GlobalFooter({
{!hideLoggedInInfo && currentEdition && (
<li className="page-footer-menu-item">{currentEdition.name}</li>
)}
- {!hideLoggedInInfo && sonarqubeVersion && (
+ {!hideLoggedInInfo && appState?.version && (
<li className="page-footer-menu-item">
- {translateWithParameters('footer.version_x', sonarqubeVersion)}
+ {translateWithParameters('footer.version_x', appState.version)}
</li>
)}
<li className="page-footer-menu-item">
@@ -99,3 +94,5 @@ export default function GlobalFooter({
</div>
);
}
+
+export default withAppStateContext(GlobalFooter);
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 8a3e610f4d2..02c47e467fb 100644
--- a/server/sonar-web/src/main/js/app/components/PageTracker.tsx
+++ b/server/sonar-web/src/main/js/app/components/PageTracker.tsx
@@ -25,12 +25,14 @@ import { gtm } from '../../helpers/analytics';
import { installScript } from '../../helpers/extensions';
import { getWebAnalyticsPageHandlerFromCache } from '../../helpers/extensionsHandler';
import { getInstance } from '../../helpers/system';
-import { getAppState, getGlobalSettingValue, Store } from '../../store/rootReducer';
+import { getGlobalSettingValue, Store } from '../../store/rootReducer';
+import { AppState } from '../../types/types';
+import withAppStateContext from './app-state/withAppStateContext';
interface Props {
location: Location;
trackingIdGTM?: string;
- webAnalytics?: string;
+ appState: AppState;
}
interface State {
@@ -41,10 +43,10 @@ export class PageTracker extends React.Component<Props, State> {
state: State = {};
componentDidMount() {
- const { trackingIdGTM, webAnalytics } = this.props;
+ const { trackingIdGTM, appState } = this.props;
- if (webAnalytics && !getWebAnalyticsPageHandlerFromCache()) {
- installScript(webAnalytics, 'head');
+ if (appState.webAnalyticsJsPath && !getWebAnalyticsPageHandlerFromCache()) {
+ installScript(appState.webAnalyticsJsPath, 'head');
}
if (trackingIdGTM) {
@@ -69,13 +71,15 @@ export class PageTracker extends React.Component<Props, State> {
};
render() {
- const { trackingIdGTM, webAnalytics } = this.props;
+ const { trackingIdGTM, appState } = this.props;
return (
<Helmet
defaultTitle={getInstance()}
defer={false}
- onChangeClientState={trackingIdGTM || webAnalytics ? this.trackPage : undefined}>
+ onChangeClientState={
+ trackingIdGTM || appState.webAnalyticsJsPath ? this.trackPage : undefined
+ }>
{this.props.children}
</Helmet>
);
@@ -85,9 +89,8 @@ export class PageTracker extends React.Component<Props, State> {
const mapStateToProps = (state: Store) => {
const trackingIdGTM = getGlobalSettingValue(state, 'sonar.analytics.gtm.trackingId');
return {
- trackingIdGTM: trackingIdGTM && trackingIdGTM.value,
- webAnalytics: getAppState(state).webAnalyticsJsPath
+ trackingIdGTM: trackingIdGTM && trackingIdGTM.value
};
};
-export default withRouter(connect(mapStateToProps)(PageTracker));
+export default withRouter(connect(mapStateToProps)(withAppStateContext(PageTracker)));
diff --git a/server/sonar-web/src/main/js/app/components/SimpleContainer.tsx b/server/sonar-web/src/main/js/app/components/SimpleContainer.tsx
index bcad1eb490f..0a9e52d54dd 100644
--- a/server/sonar-web/src/main/js/app/components/SimpleContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/SimpleContainer.tsx
@@ -20,7 +20,7 @@
import * as React from 'react';
import NavBar from '../../components/ui/NavBar';
import { rawSizes } from '../theme';
-import GlobalFooterContainer from './GlobalFooterContainer';
+import GlobalFooter from './GlobalFooter';
interface Props {
children?: React.ReactNode;
@@ -33,7 +33,7 @@ export default function SimpleContainer({ children }: Props) {
<NavBar className="navbar-global" height={rawSizes.globalNavHeightRaw} />
{children}
</div>
- <GlobalFooterContainer />
+ <GlobalFooter />
</div>
);
}
diff --git a/server/sonar-web/src/main/js/app/components/SimpleSessionsContainer.tsx b/server/sonar-web/src/main/js/app/components/SimpleSessionsContainer.tsx
index 126dd543676..c9564386eaa 100644
--- a/server/sonar-web/src/main/js/app/components/SimpleSessionsContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/SimpleSessionsContainer.tsx
@@ -19,7 +19,7 @@
*/
import * as React from 'react';
import { lazyLoadComponent } from '../../components/lazyLoadComponent';
-import GlobalFooterContainer from './GlobalFooterContainer';
+import GlobalFooter from './GlobalFooter';
const PageTracker = lazyLoadComponent(() => import('./PageTracker'));
@@ -36,7 +36,7 @@ export default function SimpleSessionsContainer({ children }: Props) {
<div className="page-wrapper" id="container">
{children}
</div>
- <GlobalFooterContainer hideLoggedInInfo={true} />
+ <GlobalFooter hideLoggedInInfo={true} />
</div>
</>
);
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 4b6606246a2..ff8216a49d1 100644
--- a/server/sonar-web/src/main/js/app/components/StartupModal.tsx
+++ b/server/sonar-web/src/main/js/app/components/StartupModal.tsx
@@ -27,9 +27,10 @@ import { parseDate, toShortNotSoISOString } from '../../helpers/dates';
import { hasMessage } from '../../helpers/l10n';
import { get, save } from '../../helpers/storage';
import { isLoggedIn } from '../../helpers/users';
-import { getAppState, getCurrentUser, Store } from '../../store/rootReducer';
+import { getCurrentUser, Store } from '../../store/rootReducer';
import { EditionKey } from '../../types/editions';
-import { CurrentUser } from '../../types/types';
+import { AppState, CurrentUser } from '../../types/types';
+import withAppStateContext from './app-state/withAppStateContext';
const LicensePromptModal = lazyLoadComponent(
() => import('../../apps/marketplace/components/LicensePromptModal'),
@@ -37,21 +38,15 @@ const LicensePromptModal = lazyLoadComponent(
);
interface StateProps {
- canAdmin?: boolean;
- currentEdition?: EditionKey;
currentUser: CurrentUser;
}
-interface OwnProps {
+type Props = {
children?: React.ReactNode;
-}
-
-interface WithRouterProps {
location: Pick<Location, 'pathname'>;
router: Pick<Router, 'push'>;
-}
-
-type Props = StateProps & OwnProps & WithRouterProps;
+ appState: AppState;
+};
interface State {
open?: boolean;
@@ -59,7 +54,7 @@ interface State {
const LICENSE_PROMPT = 'sonarqube.license.prompt';
-export class StartupModal extends React.PureComponent<Props, State> {
+export class StartupModal extends React.PureComponent<Props & StateProps, State> {
state: State = {};
componentDidMount() {
@@ -71,11 +66,11 @@ export class StartupModal extends React.PureComponent<Props, State> {
};
tryAutoOpenLicense = () => {
- const { canAdmin, currentEdition, currentUser } = this.props;
+ const { appState, currentUser } = this.props;
const hasLicenseManager = hasMessage('license.prompt.title');
- const hasLicensedEdition = currentEdition && currentEdition !== EditionKey.community;
+ const hasLicensedEdition = appState.edition && appState.edition !== EditionKey.community;
- if (canAdmin && hasLicensedEdition && isLoggedIn(currentUser) && hasLicenseManager) {
+ if (appState.canAdmin && hasLicensedEdition && isLoggedIn(currentUser) && hasLicenseManager) {
const lastPrompt = get(LICENSE_PROMPT, currentUser.login);
if (!lastPrompt || differenceInDays(new Date(), parseDate(lastPrompt)) >= 1) {
@@ -103,9 +98,7 @@ export class StartupModal extends React.PureComponent<Props, State> {
}
const mapStateToProps = (state: Store): StateProps => ({
- canAdmin: getAppState(state).canAdmin,
- currentEdition: getAppState(state).edition as EditionKey, // TODO: Fix once AppState is no longer ambiant.
currentUser: getCurrentUser(state)
});
-export default connect(mapStateToProps)(withRouter(StartupModal));
+export default connect(mapStateToProps)(withRouter(withAppStateContext(StartupModal)));
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/AdminContainer-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/AdminContainer-test.tsx
index e39878b0246..c0ca09c8ee1 100644
--- a/server/sonar-web/src/main/js/app/components/__tests__/AdminContainer-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/__tests__/AdminContainer-test.tsx
@@ -19,24 +19,23 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { mockLocation } from '../../../helpers/testMocks';
-import { AdminContainer } from '../AdminContainer';
+import { mockAppState, mockLocation } from '../../../helpers/testMocks';
+import { AdminContainer, AdminContainerProps } from '../AdminContainer';
it('should render correctly', () => {
const wrapper = shallowRender();
expect(wrapper).toMatchSnapshot();
});
-function shallowRender(props: Partial<AdminContainer['props']> = {}) {
+function shallowRender(props: Partial<AdminContainerProps> = {}) {
return shallow(
<AdminContainer
- appState={{
- adminPages: [{ key: 'foo', name: 'Foo' }],
+ appState={mockAppState({
canAdmin: true
- }}
+ })}
location={mockLocation()}
- setAdminPages={jest.fn()}
- {...props}
- />
+ {...props}>
+ <div />
+ </AdminContainer>
);
}
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx
index 6f8972a7ec3..7758e327c69 100644
--- a/server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx
@@ -19,8 +19,9 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
+import { mockAppState } from '../../../helpers/testMocks';
import { EditionKey } from '../../../types/editions';
-import GlobalFooter from '../GlobalFooter';
+import { GlobalFooter, GlobalFooterProps } from '../GlobalFooter';
jest.mock('../../../helpers/system', () => ({ isSonarCloud: jest.fn() }));
@@ -30,22 +31,34 @@ it('should render the only logged in information', () => {
it('should not render the only logged in information', () => {
expect(
- getWrapper({ hideLoggedInInfo: true, sonarqubeVersion: '6.4-SNAPSHOT' })
+ getWrapper({
+ hideLoggedInInfo: true,
+ appState: mockAppState({ version: '6.4-SNAPSHOT' })
+ })
).toMatchSnapshot();
});
it('should show the db warning message', () => {
- expect(getWrapper({ productionDatabase: false }).find('Alert')).toMatchSnapshot();
+ expect(
+ getWrapper({
+ appState: mockAppState({ productionDatabase: false, edition: EditionKey.community })
+ }).find('Alert')
+ ).toMatchSnapshot();
});
it('should display the sq version', () => {
expect(
- getWrapper({ sonarqubeEdition: EditionKey.enterprise, sonarqubeVersion: '6.4-SNAPSHOT' })
+ getWrapper({
+ appState: mockAppState({ edition: EditionKey.enterprise, version: '6.4-SNAPSHOT' })
+ })
).toMatchSnapshot();
});
-function getWrapper(props = {}) {
+function getWrapper(props?: GlobalFooterProps) {
return shallow(
- <GlobalFooter productionDatabase={true} sonarqubeEdition={EditionKey.community} {...props} />
+ <GlobalFooter
+ appState={mockAppState({ productionDatabase: true, edition: EditionKey.community })}
+ {...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 d8e1c916629..5ea91b924db 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
@@ -22,7 +22,7 @@ import * as React from 'react';
import { gtm } from '../../../helpers/analytics';
import { installScript } from '../../../helpers/extensions';
import { getWebAnalyticsPageHandlerFromCache } from '../../../helpers/extensionsHandler';
-import { mockLocation } from '../../../helpers/testMocks';
+import { mockAppState, mockLocation } from '../../../helpers/testMocks';
import { PageTracker } from '../PageTracker';
jest.mock('../../../helpers/extensions', () => ({
@@ -51,12 +51,12 @@ it('should not trigger if no analytics system is given', () => {
it('should work for WebAnalytics plugin', () => {
const pageChange = jest.fn();
- const webAnalytics = '/static/pluginKey/web_analytics.js';
- const wrapper = shallowRender({ webAnalytics });
+ const webAnalyticsJsPath = '/static/pluginKey/web_analytics.js';
+ const wrapper = shallowRender({ appState: mockAppState({ webAnalyticsJsPath }) });
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('Helmet').prop('onChangeClientState')).toBe(wrapper.instance().trackPage);
- expect(installScript).toBeCalledWith(webAnalytics, 'head');
+ expect(installScript).toBeCalledWith(webAnalyticsJsPath, 'head');
(getWebAnalyticsPageHandlerFromCache as jest.Mock).mockReturnValueOnce(pageChange);
wrapper.instance().trackPage();
@@ -81,5 +81,7 @@ it('should work for Google Tag Manager', () => {
});
function shallowRender(props: Partial<PageTracker['props']> = {}) {
- return shallow<PageTracker>(<PageTracker location={mockLocation()} {...props} />);
+ return shallow<PageTracker>(
+ <PageTracker appState={mockAppState()} location={mockLocation()} {...props} />
+ );
}
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx
index 1d1035ebfc4..4a07e47012b 100644
--- a/server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx
@@ -24,6 +24,7 @@ import { showLicense } from '../../../api/marketplace';
import { toShortNotSoISOString } from '../../../helpers/dates';
import { hasMessage } from '../../../helpers/l10n';
import { get, save } from '../../../helpers/storage';
+import { mockAppState } from '../../../helpers/testMocks';
import { waitAndUpdate } from '../../../helpers/testUtils';
import { EditionKey } from '../../../types/editions';
import { LoggedInUser } from '../../../types/types';
@@ -67,12 +68,12 @@ beforeEach(() => {
});
it('should render only the children', async () => {
- const wrapper = getWrapper({ currentEdition: EditionKey.community });
+ const wrapper = getWrapper({ appState: mockAppState({ edition: EditionKey.community }) });
await shouldNotHaveModals(wrapper);
expect(showLicense).toHaveBeenCalledTimes(0);
expect(wrapper.find('div').exists()).toBe(true);
- await shouldNotHaveModals(getWrapper({ canAdmin: false }));
+ await shouldNotHaveModals(getWrapper({ appState: mockAppState({ canAdmin: false }) }));
(hasMessage as jest.Mock<any>).mockReturnValueOnce(false);
await shouldNotHaveModals(getWrapper());
@@ -86,7 +87,7 @@ it('should render only the children', async () => {
await shouldNotHaveModals(
getWrapper({
- canAdmin: false,
+ appState: mockAppState({ canAdmin: false }),
currentUser: { ...LOGGED_IN_USER },
location: { pathname: '/documentation/' }
})
@@ -94,7 +95,7 @@ it('should render only the children', async () => {
await shouldNotHaveModals(
getWrapper({
- canAdmin: false,
+ appState: mockAppState({ canAdmin: false }),
currentUser: { ...LOGGED_IN_USER },
location: { pathname: '/create-organization' }
})
@@ -126,8 +127,7 @@ async function shouldDisplayLicense(wrapper: ShallowWrapper) {
function getWrapper(props: Partial<StartupModal['props']> = {}) {
return shallow<StartupModal>(
<StartupModal
- canAdmin={true}
- currentEdition={EditionKey.enterprise}
+ appState={mockAppState({ edition: EditionKey.enterprise, canAdmin: true })}
currentUser={LOGGED_IN_USER}
location={{ pathname: 'foo/bar' }}
router={{ push: jest.fn() }}
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/AdminContainer-test.tsx.snap b/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/AdminContainer-test.tsx.snap
index db431ca16f0..a12b91340b1 100644
--- a/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/AdminContainer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/AdminContainer-test.tsx.snap
@@ -10,14 +10,7 @@ exports[`should render correctly 1`] = `
titleTemplate="%s - layout.settings"
/>
<SettingsNav
- extensions={
- Array [
- Object {
- "key": "foo",
- "name": "Foo",
- },
- ]
- }
+ extensions={Array []}
fetchPendingPlugins={[Function]}
fetchSystemStatus={[Function]}
location={
@@ -53,6 +46,10 @@ exports[`should render correctly 1`] = `
"systemStatus": "UP",
}
}
- />
+ >
+ <div
+ adminPages={Array []}
+ />
+ </ContextProvider>
</div>
`;
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/GlobalContainer-test.tsx.snap b/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/GlobalContainer-test.tsx.snap
index 1e1d574f0a0..58f58f0bfd2 100644
--- a/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/GlobalContainer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/GlobalContainer-test.tsx.snap
@@ -3,7 +3,7 @@
exports[`should render correctly 1`] = `
<SuggestionsProvider>
<A11yProvider>
- <Connect(withRouter(StartupModal))>
+ <Connect(withRouter(withAppStateContext(StartupModal)))>
<A11ySkipLinks />
<div
className="global-container"
@@ -16,7 +16,7 @@ exports[`should render correctly 1`] = `
className="page-container"
>
<Workspace>
- <Connect(withAppState(IndexationContextProvider))>
+ <withAppStateContext(IndexationContextProvider)>
<LanguageContextProvider>
<MetricContextProvider>
<Connect(GlobalNav)
@@ -34,20 +34,20 @@ exports[`should render correctly 1`] = `
/>
<Connect(GlobalMessages) />
<Connect(withCurrentUser(withIndexationContext(IndexationNotification))) />
- <Connect(withCurrentUser(Connect(withAppState(UpdateNotification))))
+ <Connect(withCurrentUser(withAppStateContext(UpdateNotification)))
dismissable={true}
/>
<ChildComponent />
</MetricContextProvider>
</LanguageContextProvider>
- </Connect(withAppState(IndexationContextProvider))>
+ </withAppStateContext(IndexationContextProvider)>
</Workspace>
</div>
<Connect(Connect(withCurrentUser(PromotionNotification))) />
</div>
- <Connect(GlobalFooter) />
+ <withAppStateContext(GlobalFooter) />
</div>
- </Connect(withRouter(StartupModal))>
+ </Connect(withRouter(withAppStateContext(StartupModal)))>
</A11yProvider>
</SuggestionsProvider>
`;
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/GlobalFooter-test.tsx.snap b/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/GlobalFooter-test.tsx.snap
index 61e974fab75..d5450c04210 100644
--- a/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/GlobalFooter-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/GlobalFooter-test.tsx.snap
@@ -152,6 +152,11 @@ exports[`should render the only logged in information 1`] = `
<li
className="page-footer-menu-item"
>
+ footer.version_x.1.0
+ </li>
+ <li
+ className="page-footer-menu-item"
+ >
<a
href="https://www.gnu.org/licenses/lgpl-3.0.txt"
rel="noopener noreferrer"
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/AppContainer.ts b/server/sonar-web/src/main/js/app/components/app-state/AppStateContext.tsx
index 17120732063..1e2dcf47d4f 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/components/AppContainer.ts
+++ b/server/sonar-web/src/main/js/app/components/app-state/AppStateContext.tsx
@@ -17,13 +17,17 @@
* 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 { getAppState, Store } from '../../../store/rootReducer';
-import App from './App';
-const mapStateToProps = (state: Store) => ({
- branchesEnabled: getAppState(state).branchesEnabled,
- canAdmin: getAppState(state).canAdmin
-});
+import * as React from 'react';
+import { AppState } from '../../../types/types';
-export default connect(mapStateToProps)(App);
+const defaultAppState = {
+ authenticationError: false,
+ authorizationError: false,
+ edition: undefined,
+ productionDatabase: true,
+ qualifiers: [],
+ settings: {},
+ version: ''
+};
+export const AppStateContext = React.createContext<AppState>(defaultAppState);
diff --git a/server/sonar-web/src/main/js/app/components/GlobalFooterContainer.tsx b/server/sonar-web/src/main/js/app/components/app-state/AppStateContextProvider.tsx
index e0bf748c7c4..0b7336d10f1 100644
--- a/server/sonar-web/src/main/js/app/components/GlobalFooterContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/app-state/AppStateContextProvider.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable no-console */
/*
* SonarQube
* Copyright (C) 2009-2022 SonarSource SA
@@ -17,21 +18,18 @@
* 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 { getAppState, Store } from '../../store/rootReducer';
-import { EditionKey } from '../../types/editions';
-import GlobalFooter from './GlobalFooter';
-interface StateProps {
- productionDatabase: boolean;
- sonarqubeEdition?: EditionKey;
- sonarqubeVersion?: string;
-}
+import * as React from 'react';
+import { AppState } from '../../../types/types';
+import { AppStateContext } from './AppStateContext';
-const mapStateToProps = (state: Store): StateProps => ({
- productionDatabase: getAppState(state).productionDatabase,
- sonarqubeEdition: getAppState(state).edition as EditionKey, // TODO: Fix once AppState is no longer ambiant.
- sonarqubeVersion: getAppState(state).version
-});
+export interface AppStateContextProviderProps {
+ appState: AppState;
+}
-export default connect(mapStateToProps)(GlobalFooter);
+export default function AppStateContextProvider({
+ appState,
+ children
+}: React.PropsWithChildren<AppStateContextProviderProps>) {
+ return <AppStateContext.Provider value={appState}>{children}</AppStateContext.Provider>;
+}
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
new file mode 100644
index 00000000000..d6323fb0f81
--- /dev/null
+++ b/server/sonar-web/src/main/js/app/components/app-state/__tests__/withAppStateContext-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 { AppState } from '../../../../types/types';
+import withAppStateContext from '../withAppStateContext';
+
+const appState = mockAppState();
+
+jest.mock('../AppStateContext', () => {
+ return {
+ AppStateContext: {
+ Consumer: ({ children }: { children: (props: {}) => React.ReactNode }) => {
+ return children(appState);
+ }
+ }
+ };
+});
+
+class Wrapped extends React.Component<{ appState: AppState }> {
+ render() {
+ return <div />;
+ }
+}
+
+const UnderTest = withAppStateContext(Wrapped);
+
+it('should inject appState', () => {
+ const wrapper = shallow(<UnderTest />);
+ expect(wrapper.dive().type()).toBe(Wrapped);
+ expect(wrapper.dive<Wrapped>().props().appState).toEqual(appState);
+});
diff --git a/server/sonar-web/src/main/js/components/hoc/withAppState.tsx b/server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx
index 6e343d26989..64107510cf1 100644
--- a/server/sonar-web/src/main/js/components/hoc/withAppState.tsx
+++ b/server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx
@@ -17,26 +17,30 @@
* 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 { connect } from 'react-redux';
-import { getAppState, Store } from '../../store/rootReducer';
-import { AppState } from '../../types/types';
-import { getWrappedDisplayName } from './utils';
+import { getWrappedDisplayName } from '../../../components/hoc/utils';
+import { AppState } from '../../../types/types';
+import { AppStateContext } from './AppStateContext';
+
+export interface WithAppStateContextProps {
+ appState: AppState;
+}
-export function withAppState<P>(
- WrappedComponent: React.ComponentType<P & { appState: Partial<AppState> }>
+export default function withAppStateContext<P>(
+ WrappedComponent: React.ComponentType<P & WithAppStateContextProps>
) {
- class Wrapper extends React.Component<P & { appState: AppState }> {
- static displayName = getWrappedDisplayName(WrappedComponent, 'withAppState');
+ return class WithAppStateContext extends React.PureComponent<
+ Omit<P, keyof WithAppStateContextProps>
+ > {
+ static displayName = getWrappedDisplayName(WrappedComponent, 'withAppStateContext');
render() {
- return <WrappedComponent {...this.props} />;
+ return (
+ <AppStateContext.Consumer>
+ {appState => <WrappedComponent appState={appState} {...(this.props as P)} />}
+ </AppStateContext.Consumer>
+ );
}
- }
-
- function mapStateToProps(state: Store) {
- return { appState: getAppState(state) };
- }
-
- return connect(mapStateToProps)(Wrapper);
+ };
}
diff --git a/server/sonar-web/src/main/js/app/components/extensions/GlobalAdminPageExtension.tsx b/server/sonar-web/src/main/js/app/components/extensions/GlobalAdminPageExtension.tsx
index 986a581768e..28974085d08 100644
--- a/server/sonar-web/src/main/js/app/components/extensions/GlobalAdminPageExtension.tsx
+++ b/server/sonar-web/src/main/js/app/components/extensions/GlobalAdminPageExtension.tsx
@@ -18,8 +18,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { connect } from 'react-redux';
-import { getAppState, Store } from '../../../store/rootReducer';
import { Extension as TypeExtension } from '../../../types/types';
import NotFound from '../NotFound';
import Extension from './Extension';
@@ -29,14 +27,11 @@ interface Props {
params: { extensionKey: string; pluginKey: string };
}
-function GlobalAdminPageExtension(props: Props) {
- const { extensionKey, pluginKey } = props.params;
- const extension = (props.adminPages || []).find(p => p.key === `${pluginKey}/${extensionKey}`);
+export default function GlobalAdminPageExtension(props: Props) {
+ const {
+ params: { extensionKey, pluginKey },
+ adminPages
+ } = props;
+ const extension = (adminPages || []).find(p => p.key === `${pluginKey}/${extensionKey}`);
return extension ? <Extension extension={extension} /> : <NotFound withContainer={false} />;
}
-
-const mapStateToProps = (state: Store) => ({
- adminPages: getAppState(state).adminPages
-});
-
-export default connect(mapStateToProps)(GlobalAdminPageExtension);
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 1f1fd89fd30..d3b0559d35c 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,25 +18,23 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { connect } from 'react-redux';
-import { getAppState, Store } from '../../../store/rootReducer';
-import { Extension as TypeExtension } from '../../../types/types';
+import { AppState } from '../../../types/types';
+import withAppStateContext from '../app-state/withAppStateContext';
import NotFound from '../NotFound';
import Extension from './Extension';
interface Props {
- globalPages: TypeExtension[] | undefined;
+ appState: AppState;
params: { extensionKey: string; pluginKey: string };
}
function GlobalPageExtension(props: Props) {
- const { extensionKey, pluginKey } = props.params;
- const extension = (props.globalPages || []).find(p => p.key === `${pluginKey}/${extensionKey}`);
+ const {
+ params: { extensionKey, pluginKey },
+ appState: { globalPages }
+ } = props;
+ const extension = (globalPages || []).find(p => p.key === `${pluginKey}/${extensionKey}`);
return extension ? <Extension extension={extension} /> : <NotFound withContainer={false} />;
}
-const mapStateToProps = (state: Store) => ({
- globalPages: getAppState(state).globalPages
-});
-
-export default connect(mapStateToProps)(GlobalPageExtension);
+export default withAppStateContext(GlobalPageExtension);
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 84c997f6b54..091fc03024d 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,18 +19,18 @@
*/
/* eslint-disable react/no-unused-state */
import * as React from 'react';
-import { withAppState } from '../../../components/hoc/withAppState';
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';
-interface Props {
- appState: Pick<AppState, 'needIssueSync'>;
+export interface IndexationContextProviderProps {
+ appState: AppState;
}
export class IndexationContextProvider extends React.PureComponent<
- React.PropsWithChildren<Props>,
+ React.PropsWithChildren<IndexationContextProviderProps>,
IndexationContextInterface
> {
mounted = false;
@@ -66,4 +66,4 @@ export class IndexationContextProvider extends React.PureComponent<
}
}
-export default withAppState(IndexationContextProvider);
+export default withAppStateContext(IndexationContextProvider);
diff --git a/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationContextProvider-test.tsx b/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationContextProvider-test.tsx
index eed129e5a53..c6c383cf158 100644
--- a/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationContextProvider-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/indexation/__tests__/IndexationContextProvider-test.tsx
@@ -19,9 +19,13 @@
*/
import { mount } from 'enzyme';
import * as React from 'react';
+import { mockAppState } from '../../../../helpers/testMocks';
import { IndexationStatus } from '../../../../types/indexation';
import { IndexationContext } from '../IndexationContext';
-import { IndexationContextProvider } from '../IndexationContextProvider';
+import {
+ IndexationContextProvider,
+ IndexationContextProviderProps
+} from '../IndexationContextProvider';
import IndexationNotificationHelper from '../IndexationNotificationHelper';
beforeEach(() => jest.clearAllMocks());
@@ -36,7 +40,8 @@ it('should render correctly and start polling if issue sync is needed', () => {
});
it('should not start polling if no issue sync is needed', () => {
- const wrapper = mountRender({ appState: { needIssueSync: false } });
+ const appState = mockAppState({ needIssueSync: false });
+ const wrapper = mountRender({ appState });
expect(IndexationNotificationHelper.startPolling).not.toHaveBeenCalled();
@@ -72,9 +77,9 @@ it('should stop polling when component is destroyed', () => {
expect(IndexationNotificationHelper.stopPolling).toHaveBeenCalled();
});
-function mountRender(props?: IndexationContextProvider['props']) {
+function mountRender(props?: IndexationContextProviderProps) {
return mount<IndexationContextProvider>(
- <IndexationContextProvider appState={{ needIssueSync: true }} {...props}>
+ <IndexationContextProvider appState={mockAppState({ needIssueSync: true, ...props?.appState })}>
<TestComponent />
</IndexationContextProvider>
);
diff --git a/server/sonar-web/src/main/js/app/components/indexation/__tests__/__snapshots__/IndexationContextProvider-test.tsx.snap b/server/sonar-web/src/main/js/app/components/indexation/__tests__/__snapshots__/IndexationContextProvider-test.tsx.snap
index a7c918f76fe..a966efb2160 100644
--- a/server/sonar-web/src/main/js/app/components/indexation/__tests__/__snapshots__/IndexationContextProvider-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/indexation/__tests__/__snapshots__/IndexationContextProvider-test.tsx.snap
@@ -4,7 +4,14 @@ exports[`should render correctly and start polling if issue sync is needed 1`] =
<IndexationContextProvider
appState={
Object {
+ "edition": "community",
"needIssueSync": true,
+ "productionDatabase": true,
+ "qualifiers": Array [
+ "TRK",
+ ],
+ "settings": Object {},
+ "version": "1.0",
}
}
>
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 5466afe3f32..37770276d4f 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
@@ -20,15 +20,15 @@
import * as React from 'react';
import { Link } from 'react-router';
import { isValidLicense } from '../../../../api/marketplace';
-import { withAppState } from '../../../../components/hoc/withAppState';
import { Alert } from '../../../../components/ui/Alert';
import { translate, translateWithParameters } from '../../../../helpers/l10n';
import { ComponentQualifier } from '../../../../types/component';
import { Task } from '../../../../types/tasks';
import { AppState } from '../../../../types/types';
+import withAppStateContext from '../../app-state/withAppStateContext';
interface Props {
- appState: Pick<AppState, 'canAdmin'>;
+ appState: AppState;
currentTask?: Task;
}
@@ -67,7 +67,7 @@ export class ComponentNavLicenseNotif extends React.PureComponent<Props, State>
};
render() {
- const { currentTask } = this.props;
+ const { currentTask, appState } = this.props;
const { isValidLicense, loading } = this.state;
if (loading || !currentTask || !currentTask.errorType) {
@@ -88,7 +88,7 @@ export class ComponentNavLicenseNotif extends React.PureComponent<Props, State>
return (
<Alert display="banner" variant="error">
<span className="little-spacer-right">{currentTask.errorMessage}</span>
- {this.props.appState.canAdmin ? (
+ {appState.canAdmin ? (
<Link to="/admin/extension/license/app">
{translate('license.component_navigation.button', currentTask.errorType)}.
</Link>
@@ -100,4 +100,4 @@ export class ComponentNavLicenseNotif extends React.PureComponent<Props, State>
}
}
-export default withAppState(ComponentNavLicenseNotif);
+export default withAppStateContext(ComponentNavLicenseNotif);
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 7d08885238a..7f7d3c32843 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
@@ -24,7 +24,6 @@ import * as React from 'react';
import { Link, LinkProps } from 'react-router';
import Dropdown from '../../../../components/controls/Dropdown';
import Tooltip from '../../../../components/controls/Tooltip';
-import { withAppState } from '../../../../components/hoc/withAppState';
import BulletListIcon from '../../../../components/icons/BulletListIcon';
import DropdownIcon from '../../../../components/icons/DropdownIcon';
import NavBarTabs from '../../../../components/ui/NavBarTabs';
@@ -34,6 +33,7 @@ import { getPortfolioUrl, getProjectQueryUrl } from '../../../../helpers/urls';
import { BranchLike, BranchParameters } from '../../../../types/branch-like';
import { ComponentQualifier, isPortfolioLike } from '../../../../types/component';
import { AppState, Component, Extension } from '../../../../types/types';
+import withAppStateContext from '../../app-state/withAppStateContext';
import './Menu.css';
const SETTINGS_URLS = [
@@ -53,7 +53,7 @@ const SETTINGS_URLS = [
];
interface Props {
- appState: Pick<AppState, 'branchesEnabled'>;
+ appState: AppState;
branchLike: BranchLike | undefined;
branchLikes: BranchLike[] | undefined;
component: Component;
@@ -620,4 +620,4 @@ export class Menu extends React.PureComponent<Props> {
}
}
-export default withAppState(Menu);
+export default withAppStateContext(Menu);
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavLicenseNotif-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavLicenseNotif-test.tsx
index aed4d489ece..354a592c179 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavLicenseNotif-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavLicenseNotif-test.tsx
@@ -21,6 +21,7 @@ import { shallow } from 'enzyme';
import * as React from 'react';
import { isValidLicense } from '../../../../../api/marketplace';
import { mockTask } from '../../../../../helpers/mocks/tasks';
+import { mockAppState } from '../../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../../helpers/testUtils';
import { TaskStatuses } from '../../../../../types/tasks';
import { ComponentNavLicenseNotif } from '../ComponentNavLicenseNotif';
@@ -50,7 +51,7 @@ it('renders background task license info correctly', async () => {
expect(wrapper).toMatchSnapshot();
wrapper = getWrapper({
- appState: { canAdmin: false },
+ appState: mockAppState({ canAdmin: false }),
currentTask: mockTask({
status: TaskStatuses.Failed,
errorType: 'LICENSING',
@@ -90,7 +91,7 @@ it('renders correctly for LICENSING_LOC error', async () => {
function getWrapper(props: Partial<ComponentNavLicenseNotif['props']> = {}) {
return shallow(
<ComponentNavLicenseNotif
- appState={{ canAdmin: true }}
+ appState={mockAppState({ canAdmin: true })}
currentTask={mockTask({ errorMessage: 'Foo', errorType: 'LICENSING' })}
{...props}
/>
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/Menu-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/Menu-test.tsx
index 17a7293eb30..2d65f7eefa0 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/Menu-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/Menu-test.tsx
@@ -25,6 +25,7 @@ import {
mockPullRequest
} from '../../../../../helpers/mocks/branch-like';
import { mockComponent } from '../../../../../helpers/mocks/component';
+import { mockAppState } from '../../../../../helpers/testMocks';
import { ComponentQualifier } from '../../../../../types/component';
import { Menu } from '../Menu';
@@ -165,7 +166,7 @@ it('should disable links if application has inaccessible projects', () => {
function shallowRender(props: Partial<Menu['props']>) {
return shallow<Menu>(
<Menu
- appState={{ branchesEnabled: true }}
+ appState={mockAppState({ branchesEnabled: true })}
branchLike={mainBranch}
branchLikes={[mainBranch]}
component={baseComponent}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap
index 4c9f5933de5..0a372d2d7b8 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap
@@ -73,7 +73,7 @@ exports[`renders correctly: default 1`] = `
warnings={Array []}
/>
</div>
- <Connect(withAppState(Menu))
+ <withAppStateContext(Menu)
branchLikes={Array []}
component={
Object {
@@ -267,7 +267,7 @@ exports[`renders correctly: has failed notification 1`] = `
warnings={Array []}
/>
</div>
- <Connect(withAppState(Menu))
+ <withAppStateContext(Menu)
branchLikes={Array []}
component={
Object {
@@ -447,7 +447,7 @@ exports[`renders correctly: has failed project binding 1`] = `
warnings={Array []}
/>
</div>
- <Connect(withAppState(Menu))
+ <withAppStateContext(Menu)
branchLikes={Array []}
component={
Object {
@@ -629,7 +629,7 @@ exports[`renders correctly: has in progress notification 1`] = `
warnings={Array []}
/>
</div>
- <Connect(withAppState(Menu))
+ <withAppStateContext(Menu)
branchLikes={Array []}
component={
Object {
@@ -811,7 +811,7 @@ exports[`renders correctly: has pending notification 1`] = `
warnings={Array []}
/>
</div>
- <Connect(withAppState(Menu))
+ <withAppStateContext(Menu)
branchLikes={Array []}
component={
Object {
@@ -966,7 +966,7 @@ exports[`renders correctly: has warnings 1`] = `
}
/>
</div>
- <Connect(withAppState(Menu))
+ <withAppStateContext(Menu)
branchLikes={Array []}
component={
Object {
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBgTaskNotif-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBgTaskNotif-test.tsx.snap
index cfed20bb431..03f114eee5b 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBgTaskNotif-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBgTaskNotif-test.tsx.snap
@@ -22,7 +22,7 @@ exports[`renders correctly: default 1`] = `
`;
exports[`renders correctly: license issue 1`] = `
-<Connect(withAppState(ComponentNavLicenseNotif))
+<withAppStateContext(ComponentNavLicenseNotif)
currentTask={
Object {
"analysisId": "x123",
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Header-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Header-test.tsx.snap
index dbb95fd354c..c3f9e27795d 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Header-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Header-test.tsx.snap
@@ -49,7 +49,7 @@ exports[`should render correctly 1`] = `
favorite={false}
qualifier="TRK"
/>
- <Connect(withAppState(Component))
+ <withAppStateContext(Component)
branchLikes={
Array [
Object {
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 0a85e8a9e14..2e619a4e049 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
@@ -20,16 +20,16 @@
import classNames from 'classnames';
import * as React from 'react';
import Toggler from '../../../../../components/controls/Toggler';
-import { withAppState } from '../../../../../components/hoc/withAppState';
import { ProjectAlmBindingResponse } from '../../../../../types/alm-settings';
import { BranchLike } from '../../../../../types/branch-like';
import { AppState, Component } from '../../../../../types/types';
+import withAppStateContext from '../../../app-state/withAppStateContext';
import './BranchLikeNavigation.css';
import CurrentBranchLike from './CurrentBranchLike';
import Menu from './Menu';
export interface BranchLikeNavigationProps {
- appState: Pick<AppState, 'branchesEnabled'>;
+ appState: AppState;
branchLikes: BranchLike[];
component: Component;
currentBranchLike: BranchLike;
@@ -94,4 +94,4 @@ export function BranchLikeNavigation(props: BranchLikeNavigationProps) {
);
}
-export default withAppState(React.memo(BranchLikeNavigation));
+export default withAppStateContext(React.memo(BranchLikeNavigation));
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.tsx b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.tsx
index b9f73a60aef..488135ebd8b 100644
--- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.tsx
@@ -20,8 +20,8 @@
import * as React from 'react';
import { connect } from 'react-redux';
import NavBar from '../../../../components/ui/NavBar';
-import { getAppState, getCurrentUser, Store } from '../../../../store/rootReducer';
-import { AppState, CurrentUser } from '../../../../types/types';
+import { getCurrentUser, Store } from '../../../../store/rootReducer';
+import { CurrentUser } from '../../../../types/types';
import { rawSizes } from '../../../theme';
import EmbedDocsPopupHelper from '../../embed-docs-modal/EmbedDocsPopupHelper';
import Search from '../../search/Search';
@@ -31,18 +31,17 @@ import GlobalNavMenu from './GlobalNavMenu';
import GlobalNavUser from './GlobalNavUser';
export interface GlobalNavProps {
- appState: Pick<AppState, 'canAdmin' | 'globalPages' | 'qualifiers'>;
currentUser: CurrentUser;
location: { pathname: string };
}
export function GlobalNav(props: GlobalNavProps) {
- const { appState, currentUser, location } = props;
+ const { currentUser, location } = props;
return (
<NavBar className="navbar-global" height={rawSizes.globalNavHeightRaw} id="global-navigation">
<GlobalNavBranding />
- <GlobalNavMenu appState={appState} currentUser={currentUser} location={location} />
+ <GlobalNavMenu currentUser={currentUser} location={location} />
<ul className="global-navbar-menu global-navbar-menu-right">
<EmbedDocsPopupHelper />
@@ -55,8 +54,7 @@ export function GlobalNav(props: GlobalNavProps) {
const mapStateToProps = (state: Store) => {
return {
- currentUser: getCurrentUser(state),
- appState: getAppState(state)
+ currentUser: getCurrentUser(state)
};
};
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 8da1d583c0f..daf8af77711 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
@@ -27,14 +27,15 @@ import { translate } from '../../../../helpers/l10n';
import { getQualityGatesUrl } from '../../../../helpers/urls';
import { ComponentQualifier } from '../../../../types/component';
import { AppState, CurrentUser, Extension } from '../../../../types/types';
+import withAppStateContext from '../../app-state/withAppStateContext';
interface Props {
- appState: Pick<AppState, 'canAdmin' | 'globalPages' | 'qualifiers'>;
+ appState: AppState;
currentUser: CurrentUser;
location: { pathname: string };
}
-export default class GlobalNavMenu extends React.PureComponent<Props> {
+export class GlobalNavMenu extends React.PureComponent<Props> {
renderProjects() {
const active =
this.props.location.pathname.startsWith('/projects') &&
@@ -173,3 +174,5 @@ export default class GlobalNavMenu extends React.PureComponent<Props> {
);
}
}
+
+export default withAppStateContext(GlobalNavMenu);
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNav-test.tsx b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNav-test.tsx
index 0a8bd89a46a..49b64ed497c 100644
--- a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNav-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNav-test.tsx
@@ -26,11 +26,6 @@ import { GlobalNav, GlobalNavProps } from '../GlobalNav';
// https://stackoverflow.com/questions/43375079/redux-warning-only-appearing-in-tests
jest.mock('../../../../../store/rootReducer');
-const appState: GlobalNavProps['appState'] = {
- globalPages: [],
- canAdmin: false,
- qualifiers: []
-};
const location = { pathname: '' };
it('should render correctly', async () => {
@@ -44,12 +39,5 @@ it('should render correctly', async () => {
});
function shallowRender(props: Partial<GlobalNavProps> = {}) {
- return shallow(
- <GlobalNav
- appState={appState}
- currentUser={{ isLoggedIn: false }}
- location={location}
- {...props}
- />
- );
+ return shallow(<GlobalNav currentUser={{ isLoggedIn: false }} location={location} {...props} />);
}
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavMenu-test.tsx b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavMenu-test.tsx
index f18528c24cd..1a56df67d0f 100644
--- a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavMenu-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavMenu-test.tsx
@@ -19,13 +19,14 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import GlobalNavMenu from '../GlobalNavMenu';
+import { mockAppState } from '../../../../../helpers/testMocks';
+import { GlobalNavMenu } from '../GlobalNavMenu';
it('should work with extensions', () => {
- const appState = {
+ const appState = mockAppState({
globalPages: [{ key: 'foo', name: 'Foo' }],
qualifiers: ['TRK']
- };
+ });
const currentUser = {
isLoggedIn: false
};
@@ -36,11 +37,11 @@ it('should work with extensions', () => {
});
it('should show administration menu if the user has the rights', () => {
- const appState = {
+ const appState = mockAppState({
canAdmin: true,
globalPages: [],
qualifiers: ['TRK']
- };
+ });
const currentUser = {
isLoggedIn: false
};
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 d3f0165206c..cc530594fb3 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
@@ -7,14 +7,7 @@ exports[`should render correctly: anonymous users 1`] = `
id="global-navigation"
>
<Connect(GlobalNavBranding) />
- <GlobalNavMenu
- appState={
- Object {
- "canAdmin": false,
- "globalPages": Array [],
- "qualifiers": Array [],
- }
- }
+ <withAppStateContext(GlobalNavMenu)
currentUser={
Object {
"isLoggedIn": false,
@@ -55,14 +48,7 @@ exports[`should render correctly: logged in users 1`] = `
id="global-navigation"
>
<Connect(GlobalNavBranding) />
- <GlobalNavMenu
- appState={
- Object {
- "canAdmin": false,
- "globalPages": Array [],
- "qualifiers": Array [],
- }
- }
+ <withAppStateContext(GlobalNavMenu)
currentUser={
Object {
"isLoggedIn": true,
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 2195b772fa3..8958a1c02d6 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
@@ -20,7 +20,6 @@
import { groupBy, isEmpty, mapValues } from 'lodash';
import * as React from 'react';
import { getSystemUpgrades } from '../../../api/system';
-import { withAppState } from '../../../components/hoc/withAppState';
import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
import { Alert, AlertVariant } from '../../../components/ui/Alert';
import DismissableAlert from '../../../components/ui/DismissableAlert';
@@ -31,6 +30,7 @@ import { hasGlobalPermission, isLoggedIn } from '../../../helpers/users';
import { Permissions } from '../../../types/permissions';
import { SystemUpgrade } from '../../../types/system';
import { AppState, CurrentUser, Dict } from '../../../types/types';
+import withAppStateContext from '../app-state/withAppStateContext';
import './UpdateNotification.css';
const MONTH_BEFOR_PREVIOUS_LTS_NOTIFICATION = 6;
@@ -48,7 +48,7 @@ const MAP_VARIANT: Dict<AlertVariant> = {
interface Props {
dismissable: boolean;
- appState: Pick<AppState, 'version'>;
+ appState: AppState;
currentUser: CurrentUser;
}
@@ -249,4 +249,4 @@ export class UpdateNotification extends React.PureComponent<Props, State> {
}
}
-export default withCurrentUser(withAppState(UpdateNotification));
+export default withCurrentUser(withAppStateContext(UpdateNotification));
diff --git a/server/sonar-web/src/main/js/app/index.ts b/server/sonar-web/src/main/js/app/index.ts
index 02e3c39a063..d94e52dbd7e 100644
--- a/server/sonar-web/src/main/js/app/index.ts
+++ b/server/sonar-web/src/main/js/app/index.ts
@@ -31,7 +31,7 @@ if (isMainApp()) {
Promise.all([loadL10nBundle(), loadUser(), loadAppState(), loadApp()]).then(
([l10nBundle, user, appState, startReactApp]) => {
- startReactApp(l10nBundle.locale, user, appState);
+ startReactApp(l10nBundle.locale, appState, user);
},
error => {
if (isResponse(error) && error.status === 401) {
@@ -44,19 +44,27 @@ if (isMainApp()) {
} else {
// login, maintenance or setup pages
- const appStatePromise: Promise<AppState | undefined> = new Promise(resolve => {
+ const appStatePromise: Promise<AppState> = new Promise(resolve => {
loadAppState()
.then(data => {
resolve(data);
})
.catch(() => {
- resolve(undefined);
+ resolve({
+ authenticationError: false,
+ authorizationError: false,
+ edition: undefined,
+ productionDatabase: true,
+ qualifiers: [],
+ settings: {},
+ version: ''
+ });
});
});
Promise.all([loadL10nBundle(), appStatePromise, loadApp()]).then(
([l10nBundle, appState, startReactApp]) => {
- startReactApp(l10nBundle.locale, undefined, appState);
+ startReactApp(l10nBundle.locale, appState);
},
error => {
logError(error);
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 aa7fda54aa1..89456a949bb 100644
--- a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
+++ b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
@@ -62,6 +62,7 @@ import { lazyLoadComponent } from '../../components/lazyLoadComponent';
import getHistory from '../../helpers/getHistory';
import { AppState, CurrentUser } from '../../types/types';
import App from '../components/App';
+import AppStateContextProvider from '../components/app-state/AppStateContextProvider';
import GlobalContainer from '../components/GlobalContainer';
import { PageContext } from '../components/indexation/PageUnavailableDueToIndexation';
import MigrationContainer from '../components/MigrationContainer';
@@ -278,11 +279,7 @@ function renderAdminRoutes() {
);
}
-export default function startReactApp(
- lang: string,
- currentUser?: CurrentUser,
- appState?: AppState
-) {
+export default function startReactApp(lang: string, appState: AppState, currentUser?: CurrentUser) {
attachToGlobal();
const el = document.getElementById('content');
@@ -293,92 +290,96 @@ export default function startReactApp(
render(
<HelmetProvider>
<Provider store={store}>
- <IntlProvider defaultLocale={lang} locale={lang}>
- <Router history={history} onUpdate={handleUpdate}>
- {renderRedirects()}
+ <AppStateContextProvider appState={appState}>
+ <IntlProvider defaultLocale={lang} locale={lang}>
+ <Router history={history} onUpdate={handleUpdate}>
+ {renderRedirects()}
- <Route
- path="formatting/help"
- component={lazyLoadComponent(() => import('../components/FormattingHelp'))}
- />
-
- <Route component={lazyLoadComponent(() => import('../components/SimpleContainer'))}>
- <Route path="maintenance">{maintenanceRoutes}</Route>
- <Route path="setup">{setupRoutes}</Route>
- </Route>
-
- <Route component={MigrationContainer}>
<Route
- component={lazyLoadComponent(() =>
- import('../components/SimpleSessionsContainer')
- )}>
- <RouteWithChildRoutes path="/sessions" childRoutes={sessionsRoutes} />
+ path="formatting/help"
+ component={lazyLoadComponent(() => import('../components/FormattingHelp'))}
+ />
+
+ <Route component={lazyLoadComponent(() => import('../components/SimpleContainer'))}>
+ <Route path="maintenance">{maintenanceRoutes}</Route>
+ <Route path="setup">{setupRoutes}</Route>
</Route>
- <Route path="/" component={App}>
- <IndexRoute component={lazyLoadComponent(() => import('../components/Landing'))} />
+ <Route component={MigrationContainer}>
+ <Route
+ component={lazyLoadComponent(() =>
+ import('../components/SimpleSessionsContainer')
+ )}>
+ <RouteWithChildRoutes path="/sessions" childRoutes={sessionsRoutes} />
+ </Route>
+
+ <Route path="/" component={App}>
+ <IndexRoute
+ component={lazyLoadComponent(() => import('../components/Landing'))}
+ />
+
+ <Route component={GlobalContainer}>
+ <RouteWithChildRoutes path="account" childRoutes={accountRoutes} />
+ <RouteWithChildRoutes path="coding_rules" childRoutes={codingRulesRoutes} />
+ <RouteWithChildRoutes path="documentation" childRoutes={documentationRoutes} />
+ <Route
+ path="extension/:pluginKey/:extensionKey"
+ component={lazyLoadComponent(() =>
+ import('../components/extensions/GlobalPageExtension')
+ )}
+ />
+ <Route
+ path="issues"
+ component={withIndexationGuard(Issues, PageContext.Issues)}
+ />
+ <RouteWithChildRoutes path="projects" childRoutes={projectsRoutes} />
+ <RouteWithChildRoutes path="quality_gates" childRoutes={qualityGatesRoutes} />
+ <Route
+ path="portfolios"
+ component={lazyLoadComponent(() =>
+ import('../components/extensions/PortfoliosPage')
+ )}
+ />
+ <RouteWithChildRoutes path="profiles" childRoutes={qualityProfilesRoutes} />
+ <RouteWithChildRoutes path="web_api" childRoutes={webAPIRoutes} />
- <Route component={GlobalContainer}>
- <RouteWithChildRoutes path="account" childRoutes={accountRoutes} />
- <RouteWithChildRoutes path="coding_rules" childRoutes={codingRulesRoutes} />
- <RouteWithChildRoutes path="documentation" childRoutes={documentationRoutes} />
+ {renderComponentRoutes()}
+
+ {renderAdminRoutes()}
+ </Route>
+ <Route
+ // We don't want this route to have any menu.
+ // That is why we can not have it under the accountRoutes
+ path="account/reset_password"
+ component={lazyLoadComponent(() => import('../components/ResetPassword'))}
+ />
<Route
- path="extension/:pluginKey/:extensionKey"
+ // We don't want this route to have any menu. This is why we define it here
+ // rather than under the admin routes.
+ path="admin/change_admin_password"
component={lazyLoadComponent(() =>
- import('../components/extensions/GlobalPageExtension')
+ import('../../apps/change-admin-password/ChangeAdminPasswordApp')
)}
/>
<Route
- path="issues"
- component={withIndexationGuard(Issues, PageContext.Issues)}
+ // We don't want this route to have any menu. This is why we define it here
+ // rather than under the admin routes.
+ path="admin/plugin_risk_consent"
+ component={lazyLoadComponent(() => import('../components/PluginRiskConsent'))}
/>
- <RouteWithChildRoutes path="projects" childRoutes={projectsRoutes} />
- <RouteWithChildRoutes path="quality_gates" childRoutes={qualityGatesRoutes} />
<Route
- path="portfolios"
- component={lazyLoadComponent(() =>
- import('../components/extensions/PortfoliosPage')
- )}
+ path="not_found"
+ component={lazyLoadComponent(() => import('../components/NotFound'))}
+ />
+ <Route
+ path="*"
+ component={lazyLoadComponent(() => import('../components/NotFound'))}
/>
- <RouteWithChildRoutes path="profiles" childRoutes={qualityProfilesRoutes} />
- <RouteWithChildRoutes path="web_api" childRoutes={webAPIRoutes} />
-
- {renderComponentRoutes()}
-
- {renderAdminRoutes()}
</Route>
- <Route
- // We don't want this route to have any menu.
- // That is why we can not have it under the accountRoutes
- path="account/reset_password"
- component={lazyLoadComponent(() => import('../components/ResetPassword'))}
- />
- <Route
- // We don't want this route to have any menu. This is why we define it here
- // rather than under the admin routes.
- path="admin/change_admin_password"
- component={lazyLoadComponent(() =>
- import('../../apps/change-admin-password/ChangeAdminPasswordApp')
- )}
- />
- <Route
- // We don't want this route to have any menu. This is why we define it here
- // rather than under the admin routes.
- path="admin/plugin_risk_consent"
- component={lazyLoadComponent(() => import('../components/PluginRiskConsent'))}
- />
- <Route
- path="not_found"
- component={lazyLoadComponent(() => import('../components/NotFound'))}
- />
- <Route
- path="*"
- component={lazyLoadComponent(() => import('../components/NotFound'))}
- />
</Route>
- </Route>
- </Router>
- </IntlProvider>
+ </Router>
+ </IntlProvider>
+ </AppStateContextProvider>
</Provider>
</HelmetProvider>,
el
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 4854062f011..84c09c6ec85 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
@@ -19,8 +19,9 @@
*/
import * as React from 'react';
import { connect } from 'react-redux';
-import { getAppState, getGlobalSettingValue, Store } from '../../../store/rootReducer';
+import { getGlobalSettingValue, Store } from '../../../store/rootReducer';
import { AdminPageExtension } from '../../../types/extension';
+import { Extension } from '../../../types/types';
import { fetchValues } from '../../settings/store/actions';
import '../style.css';
import { HousekeepingPolicy, RangeOption } from '../utils';
@@ -29,23 +30,32 @@ import AuditAppRenderer from './AuditAppRenderer';
interface Props {
auditHousekeepingPolicy: HousekeepingPolicy;
fetchValues: typeof fetchValues;
- hasGovernanceExtension?: boolean;
+ adminPages: Extension[];
}
interface State {
dateRange?: { from?: Date; to?: Date };
+ hasGovernanceExtension?: boolean;
downloadStarted: boolean;
selection: RangeOption;
}
export class AuditApp extends React.PureComponent<Props, State> {
- state: State = {
- downloadStarted: false,
- selection: RangeOption.Today
- };
+ constructor(props: Props) {
+ super(props);
+ const hasGovernanceExtension = Boolean(
+ props.adminPages?.find(e => e.key === AdminPageExtension.GovernanceConsole)
+ );
+ this.state = {
+ downloadStarted: false,
+ selection: RangeOption.Today,
+ hasGovernanceExtension
+ };
+ }
componentDidMount() {
- const { hasGovernanceExtension } = this.props;
+ const { hasGovernanceExtension } = this.state;
+
if (hasGovernanceExtension) {
this.props.fetchValues(['sonar.dbcleaner.auditHousekeeping']);
}
@@ -64,17 +74,22 @@ export class AuditApp extends React.PureComponent<Props, State> {
};
render() {
- const { hasGovernanceExtension, auditHousekeepingPolicy } = this.props;
+ const { hasGovernanceExtension, ...auditAppRendererProps } = this.state;
+ const { auditHousekeepingPolicy } = this.props;
+
+ if (!hasGovernanceExtension) {
+ return null;
+ }
- return hasGovernanceExtension ? (
+ return (
<AuditAppRenderer
handleDateSelection={this.handleDateSelection}
handleOptionSelection={this.handleOptionSelection}
handleStartDownload={this.handleStartDownload}
housekeepingPolicy={auditHousekeepingPolicy || HousekeepingPolicy.Monthly}
- {...this.state}
+ {...auditAppRendererProps}
/>
- ) : null;
+ );
}
}
@@ -82,13 +97,8 @@ const mapDispatchToProps = { fetchValues };
const mapStateToProps = (state: Store) => {
const settingValue = getGlobalSettingValue(state, 'sonar.dbcleaner.auditHousekeeping');
- const { adminPages } = getAppState(state);
- const hasGovernanceExtension = Boolean(
- adminPages?.find(e => e.key === AdminPageExtension.GovernanceConsole)
- );
return {
- auditHousekeepingPolicy: settingValue?.value as HousekeepingPolicy,
- hasGovernanceExtension
+ auditHousekeepingPolicy: settingValue?.value as HousekeepingPolicy
};
};
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 b4d6a18eca0..1b9740af760 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
@@ -21,6 +21,7 @@ import { subDays } from 'date-fns';
import { shallow } from 'enzyme';
import * as React from 'react';
import { waitAndUpdate } from '../../../../helpers/testUtils';
+import { AdminPageExtension } from '../../../../types/extension';
import { HousekeepingPolicy, RangeOption } from '../../utils';
import { AuditApp } from '../AuditApp';
import AuditAppRenderer from '../AuditAppRenderer';
@@ -31,7 +32,7 @@ it('should render correctly', () => {
it('should do nothing if governance is not available', async () => {
const fetchValues = jest.fn();
- const wrapper = shallowRender({ fetchValues, hasGovernanceExtension: false });
+ const wrapper = shallowRender({ fetchValues, adminPages: [] });
await waitAndUpdate(wrapper);
expect(wrapper.type()).toBeNull();
@@ -80,7 +81,7 @@ function shallowRender(props: Partial<AuditApp['props']> = {}) {
<AuditApp
auditHousekeepingPolicy={HousekeepingPolicy.Monthly}
fetchValues={jest.fn()}
- hasGovernanceExtension={true}
+ 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 6c2ef497549..d1cc1e097c7 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
@@ -18,21 +18,21 @@
* 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 { colors } from '../../../app/theme';
import { ClearButton } from '../../../components/controls/buttons';
import ConfirmButton from '../../../components/controls/ConfirmButton';
import Tooltip from '../../../components/controls/Tooltip';
import { translate } from '../../../helpers/l10n';
-import { getAppState, Store } from '../../../store/rootReducer';
+import { AppState } from '../../../types/types';
export interface Props {
- isSystemAdmin?: boolean;
+ appState: AppState;
onCancelAllPending: () => void;
pendingCount?: number;
}
-export function StatPendingCount({ isSystemAdmin, onCancelAllPending, pendingCount }: Props) {
+export function StatPendingCount({ appState, onCancelAllPending, pendingCount }: Props) {
if (pendingCount === undefined) {
return null;
}
@@ -42,7 +42,7 @@ export function StatPendingCount({ isSystemAdmin, onCancelAllPending, pendingCou
<span className="emphasised-measure">{pendingCount}</span>
<span className="little-spacer-left display-inline-flex-center">
{translate('background_tasks.pending')}
- {isSystemAdmin && pendingCount > 0 && (
+ {appState.canAdmin && pendingCount > 0 && (
<ConfirmButton
cancelButtonText={translate('close')}
confirmButtonText={translate('background_tasks.cancel_all_tasks.submit')}
@@ -62,8 +62,4 @@ export function StatPendingCount({ isSystemAdmin, onCancelAllPending, pendingCou
);
}
-const mapStateToProps = (state: Store) => ({
- isSystemAdmin: getAppState(state).canAdmin
-});
-
-export default connect(mapStateToProps)(StatPendingCount);
+export default withAppStateContext(StatPendingCount);
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/StatPendingCount-test.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/StatPendingCount-test.tsx
index f0714f5a1be..4a4086955a6 100644
--- a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/StatPendingCount-test.tsx
+++ b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/StatPendingCount-test.tsx
@@ -19,6 +19,7 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
+import { mockAppState } from '../../../../helpers/testMocks';
import { Props, StatPendingCount } from '../StatPendingCount';
it('should render correctly', () => {
@@ -36,7 +37,7 @@ it('should not show cancel pending button', () => {
.exists()
).toBe(false);
expect(
- shallowRender({ isSystemAdmin: false })
+ shallowRender({ appState: mockAppState({ canAdmin: false }) })
.find('ConfirmButton')
.exists()
).toBe(false);
@@ -53,7 +54,7 @@ it('should trigger cancelling pending', () => {
function shallowRender(props: Partial<Props> = {}) {
return shallow(
<StatPendingCount
- isSystemAdmin={true}
+ appState={mockAppState({ canAdmin: true })}
onCancelAllPending={jest.fn()}
pendingCount={5}
{...props}
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Stats-test.tsx.snap b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Stats-test.tsx.snap
index c352f81491e..1253b96bb1f 100644
--- a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Stats-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Stats-test.tsx.snap
@@ -4,7 +4,7 @@ exports[`should render correctly 1`] = `
<section
className="big-spacer-top big-spacer-bottom"
>
- <Connect(StatPendingCount)
+ <withAppStateContext(StatPendingCount)
onCancelAllPending={[MockFunction]}
pendingCount={2}
/>
@@ -25,7 +25,7 @@ exports[`should render correctly for a component 1`] = `
<section
className="big-spacer-top big-spacer-bottom"
>
- <Connect(StatPendingCount)
+ <withAppStateContext(StatPendingCount)
onCancelAllPending={[MockFunction]}
pendingCount={2}
/>
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 80c821e4c79..5bb5d8b601b 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
@@ -18,16 +18,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { connect } from 'react-redux';
import { changePassword } from '../../api/users';
+import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import { Location, withRouter } from '../../components/hoc/withRouter';
-import { getAppState, Store } from '../../store/rootReducer';
+import { AppState } from '../../types/types';
import ChangeAdminPasswordAppRenderer from './ChangeAdminPasswordAppRenderer';
import { DEFAULT_ADMIN_LOGIN, DEFAULT_ADMIN_PASSWORD } from './constants';
interface Props {
- canAdmin?: boolean;
- instanceUsesDefaultAdminCredentials?: boolean;
+ appState: AppState;
location: Location;
}
@@ -49,7 +48,7 @@ export class ChangeAdminPasswordApp extends React.PureComponent<Props, State> {
passwordValue: '',
confirmPasswordValue: '',
submitting: false,
- success: !props.instanceUsesDefaultAdminCredentials
+ success: !props.appState.instanceUsesDefaultAdminCredentials
};
}
@@ -93,7 +92,10 @@ export class ChangeAdminPasswordApp extends React.PureComponent<Props, State> {
};
render() {
- const { canAdmin, location } = this.props;
+ const {
+ appState: { canAdmin },
+ location
+ } = this.props;
const { canSubmit, confirmPasswordValue, passwordValue, submitting, success } = this.state;
return (
<ChangeAdminPasswordAppRenderer
@@ -112,9 +114,4 @@ export class ChangeAdminPasswordApp extends React.PureComponent<Props, State> {
}
}
-export const mapStateToProps = (state: Store) => {
- const { canAdmin, instanceUsesDefaultAdminCredentials } = getAppState(state);
- return { canAdmin, instanceUsesDefaultAdminCredentials };
-};
-
-export default connect(mapStateToProps)(withRouter(ChangeAdminPasswordApp));
+export default withRouter(withAppStateContext(ChangeAdminPasswordApp));
diff --git a/server/sonar-web/src/main/js/apps/change-admin-password/__tests__/ChangeAdminPasswordApp-test.tsx b/server/sonar-web/src/main/js/apps/change-admin-password/__tests__/ChangeAdminPasswordApp-test.tsx
index cf173422826..fe8e3e54412 100644
--- a/server/sonar-web/src/main/js/apps/change-admin-password/__tests__/ChangeAdminPasswordApp-test.tsx
+++ b/server/sonar-web/src/main/js/apps/change-admin-password/__tests__/ChangeAdminPasswordApp-test.tsx
@@ -20,21 +20,15 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { changePassword } from '../../../api/users';
-import { mockLocation } from '../../../helpers/testMocks';
+import { mockAppState, mockLocation } from '../../../helpers/testMocks';
import { waitAndUpdate } from '../../../helpers/testUtils';
-import { getAppState, Store } from '../../../store/rootReducer';
-import { ChangeAdminPasswordApp, mapStateToProps } from '../ChangeAdminPasswordApp';
+import { ChangeAdminPasswordApp } from '../ChangeAdminPasswordApp';
import { DEFAULT_ADMIN_LOGIN, DEFAULT_ADMIN_PASSWORD } from '../constants';
jest.mock('react-redux', () => ({
connect: jest.fn(() => (a: any) => a)
}));
-jest.mock('../../../store/rootReducer', () => ({
- ...jest.requireActual('../../../store/rootReducer'),
- getAppState: jest.fn()
-}));
-
jest.mock('../../../api/users', () => ({
changePassword: jest.fn().mockResolvedValue(null)
}));
@@ -43,9 +37,9 @@ beforeEach(jest.clearAllMocks);
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot('default');
- expect(shallowRender({ instanceUsesDefaultAdminCredentials: undefined })).toMatchSnapshot(
- 'admin is not using the default password'
- );
+ expect(
+ shallowRender({ appState: mockAppState({ instanceUsesDefaultAdminCredentials: undefined }) })
+ ).toMatchSnapshot('admin is not using the default password');
});
it('should correctly handle input changes', () => {
@@ -99,29 +93,10 @@ it('should correctly update the password', async () => {
expect(wrapper.state().success).toBe(false);
});
-describe('redux', () => {
- it('should correctly map state props', () => {
- (getAppState as jest.Mock)
- .mockReturnValueOnce({})
- .mockReturnValueOnce({ canAdmin: false, instanceUsesDefaultAdminCredentials: true });
-
- expect(mapStateToProps({} as Store)).toEqual({
- canAdmin: undefined,
- instanceUsesDefaultAdminCredentials: undefined
- });
-
- expect(mapStateToProps({} as Store)).toEqual({
- canAdmin: false,
- instanceUsesDefaultAdminCredentials: true
- });
- });
-});
-
function shallowRender(props: Partial<ChangeAdminPasswordApp['props']> = {}) {
return shallow<ChangeAdminPasswordApp>(
<ChangeAdminPasswordApp
- canAdmin={true}
- instanceUsesDefaultAdminCredentials={true}
+ appState={mockAppState({ canAdmin: true, instanceUsesDefaultAdminCredentials: true })}
location={mockLocation()}
{...props}
/>
diff --git a/server/sonar-web/src/main/js/apps/change-admin-password/__tests__/__snapshots__/ChangeAdminPasswordApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/change-admin-password/__tests__/__snapshots__/ChangeAdminPasswordApp-test.tsx.snap
index 5371a8f4386..e7e4585e6ae 100644
--- a/server/sonar-web/src/main/js/apps/change-admin-password/__tests__/__snapshots__/ChangeAdminPasswordApp-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/change-admin-password/__tests__/__snapshots__/ChangeAdminPasswordApp-test.tsx.snap
@@ -2,7 +2,6 @@
exports[`should render correctly: admin is not using the default password 1`] = `
<ChangeAdminPasswordAppRenderer
- canAdmin={true}
confirmPasswordValue=""
location={
Object {
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 ef4601199f1..fa64d3cee56 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
@@ -20,8 +20,8 @@
import * as React from 'react';
import { Link } from 'react-router';
import { getFacet } from '../../../api/issues';
+import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import Tooltip from '../../../components/controls/Tooltip';
-import { withAppState } from '../../../components/hoc/withAppState';
import DeferredSpinner from '../../../components/ui/DeferredSpinner';
import { translate } from '../../../helpers/l10n';
import { formatMeasure } from '../../../helpers/measures';
@@ -29,7 +29,7 @@ import { getIssuesUrl } from '../../../helpers/urls';
import { AppState, RuleDetails } from '../../../types/types';
interface Props {
- appState: Pick<AppState, 'branchesEnabled'>;
+ appState: AppState;
ruleDetails: Pick<RuleDetails, 'key' | 'type'>;
}
@@ -173,4 +173,4 @@ export class RuleDetailsIssues extends React.PureComponent<Props, State> {
}
}
-export default withAppState(RuleDetailsIssues);
+export default withAppStateContext(RuleDetailsIssues);
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsIssues-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsIssues-test.tsx
index 207b08648f7..7aeb0542100 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsIssues-test.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsIssues-test.tsx
@@ -20,6 +20,7 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { getFacet } from '../../../../api/issues';
+import { mockAppState } from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';
import { RuleDetailsIssues } from '../RuleDetailsIssues';
@@ -59,7 +60,7 @@ it('should fetch issues and render', async () => {
function shallowRender(props: Partial<RuleDetailsIssues['props']> = {}) {
return shallow(
<RuleDetailsIssues
- appState={{ branchesEnabled: false }}
+ appState={mockAppState({ branchesEnabled: false })}
ruleDetails={{ key: 'foo', type: 'BUG' }}
{...props}
/>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap
index e7230ad913a..5de95675322 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap
@@ -156,7 +156,7 @@ exports[`should render correctly: loaded 1`] = `
}
}
/>
- <Connect(withAppState(RuleDetailsIssues))
+ <withAppStateContext(RuleDetailsIssues)
ruleDetails={
Object {
"createdAt": "2014-12-16T17:26:54+0100",
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 7e4819ac484..3cd2701916e 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
@@ -19,7 +19,7 @@
*/
import classNames from 'classnames';
import * as React from 'react';
-import { withAppState } from '../../../components/hoc/withAppState';
+import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import ChevronsIcon from '../../../components/icons/ChevronsIcon';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
@@ -31,7 +31,7 @@ export interface CreateProjectModeSelectionProps {
almCounts: {
[k in AlmKeys]: number;
};
- appState: Pick<AppState, 'canAdmin'>;
+ appState: AppState;
loadingBindings: boolean;
onSelectMode: (mode: CreateProjectModes) => void;
onConfigMode: (mode: AlmKeys) => void;
@@ -166,4 +166,4 @@ export function CreateProjectModeSelection(props: CreateProjectModeSelectionProp
);
}
-export default withAppState(CreateProjectModeSelection);
+export default withAppStateContext(CreateProjectModeSelection);
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 a97188e9969..6f250980373 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
@@ -22,8 +22,8 @@ import { Helmet } from 'react-helmet-async';
import { WithRouterProps } from 'react-router';
import { getAlmSettings } from '../../../api/alm-settings';
import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget';
+import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import { whenLoggedIn } from '../../../components/hoc/whenLoggedIn';
-import { withAppState } from '../../../components/hoc/withAppState';
import { translate } from '../../../helpers/l10n';
import { getProjectUrl } from '../../../helpers/urls';
import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings';
@@ -40,7 +40,7 @@ import './style.css';
import { CreateProjectModes } from './types';
interface Props extends Pick<WithRouterProps, 'router' | 'location'> {
- appState: Pick<AppState, 'canAdmin' | 'branchesEnabled'>;
+ appState: AppState;
currentUser: LoggedInUser;
}
@@ -272,4 +272,4 @@ export class CreateProjectPage extends React.PureComponent<Props, State> {
}
}
-export default whenLoggedIn(withAppState(CreateProjectPage));
+export default whenLoggedIn(withAppStateContext(CreateProjectPage));
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectModeSelection-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectModeSelection-test.tsx
index f9cee16a180..4be1b325d6a 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectModeSelection-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectModeSelection-test.tsx
@@ -19,6 +19,7 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
+import { mockAppState } from '../../../../helpers/testMocks';
import { click } from '../../../../helpers/testUtils';
import { AlmKeys } from '../../../../types/alm-settings';
import {
@@ -35,19 +36,19 @@ it('should render correctly', () => {
);
expect(
shallowRender(
- { appState: { canAdmin: true } },
+ { appState: mockAppState({ canAdmin: true }) },
{ [AlmKeys.BitbucketServer]: 0, [AlmKeys.GitHub]: 2 }
)
).toMatchSnapshot('invalid configs, admin');
expect(
shallowRender(
- { appState: { canAdmin: true } },
+ { appState: mockAppState({ canAdmin: true }) },
{ [AlmKeys.BitbucketServer]: 0, [AlmKeys.BitbucketCloud]: 0, [AlmKeys.GitHub]: 2 }
)
).toMatchSnapshot('invalid configs, admin');
expect(
shallowRender(
- { appState: { canAdmin: true } },
+ { appState: mockAppState({ canAdmin: true }) },
{
[AlmKeys.Azure]: 0,
[AlmKeys.BitbucketCloud]: 0,
@@ -118,7 +119,7 @@ it('should call the proper click handler', () => {
onSelectMode.mockClear();
wrapper = shallowRender(
- { onSelectMode, onConfigMode, appState: { canAdmin: true } },
+ { onSelectMode, onConfigMode, appState: mockAppState({ canAdmin: true }) },
{ [AlmKeys.Azure]: 0 }
);
@@ -144,7 +145,7 @@ function shallowRender(
return shallow<CreateProjectModeSelectionProps>(
<CreateProjectModeSelection
almCounts={almCounts}
- appState={{ canAdmin: false }}
+ appState={mockAppState({ canAdmin: false })}
loadingBindings={false}
onSelectMode={jest.fn()}
onConfigMode={jest.fn()}
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPage-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPage-test.tsx
index 59c21d71170..541f478f55c 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPage-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPage-test.tsx
@@ -20,7 +20,12 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { getAlmSettings } from '../../../../api/alm-settings';
-import { mockLocation, mockLoggedInUser, mockRouter } from '../../../../helpers/testMocks';
+import {
+ mockAppState,
+ mockLocation,
+ mockLoggedInUser,
+ mockRouter
+} from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';
import { AlmKeys } from '../../../../types/alm-settings';
import AlmBindingDefinitionForm from '../../../settings/components/almIntegration/AlmBindingDefinitionForm';
@@ -124,7 +129,7 @@ it('should submit alm configuration creation properly for BBC', async () => {
function shallowRender(props: Partial<CreateProjectPage['props']> = {}) {
return shallow<CreateProjectPage>(
<CreateProjectPage
- appState={{}}
+ appState={mockAppState()}
currentUser={mockLoggedInUser()}
location={mockLocation()}
router={mockRouter()}
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap
index 4b824a163f9..70474361ec7 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap
@@ -16,7 +16,7 @@ exports[`should render alm configuration creation correctly 1`] = `
className="page page-limited huge-spacer-bottom position-relative"
id="create-project"
>
- <Connect(withAppState(CreateProjectModeSelection))
+ <withAppStateContext(CreateProjectModeSelection)
almCounts={
Object {
"azure": 0,
@@ -57,7 +57,7 @@ exports[`should render correctly 1`] = `
className="page page-limited huge-spacer-bottom position-relative"
id="create-project"
>
- <Connect(withAppState(CreateProjectModeSelection))
+ <withAppStateContext(CreateProjectModeSelection)
almCounts={
Object {
"azure": 0,
diff --git a/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx b/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx
index 7ba8b943083..379c082bfe8 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx
+++ b/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx
@@ -20,38 +20,47 @@
import * as React from 'react';
import { connect } from 'react-redux';
import AdminContext from '../../app/components/AdminContext';
-import { getAppState, getGlobalSettingValue, Store } from '../../store/rootReducer';
+import withAppStateContext from '../../app/components/app-state/withAppStateContext';
+import { getGlobalSettingValue, Store } from '../../store/rootReducer';
import { EditionKey } from '../../types/editions';
-import { RawQuery } from '../../types/types';
+import { AppState, RawQuery } from '../../types/types';
import App from './App';
interface OwnProps {
location: { pathname: string; query: RawQuery };
+ appState: AppState;
}
interface StateToProps {
- currentEdition?: EditionKey;
- standaloneMode?: boolean;
updateCenterActive: boolean;
}
const mapStateToProps = (state: Store) => {
const updateCenterActive = getGlobalSettingValue(state, 'sonar.updatecenter.activate');
return {
- currentEdition: getAppState(state).edition as EditionKey, // TODO: Fix once AppState is no longer ambiant.
- standaloneMode: getAppState(state).standalone,
updateCenterActive: Boolean(updateCenterActive && updateCenterActive.value === 'true')
};
};
function WithAdminContext(props: StateToProps & OwnProps) {
+ const propsToPass = {
+ location: props.location,
+ updateCenterActive: props.updateCenterActive,
+ currentEdition: props.appState.edition as EditionKey,
+ standaloneMode: props.appState.standalone
+ };
+
return (
<AdminContext.Consumer>
{({ fetchPendingPlugins, pendingPlugins }) => (
- <App fetchPendingPlugins={fetchPendingPlugins} pendingPlugins={pendingPlugins} {...props} />
+ <App
+ fetchPendingPlugins={fetchPendingPlugins}
+ pendingPlugins={pendingPlugins}
+ {...propsToPass}
+ />
)}
</AdminContext.Consumer>
);
}
-export default connect(mapStateToProps)(WithAdminContext);
+export default connect(mapStateToProps)(withAppStateContext(WithAdminContext));
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
index fbafb1047d8..d306400526f 100644
--- 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
@@ -19,8 +19,7 @@
*/
import { connect } from 'react-redux';
import { mockStore } from '../../../helpers/testMocks';
-import { getAppState, getGlobalSettingValue } from '../../../store/rootReducer';
-import { EditionKey } from '../../../types/editions';
+import { getGlobalSettingValue } from '../../../store/rootReducer';
import '../AppContainer';
jest.mock('react-redux', () => ({
@@ -29,7 +28,6 @@ jest.mock('react-redux', () => ({
jest.mock('../../../store/rootReducer', () => {
return {
- getAppState: jest.fn(),
getGlobalSettingValue: jest.fn()
};
});
@@ -37,10 +35,7 @@ jest.mock('../../../store/rootReducer', () => {
describe('redux', () => {
it('should correctly map state and dispatch props', () => {
const store = mockStore();
- const edition = EditionKey.developer;
- const standalone = true;
const updateCenterActive = true;
- (getAppState as jest.Mock).mockReturnValue({ edition, standalone });
(getGlobalSettingValue as jest.Mock).mockReturnValueOnce({
value: `${updateCenterActive}`
});
@@ -49,8 +44,6 @@ describe('redux', () => {
const props = mapStateToProps(store);
expect(props).toEqual({
- currentEdition: edition,
- standaloneMode: standalone,
updateCenterActive
});
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 bfa2e17b359..665b63e6ddc 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
@@ -18,8 +18,8 @@
* 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 Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
-import { withAppState } from '../../../components/hoc/withAppState';
import { Router, withRouter } from '../../../components/hoc/withRouter';
import { lazyLoadComponent } from '../../../components/lazyLoadComponent';
import { isPullRequest } from '../../../helpers/branch-like';
@@ -33,7 +33,7 @@ const EmptyOverview = lazyLoadComponent(() => import('./EmptyOverview'));
const PullRequestOverview = lazyLoadComponent(() => import('../pullRequests/PullRequestOverview'));
interface Props {
- appState: Pick<AppState, 'branchesEnabled'>;
+ appState: AppState;
branchLike?: BranchLike;
branchLikes: BranchLike[];
component: Component;
@@ -93,4 +93,4 @@ export class App extends React.PureComponent<Props> {
}
}
-export default withRouter(withAppState(App));
+export default withRouter(withAppStateContext(App));
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 8f1f508aca0..f5fea8b899a 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
@@ -20,12 +20,11 @@
import { Location } from 'history';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
-import { connect } from 'react-redux';
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 { getAppState, Store } from '../../../store/rootReducer';
-import { Permission, PermissionTemplate } from '../../../types/types';
+import { AppState, Permission, PermissionTemplate } from '../../../types/types';
import '../../permissions/styles.css';
import { mergeDefaultsToTemplates, mergePermissionsToTemplates, sortPermissions } from '../utils';
import Home from './Home';
@@ -33,7 +32,7 @@ import Template from './Template';
interface Props {
location: Location;
- topQualifiers: string[];
+ appState: AppState;
}
interface State {
@@ -90,7 +89,7 @@ export class App extends React.PureComponent<Props, State> {
<Template
refresh={this.requestPermissions}
template={template}
- topQualifiers={this.props.topQualifiers}
+ topQualifiers={this.props.appState.qualifiers}
/>
);
}
@@ -102,7 +101,7 @@ export class App extends React.PureComponent<Props, State> {
permissions={this.state.permissions}
ready={this.state.ready}
refresh={this.requestPermissions}
- topQualifiers={this.props.topQualifiers}
+ topQualifiers={this.props.appState.qualifiers}
/>
);
}
@@ -121,6 +120,4 @@ export class App extends React.PureComponent<Props, State> {
}
}
-const mapStateToProps = (state: Store) => ({ topQualifiers: getAppState(state).qualifiers });
-
-export default connect(mapStateToProps)(App);
+export default withAppStateContext(App);
diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/App-test.tsx
index fa5469f91b5..7dbbdfa0f9f 100644
--- a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/App-test.tsx
@@ -19,7 +19,7 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { mockLocation } from '../../../../helpers/testMocks';
+import { mockAppState, mockLocation } from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';
import { App } from '../App';
@@ -54,5 +54,7 @@ it('should render correctly', async () => {
});
function shallowRender(props: Partial<App['props']> = {}) {
- return shallow(<App location={mockLocation()} topQualifiers={['TRK']} {...props} />);
+ return shallow(
+ <App location={mockLocation()} appState={mockAppState({ qualifiers: ['TRK'] })} {...props} />
+ );
}
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 def9d1e4ae7..0f2d5f98748 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
@@ -18,9 +18,8 @@
* 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 ListFooter from '../../../../components/controls/ListFooter';
-import { getAppState, Store } from '../../../../store/rootReducer';
import { ComponentQualifier } from '../../../../types/component';
import { AppState, Paging, PermissionGroup, PermissionUser } from '../../../../types/types';
import HoldersList from '../../shared/components/HoldersList';
@@ -32,7 +31,7 @@ import {
} from '../../utils';
interface StateProps {
- appState: Pick<AppState, 'qualifiers'>;
+ appState: AppState;
}
interface OwnProps {
@@ -60,9 +59,8 @@ export class AllHoldersList extends React.PureComponent<Props> {
const hasPermission = user.permissions.includes(permission);
if (hasPermission) {
return this.props.revokePermissionFromUser(user.login, permission);
- } else {
- return this.props.grantPermissionToUser(user.login, permission);
}
+ return this.props.grantPermissionToUser(user.login, permission);
};
handleToggleGroup = (group: PermissionGroup, permission: string) => {
@@ -70,13 +68,21 @@ export class AllHoldersList extends React.PureComponent<Props> {
if (hasPermission) {
return this.props.revokePermissionFromGroup(group.name, permission);
- } else {
- return this.props.grantPermissionToGroup(group.name, permission);
}
+ return this.props.grantPermissionToGroup(group.name, permission);
};
render() {
- const { appState, filter, groups, groupsPaging, users, usersPaging } = this.props;
+ const {
+ appState,
+ filter,
+ groups,
+ groupsPaging,
+ users,
+ usersPaging,
+ loading,
+ query
+ } = this.props;
const l10nPrefix = 'global_permissions';
const hasPortfoliosEnabled = appState.qualifiers.includes(ComponentQualifier.Portfolio);
@@ -100,19 +106,19 @@ export class AllHoldersList extends React.PureComponent<Props> {
return (
<>
<HoldersList
- filter={this.props.filter}
- groups={this.props.groups}
- loading={this.props.loading}
+ filter={filter}
+ groups={groups}
+ loading={loading}
onToggleGroup={this.handleToggleGroup}
onToggleUser={this.handleToggleUser}
permissions={permissions}
- query={this.props.query}
- users={this.props.users}>
+ query={query}
+ users={users}>
<SearchForm
- filter={this.props.filter}
+ filter={filter}
onFilter={this.props.onFilter}
onSearch={this.props.onSearch}
- query={this.props.query}
+ query={query}
/>
</HoldersList>
<ListFooter count={count} loadMore={this.props.onLoadMore} total={total} />
@@ -121,8 +127,4 @@ export class AllHoldersList extends React.PureComponent<Props> {
}
}
-const mapStateToProps = (state: Store): StateProps => ({
- appState: getAppState(state)
-});
-
-export default connect(mapStateToProps)(AllHoldersList);
+export default withAppStateContext(AllHoldersList);
diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/AllHoldersList-test.tsx b/server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/AllHoldersList-test.tsx
index 354bbf20c36..7ca580ec0db 100644
--- a/server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/AllHoldersList-test.tsx
+++ b/server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/AllHoldersList-test.tsx
@@ -20,6 +20,7 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockPermissionGroup, mockPermissionUser } from '../../../../../helpers/mocks/permissions';
+import { mockAppState } from '../../../../../helpers/testMocks';
import { ComponentQualifier } from '../../../../../types/component';
import { AllHoldersList } from '../AllHoldersList';
@@ -29,12 +30,16 @@ it('should render correctly', () => {
expect(shallowRender({ filter: 'groups' })).toMatchSnapshot('filter groups');
expect(
shallowRender({
- appState: { qualifiers: [ComponentQualifier.Project, ComponentQualifier.Application] }
+ appState: mockAppState({
+ qualifiers: [ComponentQualifier.Project, ComponentQualifier.Application]
+ })
})
).toMatchSnapshot('applications available');
expect(
shallowRender({
- appState: { qualifiers: [ComponentQualifier.Project, ComponentQualifier.Portfolio] }
+ appState: mockAppState({
+ qualifiers: [ComponentQualifier.Project, ComponentQualifier.Portfolio]
+ })
})
).toMatchSnapshot('portfolios available');
});
@@ -74,7 +79,7 @@ it('should correctly toggle group permissions', () => {
function shallowRender(props: Partial<AllHoldersList['props']> = {}) {
return shallow<AllHoldersList>(
<AllHoldersList
- appState={{ qualifiers: [ComponentQualifier.Project] }}
+ appState={mockAppState({ qualifiers: [ComponentQualifier.Project] })}
filter=""
grantPermissionToGroup={jest.fn()}
grantPermissionToUser={jest.fn()}
diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/__snapshots__/App-test.tsx.snap
index 9ccdf81dfce..bf85f340bc3 100644
--- a/server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/__snapshots__/App-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/__snapshots__/App-test.tsx.snap
@@ -16,7 +16,7 @@ exports[`should render correctly 1`] = `
<PageHeader
loading={true}
/>
- <Connect(AllHoldersList)
+ <withAppStateContext(AllHoldersList)
filter="all"
grantPermissionToGroup={[Function]}
grantPermissionToUser={[Function]}
@@ -50,7 +50,7 @@ exports[`should render correctly 2`] = `
<PageHeader
loading={false}
/>
- <Connect(AllHoldersList)
+ <withAppStateContext(AllHoldersList)
filter="all"
grantPermissionToGroup={[Function]}
grantPermissionToUser={[Function]}
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 eb380cbc7b1..d515f2294e7 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
@@ -21,6 +21,7 @@ import classNames from 'classnames';
import { debounce } from 'lodash';
import * as React from 'react';
import { getNewCodePeriod, resetNewCodePeriod, setNewCodePeriod } from '../../../api/newCodePeriod';
+import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
import AlertSuccessIcon from '../../../components/icons/AlertSuccessIcon';
import DeferredSpinner from '../../../components/ui/DeferredSpinner';
@@ -28,6 +29,7 @@ import { isBranch, sortBranches } from '../../../helpers/branch-like';
import { translate } from '../../../helpers/l10n';
import { Branch, BranchLike } from '../../../types/branch-like';
import {
+ AppState,
Component,
NewCodePeriod,
NewCodePeriodSettingType,
@@ -42,9 +44,8 @@ import ProjectBaselineSelector from './ProjectBaselineSelector';
interface Props {
branchLike: Branch;
branchLikes: BranchLike[];
- branchesEnabled?: boolean;
- canAdmin?: boolean;
component: Component;
+ appState: AppState;
}
interface State {
@@ -68,7 +69,7 @@ const DEFAULT_GENERAL_SETTING: { type: NewCodePeriodSettingType } = {
type: 'PREVIOUS_VERSION'
};
-export default class App extends React.PureComponent<Props, State> {
+export class App extends React.PureComponent<Props, State> {
mounted = false;
state: State = {
branchList: [],
@@ -127,14 +128,14 @@ export default class App extends React.PureComponent<Props, State> {
}
fetchLeakPeriodSetting() {
- const { branchLike, branchesEnabled, component } = this.props;
+ const { branchLike, appState, component } = this.props;
this.setState({ loading: true });
Promise.all([
getNewCodePeriod(),
getNewCodePeriod({
- branch: branchesEnabled ? undefined : branchLike.name,
+ branch: appState.branchesEnabled ? undefined : branchLike.name,
project: component.key
})
]).then(
@@ -235,7 +236,7 @@ export default class App extends React.PureComponent<Props, State> {
};
render() {
- const { branchesEnabled, canAdmin, component, branchLike } = this.props;
+ const { appState, component, branchLike } = this.props;
const {
analysis,
branchList,
@@ -255,19 +256,19 @@ export default class App extends React.PureComponent<Props, State> {
<>
<Suggestions suggestions="project_baseline" />
<div className="page page-limited">
- <AppHeader canAdmin={!!canAdmin} />
+ <AppHeader canAdmin={!!appState.canAdmin} />
{loading ? (
<DeferredSpinner />
) : (
<div className="panel-white project-baseline">
- {branchesEnabled && <h2>{translate('project_baseline.default_setting')}</h2>}
+ {appState.branchesEnabled && <h2>{translate('project_baseline.default_setting')}</h2>}
{generalSetting && overrideGeneralSetting !== undefined && (
<ProjectBaselineSelector
analysis={analysis}
branch={branchLike}
branchList={branchList}
- branchesEnabled={branchesEnabled}
+ branchesEnabled={appState.branchesEnabled}
component={component.key}
currentSetting={currentSetting}
currentSettingValue={currentSettingValue}
@@ -293,7 +294,7 @@ export default class App extends React.PureComponent<Props, State> {
{translate('settings.state.saved')}
</span>
</div>
- {generalSetting && branchesEnabled && (
+ {generalSetting && appState.branchesEnabled && (
<div className="huge-spacer-top branch-baseline-selector">
<hr />
<h2>{translate('project_baseline.configure_branches')}</h2>
@@ -318,3 +319,5 @@ export default class App extends React.PureComponent<Props, State> {
);
}
}
+
+export default withAppStateContext(App);
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/App-test.tsx
index f05c9404271..e99eb125f1d 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/App-test.tsx
@@ -26,9 +26,9 @@ import {
} from '../../../../api/newCodePeriod';
import { mockBranch, mockMainBranch, mockPullRequest } from '../../../../helpers/mocks/branch-like';
import { mockComponent } from '../../../../helpers/mocks/component';
-import { mockEvent } from '../../../../helpers/testMocks';
+import { mockAppState, mockEvent } from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';
-import App from '../App';
+import { App } from '../App';
jest.mock('../../../../api/newCodePeriod', () => ({
getNewCodePeriod: jest.fn().mockResolvedValue({}),
@@ -41,7 +41,7 @@ it('should render correctly', async () => {
await waitAndUpdate(wrapper);
expect(wrapper).toMatchSnapshot();
- wrapper = shallowRender({ branchesEnabled: false });
+ wrapper = shallowRender({ appState: mockAppState({ branchesEnabled: false, canAdmin: true }) });
await waitAndUpdate(wrapper);
expect(wrapper).toMatchSnapshot('without branch support');
});
@@ -109,8 +109,7 @@ function shallowRender(props: Partial<App['props']> = {}) {
<App
branchLike={mockBranch()}
branchLikes={[mockMainBranch()]}
- branchesEnabled={true}
- canAdmin={true}
+ appState={mockAppState({ branchesEnabled: true, canAdmin: true })}
component={mockComponent()}
{...props}
/>
diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/routes.ts b/server/sonar-web/src/main/js/apps/projectBaseline/routes.ts
index 16e3be4e88a..9d8f2bd42e4 100644
--- a/server/sonar-web/src/main/js/apps/projectBaseline/routes.ts
+++ b/server/sonar-web/src/main/js/apps/projectBaseline/routes.ts
@@ -21,7 +21,7 @@ import { lazyLoadComponent } from '../../components/lazyLoadComponent';
const routes = [
{
- indexRoute: { component: lazyLoadComponent(() => import('./components/AppContainer')) }
+ indexRoute: { component: lazyLoadComponent(() => import('./components/App')) }
}
];
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 05c1e3b2f71..3e855acdfa6 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
@@ -18,14 +18,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { connect } from 'react-redux';
import { getValues } from '../../../api/settings';
-import { getAppState, Store } from '../../../store/rootReducer';
+import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import { SettingsKey } from '../../../types/settings';
+import { AppState } from '../../../types/types';
import LifetimeInformationRenderer from './LifetimeInformationRenderer';
interface Props {
- canAdmin?: boolean;
+ appState: AppState;
}
interface State {
@@ -65,7 +65,9 @@ export class LifetimeInformation extends React.PureComponent<Props, State> {
}
render() {
- const { canAdmin } = this.props;
+ const {
+ appState: { canAdmin }
+ } = this.props;
const { branchAndPullRequestLifeTimeInDays, loading } = this.state;
return (
@@ -78,8 +80,4 @@ export class LifetimeInformation extends React.PureComponent<Props, State> {
}
}
-const mapStoreToProps = (state: Store) => ({
- canAdmin: getAppState(state).canAdmin
-});
-
-export default connect(mapStoreToProps)(LifetimeInformation);
+export default withAppStateContext(LifetimeInformation);
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/LifetimeInformation-test.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/LifetimeInformation-test.tsx
index 65326bee132..f34d9e24983 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/LifetimeInformation-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/LifetimeInformation-test.tsx
@@ -20,6 +20,7 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { getValues } from '../../../../api/settings';
+import { mockAppState } from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';
import { SettingsKey } from '../../../../types/settings';
import { LifetimeInformation } from '../LifetimeInformation';
@@ -41,5 +42,7 @@ it('should render correctly', async () => {
});
function shallowRender(props: Partial<LifetimeInformation['props']> = {}) {
- return shallow<LifetimeInformation>(<LifetimeInformation canAdmin={true} {...props} />);
+ return shallow<LifetimeInformation>(
+ <LifetimeInformation appState={mockAppState({ canAdmin: true })} {...props} />
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap
index 0bd2000c616..e673ebd9ff5 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap
@@ -11,7 +11,7 @@ exports[`should render correctly 1`] = `
<h1>
project_branch_pull_request.page
</h1>
- <Connect(LifetimeInformation) />
+ <withAppStateContext(LifetimeInformation) />
</header>
<BranchLikeTabs
branchLikes={
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 0ad4fcc38de..cd067423adf 100644
--- a/server/sonar-web/src/main/js/apps/projectDump/ProjectDumpApp.tsx
+++ b/server/sonar-web/src/main/js/apps/projectDump/ProjectDumpApp.tsx
@@ -20,8 +20,8 @@
import * as React from 'react';
import { getActivity } from '../../api/ce';
import { getStatus } from '../../api/project-dump';
+import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import throwGlobalError from '../../app/utils/throwGlobalError';
-import { withAppState } from '../../components/hoc/withAppState';
import { translate } from '../../helpers/l10n';
import { DumpStatus, DumpTask } from '../../types/project-dump';
import { TaskStatuses, TaskTypes } from '../../types/tasks';
@@ -33,7 +33,7 @@ import './styles.css';
const POLL_INTERNAL = 5000;
interface Props {
- appState: Pick<AppState, 'projectImportFeatureEnabled'>;
+ appState: AppState;
component: Component;
}
@@ -198,4 +198,4 @@ export class ProjectDumpApp extends React.Component<Props, State> {
}
}
-export default withAppState(ProjectDumpApp);
+export default withAppStateContext(ProjectDumpApp);
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 be94169551e..38fa2a56eaf 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
@@ -21,6 +21,7 @@ import { omitBy } from 'lodash';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget';
+import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
import ListFooter from '../../../components/controls/ListFooter';
@@ -33,7 +34,7 @@ import { addSideBarClass, removeSideBarClass } from '../../../helpers/pages';
import { get, save } from '../../../helpers/storage';
import { isLoggedIn } from '../../../helpers/users';
import { ComponentQualifier } from '../../../types/component';
-import { CurrentUser, RawQuery } from '../../../types/types';
+import { AppState, CurrentUser, RawQuery } from '../../../types/types';
import { hasFilterParams, hasViewParams, parseUrlQuery, Query } from '../query';
import '../styles.css';
import { Facets, Project } from '../types';
@@ -46,7 +47,7 @@ interface Props {
currentUser: CurrentUser;
isFavorite: boolean;
location: Pick<Location, 'pathname' | 'query'>;
- qualifiers: ComponentQualifier[];
+ appState: AppState;
router: Pick<Router, 'push' | 'replace'>;
}
@@ -226,7 +227,9 @@ export class AllProjects extends React.PureComponent<Props, State> {
/>
<PageSidebar
- applicationsEnabled={this.props.qualifiers.includes(ComponentQualifier.Application)}
+ applicationsEnabled={this.props.appState.qualifiers.includes(
+ ComponentQualifier.Application
+ )}
facets={this.state.facets}
onClearAll={this.handleClearAll}
onQueryChange={this.updateLocationQuery}
@@ -313,4 +316,4 @@ export class AllProjects extends React.PureComponent<Props, State> {
}
}
-export default withRouter(AllProjects);
+export default withRouter(withAppStateContext(AllProjects));
diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.tsx b/server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.tsx
index 01165132238..c1d5e5491ca 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.tsx
@@ -19,12 +19,10 @@
*/
import { connect } from 'react-redux';
import { lazyLoadComponent } from '../../../components/lazyLoadComponent';
-import { getAppState, getCurrentUser, Store } from '../../../store/rootReducer';
-import { ComponentQualifier } from '../../../types/component';
+import { getCurrentUser, Store } from '../../../store/rootReducer';
const stateToProps = (state: Store) => ({
- currentUser: getCurrentUser(state),
- qualifiers: getAppState(state).qualifiers as ComponentQualifier[]
+ currentUser: getCurrentUser(state)
});
export default connect(stateToProps)(lazyLoadComponent(() => import('./AllProjects')));
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 268fbd19754..4fe00dc2785 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
@@ -19,9 +19,9 @@
*/
import * as React from 'react';
import { getComponentNavigation } from '../../../api/nav';
+import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import CreateApplicationForm from '../../../app/components/extensions/CreateApplicationForm';
import { Button } from '../../../components/controls/buttons';
-import { withAppState } from '../../../components/hoc/withAppState';
import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
import { Router, withRouter } from '../../../components/hoc/withRouter';
import { translate } from '../../../helpers/l10n';
@@ -32,7 +32,7 @@ import { Permissions } from '../../../types/permissions';
import { AppState, LoggedInUser } from '../../../types/types';
export interface ApplicationCreationProps {
- appState: Pick<AppState, 'qualifiers'>;
+ appState: AppState;
className?: string;
currentUser: LoggedInUser;
router: Router;
@@ -84,4 +84,4 @@ export function ApplicationCreation(props: ApplicationCreationProps) {
);
}
-export default withAppState(withCurrentUser(withRouter(ApplicationCreation)));
+export default withCurrentUser(withRouter(withAppStateContext(ApplicationCreation)));
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx
index 819e956a14a..63b97a12434 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx
@@ -20,6 +20,7 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { get, save } from '../../../../helpers/storage';
+import { mockAppState } from '../../../../helpers/testMocks';
import { ComponentQualifier } from '../../../../types/component';
import { Dict } from '../../../../types/types';
import { AllProjects, LS_PROJECTS_SORT, LS_PROJECTS_VIEW } from '../AllProjects';
@@ -174,7 +175,9 @@ function shallowRender(
currentUser={{ isLoggedIn: true }}
isFavorite={false}
location={{ pathname: '/projects', query: {} }}
- qualifiers={[ComponentQualifier.Project, ComponentQualifier.Application]}
+ appState={mockAppState({
+ qualifiers: [ComponentQualifier.Project, ComponentQualifier.Application]
+ })}
router={{ push, replace }}
{...props}
/>
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap
index 6f89a384375..8dff4f4dd70 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap
@@ -21,7 +21,7 @@ exports[`should render correctly 1`] = `
<Connect(withCurrentUser(ProjectCreationMenu))
className="little-spacer-right"
/>
- <Connect(withAppState(Connect(withCurrentUser(withRouter(ApplicationCreation)))))
+ <Connect(withCurrentUser(withRouter(withAppStateContext(ApplicationCreation))))
className="little-spacer-right"
/>
<Connect(HomePageSelect)
@@ -97,7 +97,7 @@ exports[`should render correctly while loading 1`] = `
<Connect(withCurrentUser(ProjectCreationMenu))
className="little-spacer-right"
/>
- <Connect(withAppState(Connect(withCurrentUser(withRouter(ApplicationCreation)))))
+ <Connect(withCurrentUser(withRouter(withAppStateContext(ApplicationCreation))))
className="little-spacer-right"
/>
<Connect(HomePageSelect)
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/App.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/App.tsx
index 7b090bbc3ee..be9c7f03620 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/App.tsx
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/App.tsx
@@ -29,10 +29,10 @@ import ListFooter from '../../components/controls/ListFooter';
import { toShortNotSoISOString } from '../../helpers/dates';
import { translate } from '../../helpers/l10n';
import { hasGlobalPermission } from '../../helpers/users';
-import { getAppState, getCurrentUser, Store } from '../../store/rootReducer';
+import { getCurrentUser, Store } from '../../store/rootReducer';
import { Permissions } from '../../types/permissions';
import { SettingsKey } from '../../types/settings';
-import { AppState, LoggedInUser, Visibility } from '../../types/types';
+import { LoggedInUser, Visibility } from '../../types/types';
import CreateProjectForm from './CreateProjectForm';
import Header from './Header';
import Projects from './Projects';
@@ -40,7 +40,6 @@ import Search from './Search';
export interface Props {
currentUser: LoggedInUser;
- appState: Pick<AppState, 'qualifiers'>;
}
interface State {
@@ -198,7 +197,7 @@ export class App extends React.PureComponent<Props, State> {
};
render() {
- const { appState, currentUser } = this.props;
+ const { currentUser } = this.props;
const { defaultProjectVisibility } = this.state;
return (
<div className="page page-limited" id="projects-management-page">
@@ -228,7 +227,6 @@ export class App extends React.PureComponent<Props, State> {
query={this.state.query}
ready={this.state.ready}
selection={this.state.selection}
- topLevelQualifiers={appState.qualifiers}
total={this.state.total}
visibility={this.state.visibility}
/>
@@ -262,7 +260,6 @@ export class App extends React.PureComponent<Props, State> {
}
const mapStateToProps = (state: Store) => ({
- appState: getAppState(state),
currentUser: getCurrentUser(state) as LoggedInUser
});
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 8f47af0fdf4..4d19a37e27a 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx
@@ -20,6 +20,7 @@
import { sortBy } from 'lodash';
import * as React from 'react';
import { Project } from '../../api/components';
+import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import { Button } from '../../components/controls/buttons';
import Checkbox from '../../components/controls/Checkbox';
import DateInput from '../../components/controls/DateInput';
@@ -28,7 +29,7 @@ import SearchBox from '../../components/controls/SearchBox';
import SelectLegacy from '../../components/controls/SelectLegacy';
import QualifierIcon from '../../components/icons/QualifierIcon';
import { translate } from '../../helpers/l10n';
-import { Visibility } from '../../types/types';
+import { AppState, Visibility } from '../../types/types';
import BulkApplyTemplateModal from './BulkApplyTemplateModal';
import DeleteModal from './DeleteModal';
@@ -48,7 +49,7 @@ export interface Props {
query: string;
ready: boolean;
selection: any[];
- topLevelQualifiers: string[];
+ appState: AppState;
total: number;
visibility?: Visibility;
}
@@ -60,12 +61,12 @@ interface State {
const QUALIFIERS_ORDER = ['TRK', 'VW', 'APP'];
-export default class Search extends React.PureComponent<Props, State> {
+export class Search extends React.PureComponent<Props, State> {
mounted = false;
state: State = { bulkApplyTemplateModal: false, deleteModal: false };
getQualifierOptions = () => {
- const options = this.props.topLevelQualifiers.map(q => ({
+ const options = this.props.appState.qualifiers.map(q => ({
label: translate('qualifiers', q),
value: q
}));
@@ -281,3 +282,5 @@ export default class Search extends React.PureComponent<Props, State> {
);
}
}
+
+export default withAppStateContext(Search);
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/App-test.tsx
index cc02fe3436f..653e059a155 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/App-test.tsx
@@ -22,10 +22,9 @@ import * as React from 'react';
import { getComponents } from '../../../api/components';
import { changeProjectDefaultVisibility } from '../../../api/permissions';
import { getValues } from '../../../api/settings';
-import { mockAppState, mockLoggedInUser } from '../../../helpers/testMocks';
+import { mockLoggedInUser } from '../../../helpers/testMocks';
import { waitAndUpdate } from '../../../helpers/testUtils';
import { App, Props } from '../App';
-import Search from '../Search';
jest.mock('lodash', () => {
const lodash = jest.requireActual('lodash');
@@ -65,7 +64,7 @@ it('fetches all projects on mount', async () => {
it('selects provisioned', () => {
const wrapper = shallowRender();
- wrapper.find('Search').prop<Function>('onProvisionedChanged')(true);
+ wrapper.find('withAppStateContext(Search)').prop<Function>('onProvisionedChanged')(true);
expect(getComponents).lastCalledWith({
...defaultSearchParameters,
onProvisionedOnly: true,
@@ -76,22 +75,21 @@ it('selects provisioned', () => {
it('changes qualifier and resets provisioned', () => {
const wrapper = shallowRender();
wrapper.setState({ provisioned: true });
- wrapper.find('Search').prop<Function>('onQualifierChanged')('VW');
+ wrapper.find('withAppStateContext(Search)').prop<Function>('onQualifierChanged')('VW');
expect(getComponents).lastCalledWith({ ...defaultSearchParameters, qualifiers: 'VW' });
});
it('searches', () => {
const wrapper = shallowRender();
- wrapper.find('Search').prop<Function>('onSearch')('foo');
+ wrapper.find('withAppStateContext(Search)').prop<Function>('onSearch')('foo');
expect(getComponents).lastCalledWith({ ...defaultSearchParameters, q: 'foo', qualifiers: 'TRK' });
});
it('should handle date filtering', () => {
const wrapper = shallowRender();
- wrapper
- .find(Search)
- .props()
- .onDateChanged(new Date('2019-11-14T06:55:02.663Z'));
+ wrapper.find('withAppStateContext(Search)').prop<Function>('onDateChanged')(
+ '2019-11-14T06:55:02.663Z'
+ );
expect(getComponents).toHaveBeenCalledWith({
...defaultSearchParameters,
qualifiers: 'TRK',
@@ -138,10 +136,10 @@ it('selects and deselects projects', async () => {
wrapper.find('Projects').prop<Function>('onProjectDeselected')('foo');
expect(wrapper.state('selection')).toEqual(['bar']);
- wrapper.find('Search').prop<Function>('onAllDeselected')();
+ wrapper.find('withAppStateContext(Search)').prop<Function>('onAllDeselected')();
expect(wrapper.state('selection')).toEqual([]);
- wrapper.find('Search').prop<Function>('onAllSelected')();
+ wrapper.find('withAppStateContext(Search)').prop<Function>('onAllSelected')();
expect(wrapper.state('selection')).toEqual(['foo', 'bar']);
});
@@ -165,7 +163,6 @@ it('creates project', () => {
function shallowRender(props?: { [P in keyof Props]?: Props[P] }) {
return shallow<App>(
<App
- appState={mockAppState({ qualifiers: ['TRK', 'VW', 'APP'] })}
currentUser={mockLoggedInUser({ login: 'foo', permissions: { global: ['provisioning'] } })}
{...props}
/>
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Search-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Search-test.tsx
index 11dc6ea01f0..b4e3f13b637 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Search-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Search-test.tsx
@@ -19,8 +19,9 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
+import { mockAppState } from '../../../helpers/testMocks';
import { click } from '../../../helpers/testUtils';
-import Search, { Props } from '../Search';
+import { Props, Search } from '../Search';
it('renders', () => {
expect(shallowRender()).toMatchSnapshot();
@@ -37,12 +38,17 @@ it('disables the delete and bulk apply buttons unless a project is selected', ()
});
it('render qualifiers filter', () => {
- expect(shallowRender({ topLevelQualifiers: ['TRK', 'VW', 'APP'] })).toMatchSnapshot();
+ expect(
+ shallowRender({ appState: mockAppState({ qualifiers: ['TRK', 'VW', 'APP'] }) })
+ ).toMatchSnapshot();
});
it('updates qualifier', () => {
const onQualifierChanged = jest.fn();
- const wrapper = shallowRender({ onQualifierChanged, topLevelQualifiers: ['TRK', 'VW', 'APP'] });
+ const wrapper = shallowRender({
+ onQualifierChanged,
+ appState: mockAppState({ qualifiers: ['TRK', 'VW', 'APP'] })
+ });
wrapper.find('SelectLegacy[name="projects-qualifier"]').prop<Function>('onChange')({
value: 'VW'
});
@@ -129,7 +135,7 @@ function shallowRender(props?: { [P in keyof Props]?: Props[P] }) {
query=""
ready={true}
selection={[]}
- topLevelQualifiers={['TRK']}
+ appState={mockAppState({ qualifiers: ['TRK'] })}
total={17}
{...props}
/>
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 d7ae7b87b7d..81dbb739289 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
@@ -19,11 +19,11 @@
*/
import { differenceWith, map, sortBy, uniqBy } from 'lodash';
import * as React from 'react';
+import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import withMetricsContext from '../../../app/components/metrics/withMetricsContext';
import DocumentationTooltip from '../../../components/common/DocumentationTooltip';
import { Button } from '../../../components/controls/buttons';
import ModalButton from '../../../components/controls/ModalButton';
-import { withAppState } from '../../../components/hoc/withAppState';
import { Alert } from '../../../components/ui/Alert';
import { getLocalizedMetricName, translate } from '../../../helpers/l10n';
import { isDiffMetric } from '../../../helpers/measures';
@@ -39,7 +39,7 @@ import Condition from './Condition';
import ConditionModal from './ConditionModal';
interface Props {
- appState: Pick<AppState, 'branchesEnabled'>;
+ appState: AppState;
canEdit: boolean;
conditions: ConditionType[];
metrics: Dict<Metric>;
@@ -228,4 +228,4 @@ export class Conditions extends React.PureComponent<Props> {
}
}
-export default withAppState(withMetricsContext(Conditions));
+export default withMetricsContext(withAppStateContext(Conditions));
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Conditions-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Conditions-test.tsx
index c63143a38da..2963e8c21f2 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Conditions-test.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Conditions-test.tsx
@@ -20,7 +20,7 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockQualityGate } from '../../../../helpers/mocks/quality-gates';
-import { mockCondition, mockMetric } from '../../../../helpers/testMocks';
+import { mockAppState, mockCondition, mockMetric } from '../../../../helpers/testMocks';
import { MetricKey } from '../../../../types/metrics';
import { Conditions } from '../Conditions';
@@ -57,7 +57,7 @@ it('should render the add conditions button and modal', () => {
function shallowRender(props: Partial<Conditions['props']> = {}) {
return shallow<Conditions>(
<Conditions
- appState={{ branchesEnabled: true }}
+ appState={mockAppState({ branchesEnabled: true })}
canEdit={false}
conditions={[mockCondition(), mockCondition({ id: 2, metric: MetricKey.duplicated_lines })]}
metrics={{
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsContent-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsContent-test.tsx.snap
index 456f401e581..7354a115ce8 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsContent-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsContent-test.tsx.snap
@@ -4,7 +4,7 @@ exports[`should render correctly: Admin 1`] = `
<div
className="layout-page-main-inner"
>
- <Connect(withAppState(withMetricsContext(Conditions)))
+ <withMetricsContext(withAppStateContext(Conditions))
canEdit={false}
conditions={Array []}
onAddCondition={[MockFunction]}
@@ -80,7 +80,7 @@ exports[`should render correctly: is default 1`] = `
<div
className="layout-page-main-inner"
>
- <Connect(withAppState(withMetricsContext(Conditions)))
+ <withMetricsContext(withAppStateContext(Conditions))
canEdit={false}
conditions={
Array [
@@ -150,7 +150,7 @@ exports[`should render correctly: is default, no conditions 1`] = `
>
quality_gates.is_default_no_conditions
</Alert>
- <Connect(withAppState(withMetricsContext(Conditions)))
+ <withMetricsContext(withAppStateContext(Conditions))
canEdit={false}
conditions={Array []}
onAddCondition={[MockFunction]}
@@ -198,7 +198,7 @@ exports[`should render correctly: is not default 1`] = `
<div
className="layout-page-main-inner"
>
- <Connect(withAppState(withMetricsContext(Conditions)))
+ <withMetricsContext(withAppStateContext(Conditions))
canEdit={false}
conditions={
Array [
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 c05f1fb30fc..69722c98a05 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
@@ -20,17 +20,16 @@
import classNames from 'classnames';
import { sortBy } from 'lodash';
import * as React from 'react';
-import { connect } from 'react-redux';
import { IndexLink } from 'react-router';
+import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import { getGlobalSettingsUrl, getProjectSettingsUrl } from '../../../helpers/urls';
-import { getAppState, Store } from '../../../store/rootReducer';
-import { Component } from '../../../types/types';
+import { AppState, Component } from '../../../types/types';
import { getCategoryName } from '../utils';
import { ADDITIONAL_CATEGORIES } from './AdditionalCategories';
import CATEGORY_OVERRIDES from './CategoryOverrides';
export interface CategoriesListProps {
- branchesEnabled?: boolean;
+ appState: AppState;
categories: string[];
component?: Component;
defaultCategory: string;
@@ -38,7 +37,7 @@ export interface CategoriesListProps {
}
export function CategoriesList(props: CategoriesListProps) {
- const { branchesEnabled, categories, component, defaultCategory, selectedCategory } = props;
+ const { appState, categories, component, defaultCategory, selectedCategory } = props;
const categoriesWithName = categories
.filter(key => !CATEGORY_OVERRIDES[key.toLowerCase()])
@@ -55,7 +54,7 @@ export function CategoriesList(props: CategoriesListProps) {
: // Global settings
c.availableGlobally
)
- .filter(c => branchesEnabled || !c.requiresBranchesEnabled)
+ .filter(c => appState.branchesEnabled || !c.requiresBranchesEnabled)
);
const sortedCategories = sortBy(categoriesWithName, category => category.name.toLowerCase());
@@ -84,8 +83,4 @@ export function CategoriesList(props: CategoriesListProps) {
);
}
-const mapStateToProps = (state: Store) => ({
- branchesEnabled: getAppState(state).branchesEnabled
-});
-
-export default connect(mapStateToProps)(CategoriesList);
+export default withAppStateContext(CategoriesList);
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx
index 35e919e9f31..3d7a1401872 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx
@@ -20,6 +20,7 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockComponent } from '../../../../helpers/mocks/component';
+import { mockAppState } from '../../../../helpers/testMocks';
import { AdditionalCategory } from '../AdditionalCategories';
import { CategoriesList, CategoriesListProps } from '../AllCategoriesList';
@@ -65,13 +66,15 @@ it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot('global mode');
expect(shallowRender({ selectedCategory: 'CAT_2' })).toMatchSnapshot('selected category');
expect(shallowRender({ component: mockComponent() })).toMatchSnapshot('project mode');
- expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot('branches disabled');
+ expect(shallowRender({ appState: mockAppState({ branchesEnabled: false }) })).toMatchSnapshot(
+ 'branches disabled'
+ );
});
function shallowRender(props?: Partial<CategoriesListProps>) {
return shallow<CategoriesListProps>(
<CategoriesList
- branchesEnabled={true}
+ appState={mockAppState({ branchesEnabled: true })}
categories={['general']}
defaultCategory="general"
selectedCategory=""
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap
index 7d3b5667d2b..8787399d313 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap
@@ -63,7 +63,7 @@ exports[`should render additional categories component correctly 3`] = `
`;
exports[`should render additional categories component correctly 4`] = `
-<withRouter(Connect(withAppState(AlmIntegration)))
+<withRouter(withAppStateContext(AlmIntegration))
categories={Array []}
component={
Object {
@@ -93,7 +93,7 @@ exports[`should render additional categories component correctly 4`] = `
`;
exports[`should render additional categories component correctly 5`] = `
-<Connect(Connect(withCurrentUser(PRDecorationBinding)))
+<Connect(withCurrentUser(PRDecorationBinding))
component={
Object {
"breadcrumbs": Array [],
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsAppRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsAppRenderer-test.tsx.snap
index 1b1ea74df0c..a40cc74b2c1 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsAppRenderer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsAppRenderer-test.tsx.snap
@@ -50,7 +50,7 @@ exports[`should render almintegration correctly 1`] = `
<div
className="big-padded"
>
- <withRouter(Connect(withAppState(AlmIntegration)))
+ <withRouter(withAppStateContext(AlmIntegration))
categories={
Array [
"foo category",
@@ -177,7 +177,7 @@ exports[`should render default view correctly: All Categories List 1`] = `
<div
className="layout-page-side-inner"
>
- <Connect(CategoriesList)
+ <withAppStateContext(CategoriesList)
categories={
Array [
"foo category",
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 f26b1f7ffc2..7a54c0bc12b 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
@@ -25,7 +25,7 @@ import {
getAlmDefinitions,
validateAlmSettings
} from '../../../../api/alm-settings';
-import { withAppState } from '../../../../components/hoc/withAppState';
+import withAppStateContext from '../../../../app/components/app-state/withAppStateContext';
import { withRouter } from '../../../../components/hoc/withRouter';
import {
AlmBindingDefinitionBase,
@@ -39,7 +39,7 @@ import { AppState, Dict } from '../../../../types/types';
import AlmIntegrationRenderer from './AlmIntegrationRenderer';
interface Props extends Pick<WithRouterProps, 'location' | 'router'> {
- appState: Pick<AppState, 'branchesEnabled' | 'multipleAlmEnabled'>;
+ appState: AppState;
definitions: ExtendedSettingDefinition[];
}
@@ -246,4 +246,4 @@ export class AlmIntegration extends React.PureComponent<Props, State> {
}
}
-export default withRouter(withAppState(AlmIntegration));
+export default withRouter(withAppStateContext(AlmIntegration));
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 147f90d8c46..9f53a41396f 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
@@ -19,8 +19,8 @@
*/
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
+import withAppStateContext from '../../../../app/components/app-state/withAppStateContext';
import Tooltip from '../../../../components/controls/Tooltip';
-import { withAppState } from '../../../../components/hoc/withAppState';
import { getEdition, getEditionUrl } from '../../../../helpers/editions';
import { translate } from '../../../../helpers/l10n';
import { AlmKeys } from '../../../../types/alm-settings';
@@ -73,4 +73,4 @@ export function CreationTooltip(props: CreationTooltipProps) {
);
}
-export default withAppState(CreationTooltip);
+export default withAppStateContext(CreationTooltip);
diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegration-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegration-test.tsx
index c6f66f233bb..aa701feea92 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegration-test.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegration-test.tsx
@@ -25,7 +25,7 @@ import {
getAlmDefinitions,
validateAlmSettings
} from '../../../../../api/alm-settings';
-import { mockLocation, mockRouter } from '../../../../../helpers/testMocks';
+import { mockAppState, mockLocation, mockRouter } from '../../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../../helpers/testUtils';
import { AlmKeys, AlmSettingsBindingStatusType } from '../../../../../types/alm-settings';
import { AlmIntegration } from '../AlmIntegration';
@@ -189,7 +189,7 @@ it('should detect the current ALM from the query', () => {
function shallowRender(props: Partial<AlmIntegration['props']> = {}) {
return shallow<AlmIntegration>(
<AlmIntegration
- appState={{ branchesEnabled: true }}
+ appState={mockAppState({ branchesEnabled: true })}
definitions={[]}
location={mockLocation()}
router={mockRouter()}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap
index ba36a1e7f3f..b987913a570 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap
@@ -13,7 +13,7 @@ exports[`should render correctly for multi-ALM binding: editing a definition 1`]
<div
className="spacer-bottom text-right"
>
- <Connect(withAppState(CreationTooltip))
+ <withAppStateContext(CreationTooltip)
alm="azure"
preventCreation={false}
>
@@ -24,7 +24,7 @@ exports[`should render correctly for multi-ALM binding: editing a definition 1`]
>
settings.almintegration.create
</Button>
- </Connect(withAppState(CreationTooltip))>
+ </withAppStateContext(CreationTooltip)>
</div>
<AlmBindingDefinitionBox
alm="azure"
@@ -71,7 +71,7 @@ exports[`should render correctly for multi-ALM binding: loaded 1`] = `
<div
className="spacer-bottom text-right"
>
- <Connect(withAppState(CreationTooltip))
+ <withAppStateContext(CreationTooltip)
alm="azure"
preventCreation={false}
>
@@ -82,7 +82,7 @@ exports[`should render correctly for multi-ALM binding: loaded 1`] = `
>
settings.almintegration.create
</Button>
- </Connect(withAppState(CreationTooltip))>
+ </withAppStateContext(CreationTooltip)>
</div>
<AlmBindingDefinitionBox
alm="azure"
@@ -129,7 +129,7 @@ exports[`should render correctly for multi-ALM binding: loading ALM definitions
<div
className="spacer-bottom text-right"
>
- <Connect(withAppState(CreationTooltip))
+ <withAppStateContext(CreationTooltip)
alm="azure"
preventCreation={false}
>
@@ -140,7 +140,7 @@ exports[`should render correctly for multi-ALM binding: loading ALM definitions
>
settings.almintegration.create
</Button>
- </Connect(withAppState(CreationTooltip))>
+ </withAppStateContext(CreationTooltip)>
</div>
<AlmBindingDefinitionBox
alm="azure"
@@ -187,7 +187,7 @@ exports[`should render correctly for multi-ALM binding: loading project count 1`
<div
className="spacer-bottom text-right"
>
- <Connect(withAppState(CreationTooltip))
+ <withAppStateContext(CreationTooltip)
alm="azure"
preventCreation={true}
>
@@ -198,7 +198,7 @@ exports[`should render correctly for multi-ALM binding: loading project count 1`
>
settings.almintegration.create
</Button>
- </Connect(withAppState(CreationTooltip))>
+ </withAppStateContext(CreationTooltip)>
</div>
<AlmBindingDefinitionBox
alm="azure"
@@ -245,7 +245,7 @@ exports[`should render correctly for single-ALM binding 1`] = `
<div
className="spacer-bottom text-right"
>
- <Connect(withAppState(CreationTooltip))
+ <withAppStateContext(CreationTooltip)
alm="azure"
preventCreation={true}
>
@@ -256,7 +256,7 @@ exports[`should render correctly for single-ALM binding 1`] = `
>
settings.almintegration.create
</Button>
- </Connect(withAppState(CreationTooltip))>
+ </withAppStateContext(CreationTooltip)>
</div>
<AlmBindingDefinitionBox
alm="azure"
@@ -303,7 +303,7 @@ exports[`should render correctly for single-ALM binding 2`] = `
<div
className="spacer-bottom text-right"
>
- <Connect(withAppState(CreationTooltip))
+ <withAppStateContext(CreationTooltip)
alm="azure"
preventCreation={true}
>
@@ -314,7 +314,7 @@ exports[`should render correctly for single-ALM binding 2`] = `
>
settings.almintegration.create
</Button>
- </Connect(withAppState(CreationTooltip))>
+ </withAppStateContext(CreationTooltip)>
</div>
<AlmBindingDefinitionBox
alm="azure"
@@ -361,7 +361,7 @@ exports[`should render correctly for single-ALM binding 3`] = `
<div
className="spacer-bottom text-right"
>
- <Connect(withAppState(CreationTooltip))
+ <withAppStateContext(CreationTooltip)
alm="azure"
preventCreation={true}
>
@@ -372,7 +372,7 @@ exports[`should render correctly for single-ALM binding 3`] = `
>
settings.almintegration.create
</Button>
- </Connect(withAppState(CreationTooltip))>
+ </withAppStateContext(CreationTooltip)>
</div>
<AlmBindingDefinitionBox
alm="azure"
@@ -424,7 +424,7 @@ exports[`should render correctly with validation: create a first 1`] = `
<div
className="big-spacer-top"
>
- <Connect(withAppState(CreationTooltip))
+ <withAppStateContext(CreationTooltip)
alm="azure"
preventCreation={false}
>
@@ -435,7 +435,7 @@ exports[`should render correctly with validation: create a first 1`] = `
>
settings.almintegration.create
</Button>
- </Connect(withAppState(CreationTooltip))>
+ </withAppStateContext(CreationTooltip)>
</div>
</DeferredSpinner>
</div>
@@ -467,7 +467,7 @@ exports[`should render correctly with validation: create a second 1`] = `
<div
className="spacer-bottom text-right"
>
- <Connect(withAppState(CreationTooltip))
+ <withAppStateContext(CreationTooltip)
alm="azure"
preventCreation={false}
>
@@ -478,7 +478,7 @@ exports[`should render correctly with validation: create a second 1`] = `
>
settings.almintegration.create
</Button>
- </Connect(withAppState(CreationTooltip))>
+ </withAppStateContext(CreationTooltip)>
</div>
<AlmBindingDefinitionBox
alm="azure"
@@ -529,7 +529,7 @@ exports[`should render correctly with validation: default 1`] = `
<div
className="spacer-bottom text-right"
>
- <Connect(withAppState(CreationTooltip))
+ <withAppStateContext(CreationTooltip)
alm="azure"
preventCreation={false}
>
@@ -540,7 +540,7 @@ exports[`should render correctly with validation: default 1`] = `
>
settings.almintegration.create
</Button>
- </Connect(withAppState(CreationTooltip))>
+ </withAppStateContext(CreationTooltip)>
</div>
<AlmBindingDefinitionBox
alm="azure"
@@ -596,7 +596,7 @@ exports[`should render correctly with validation: empty 1`] = `
<div
className="big-spacer-top"
>
- <Connect(withAppState(CreationTooltip))
+ <withAppStateContext(CreationTooltip)
alm="azure"
preventCreation={false}
>
@@ -607,7 +607,7 @@ exports[`should render correctly with validation: empty 1`] = `
>
settings.almintegration.create
</Button>
- </Connect(withAppState(CreationTooltip))>
+ </withAppStateContext(CreationTooltip)>
</div>
</DeferredSpinner>
</div>
@@ -639,7 +639,7 @@ exports[`should render correctly with validation: pass the correct key for bitbu
<div
className="spacer-bottom text-right"
>
- <Connect(withAppState(CreationTooltip))
+ <withAppStateContext(CreationTooltip)
alm="bitbucket"
preventCreation={false}
>
@@ -650,7 +650,7 @@ exports[`should render correctly with validation: pass the correct key for bitbu
>
settings.almintegration.create
</Button>
- </Connect(withAppState(CreationTooltip))>
+ </withAppStateContext(CreationTooltip)>
</div>
<AlmBindingDefinitionBox
alm="bitbucketcloud"
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 6020027303c..cd60e263b9d 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
@@ -20,6 +20,7 @@
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router';
+import withAppStateContext from '../../../../app/components/app-state/withAppStateContext';
import Toggle from '../../../../components/controls/Toggle';
import { Alert } from '../../../../components/ui/Alert';
import MandatoryFieldMarker from '../../../../components/ui/MandatoryFieldMarker';
@@ -31,14 +32,15 @@ import {
AlmSettingsInstance,
ProjectAlmBindingResponse
} from '../../../../types/alm-settings';
-import { Dict } from '../../../../types/types';
+import { EditionKey } from '../../../../types/editions';
+import { AppState, Dict } from '../../../../types/types';
export interface AlmSpecificFormProps {
alm: AlmKeys;
instances: AlmSettingsInstance[];
formData: Omit<ProjectAlmBindingResponse, 'alm'>;
onFieldChange: (id: keyof ProjectAlmBindingResponse, value: string | boolean) => void;
- monorepoEnabled: boolean;
+ appState: AppState;
}
interface LabelProps {
@@ -140,12 +142,12 @@ function renderField(
);
}
-export default function AlmSpecificForm(props: AlmSpecificFormProps) {
+export function AlmSpecificForm(props: AlmSpecificFormProps) {
const {
alm,
instances,
formData: { repository, slug, summaryCommentEnabled, monorepo },
- monorepoEnabled
+ appState
} = props;
let formFields: JSX.Element;
@@ -275,6 +277,11 @@ export default function AlmSpecificForm(props: AlmSpecificFormProps) {
break;
}
+ // This feature trigger will be replaced when SONAR-14349 is implemented
+ const monorepoEnabled = [EditionKey.enterprise, EditionKey.datacenter].includes(
+ appState.edition as EditionKey
+ );
+
return (
<>
{formFields}
@@ -301,3 +308,5 @@ export default function AlmSpecificForm(props: AlmSpecificFormProps) {
</>
);
}
+
+export default withAppStateContext(AlmSpecificForm);
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx
index 6c1369995f4..885689f50c9 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx
@@ -18,7 +18,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { connect } from 'react-redux';
import {
deleteProjectAlmBinding,
getAlmSettings,
@@ -34,24 +33,18 @@ import throwGlobalError from '../../../../app/utils/throwGlobalError';
import { withCurrentUser } from '../../../../components/hoc/withCurrentUser';
import { HttpStatus } from '../../../../helpers/request';
import { hasGlobalPermission } from '../../../../helpers/users';
-import { getAppState, Store } from '../../../../store/rootReducer';
import {
AlmKeys,
AlmSettingsInstance,
ProjectAlmBindingConfigurationErrors,
ProjectAlmBindingResponse
} from '../../../../types/alm-settings';
-import { EditionKey } from '../../../../types/editions';
import { Permissions } from '../../../../types/permissions';
import { Component, CurrentUser } from '../../../../types/types';
import PRDecorationBindingRenderer from './PRDecorationBindingRenderer';
type FormData = Omit<ProjectAlmBindingResponse, 'alm'>;
-interface StateProps {
- monorepoEnabled: boolean;
-}
-
interface Props {
component: Component;
currentUser: CurrentUser;
@@ -81,7 +74,7 @@ const REQUIRED_FIELDS_BY_ALM: {
[AlmKeys.GitLab]: ['repository']
};
-export class PRDecorationBinding extends React.PureComponent<Props & StateProps, State> {
+export class PRDecorationBinding extends React.PureComponent<Props, State> {
mounted = false;
state: State = {
formData: { key: '', monorepo: false },
@@ -343,7 +336,7 @@ export class PRDecorationBinding extends React.PureComponent<Props & StateProps,
};
render() {
- const { currentUser, monorepoEnabled } = this.props;
+ const { currentUser } = this.props;
return (
<PRDecorationBindingRenderer
@@ -351,7 +344,6 @@ export class PRDecorationBinding extends React.PureComponent<Props & StateProps,
onReset={this.handleReset}
onSubmit={this.handleSubmit}
onCheckConfiguration={this.handleCheckConfiguration}
- monorepoEnabled={monorepoEnabled}
isSysAdmin={hasGlobalPermission(currentUser, Permissions.Admin)}
{...this.state}
/>
@@ -359,11 +351,4 @@ export class PRDecorationBinding extends React.PureComponent<Props & StateProps,
}
}
-const mapStateToProps = (state: Store): StateProps => ({
- // This feature trigger will be replaced when SONAR-14349 is implemented
- monorepoEnabled: [EditionKey.enterprise, EditionKey.datacenter].includes(
- getAppState(state).edition as EditionKey
- )
-});
-
-export default connect(mapStateToProps)(withCurrentUser(PRDecorationBinding));
+export default withCurrentUser(PRDecorationBinding);
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx
index 2b113aef557..29162ee65e5 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx
@@ -50,7 +50,6 @@ export interface PRDecorationBindingRendererProps {
onSubmit: () => void;
updating: boolean;
successfullyUpdated: boolean;
- monorepoEnabled: boolean;
onCheckConfiguration: () => void;
checkingConfiguration: boolean;
configurationErrors?: ProjectAlmBindingConfigurationErrors;
@@ -78,7 +77,6 @@ export default function PRDecorationBindingRenderer(props: PRDecorationBindingRe
loading,
updating,
successfullyUpdated,
- monorepoEnabled,
checkingConfiguration,
configurationErrors,
isSysAdmin
@@ -169,7 +167,6 @@ export default function PRDecorationBindingRenderer(props: PRDecorationBindingRe
instances={instances}
formData={formData}
onFieldChange={props.onFieldChange}
- monorepoEnabled={monorepoEnabled}
/>
)}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/AlmSpecificForm-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/AlmSpecificForm-test.tsx
index 4a7151f0ada..cdc9538d83a 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/AlmSpecificForm-test.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/AlmSpecificForm-test.tsx
@@ -20,8 +20,10 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockAlmSettingsInstance } from '../../../../../helpers/mocks/alm-settings';
+import { mockAppState } from '../../../../../helpers/testMocks';
import { AlmKeys, AlmSettingsInstance } from '../../../../../types/alm-settings';
-import AlmSpecificForm, { AlmSpecificFormProps } from '../AlmSpecificForm';
+import { EditionKey } from '../../../../../types/editions';
+import { AlmSpecificForm, AlmSpecificFormProps } from '../AlmSpecificForm';
it.each([
[AlmKeys.Azure],
@@ -48,7 +50,9 @@ it.each([
);
it('should render the monorepo field when the feature is supported', () => {
- expect(shallowRender(AlmKeys.Azure, { monorepoEnabled: true })).toMatchSnapshot();
+ expect(
+ shallowRender(AlmKeys.Azure, { appState: mockAppState({ edition: EditionKey.enterprise }) })
+ ).toMatchSnapshot();
});
function shallowRender(alm: AlmKeys, props: Partial<AlmSpecificFormProps> = {}) {
@@ -63,7 +67,7 @@ function shallowRender(alm: AlmKeys, props: Partial<AlmSpecificFormProps> = {})
monorepo: false
}}
onFieldChange={jest.fn()}
- monorepoEnabled={false}
+ appState={mockAppState({ edition: EditionKey.developer })}
{...props}
/>
);
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx
index eeb1756458f..793ee25b46b 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx
@@ -420,7 +420,6 @@ function shallowRender(props: Partial<PRDecorationBinding['props']> = {}) {
<PRDecorationBinding
currentUser={mockCurrentUser()}
component={mockComponent({ key: PROJECT_KEY })}
- monorepoEnabled={false}
{...props}
/>
);
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx
index 96c6dc4d450..a4bf1454e6d 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx
@@ -154,7 +154,6 @@ function shallowRender(props: Partial<PRDecorationBindingRendererProps> = {}) {
onSubmit={jest.fn()}
updating={false}
successfullyUpdated={false}
- monorepoEnabled={false}
checkingConfiguration={false}
onCheckConfiguration={jest.fn()}
isSysAdmin={false}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBinding-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBinding-test.tsx.snap
index eb3d7ad054b..aa67734fdaf 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBinding-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBinding-test.tsx.snap
@@ -15,7 +15,6 @@ exports[`should render correctly 1`] = `
isSysAdmin={false}
isValid={false}
loading={true}
- monorepoEnabled={false}
onCheckConfiguration={[Function]}
onFieldChange={[Function]}
onReset={[Function]}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap
index 1ebb0bf76f4..f1bf70fdb61 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap
@@ -172,7 +172,7 @@ exports[`should render correctly: when there are configuration errors (admin use
/>
</div>
</div>
- <AlmSpecificForm
+ <withAppStateContext(AlmSpecificForm)
alm="github"
formData={
Object {
@@ -204,7 +204,6 @@ exports[`should render correctly: when there are configuration errors (admin use
},
]
}
- monorepoEnabled={false}
onFieldChange={[MockFunction]}
/>
<div
@@ -743,7 +742,7 @@ exports[`should render correctly: with a valid and saved form 1`] = `
/>
</div>
</div>
- <AlmSpecificForm
+ <withAppStateContext(AlmSpecificForm)
alm="github"
formData={
Object {
@@ -775,7 +774,6 @@ exports[`should render correctly: with a valid and saved form 1`] = `
},
]
}
- monorepoEnabled={false}
onFieldChange={[MockFunction]}
/>
<div
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 53cf6c5c629..486c80de4c3 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
@@ -18,12 +18,12 @@
* 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 { ClipboardButton } from '../../../components/controls/clipboard';
import { Alert } from '../../../components/ui/Alert';
import { toShortNotSoISOString } from '../../../helpers/dates';
import { translate } from '../../../helpers/l10n';
-import { getAppState, Store } from '../../../store/rootReducer';
+import { AppState } from '../../../types/types';
import PageActions from './PageActions';
export interface Props {
@@ -31,22 +31,14 @@ export interface Props {
loading: boolean;
logLevel: string;
onLogLevelChange: () => void;
- productionDatabase: boolean;
+ appState: AppState;
serverId?: string;
showActions: boolean;
version?: string;
}
export function PageHeader(props: Props) {
- const {
- isCluster,
- loading,
- logLevel,
- serverId,
- showActions,
- version,
- productionDatabase
- } = props;
+ const { isCluster, loading, logLevel, serverId, showActions, version, appState } = props;
return (
<header className="page-header">
<h1 className="page-title">{translate('system_info.page')}</h1>
@@ -67,7 +59,7 @@ export function PageHeader(props: Props) {
)}
{serverId && version && (
<div className="system-info-copy-paste-id-info boxed-group ">
- {!productionDatabase && (
+ {!appState.productionDatabase && (
<Alert className="width-100" variant="warning">
{translate('system.not_production_database_warning')}
</Alert>
@@ -109,8 +101,4 @@ Date: ${toShortNotSoISOString(Date.now())}
);
}
-const mapStateToProps = (store: Store) => ({
- productionDatabase: getAppState(store).productionDatabase
-});
-
-export default connect(mapStateToProps)(PageHeader);
+export default withAppStateContext(PageHeader);
diff --git a/server/sonar-web/src/main/js/apps/system/components/__tests__/PageHeader-test.tsx b/server/sonar-web/src/main/js/apps/system/components/__tests__/PageHeader-test.tsx
index b79bdf364e1..e8725a325af 100644
--- a/server/sonar-web/src/main/js/apps/system/components/__tests__/PageHeader-test.tsx
+++ b/server/sonar-web/src/main/js/apps/system/components/__tests__/PageHeader-test.tsx
@@ -19,6 +19,7 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
+import { mockAppState } from '../../../../helpers/testMocks';
import { PageHeader, Props } from '../PageHeader';
jest.mock('../../../../helpers/dates', () => ({
@@ -28,7 +29,11 @@ jest.mock('../../../../helpers/dates', () => ({
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
expect(
- shallowRender({ productionDatabase: false, serverId: 'foo-bar', version: '7.7.0.1234' })
+ shallowRender({
+ appState: mockAppState({ productionDatabase: false }),
+ serverId: 'foo-bar',
+ version: '7.7.0.1234'
+ })
).toMatchSnapshot('on embedded database');
expect(shallowRender({ loading: true, showActions: false })).toMatchSnapshot();
expect(shallowRender({ serverId: 'foo-bar', version: '7.7.0.1234' })).toMatchSnapshot();
@@ -41,7 +46,7 @@ function shallowRender(props: Partial<Props> = {}) {
loading={false}
logLevel="INFO"
onLogLevelChange={jest.fn()}
- productionDatabase={true}
+ appState={mockAppState({ productionDatabase: true })}
showActions={true}
{...props}
/>
diff --git a/server/sonar-web/src/main/js/apps/system/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/system/components/__tests__/__snapshots__/App-test.tsx.snap
index 9e6b4ffab23..274ea824bba 100644
--- a/server/sonar-web/src/main/js/apps/system/components/__tests__/__snapshots__/App-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/system/components/__tests__/__snapshots__/App-test.tsx.snap
@@ -16,11 +16,11 @@ exports[`should render correctly: cluster sysinfo 1`] = `
<div
className="page-notifs"
>
- <Connect(withCurrentUser(Connect(withAppState(UpdateNotification))))
+ <Connect(withCurrentUser(withAppStateContext(UpdateNotification)))
dismissable={false}
/>
</div>
- <Connect(PageHeader)
+ <withAppStateContext(PageHeader)
isCluster={true}
loading={false}
logLevel="DEBUG"
@@ -209,7 +209,7 @@ exports[`should render correctly: loading 1`] = `
<div
className="page-notifs"
>
- <Connect(withCurrentUser(Connect(withAppState(UpdateNotification))))
+ <Connect(withCurrentUser(withAppStateContext(UpdateNotification)))
dismissable={false}
/>
</div>
@@ -232,11 +232,11 @@ exports[`should render correctly: stand-alone sysinfo 1`] = `
<div
className="page-notifs"
>
- <Connect(withCurrentUser(Connect(withAppState(UpdateNotification))))
+ <Connect(withCurrentUser(withAppStateContext(UpdateNotification)))
dismissable={false}
/>
</div>
- <Connect(PageHeader)
+ <withAppStateContext(PageHeader)
isCluster={false}
loading={false}
logLevel="DEBUG"
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 344f0b1a1ce..1263177bf8f 100644
--- a/server/sonar-web/src/main/js/components/docs/DocLink.tsx
+++ b/server/sonar-web/src/main/js/components/docs/DocLink.tsx
@@ -19,13 +19,13 @@
*/
import * as React from 'react';
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 { withAppState } from '../hoc/withAppState';
interface OwnProps {
- appState: Pick<AppState, 'canAdmin'>;
+ appState: AppState;
customProps?: {
[k: string]: any;
};
@@ -66,14 +66,13 @@ export class DocLink extends React.PureComponent<Props> {
{children}
</SonarQubeAdminLink>
);
- } else {
- const url = '/documentation' + href;
- return (
- <Link to={url} {...other}>
- {children}
- </Link>
- );
}
+ const url = '/documentation' + href;
+ return (
+ <Link to={url} {...other}>
+ {children}
+ </Link>
+ );
}
return (
@@ -90,7 +89,7 @@ export class DocLink extends React.PureComponent<Props> {
}
}
-export default withAppState(DocLink);
+export default withAppStateContext(DocLink);
interface SonarCloudLinkProps {
children: React.ReactNode;
@@ -100,10 +99,9 @@ interface SonarCloudLinkProps {
function SonarCloudLink({ children, url }: SonarCloudLinkProps) {
if (!isSonarCloud()) {
return <>{children}</>;
- } else {
- const to = `/${url.substr(SONARCLOUD_LINK.length)}`;
- return <Link to={to}>{children}</Link>;
}
+ const to = `/${url.substr(SONARCLOUD_LINK.length)}`;
+ return <Link to={to}>{children}</Link>;
}
interface SonarQubeLinkProps {
@@ -114,14 +112,13 @@ interface SonarQubeLinkProps {
function SonarQubeLink({ children, url }: SonarQubeLinkProps) {
if (isSonarCloud()) {
return <>{children}</>;
- } else {
- const to = `/${url.substr(SONARQUBE_LINK.length)}`;
- return (
- <Link target="_blank" to={to}>
- {children}
- </Link>
- );
}
+ const to = `/${url.substr(SONARQUBE_LINK.length)}`;
+ return (
+ <Link target="_blank" to={to}>
+ {children}
+ </Link>
+ );
}
interface SonarQubeAdminLinkProps {
@@ -133,12 +130,11 @@ interface SonarQubeAdminLinkProps {
function SonarQubeAdminLink({ canAdmin, children, url }: SonarQubeAdminLinkProps) {
if (isSonarCloud() || !canAdmin) {
return <>{children}</>;
- } else {
- const to = `/${url.substr(SONARQUBE_ADMIN_LINK.length)}`;
- return (
- <Link target="_blank" to={to}>
- {children}
- </Link>
- );
}
+ const to = `/${url.substr(SONARQUBE_ADMIN_LINK.length)}`;
+ return (
+ <Link target="_blank" to={to}>
+ {children}
+ </Link>
+ );
}
diff --git a/server/sonar-web/src/main/js/components/docs/__tests__/DocLink-test.tsx b/server/sonar-web/src/main/js/components/docs/__tests__/DocLink-test.tsx
index 246452af3ca..86e613815e0 100644
--- a/server/sonar-web/src/main/js/components/docs/__tests__/DocLink-test.tsx
+++ b/server/sonar-web/src/main/js/components/docs/__tests__/DocLink-test.tsx
@@ -20,6 +20,7 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { isSonarCloud } from '../../../helpers/system';
+import { mockAppState } from '../../../helpers/testMocks';
import { DocLink } from '../DocLink';
jest.mock('../../../helpers/system', () => ({
@@ -29,7 +30,7 @@ jest.mock('../../../helpers/system', () => ({
it('should render simple link', () => {
expect(
shallow(
- <DocLink appState={{ canAdmin: false }} href="http://sample.com">
+ <DocLink appState={mockAppState({ canAdmin: false })} href="http://sample.com">
link text
</DocLink>
)
@@ -39,7 +40,7 @@ it('should render simple link', () => {
it('should render documentation link', () => {
expect(
shallow(
- <DocLink appState={{ canAdmin: false }} href="/foo/bar">
+ <DocLink appState={mockAppState({ canAdmin: false })} href="/foo/bar">
link text
</DocLink>
)
@@ -49,7 +50,7 @@ it('should render documentation link', () => {
it('should render sonarcloud link on sonarcloud', () => {
(isSonarCloud as jest.Mock).mockImplementationOnce(() => true);
const wrapper = shallow(
- <DocLink appState={{ canAdmin: false }} href="/#sonarcloud#/foo/bar">
+ <DocLink appState={mockAppState({ canAdmin: false })} href="/#sonarcloud#/foo/bar">
link text
</DocLink>
);
@@ -60,7 +61,7 @@ it('should render sonarcloud link on sonarcloud', () => {
it('should not render sonarcloud link on sonarcloud', () => {
(isSonarCloud as jest.Mock).mockImplementationOnce(() => false);
const wrapper = shallow(
- <DocLink appState={{ canAdmin: false }} href="/#sonarcloud#/foo/bar">
+ <DocLink appState={mockAppState({ canAdmin: false })} href="/#sonarcloud#/foo/bar">
link text
</DocLink>
);
@@ -69,7 +70,7 @@ it('should not render sonarcloud link on sonarcloud', () => {
it('should render sonarqube link on sonarqube', () => {
const wrapper = shallow(
- <DocLink appState={{ canAdmin: false }} href="/#sonarqube#/foo/bar">
+ <DocLink appState={mockAppState({ canAdmin: false })} href="/#sonarqube#/foo/bar">
link text
</DocLink>
);
@@ -80,7 +81,7 @@ it('should render sonarqube link on sonarqube', () => {
it('should not render sonarqube link on sonarcloud', () => {
(isSonarCloud as jest.Mock).mockImplementationOnce(() => true);
const wrapper = shallow(
- <DocLink appState={{ canAdmin: false }} href="/#sonarqube#/foo/bar">
+ <DocLink appState={mockAppState({ canAdmin: false })} href="/#sonarqube#/foo/bar">
link text
</DocLink>
);
@@ -89,7 +90,7 @@ it('should not render sonarqube link on sonarcloud', () => {
it('should render sonarqube admin link on sonarqube for admin', () => {
const wrapper = shallow(
- <DocLink appState={{ canAdmin: true }} href="/#sonarqube-admin#/foo/bar">
+ <DocLink appState={mockAppState({ canAdmin: true })} href="/#sonarqube-admin#/foo/bar">
link text
</DocLink>
);
@@ -99,7 +100,7 @@ it('should render sonarqube admin link on sonarqube for admin', () => {
it('should not render sonarqube admin link on sonarqube for non-admin', () => {
const wrapper = shallow(
- <DocLink appState={{ canAdmin: false }} href="/#sonarqube-admin#/foo/bar">
+ <DocLink appState={mockAppState({ canAdmin: false })} href="/#sonarqube-admin#/foo/bar">
link text
</DocLink>
);
@@ -109,7 +110,7 @@ it('should not render sonarqube admin link on sonarqube for non-admin', () => {
it('should not render sonarqube admin link on sonarcloud', () => {
(isSonarCloud as jest.Mock).mockImplementationOnce(() => true);
const wrapper = shallow(
- <DocLink appState={{ canAdmin: true }} href="/#sonarqube-admin#/foo/bar">
+ <DocLink appState={mockAppState({ canAdmin: true })} href="/#sonarqube-admin#/foo/bar">
link text
</DocLink>
);
@@ -119,7 +120,7 @@ it('should not render sonarqube admin link on sonarcloud', () => {
it('should render documentation anchor', () => {
expect(
shallow(
- <DocLink appState={{ canAdmin: false }} href="#quality-profiles">
+ <DocLink appState={mockAppState({ canAdmin: false })} href="#quality-profiles">
link text
</DocLink>
)
diff --git a/server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap
index e1250221011..e9acf7bd30b 100644
--- a/server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap
@@ -487,7 +487,7 @@ exports[`should render correctly: gitlab tutorial 1`] = `
exports[`should render correctly: jenkins tutorial 1`] = `
<Fragment>
- <Connect(JenkinsTutorial)
+ <Connect(withAppStateContext(JenkinsTutorial))
almBinding={
Object {
"alm": "github",
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 bdfbe237752..3d8c44c888b 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
@@ -20,12 +20,12 @@
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router';
+import withAppStateContext from '../../../../app/components/app-state/withAppStateContext';
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 { withAppState } from '../../../hoc/withAppState';
import SentenceWithHighlights from '../../components/SentenceWithHighlights';
export interface PublishStepsProps {
@@ -83,4 +83,4 @@ export function PublishSteps(props: PublishStepsProps) {
);
}
-export default withAppState(PublishSteps);
+export default withAppStateContext(PublishSteps);
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap
index 53c9be6755f..2b40b0cdf5f 100644
--- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap
@@ -117,7 +117,7 @@ unzip build-wrapper.zip"
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.ccpp"
/>
</li>
- <Connect(withAppState(PublishSteps)) />
+ <withAppStateContext(PublishSteps) />
</ol>
</Fragment>
`;
@@ -239,7 +239,7 @@ unzip build-wrapper.zip"
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.ccpp"
/>
</li>
- <Connect(withAppState(PublishSteps)) />
+ <withAppStateContext(PublishSteps) />
</ol>
</Fragment>
`;
@@ -361,7 +361,7 @@ Expand-Archive -Path 'build-wrapper.zip' -DestinationPath '.'"
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.ccpp"
/>
</li>
- <Connect(withAppState(PublishSteps)) />
+ <withAppStateContext(PublishSteps) />
</ol>
</Fragment>
`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/DotNet-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/DotNet-test.tsx.snap
index 5ca310653e6..ab15daebc76 100644
--- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/DotNet-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/DotNet-test.tsx.snap
@@ -34,7 +34,7 @@ exports[`should render correctly 1`] = `
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run"
/>
</li>
- <Connect(withAppState(PublishSteps)) />
+ <withAppStateContext(PublishSteps) />
</ol>
</Fragment>
`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap
index d46bb3b80f4..47f592f2def 100644
--- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap
@@ -42,7 +42,7 @@ exports[`should render correctly 1`] = `
/>
</li>
</ul>
- <Connect(withAppState(PublishSteps)) />
+ <withAppStateContext(PublishSteps) />
</ol>
</Fragment>
`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap
index 06c9d9ce809..3eaf0afd712 100644
--- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap
@@ -42,7 +42,7 @@ exports[`should render correctly 1`] = `
/>
</li>
</ul>
- <Connect(withAppState(PublishSteps)) />
+ <withAppStateContext(PublishSteps) />
</ol>
</Fragment>
`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/Other-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/Other-test.tsx.snap
index 44c9b28a3c9..87f7bf39c72 100644
--- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/Other-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/Other-test.tsx.snap
@@ -34,7 +34,7 @@ exports[`should render correctly 1`] = `
translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run"
/>
</li>
- <Connect(withAppState(PublishSteps)) />
+ <withAppStateContext(PublishSteps) />
</ol>
</Fragment>
`;
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 4605bebcc0c..31c332fa14f 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
@@ -19,8 +19,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 { withAppState } from '../../hoc/withAppState';
import { CompilationInfo } from '../components/CompilationInfo';
import CreateYmlFile from '../components/CreateYmlFile';
import { BuildTools } from '../types';
@@ -67,4 +67,4 @@ export function AnalysisCommand(props: AnalysisCommandProps) {
);
}
-export default withAppState(AnalysisCommand);
+export default withAppStateContext(AnalysisCommand);
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 a503bbc0baa..20b11cbb19d 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
@@ -18,11 +18,11 @@
* 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 } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
import { AlmKeys } from '../../../types/alm-settings';
import { AppState } from '../../../types/types';
-import { withAppState } from '../../hoc/withAppState';
import SentenceWithHighlights from './SentenceWithHighlights';
export interface AllSetProps {
@@ -96,4 +96,4 @@ export function AllSet(props: AllSetProps) {
);
}
-export default withAppState(AllSet);
+export default withAppStateContext(AllSet);
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/AllSetStep-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/AllSetStep-test.tsx.snap
index 81965c60b99..25a6c4c6240 100644
--- a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/AllSetStep-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/AllSetStep-test.tsx.snap
@@ -14,7 +14,7 @@ exports[`should render correctly: step content 1`] = `
<div
className="boxed-group-inner"
>
- <Connect(withAppState(AllSet))
+ <withAppStateContext(AllSet)
alm="azure"
willRefreshAutomatically={true}
/>
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 142b08f6b0d..4038f37237e 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
@@ -18,8 +18,8 @@
* 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 { AppState, Component } from '../../../types/types';
-import { withAppState } from '../../hoc/withAppState';
import { BuildTools } from '../types';
import CFamily from './commands/CFamily';
import DotNet from './commands/DotNet';
@@ -70,4 +70,4 @@ export function AnalysisCommand(props: AnalysisCommandProps) {
return null;
}
-export default withAppState(AnalysisCommand);
+export default withAppStateContext(AnalysisCommand);
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 85b65c8186a..bc6ec8c0039 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
@@ -19,10 +19,10 @@
*/
import * as React from 'react';
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 { withAppState } from '../../hoc/withAppState';
import FinishButton from '../components/FinishButton';
import GithubCFamilyExampleRepositories from '../components/GithubCFamilyExampleRepositories';
import Step from '../components/Step';
@@ -112,4 +112,4 @@ export function YmlFileStep(props: YmlFileStepProps) {
);
}
-export default withAppState(YmlFileStep);
+export default withAppStateContext(YmlFileStep);
diff --git a/server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/__snapshots__/GitLabCITutorial-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/__snapshots__/GitLabCITutorial-test.tsx.snap
index 6082843f8d0..e3e33091f2b 100644
--- a/server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/__snapshots__/GitLabCITutorial-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/__snapshots__/GitLabCITutorial-test.tsx.snap
@@ -78,7 +78,7 @@ exports[`should render correctly 1`] = `
onOpen={[Function]}
open={false}
/>
- <Connect(withAppState(YmlFileStep))
+ <withAppStateContext(YmlFileStep)
finished={false}
onDone={[Function]}
onOpen={[Function]}
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 e8ed96e6bb9..15ddfb7fa86 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
@@ -19,15 +19,16 @@
*/
import * as React from 'react';
import { connect } from 'react-redux';
+import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import { translate } from '../../../helpers/l10n';
-import { getAppState, getCurrentUserSetting, Store } from '../../../store/rootReducer';
+import { getCurrentUserSetting, Store } from '../../../store/rootReducer';
import { setCurrentUserSetting } from '../../../store/users';
import {
AlmKeys,
AlmSettingsInstance,
ProjectAlmBindingResponse
} from '../../../types/alm-settings';
-import { Component, CurrentUserSetting } from '../../../types/types';
+import { AppState, Component, CurrentUserSetting } from '../../../types/types';
import AllSetStep from '../components/AllSetStep';
import JenkinsfileStep from './JenkinsfileStep';
import MultiBranchPipelineStep from './MultiBranchPipelineStep';
@@ -39,7 +40,7 @@ import WebhookStep from './WebhookStep';
export interface JenkinsTutorialProps {
almBinding?: AlmSettingsInstance;
baseUrl: string;
- branchesEnabled: boolean;
+ appState: AppState;
component: Component;
projectBinding?: ProjectAlmBindingResponse;
setCurrentUserSetting: (setting: CurrentUserSetting) => void;
@@ -62,7 +63,7 @@ export function JenkinsTutorial(props: JenkinsTutorialProps) {
const {
almBinding,
baseUrl,
- branchesEnabled,
+ appState,
component,
projectBinding,
skipPreReqs,
@@ -101,7 +102,7 @@ export function JenkinsTutorial(props: JenkinsTutorialProps) {
<>
<PreRequisitesStep
alm={alm}
- branchesEnabled={branchesEnabled}
+ branchesEnabled={!!appState.branchesEnabled}
finished={step > Steps.PreRequisites}
onDone={() => setStep(Steps.MultiBranchPipeline)}
onOpen={() => setStep(Steps.PreRequisites)}
@@ -115,7 +116,7 @@ export function JenkinsTutorial(props: JenkinsTutorialProps) {
skipNextTime={skipPreReqs}
/>
- {branchesEnabled ? (
+ {appState.branchesEnabled ? (
<MultiBranchPipelineStep
alm={alm}
almBinding={almBinding}
@@ -138,7 +139,7 @@ export function JenkinsTutorial(props: JenkinsTutorialProps) {
<WebhookStep
alm={alm}
almBinding={almBinding}
- branchesEnabled={branchesEnabled}
+ branchesEnabled={!!appState.branchesEnabled}
finished={step > Steps.Webhook}
onDone={() => setStep(Steps.Jenkinsfile)}
onOpen={() => setStep(Steps.Webhook)}
@@ -167,15 +168,12 @@ export function JenkinsTutorial(props: JenkinsTutorialProps) {
);
}
-const mapStateToProps = (
- state: Store
-): Pick<JenkinsTutorialProps, 'branchesEnabled' | 'skipPreReqs'> => {
+const mapStateToProps = (state: Store): Pick<JenkinsTutorialProps, 'skipPreReqs'> => {
return {
- branchesEnabled: Boolean(getAppState(state).branchesEnabled),
skipPreReqs: getCurrentUserSetting(state, USER_SETTING_SKIP_BITBUCKET_PREREQS) === 'true'
};
};
const mapDispatchToProps = { setCurrentUserSetting };
-export default connect(mapStateToProps, mapDispatchToProps)(JenkinsTutorial);
+export default connect(mapStateToProps, mapDispatchToProps)(withAppStateContext(JenkinsTutorial));
diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-test.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-test.tsx
index 4fb31b5e9b7..beb1264b452 100644
--- a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-test.tsx
+++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-test.tsx
@@ -21,6 +21,7 @@ import { shallow } from 'enzyme';
import * as React from 'react';
import { mockProjectBitbucketBindingResponse } from '../../../../helpers/mocks/alm-settings';
import { mockComponent } from '../../../../helpers/mocks/component';
+import { mockAppState } from '../../../../helpers/testMocks';
import { AlmKeys } from '../../../../types/alm-settings';
import JenkinsfileStep from '../JenkinsfileStep';
import { JenkinsTutorial, JenkinsTutorialProps } from '../JenkinsTutorial';
@@ -31,7 +32,9 @@ import WebhookStep from '../WebhookStep';
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot('default');
- expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot('branches not enabled');
+ expect(shallowRender({ appState: mockAppState({ branchesEnabled: false }) })).toMatchSnapshot(
+ 'branches not enabled'
+ );
expect(shallowRender({ projectBinding: undefined })).toMatchSnapshot('no project binding');
});
@@ -133,7 +136,7 @@ function shallowRender(props: Partial<JenkinsTutorialProps> = {}) {
return shallow<JenkinsTutorialProps>(
<JenkinsTutorial
baseUrl=""
- branchesEnabled={true}
+ appState={mockAppState({ branchesEnabled: true })}
component={mockComponent()}
projectBinding={mockProjectBitbucketBindingResponse()}
setCurrentUserSetting={jest.fn()}
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 9af265ff697..6826d56c336 100644
--- a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx
+++ b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx
@@ -19,19 +19,19 @@
*/
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 { 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 { withAppState } from '../hoc/withAppState';
import { Alert, AlertVariant } from '../ui/Alert';
import SystemUpgradeItem from './SystemUpgradeItem';
import { UpdateUseCase } from './utils';
interface Props {
- appState: Pick<AppState, 'edition' | 'version'>;
+ appState: AppState;
onClose: () => void;
systemUpgrades: SystemUpgrade[][];
latestLTS: string;
@@ -120,4 +120,4 @@ export class SystemUpgradeForm extends React.PureComponent<Props, State> {
}
}
-export default withAppState(SystemUpgradeForm);
+export default withAppStateContext(SystemUpgradeForm);
diff --git a/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeForm-test.tsx b/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeForm-test.tsx
index 9de6742c42a..c3e37a9c693 100644
--- a/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeForm-test.tsx
+++ b/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgradeForm-test.tsx
@@ -19,6 +19,7 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
+import { mockAppState } from '../../../helpers/testMocks';
import { EditionKey } from '../../../types/editions';
import { SystemUpgradeForm } from '../SystemUpgradeForm';
import { UpdateUseCase } from '../utils';
@@ -76,7 +77,7 @@ it.each([...Object.values(UpdateUseCase), undefined])(
expect(
shallow(
<SystemUpgradeForm
- appState={{ edition: EditionKey.community, version: '5.6.3' }}
+ appState={mockAppState({ edition: EditionKey.community, version: '5.6.3' })}
onClose={jest.fn()}
systemUpgrades={UPGRADES}
latestLTS="9.1"
diff --git a/server/sonar-web/src/main/js/store/appState.ts b/server/sonar-web/src/main/js/store/appState.ts
index dfe4553cb3a..c3175010645 100644
--- a/server/sonar-web/src/main/js/store/appState.ts
+++ b/server/sonar-web/src/main/js/store/appState.ts
@@ -17,28 +17,22 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { AppState, Extension } from '../types/types';
+import { AppState } from '../types/types';
import { ActionType } from './utils/actions';
export const enum Actions {
SetAppState = 'SET_APP_STATE',
- SetAdminPages = 'SET_ADMIN_PAGES',
RequireAuthorization = 'REQUIRE_AUTHORIZATION'
}
export type Action =
| ActionType<typeof setAppState, Actions.SetAppState>
- | ActionType<typeof setAdminPages, Actions.SetAdminPages>
| ActionType<typeof requireAuthorization, Actions.RequireAuthorization>;
export function setAppState(appState: AppState) {
return { type: Actions.SetAppState, appState };
}
-export function setAdminPages(adminPages: Extension[]) {
- return { type: Actions.SetAdminPages, adminPages };
-}
-
export function requireAuthorization() {
return { type: Actions.RequireAuthorization };
}
@@ -57,9 +51,6 @@ export default function(state: AppState = defaultValue, action: Action): AppStat
if (action.type === Actions.SetAppState) {
return { ...state, ...action.appState };
}
- if (action.type === Actions.SetAdminPages) {
- return { ...state, adminPages: action.adminPages };
- }
if (action.type === Actions.RequireAuthorization) {
return { ...state, authorizationError: true };
}
diff --git a/server/sonar-web/src/main/js/types/types.ts b/server/sonar-web/src/main/js/types/types.ts
index 574818ce47b..01544faf61e 100644
--- a/server/sonar-web/src/main/js/types/types.ts
+++ b/server/sonar-web/src/main/js/types/types.ts
@@ -84,7 +84,6 @@ export interface AnalysisEvent {
}
export interface AppState {
- adminPages?: Extension[];
authenticationError?: boolean;
authorizationError?: boolean;
branchesEnabled?: boolean;