aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStas Vilchik <stas.vilchik@sonarsource.com>2018-12-12 10:39:12 +0100
committerSonarTech <sonartech@sonarsource.com>2018-12-12 20:21:03 +0100
commit7bba1341b905e96f9b85e965226b39ddc787dee2 (patch)
tree7ed409d7aa684082d290eb5bb53a41e73b8de636
parentc1caffa9fb8cabe9659d81372d82481edc00b545 (diff)
downloadsonarqube-7bba1341b905e96f9b85e965226b39ddc787dee2.tar.gz
sonarqube-7bba1341b905e96f9b85e965226b39ddc787dee2.zip
finish removing legacy react context (#1055)
-rw-r--r--server/sonar-web/src/main/js/app/components/App.tsx16
-rw-r--r--server/sonar-web/src/main/js/app/components/OnboardingContext.tsx24
-rw-r--r--server/sonar-web/src/main/js/app/components/StartupModal.tsx14
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.tsx18
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx3
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNav-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/Component.tsx13
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/AvailableSinceFacet.tsx18
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx15
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.tsx.snap6
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx29
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.tsx14
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/Sidebar-test.tsx.snap12
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationJustCreated.tsx13
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx10
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationJustCreated-test.tsx31
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx14
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityDateInput-test.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/EmptyInstance.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.tsx101
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx17
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/EmptyInstance-test.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/NoFavoriteProjects-test.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/NoFavoriteProjects-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsList-test.tsx.snap14
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilterContainer-test.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterFooter-test.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/securityReports/components/__tests__/App-test.tsx24
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TokenStep-test.tsx17
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingPage.tsx20
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OnboardingModal-test.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OnboardingModal-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-test.tsx5
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx37
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.tsx12
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/DuplicationPopup.tsx13
-rw-r--r--server/sonar-web/src/main/js/components/controls/DateInput.tsx30
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/DateInput-test.tsx8
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/DateInput-test.tsx.snap18
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/IssueMessage.tsx11
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx20
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/__tests__/IssueMessage-test.tsx1
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.tsx.snap18
-rw-r--r--server/sonar-web/src/main/js/components/workspace/Workspace.tsx14
-rw-r--r--server/sonar-web/src/main/js/components/workspace/context.ts8
-rw-r--r--server/sonar-web/src/main/js/helpers/testUtils.ts13
50 files changed, 364 insertions, 383 deletions
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 d3f99a4985b..c4c50beb7ee 100644
--- a/server/sonar-web/src/main/js/app/components/App.tsx
+++ b/server/sonar-web/src/main/js/app/components/App.tsx
@@ -18,7 +18,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import * as PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Helmet from 'react-helmet';
import { fetchLanguages } from '../../store/rootActions';
@@ -51,21 +50,6 @@ type Props = StateProps & DispatchProps & OwnProps;
class App extends React.PureComponent<Props> {
mounted = false;
- static childContextTypes = {
- branchesEnabled: PropTypes.bool.isRequired,
- canAdmin: PropTypes.bool.isRequired,
- organizationsEnabled: PropTypes.bool
- };
-
- getChildContext() {
- const { appState } = this.props;
- return {
- branchesEnabled: (appState && appState.branchesEnabled) || false,
- canAdmin: (appState && appState.canAdmin) || false,
- organizationsEnabled: (appState && appState.organizationsEnabled) || false
- };
- }
-
componentDidMount() {
this.mounted = true;
this.props.fetchLanguages();
diff --git a/server/sonar-web/src/main/js/app/components/OnboardingContext.tsx b/server/sonar-web/src/main/js/app/components/OnboardingContext.tsx
new file mode 100644
index 00000000000..9b8d000a9d3
--- /dev/null
+++ b/server/sonar-web/src/main/js/app/components/OnboardingContext.tsx
@@ -0,0 +1,24 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { createContext } from 'react';
+
+export type OnboardingContextShape = (organization?: T.Organization) => void;
+
+export const OnboardingContext = createContext<OnboardingContextShape>(() => {});
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 c26d26f032a..8d70aa9d802 100644
--- a/server/sonar-web/src/main/js/app/components/StartupModal.tsx
+++ b/server/sonar-web/src/main/js/app/components/StartupModal.tsx
@@ -18,9 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import * as PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withRouter, WithRouterProps } from 'react-router';
+import { OnboardingContext } from './OnboardingContext';
import { differenceInDays, parseDate, toShortNotSoISOString } from '../../helpers/dates';
import { getCurrentUser, getAppState, Store } from '../../store/rootReducer';
import { skipOnboarding } from '../../store/users';
@@ -75,16 +75,8 @@ interface State {
const LICENSE_PROMPT = 'sonarqube.license.prompt';
export class StartupModal extends React.PureComponent<Props, State> {
- static childContextTypes = {
- openProjectOnboarding: PropTypes.func
- };
-
state: State = { automatic: false };
- getChildContext() {
- return { openProjectOnboarding: this.openProjectOnboarding };
- }
-
componentDidMount() {
this.tryAutoOpenLicense().catch(this.tryAutoOpenOnboarding);
}
@@ -172,7 +164,7 @@ export class StartupModal extends React.PureComponent<Props, State> {
render() {
const { automatic, modal } = this.state;
return (
- <>
+ <OnboardingContext.Provider value={this.openProjectOnboarding}>
{this.props.children}
{modal === ModalKey.license && <LicensePromptModal onClose={this.closeLicense} />}
{modal === ModalKey.onboarding && (
@@ -188,7 +180,7 @@ export class StartupModal extends React.PureComponent<Props, State> {
{modal === ModalKey.teamOnboarding && (
<TeamOnboardingModal onFinish={this.closeOnboarding} />
)}
- </>
+ </OnboardingContext.Provider>
);
}
}
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 a136ce19518..ef1d79fe8ce 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
@@ -18,7 +18,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import * as PropTypes from 'prop-types';
import { connect } from 'react-redux';
import GlobalNavBranding, { SonarCloudNavBranding } from './GlobalNavBranding';
import GlobalNavMenu from './GlobalNavMenu';
@@ -32,6 +31,7 @@ import { lazyLoad } from '../../../../components/lazyLoad';
import { getCurrentUser, getAppState, Store } from '../../../../store/rootReducer';
import { isSonarCloud } from '../../../../helpers/system';
import { isLoggedIn } from '../../../../helpers/users';
+import { OnboardingContext } from '../../OnboardingContext';
import './GlobalNav.css';
const GlobalNavPlus = lazyLoad(() => import('./GlobalNavPlus'), 'GlobalNavPlus');
@@ -48,8 +48,6 @@ interface OwnProps {
type Props = StateProps & OwnProps;
export class GlobalNav extends React.PureComponent<Props> {
- static contextTypes = { openProjectOnboarding: PropTypes.func };
-
render() {
const { appState, currentUser } = this.props;
return (
@@ -63,11 +61,15 @@ export class GlobalNav extends React.PureComponent<Props> {
<EmbedDocsPopupHelper />
<Search appState={appState} currentUser={currentUser} />
{isLoggedIn(currentUser) && (
- <GlobalNavPlus
- appState={appState}
- currentUser={currentUser}
- openProjectOnboarding={this.context.openProjectOnboarding}
- />
+ <OnboardingContext.Consumer data-test="global-nav-plus">
+ {openProjectOnboarding => (
+ <GlobalNavPlus
+ appState={appState}
+ currentUser={currentUser}
+ openProjectOnboarding={openProjectOnboarding}
+ />
+ )}
+ </OnboardingContext.Consumer>
)}
<GlobalNavUserContainer appState={appState} currentUser={currentUser} />
</ul>
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx
index e090986643f..336d8734b38 100644
--- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx
@@ -28,11 +28,12 @@ import { translate } from '../../../../helpers/l10n';
import { isSonarCloud } from '../../../../helpers/system';
import { getPortfolioAdminUrl, getPortfolioUrl } from '../../../../helpers/urls';
import { hasGlobalPermission } from '../../../../helpers/users';
+import { OnboardingContextShape } from '../../OnboardingContext';
interface Props {
appState: Pick<T.AppState, 'qualifiers'>;
currentUser: T.LoggedInUser;
- openProjectOnboarding: () => void;
+ openProjectOnboarding: OnboardingContextShape;
}
interface State {
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 1656f723eda..edcf7972084 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
@@ -47,5 +47,5 @@ function runTest(mockedIsSonarCloud: boolean) {
);
expect(wrapper).toMatchSnapshot();
wrapper.setProps({ currentUser: { isLoggedIn: true } });
- expect(wrapper.find('GlobalNavPlus').exists()).toBe(true);
+ expect(wrapper.find('[data-test="global-nav-plus"]').exists()).toBe(true);
}
diff --git a/server/sonar-web/src/main/js/apps/code/components/Component.tsx b/server/sonar-web/src/main/js/apps/code/components/Component.tsx
index 9352241bd26..3e3dc6e6e3c 100644
--- a/server/sonar-web/src/main/js/apps/code/components/Component.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/Component.tsx
@@ -23,6 +23,7 @@ import ComponentName from './ComponentName';
import ComponentMeasure from './ComponentMeasure';
import ComponentLink from './ComponentLink';
import ComponentPin from './ComponentPin';
+import { WorkspaceContext } from '../../../components/workspace/context';
const TOP_OFFSET = 200;
const BOTTOM_OFFSET = 10;
@@ -90,7 +91,17 @@ export default class Component extends React.PureComponent<Props> {
switch (component.qualifier) {
case 'FIL':
case 'UTS':
- componentAction = <ComponentPin branchLike={branchLike} component={component} />;
+ componentAction = (
+ <WorkspaceContext.Consumer>
+ {({ openComponent }) => (
+ <ComponentPin
+ branchLike={branchLike}
+ component={component}
+ openComponent={openComponent}
+ />
+ )}
+ </WorkspaceContext.Consumer>
+ );
break;
default:
componentAction = <ComponentLink branchLike={branchLike} component={component} />;
diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx b/server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx
index f486ce21bf3..424c0d0f7b0 100644
--- a/server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx
@@ -18,27 +18,21 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import * as PropTypes from 'prop-types';
import PinIcon from '../../../components/icons-components/PinIcon';
-import { WorkspaceContext } from '../../../components/workspace/context';
+import { WorkspaceContextShape } from '../../../components/workspace/context';
import { translate } from '../../../helpers/l10n';
interface Props {
branchLike?: T.BranchLike;
component: T.ComponentMeasure;
+ openComponent: WorkspaceContextShape['openComponent'];
}
export default class ComponentPin extends React.PureComponent<Props> {
- context!: { workspace: WorkspaceContext };
-
- static contextTypes = {
- workspace: PropTypes.object.isRequired
- };
-
handleClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
event.preventDefault();
event.currentTarget.blur();
- this.context.workspace.openComponent({
+ this.props.openComponent({
branchLike: this.props.branchLike,
key: this.props.component.key,
name: this.props.component.path,
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/AvailableSinceFacet.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/AvailableSinceFacet.tsx
index 7bc746a447b..ebe5996c7e2 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/AvailableSinceFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/AvailableSinceFacet.tsx
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { intlShape } from 'react-intl';
+import { injectIntl, InjectedIntlProps } from 'react-intl';
import { Query } from '../query';
import DateInput from '../../../components/controls/DateInput';
import FacetBox from '../../../components/facet/FacetBox';
@@ -33,14 +33,14 @@ interface Props {
value?: Date;
}
-export default class AvailableSinceFacet extends React.PureComponent<Props> {
- static contextTypes = {
- intl: intlShape
+class AvailableSinceFacet extends React.PureComponent<Props & InjectedIntlProps> {
+ handleHeaderClick = () => {
+ this.props.onToggle('availableSince');
};
- handleHeaderClick = () => this.props.onToggle('availableSince');
-
- handleClear = () => this.props.onChange({ availableSince: undefined });
+ handleClear = () => {
+ this.props.onChange({ availableSince: undefined });
+ };
handlePeriodChange = (date: Date | undefined) => {
this.props.onChange({ availableSince: date });
@@ -48,7 +48,7 @@ export default class AvailableSinceFacet extends React.PureComponent<Props> {
getValues = () =>
this.props.value
- ? [this.context.intl.formatDate(this.props.value, longFormatterOption)]
+ ? [this.props.intl.formatDate(this.props.value, longFormatterOption)]
: undefined;
render() {
@@ -74,3 +74,5 @@ export default class AvailableSinceFacet extends React.PureComponent<Props> {
);
}
}
+
+export default injectIntl(AvailableSinceFacet);
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx
index 9f145e734af..41c3829f368 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx
@@ -19,7 +19,7 @@
*/
import * as React from 'react';
import * as classNames from 'classnames';
-import * as PropTypes from 'prop-types';
+import { injectIntl, InjectedIntlProps } from 'react-intl';
import DateFromNow from '../../../components/intl/DateFromNow';
import DateFormatter, { longFormatterOption } from '../../../components/intl/DateFormatter';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
@@ -34,13 +34,9 @@ interface Props {
period: T.Period;
}
-export default class LeakPeriodLegend extends React.PureComponent<Props> {
- static contextTypes = {
- intl: PropTypes.object.isRequired
- };
-
+export class LeakPeriodLegend extends React.PureComponent<Props & InjectedIntlProps> {
formatDate = (date: string) => {
- return this.context.intl.formatDate(date, longFormatterOption);
+ return this.props.intl.formatDate(date, longFormatterOption);
};
render() {
@@ -81,3 +77,5 @@ export default class LeakPeriodLegend extends React.PureComponent<Props> {
return <Tooltip overlay={tooltip}>{label}</Tooltip>;
}
}
+
+export default injectIntl(LeakPeriodLegend);
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx
index 3aa1046f6dc..bbb14e014d8 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx
@@ -19,7 +19,8 @@
*/
import * as React from 'react';
import { shallow } from 'enzyme';
-import LeakPeriodLegend from '../LeakPeriodLegend';
+import { InjectedIntlProps } from 'react-intl';
+import { LeakPeriodLegend } from '../LeakPeriodLegend';
import { differenceInDays } from '../../../../helpers/dates';
jest.mock('../../../../helpers/dates', () => {
@@ -69,9 +70,11 @@ it('should render a more precise date', () => {
});
function getWrapper(component: T.ComponentMeasure, period: T.Period) {
- return shallow(<LeakPeriodLegend component={component} period={period} />, {
- context: {
- intl: { formatDate: (date: string) => 'formatted.' + date }
- }
- });
+ return shallow(
+ <LeakPeriodLegend
+ component={component}
+ intl={{ formatDate: (x: any) => x } as InjectedIntlProps['intl']}
+ period={period}
+ />
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.tsx.snap
index 7a4677c1b7c..7de2ed12e3b 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.tsx.snap
@@ -51,7 +51,7 @@ exports[`should render correctly 1`] = `
<div
className="measure-details-primary-actions"
>
- <LeakPeriodLegend
+ <InjectIntl(LeakPeriodLegend)
className="spacer-left"
component={
Object {
@@ -105,7 +105,7 @@ exports[`should render correctly for leak 1`] = `
<div
className="measure-details-primary-actions"
>
- <LeakPeriodLegend
+ <InjectIntl(LeakPeriodLegend)
className="spacer-left"
component={
Object {
@@ -234,7 +234,7 @@ exports[`should work with measure without value 1`] = `
<div
className="measure-details-primary-actions"
>
- <LeakPeriodLegend
+ <InjectIntl(LeakPeriodLegend)
className="spacer-left"
component={
Object {
diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx
index ff574566f0d..85980a6ec2b 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx
@@ -18,10 +18,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { shallow } from 'enzyme';
import { App } from '../App';
-import { shallowWithIntl, waitAndUpdate } from '../../../../helpers/testUtils';
+import { waitAndUpdate } from '../../../../helpers/testUtils';
-const replace = jest.fn();
const issues = [
{ key: 'foo' } as T.Issue,
{ key: 'bar' } as T.Issue,
@@ -65,10 +65,7 @@ const PROPS = {
};
it('should render a list of issue', async () => {
- const wrapper = shallowWithIntl<App>(<App {...PROPS} />, {
- context: { router: { replace } }
- });
-
+ const wrapper = shallow<App>(<App {...PROPS} />);
await waitAndUpdate(wrapper);
expect(wrapper.state().issues.length).toBe(4);
expect(wrapper.state().referencedComponentsById).toEqual({ 'foo-uuid': referencedComponent });
@@ -76,10 +73,7 @@ it('should render a list of issue', async () => {
});
it('should be able to check/uncheck a group of issues with the Shift key', async () => {
- const wrapper = shallowWithIntl<App>(<App {...PROPS} />, {
- context: { router: { replace } }
- });
-
+ const wrapper = shallow<App>(<App {...PROPS} />);
await waitAndUpdate(wrapper);
expect(wrapper.state().issues.length).toBe(4);
@@ -98,10 +92,7 @@ it('should be able to check/uncheck a group of issues with the Shift key', async
});
it('should avoid non-existing keys', async () => {
- const wrapper = shallowWithIntl<App>(<App {...PROPS} />, {
- context: { router: { replace } }
- });
-
+ const wrapper = shallow<App>(<App {...PROPS} />);
await waitAndUpdate(wrapper);
expect(wrapper.state().issues.length).toBe(4);
@@ -114,10 +105,7 @@ it('should avoid non-existing keys', async () => {
});
it('should be able to uncheck all issue with global checkbox', async () => {
- const wrapper = shallowWithIntl<App>(<App {...PROPS} />, {
- context: { router: { replace } }
- });
-
+ const wrapper = shallow<App>(<App {...PROPS} />);
await waitAndUpdate(wrapper);
expect(wrapper.state().issues.length).toBe(4);
@@ -131,10 +119,7 @@ it('should be able to uncheck all issue with global checkbox', async () => {
});
it('should be able to check all issue with global checkbox', async () => {
- const wrapper = shallowWithIntl<App>(<App {...PROPS} />, {
- context: { router: { replace } }
- });
-
+ const wrapper = shallow<App>(<App {...PROPS} />);
await waitAndUpdate(wrapper);
const instance = wrapper.instance();
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.tsx
index 240d69eaf98..5a92120c88d 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.tsx
@@ -19,7 +19,7 @@
*/
import * as React from 'react';
import { max } from 'lodash';
-import { intlShape } from 'react-intl';
+import { injectIntl, InjectedIntlProps } from 'react-intl';
import { Query } from '../utils';
import FacetBox from '../../../components/facet/FacetBox';
import FacetHeader from '../../../components/facet/FacetHeader';
@@ -48,17 +48,13 @@ interface Props {
stats: { [x: string]: number } | undefined;
}
-export default class CreationDateFacet extends React.PureComponent<Props> {
+class CreationDateFacet extends React.PureComponent<Props & InjectedIntlProps> {
property = 'createdAt';
static defaultProps = {
open: true
};
- static contextTypes = {
- intl: intlShape
- };
-
hasValue = () =>
this.props.createdAfter !== undefined ||
this.props.createdAt.length > 0 ||
@@ -105,7 +101,7 @@ export default class CreationDateFacet extends React.PureComponent<Props> {
getValues() {
const { createdAfter, createdAt, createdBefore, createdInLast, sinceLeakPeriod } = this.props;
- const { formatDate } = this.context.intl;
+ const { formatDate } = this.props.intl;
const values = [];
if (createdAfter) {
values.push(formatDate(createdAfter, longFormatterOption));
@@ -144,7 +140,7 @@ export default class CreationDateFacet extends React.PureComponent<Props> {
return null;
}
- const { formatDate } = this.context.intl;
+ const { formatDate } = this.props.intl;
const data = periods.map((start, index) => {
const startDate = parseDate(start);
let endDate;
@@ -296,3 +292,5 @@ export default class CreationDateFacet extends React.PureComponent<Props> {
);
}
}
+
+export default injectIntl(CreationDateFacet);
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/Sidebar-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/Sidebar-test.tsx.snap
index bc2e66a808c..d12f3038760 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/Sidebar-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/Sidebar-test.tsx.snap
@@ -6,7 +6,7 @@ Array [
"SeverityFacet",
"ResolutionFacet",
"StatusFacet",
- "CreationDateFacet",
+ "InjectIntl(CreationDateFacet)",
"Connect(LanguageFacet)",
"RuleFacet",
"StandardFacet",
@@ -25,7 +25,7 @@ Array [
"SeverityFacet",
"ResolutionFacet",
"StatusFacet",
- "CreationDateFacet",
+ "InjectIntl(CreationDateFacet)",
"Connect(LanguageFacet)",
"RuleFacet",
"StandardFacet",
@@ -42,7 +42,7 @@ Array [
"SeverityFacet",
"ResolutionFacet",
"StatusFacet",
- "CreationDateFacet",
+ "InjectIntl(CreationDateFacet)",
"Connect(LanguageFacet)",
"RuleFacet",
"StandardFacet",
@@ -59,7 +59,7 @@ Array [
"SeverityFacet",
"ResolutionFacet",
"StatusFacet",
- "CreationDateFacet",
+ "InjectIntl(CreationDateFacet)",
"Connect(LanguageFacet)",
"RuleFacet",
"StandardFacet",
@@ -78,7 +78,7 @@ Array [
"SeverityFacet",
"ResolutionFacet",
"StatusFacet",
- "CreationDateFacet",
+ "InjectIntl(CreationDateFacet)",
"Connect(LanguageFacet)",
"RuleFacet",
"StandardFacet",
@@ -97,7 +97,7 @@ Array [
"SeverityFacet",
"ResolutionFacet",
"StatusFacet",
- "CreationDateFacet",
+ "InjectIntl(CreationDateFacet)",
"Connect(LanguageFacet)",
"RuleFacet",
"StandardFacet",
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationJustCreated.tsx b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationJustCreated.tsx
index 23751fc6dc4..89f1d217de5 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationJustCreated.tsx
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationJustCreated.tsx
@@ -18,25 +18,24 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { withRouter, WithRouterProps } from 'react-router';
import { Button } from '../../../components/ui/buttons';
import OnboardingProjectIcon from '../../../components/icons-components/OnboardingProjectIcon';
import OnboardingAddMembersIcon from '../../../components/icons-components/OnboardingAddMembersIcon';
import { translate } from '../../../helpers/l10n';
+import { OnboardingContextShape } from '../../../app/components/OnboardingContext';
+import { withRouter, Router } from '../../../components/hoc/withRouter';
import '../../tutorials/styles.css';
import './OrganizationJustCreated.css';
interface Props {
+ openProjectOnboarding: OnboardingContextShape;
organization: T.Organization;
+ router: Pick<Router, 'push'>;
}
-export class OrganizationJustCreated extends React.PureComponent<Props & WithRouterProps> {
- static contextTypes = {
- openProjectOnboarding: () => null
- };
-
+export class OrganizationJustCreated extends React.PureComponent<Props> {
handleNewProjectClick = () => {
- this.context.openProjectOnboarding(this.props.organization);
+ this.props.openProjectOnboarding(this.props.organization);
};
handleAddMembersClick = () => {
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx
index f0d281877eb..d8770331308 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx
@@ -32,6 +32,7 @@ import {
getMyOrganizations,
Store
} from '../../../store/rootReducer';
+import { OnboardingContext } from '../../../app/components/OnboardingContext';
interface OwnProps {
children?: React.ReactNode;
@@ -89,7 +90,14 @@ export class OrganizationPage extends React.PureComponent<Props, State> {
const { location } = this.props;
const justCreated = Boolean(location.state && location.state.justCreated);
return justCreated ? (
- <OrganizationJustCreated organization={organization} />
+ <OnboardingContext.Consumer>
+ {openProjectOnboarding => (
+ <OrganizationJustCreated
+ openProjectOnboarding={openProjectOnboarding}
+ organization={organization}
+ />
+ )}
+ </OnboardingContext.Consumer>
) : (
this.props.children
);
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationJustCreated-test.tsx b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationJustCreated-test.tsx
index 08ee42d00db..0bcc3631ed5 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationJustCreated-test.tsx
+++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationJustCreated-test.tsx
@@ -25,24 +25,39 @@ import { click } from '../../../../helpers/testUtils';
const organization: T.Organization = { key: 'foo', name: 'Foo' };
it('should render', () => {
- // @ts-ignore
- expect(shallow(<OrganizationJustCreated organization={organization} />)).toMatchSnapshot();
+ expect(
+ shallow(
+ <OrganizationJustCreated
+ openProjectOnboarding={jest.fn()}
+ organization={organization}
+ router={{ push: jest.fn() }}
+ />
+ )
+ ).toMatchSnapshot();
});
it('should create new project', () => {
const openProjectOnboarding = jest.fn();
- // @ts-ignore
- const wrapper = shallow(<OrganizationJustCreated organization={organization} />, {
- context: { openProjectOnboarding }
- });
+ const wrapper = shallow(
+ <OrganizationJustCreated
+ openProjectOnboarding={openProjectOnboarding}
+ organization={organization}
+ router={{ push: jest.fn() }}
+ />
+ );
click(wrapper.find('Button').first());
expect(openProjectOnboarding).toBeCalledWith({ key: 'foo', name: 'Foo' });
});
it('should add members', () => {
const router = { push: jest.fn() };
- // @ts-ignore
- const wrapper = shallow(<OrganizationJustCreated organization={organization} router={router} />);
+ const wrapper = shallow(
+ <OrganizationJustCreated
+ openProjectOnboarding={jest.fn()}
+ organization={organization}
+ router={router}
+ />
+ );
click(wrapper.find('Button').last());
expect(router.push).toBeCalledWith('/organizations/foo/members');
});
diff --git a/server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx b/server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx
index fd3615653aa..28e94e67a51 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import * as PropTypes from 'prop-types';
+import { injectIntl, InjectedIntlProps } from 'react-intl';
import DateFromNow from '../../../components/intl/DateFromNow';
import DateFormatter, { longFormatterOption } from '../../../components/intl/DateFormatter';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
@@ -31,13 +31,9 @@ interface Props {
period: T.Period;
}
-export default class LeakPeriodLegend extends React.PureComponent<Props> {
- static contextTypes = {
- intl: PropTypes.object.isRequired
- };
-
+export class LeakPeriodLegend extends React.PureComponent<Props & InjectedIntlProps> {
formatDate = (date: string) => {
- return this.context.intl.formatDate(date, longFormatterOption);
+ return this.props.intl.formatDate(date, longFormatterOption);
};
render() {
@@ -102,3 +98,5 @@ export default class LeakPeriodLegend extends React.PureComponent<Props> {
);
}
}
+
+export default injectIntl(LeakPeriodLegend);
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx
index f4a39fd5f13..48efcd8ce5d 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx
@@ -18,8 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { InjectedIntlProps } from 'react-intl';
import { shallow } from 'enzyme';
-import LeakPeriodLegend from '../LeakPeriodLegend';
+import { LeakPeriodLegend } from '../LeakPeriodLegend';
import { differenceInDays } from '../../../../helpers/dates';
jest.mock('../../../../helpers/dates', () => {
@@ -93,9 +94,10 @@ it('should render a more precise date', () => {
});
function getWrapper(period: T.Period) {
- return shallow(<LeakPeriodLegend period={period} />, {
- context: {
- intl: { formatDate: (date: string) => 'formatted.' + date }
- }
- });
+ return shallow(
+ <LeakPeriodLegend
+ intl={{ formatDate: (date: string) => 'formatted.' + date } as InjectedIntlProps['intl']}
+ period={period}
+ />
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityDateInput-test.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityDateInput-test.tsx
index fa65ca538d0..c2dacd4fe90 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityDateInput-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityDateInput-test.tsx
@@ -18,13 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { shallowWithIntl } from '../../../../helpers/testUtils';
+import { shallow } from 'enzyme';
import ProjectActivityDateInput from '../ProjectActivityDateInput';
import { parseDate } from '../../../../helpers/dates';
it('should render correctly the date inputs', () => {
expect(
- shallowWithIntl(
+ shallow(
<ProjectActivityDateInput
from={parseDate('2016-10-27T12:21:15+0000')}
onChange={() => {}}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/EmptyInstance.tsx b/server/sonar-web/src/main/js/apps/projects/components/EmptyInstance.tsx
index 790e0970db3..4ef97f6d99f 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/EmptyInstance.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/EmptyInstance.tsx
@@ -18,24 +18,21 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import * as PropTypes from 'prop-types';
import { translate } from '../../../helpers/l10n';
import { Button } from '../../../components/ui/buttons';
import { isSonarCloud } from '../../../helpers/system';
import { hasGlobalPermission, isLoggedIn } from '../../../helpers/users';
+import { OnboardingContextShape } from '../../../app/components/OnboardingContext';
interface Props {
- organization?: T.Organization;
currentUser: T.CurrentUser;
+ openProjectOnboarding: OnboardingContextShape;
+ organization?: T.Organization;
}
export default class EmptyInstance extends React.PureComponent<Props> {
- static contextTypes = {
- openProjectOnboarding: PropTypes.func
- };
-
analyzeNewProject = () => {
- this.context.openProjectOnboarding(this.props.organization);
+ this.props.openProjectOnboarding(this.props.organization);
};
render() {
diff --git a/server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.tsx b/server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.tsx
index c28dc6312b7..f192a900b14 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.tsx
@@ -20,7 +20,6 @@
import * as React from 'react';
import { Link } from 'react-router';
import { connect } from 'react-redux';
-import * as PropTypes from 'prop-types';
import { sortBy } from 'lodash';
import DropdownIcon from '../../../components/icons-components/DropdownIcon';
import Dropdown from '../../../components/controls/Dropdown';
@@ -29,67 +28,59 @@ import { Button } from '../../../components/ui/buttons';
import { getMyOrganizations, Store } from '../../../store/rootReducer';
import { isSonarCloud } from '../../../helpers/system';
import { translate } from '../../../helpers/l10n';
+import { OnboardingContextShape } from '../../../app/components/OnboardingContext';
+
+interface OwnProps {
+ openProjectOnboarding: OnboardingContextShape;
+}
interface StateProps {
organizations: T.Organization[];
}
-export class NoFavoriteProjects extends React.PureComponent<StateProps> {
- static contextTypes = {
- openProjectOnboarding: PropTypes.func
- };
-
- onAnalyzeProjectClick = () => {
- this.context.openProjectOnboarding();
- };
+export function NoFavoriteProjects(props: StateProps & OwnProps) {
+ return (
+ <div className="projects-empty-list">
+ <h3>{translate('projects.no_favorite_projects')}</h3>
+ {isSonarCloud() ? (
+ <div className="spacer-top">
+ <p>{translate('projects.no_favorite_projects.how_to_add_projects')}</p>
+ <div className="huge-spacer-top">
+ <Button onClick={props.openProjectOnboarding}>
+ {translate('provisioning.analyze_new_project')}
+ </Button>
- render() {
- const { organizations } = this.props;
- return (
- <div className="projects-empty-list">
- <h3>{translate('projects.no_favorite_projects')}</h3>
- {isSonarCloud() ? (
- <div className="spacer-top">
- <p>{translate('projects.no_favorite_projects.how_to_add_projects')}</p>
- <div className="huge-spacer-top">
- <Button onClick={this.onAnalyzeProjectClick}>
- {translate('provisioning.analyze_new_project')}
- </Button>
-
- <Dropdown
- className="display-inline-block big-spacer-left"
- overlay={
- <ul className="menu">
- {sortBy(organizations, org => org.name.toLowerCase()).map(organization => (
- <OrganizationListItem key={organization.key} organization={organization} />
- ))}
- </ul>
- }>
- <a className="button" href="#">
- {translate('projects.no_favorite_projects.favorite_projects_from_orgs')}
- <DropdownIcon className="little-spacer-left" />
- </a>
- </Dropdown>
- <Link className="button big-spacer-left" to="/explore/projects">
- {translate('projects.no_favorite_projects.favorite_public_projects')}
- </Link>
- </div>
- </div>
- ) : (
- <div>
- <p className="big-spacer-top">
- {translate('projects.no_favorite_projects.engagement')}
- </p>
- <p className="big-spacer-top">
- <Link className="button" to="/projects/all">
- {translate('projects.explore_projects')}
- </Link>
- </p>
+ <Dropdown
+ className="display-inline-block big-spacer-left"
+ overlay={
+ <ul className="menu">
+ {sortBy(props.organizations, org => org.name.toLowerCase()).map(organization => (
+ <OrganizationListItem key={organization.key} organization={organization} />
+ ))}
+ </ul>
+ }>
+ <a className="button" href="#">
+ {translate('projects.no_favorite_projects.favorite_projects_from_orgs')}
+ <DropdownIcon className="little-spacer-left" />
+ </a>
+ </Dropdown>
+ <Link className="button big-spacer-left" to="/explore/projects">
+ {translate('projects.no_favorite_projects.favorite_public_projects')}
+ </Link>
</div>
- )}
- </div>
- );
- }
+ </div>
+ ) : (
+ <div>
+ <p className="big-spacer-top">{translate('projects.no_favorite_projects.engagement')}</p>
+ <p className="big-spacer-top">
+ <Link className="button" to="/projects/all">
+ {translate('projects.explore_projects')}
+ </Link>
+ </p>
+ </div>
+ )}
+ </div>
+ );
}
const mapStateToProps = (state: Store): StateProps => ({
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx
index 8bb2f535070..f54e32f95d2 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx
@@ -28,6 +28,7 @@ import EmptyFavoriteSearch from './EmptyFavoriteSearch';
import EmptySearch from '../../../components/common/EmptySearch';
import { Project } from '../types';
import { Query } from '../query';
+import { OnboardingContext } from '../../../app/components/OnboardingContext';
interface Props {
cardType?: string;
@@ -50,9 +51,21 @@ export default class ProjectsList extends React.PureComponent<Props> {
return isFavorite ? <EmptyFavoriteSearch query={query} /> : <EmptySearch />;
}
return isFavorite ? (
- <NoFavoriteProjects />
+ <OnboardingContext.Consumer>
+ {openProjectOnboarding => (
+ <NoFavoriteProjects openProjectOnboarding={openProjectOnboarding} />
+ )}
+ </OnboardingContext.Consumer>
) : (
- <EmptyInstance currentUser={currentUser} organization={organization} />
+ <OnboardingContext.Consumer>
+ {openProjectOnboarding => (
+ <EmptyInstance
+ currentUser={currentUser}
+ openProjectOnboarding={openProjectOnboarding}
+ organization={organization}
+ />
+ )}
+ </OnboardingContext.Consumer>
);
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/EmptyInstance-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/EmptyInstance-test.tsx
index 6cc4deb62f0..667a36abe8a 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/EmptyInstance-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/EmptyInstance-test.tsx
@@ -29,12 +29,19 @@ jest.mock('../../../../helpers/system', () => ({
it('renders correctly for SQ', () => {
(isSonarCloud as jest.Mock<any>).mockReturnValue(false);
expect(
- shallow(<EmptyInstance currentUser={{ isLoggedIn: false }} organization={undefined} />)
+ shallow(
+ <EmptyInstance
+ currentUser={{ isLoggedIn: false }}
+ openProjectOnboarding={jest.fn()}
+ organization={undefined}
+ />
+ )
).toMatchSnapshot();
expect(
shallow(
<EmptyInstance
currentUser={{ isLoggedIn: true, permissions: { global: ['provisioning'] } }}
+ openProjectOnboarding={jest.fn()}
organization={undefined}
/>
)
@@ -47,6 +54,7 @@ it('renders correctly for SC', () => {
shallow(
<EmptyInstance
currentUser={{ isLoggedIn: false }}
+ openProjectOnboarding={jest.fn()}
organization={{ key: 'foo', name: 'Foo' }}
/>
)
@@ -55,6 +63,7 @@ it('renders correctly for SC', () => {
shallow(
<EmptyInstance
currentUser={{ isLoggedIn: false }}
+ openProjectOnboarding={jest.fn()}
organization={{ actions: { provision: true }, key: 'foo', name: 'Foo' }}
/>
)
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/NoFavoriteProjects-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/NoFavoriteProjects-test.tsx
index 0e0f2af3a93..35804e7f382 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/NoFavoriteProjects-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/NoFavoriteProjects-test.tsx
@@ -26,7 +26,9 @@ jest.mock('../../../../helpers/system', () => ({ isSonarCloud: jest.fn() }));
it('renders', () => {
(isSonarCloud as jest.Mock).mockImplementation(() => false);
- expect(shallow(<NoFavoriteProjects organizations={[]} />)).toMatchSnapshot();
+ expect(
+ shallow(<NoFavoriteProjects openProjectOnboarding={jest.fn()} organizations={[]} />)
+ ).toMatchSnapshot();
});
it('renders for SonarCloud', () => {
@@ -35,5 +37,7 @@ it('renders for SonarCloud', () => {
{ actions: { admin: true }, key: 'org1', name: 'org1', projectVisibility: 'public' },
{ actions: { admin: false }, key: 'org2', name: 'org2', projectVisibility: 'public' }
];
- expect(shallow(<NoFavoriteProjects organizations={organizations} />)).toMatchSnapshot();
+ expect(
+ shallow(<NoFavoriteProjects openProjectOnboarding={jest.fn()} organizations={organizations} />)
+ ).toMatchSnapshot();
});
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/NoFavoriteProjects-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/NoFavoriteProjects-test.tsx.snap
index 5b57d76dec3..d1b0a82b9c6 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/NoFavoriteProjects-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/NoFavoriteProjects-test.tsx.snap
@@ -46,7 +46,7 @@ exports[`renders for SonarCloud 1`] = `
className="huge-spacer-top"
>
<Button
- onClick={[Function]}
+ onClick={[MockFunction]}
>
provisioning.analyze_new_project
</Button>
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsList-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsList-test.tsx.snap
index bc326ba6477..14bf2862347 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsList-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsList-test.tsx.snap
@@ -21,13 +21,9 @@ exports[`renders different types of "no projects" 1`] = `
<div
className="projects-list"
>
- <EmptyInstance
- currentUser={
- Object {
- "isLoggedIn": true,
- }
- }
- />
+ <ContextConsumer>
+ <Component />
+ </ContextConsumer>
</div>
`;
@@ -43,6 +39,8 @@ exports[`renders different types of "no projects" 3`] = `
<div
className="projects-list"
>
- <Connect(NoFavoriteProjects) />
+ <ContextConsumer>
+ <Component />
+ </ContextConsumer>
</div>
`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilterContainer-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilterContainer-test.tsx
index ca70676d485..3af18a5a408 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilterContainer-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilterContainer-test.tsx
@@ -23,9 +23,7 @@ import SearchFilterContainer from '../SearchFilterContainer';
it('searches', () => {
const onQueryChange = jest.fn();
- const wrapper = shallow(<SearchFilterContainer onQueryChange={onQueryChange} query={{}} />, {
- context: { router: { push: jest.fn() } }
- });
+ const wrapper = shallow(<SearchFilterContainer onQueryChange={onQueryChange} query={{}} />);
expect(wrapper).toMatchSnapshot();
wrapper.find('SearchBox').prop<Function>('onChange')('foo');
expect(onQueryChange).toBeCalledWith({ search: 'foo' });
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterFooter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterFooter-test.tsx
index 4bc227b9949..25f2ecddd41 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterFooter-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterFooter-test.tsx
@@ -34,8 +34,7 @@ it('should render items without the ones in the facet', () => {
options={options}
property="languages"
query={{ languages: ['java'] }}
- />,
- { context: { router: { push: jest.fn() } } }
+ />
);
expect(wrapper.find('Select').prop('options')).toMatchSnapshot();
});
@@ -48,8 +47,7 @@ it('should render items without the ones in the facet', () => {
options={options}
property="languages"
query={{ languages: ['java'] }}
- />,
- { context: { router: { push: jest.fn() } } }
+ />
);
(wrapper.find('Select').prop('onChange') as Function)({ value: 'js' });
expect(onQueryChange).toBeCalledWith({ languages: 'java,js' });
diff --git a/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/App-test.tsx
index 983786ae8df..162ce640974 100644
--- a/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/App-test.tsx
@@ -85,7 +85,6 @@ const getSecurityHotspots = require('../../../../api/security-reports')
.getSecurityHotspots as jest.Mock<any>;
const component = { key: 'foo', name: 'Foo', qualifier: 'TRK' } as T.Component;
-const context = { router: { push: jest.fn() } };
const location = { pathname: 'foo', query: {} };
const locationWithCWE = { pathname: 'foo', query: { showCWE: 'true' } };
const owaspParams = { type: 'owasp_top_10' };
@@ -103,10 +102,7 @@ it('renders error on wrong type parameters', () => {
location={location}
params={wrongParams}
router={{ push: jest.fn() }}
- />,
- {
- context
- }
+ />
);
expect(wrapper).toMatchSnapshot();
});
@@ -118,10 +114,7 @@ it('renders owaspTop10', async () => {
location={location}
params={owaspParams}
router={{ push: jest.fn() }}
- />,
- {
- context
- }
+ />
);
await waitAndUpdate(wrapper);
expect(getSecurityHotspots).toBeCalledWith({
@@ -140,8 +133,7 @@ it('renders with cwe', () => {
location={locationWithCWE}
params={owaspParams}
router={{ push: jest.fn() }}
- />,
- { context }
+ />
);
expect(getSecurityHotspots).toBeCalledWith({
project: 'foo',
@@ -159,10 +151,7 @@ it('handle checkbox for cwe display', async () => {
location={location}
params={owaspParams}
router={{ push: jest.fn() }}
- />,
- {
- context
- }
+ />
);
expect(getSecurityHotspots).toBeCalledWith({
project: 'foo',
@@ -191,10 +180,7 @@ it('renders sansTop25', () => {
location={location}
params={sansParams}
router={{ push: jest.fn() }}
- />,
- {
- context
- }
+ />
);
expect(getSecurityHotspots).toBeCalledWith({
project: 'foo',
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TokenStep-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TokenStep-test.tsx
index 947b255b5f6..89266e206ec 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TokenStep-test.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TokenStep-test.tsx
@@ -18,14 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { shallow } from 'enzyme';
import TokenStep from '../TokenStep';
-import {
- change,
- click,
- submit,
- waitAndUpdate,
- shallowWithIntl
-} from '../../../../helpers/testUtils';
+import { change, click, submit, waitAndUpdate } from '../../../../helpers/testUtils';
jest.mock('../../../../api/user-tokens', () => ({
getTokens: () => Promise.resolve([{ name: 'foo' }]),
@@ -38,7 +33,7 @@ jest.mock('../../../../components/icons-components/ClearIcon');
const currentUser = { login: 'user' };
it('generates token', async () => {
- const wrapper = shallowWithIntl(
+ const wrapper = shallow(
<TokenStep
currentUser={currentUser}
finished={false}
@@ -58,7 +53,7 @@ it('generates token', async () => {
});
it('revokes token', async () => {
- const wrapper = shallowWithIntl(
+ const wrapper = shallow(
<TokenStep
currentUser={currentUser}
finished={false}
@@ -83,7 +78,7 @@ it('revokes token', async () => {
it('continues', async () => {
const onContinue = jest.fn();
- const wrapper = shallowWithIntl(
+ const wrapper = shallow(
<TokenStep
currentUser={currentUser}
finished={false}
@@ -101,7 +96,7 @@ it('continues', async () => {
it('uses existing token', async () => {
const onContinue = jest.fn();
- const wrapper = shallowWithIntl(
+ const wrapper = shallow(
<TokenStep
currentUser={currentUser}
finished={false}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx
index 1df6e78e993..1d30dfc7d8a 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx
@@ -48,10 +48,6 @@ export class OnboardingModal extends React.PureComponent<Props> {
}
}
- handleOpenProjectOnboarding = () => {
- this.props.onOpenProjectOnboarding();
- };
-
render() {
if (!isLoggedIn(this.props.currentUser)) {
return null;
@@ -70,7 +66,7 @@ export class OnboardingModal extends React.PureComponent<Props> {
<p className="spacer-top">{translate('onboarding.header.description')}</p>
</div>
<div className="modal-simple-body text-center onboarding-choices">
- <Button className="onboarding-choice" onClick={this.handleOpenProjectOnboarding}>
+ <Button className="onboarding-choice" onClick={this.props.onOpenProjectOnboarding}>
<OnboardingProjectIcon className="big-spacer-bottom" />
<h6 className="onboarding-choice-name">{translate('onboarding.analyze_your_code')}</h6>
</Button>
diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingPage.tsx b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingPage.tsx
index f8884dd68f1..2df79bd4a3f 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingPage.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingPage.tsx
@@ -18,12 +18,12 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import * as PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { InjectedRouter } from 'react-router';
import OnboardingModal from './OnboardingModal';
import { skipOnboarding } from '../../../store/users';
import TeamOnboardingModal from '../teamOnboarding/TeamOnboardingModal';
+import { OnboardingContext } from '../../../app/components/OnboardingContext';
interface DispatchProps {
skipOnboarding: () => void;
@@ -43,10 +43,6 @@ interface State {
}
export class OnboardingPage extends React.PureComponent<OwnProps & DispatchProps, State> {
- static contextTypes = {
- openProjectOnboarding: PropTypes.func.isRequired
- };
-
state: State = { modal: ModalKey.onboarding };
closeOnboarding = () => {
@@ -63,11 +59,15 @@ export class OnboardingPage extends React.PureComponent<OwnProps & DispatchProps
return (
<>
{modal === ModalKey.onboarding && (
- <OnboardingModal
- onClose={this.closeOnboarding}
- onOpenProjectOnboarding={this.context.openProjectOnboarding}
- onOpenTeamOnboarding={this.openTeamOnboarding}
- />
+ <OnboardingContext.Consumer>
+ {openProjectOnboarding => (
+ <OnboardingModal
+ onClose={this.closeOnboarding}
+ onOpenProjectOnboarding={openProjectOnboarding}
+ onOpenTeamOnboarding={this.openTeamOnboarding}
+ />
+ )}
+ </OnboardingContext.Consumer>
)}
{modal === ModalKey.teamOnboarding && (
<TeamOnboardingModal onFinish={this.closeOnboarding} />
diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OnboardingModal-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OnboardingModal-test.tsx
index 3ff570e8cde..24bd0ef5e63 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OnboardingModal-test.tsx
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OnboardingModal-test.tsx
@@ -39,15 +39,13 @@ it('should correctly open the different tutorials', () => {
const onClose = jest.fn();
const onOpenProjectOnboarding = jest.fn();
const onOpenTeamOnboarding = jest.fn();
- const push = jest.fn();
const wrapper = shallow(
<OnboardingModal
currentUser={{ isLoggedIn: true }}
onClose={onClose}
onOpenProjectOnboarding={onOpenProjectOnboarding}
onOpenTeamOnboarding={onOpenTeamOnboarding}
- />,
- { context: { router: { push } } }
+ />
);
click(wrapper.find('ResetButtonLink'));
diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OnboardingModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OnboardingModal-test.tsx.snap
index 423382e72c1..23ded61ecb6 100644
--- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OnboardingModal-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OnboardingModal-test.tsx.snap
@@ -25,7 +25,7 @@ exports[`renders correctly 1`] = `
>
<Button
className="onboarding-choice"
- onClick={[Function]}
+ onClick={[MockFunction]}
>
<OnboardingProjectIcon
className="big-spacer-bottom"
diff --git a/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-test.tsx b/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-test.tsx
index b706ad6a175..b973618c959 100644
--- a/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-test.tsx
+++ b/server/sonar-web/src/main/js/apps/users/__tests__/UsersApp-test.tsx
@@ -85,9 +85,6 @@ function getWrapper(props: Partial<UsersApp['props']> = {}) {
organizationsEnabled={true}
router={{ push: jest.fn() }}
{...props}
- />,
- {
- context: { router: {} }
- }
+ />
);
}
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx
index 34ce61ab17e..f3be5b6d6a0 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx
@@ -40,6 +40,7 @@ import {
import { isSameBranchLike, getBranchLikeQuery } from '../../helpers/branches';
import { translate } from '../../helpers/l10n';
import { Alert } from '../ui/Alert';
+import { WorkspaceContext } from '../workspace/context';
import './styles.css';
// TODO react-virtualized
@@ -607,14 +608,19 @@ export default class SourceViewerBase extends React.PureComponent<Props, State>
/* eslint-enable no-underscore-dangle */
return (
- <DuplicationPopup
- blocks={blocks}
- branchLike={this.props.branchLike}
- duplicatedFiles={duplicatedFiles}
- inRemovedComponent={inRemovedComponent}
- onClose={this.closeLinePopup}
- sourceViewerFile={component}
- />
+ <WorkspaceContext.Consumer>
+ {({ openComponent }) => (
+ <DuplicationPopup
+ blocks={blocks}
+ branchLike={this.props.branchLike}
+ duplicatedFiles={duplicatedFiles}
+ inRemovedComponent={inRemovedComponent}
+ onClose={this.closeLinePopup}
+ openComponent={openComponent}
+ sourceViewerFile={component}
+ />
+ )}
+ </WorkspaceContext.Consumer>
);
};
@@ -698,12 +704,15 @@ export default class SourceViewerBase extends React.PureComponent<Props, State>
return (
<div className={className} ref={node => (this.node = node)}>
- {this.state.component && (
- <SourceViewerHeader
- branchLike={this.props.branchLike}
- sourceViewerFile={this.state.component}
- />
- )}
+ <WorkspaceContext.Consumer>
+ {({ openComponent }) => (
+ <SourceViewerHeader
+ branchLike={this.props.branchLike}
+ openComponent={openComponent}
+ sourceViewerFile={component}
+ />
+ )}
+ </WorkspaceContext.Consumer>
{sourceRemoved && (
<Alert className="spacer-top" variant="warning">
{translate('code_viewer.no_source_code_displayed_due_to_source_removed')}
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.tsx b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.tsx
index 5bf963a9fb1..e63caa8ef21 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.tsx
@@ -20,7 +20,6 @@
import { stringify } from 'querystring';
import * as React from 'react';
import { Link } from 'react-router';
-import * as PropTypes from 'prop-types';
import MeasuresOverlay from './components/MeasuresOverlay';
import QualifierIcon from '../icons-components/QualifierIcon';
import Dropdown from '../controls/Dropdown';
@@ -28,7 +27,7 @@ import Favorite from '../controls/Favorite';
import ListIcon from '../icons-components/ListIcon';
import { ButtonIcon } from '../ui/buttons';
import { PopupPlacement } from '../ui/popups';
-import { WorkspaceContext } from '../workspace/context';
+import { WorkspaceContextShape } from '../workspace/context';
import {
getPathUrlAsString,
getBranchLikeUrl,
@@ -43,6 +42,7 @@ import { omitNil } from '../../helpers/request';
interface Props {
branchLike: T.BranchLike | undefined;
+ openComponent: WorkspaceContextShape['openComponent'];
sourceViewerFile: T.SourceViewerFile;
}
@@ -51,12 +51,6 @@ interface State {
}
export default class SourceViewerHeader extends React.PureComponent<Props, State> {
- context!: { workspace: WorkspaceContext };
-
- static contextTypes = {
- workspace: PropTypes.object.isRequired
- };
-
state: State = { measuresOverlay: false };
handleShowMeasuresClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
@@ -71,7 +65,7 @@ export default class SourceViewerHeader extends React.PureComponent<Props, State
openInWorkspace = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
event.preventDefault();
const { key } = this.props.sourceViewerFile;
- this.context.workspace.openComponent({ branchLike: this.props.branchLike, key });
+ this.props.openComponent({ branchLike: this.props.branchLike, key });
};
render() {
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/DuplicationPopup.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/DuplicationPopup.tsx
index 1f348fde012..da22591c654 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/DuplicationPopup.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/DuplicationPopup.tsx
@@ -19,12 +19,11 @@
*/
import * as React from 'react';
import { Link } from 'react-router';
-import * as PropTypes from 'prop-types';
import { groupBy, sortBy } from 'lodash';
import { DropdownOverlay } from '../../controls/Dropdown';
import QualifierIcon from '../../icons-components/QualifierIcon';
import { PopupPlacement } from '../../ui/popups';
-import { WorkspaceContext } from '../../workspace/context';
+import { WorkspaceContextShape } from '../../workspace/context';
import { isShortLivingBranch, isPullRequest } from '../../../helpers/branches';
import { translate } from '../../../helpers/l10n';
import { collapsedDirFromPath, fileFromPath } from '../../../helpers/path';
@@ -37,17 +36,11 @@ interface Props {
duplicatedFiles?: { [ref: string]: T.DuplicatedFile };
inRemovedComponent: boolean;
onClose: () => void;
- popupPosition?: any;
+ openComponent: WorkspaceContextShape['openComponent'];
sourceViewerFile: T.SourceViewerFile;
}
export default class DuplicationPopup extends React.PureComponent<Props> {
- context!: { workspace: WorkspaceContext };
-
- static contextTypes = {
- workspace: PropTypes.object.isRequired
- };
-
shouldLink() {
const { branchLike } = this.props;
return !isShortLivingBranch(branchLike) && !isPullRequest(branchLike);
@@ -65,7 +58,7 @@ export default class DuplicationPopup extends React.PureComponent<Props> {
event.currentTarget.blur();
const { key, line } = event.currentTarget.dataset;
if (this.shouldLink() && key) {
- this.context.workspace.openComponent({
+ this.props.openComponent({
branchLike: this.props.branchLike,
key,
line: line ? Number(line) : undefined
diff --git a/server/sonar-web/src/main/js/components/controls/DateInput.tsx b/server/sonar-web/src/main/js/components/controls/DateInput.tsx
index 4da2144f9ac..d1d57fffda8 100644
--- a/server/sonar-web/src/main/js/components/controls/DateInput.tsx
+++ b/server/sonar-web/src/main/js/components/controls/DateInput.tsx
@@ -20,7 +20,7 @@
import * as React from 'react';
import * as classNames from 'classnames';
import { DayModifiers, Modifier, Modifiers } from 'react-day-picker';
-import { intlShape, InjectedIntlProps } from 'react-intl';
+import { InjectedIntlProps, injectIntl } from 'react-intl';
import { range } from 'lodash';
import * as addMonths from 'date-fns/add_months';
import * as setMonth from 'date-fns/set_month';
@@ -41,7 +41,7 @@ import './styles.css';
const DayPicker = lazyLoad(() => import('react-day-picker'));
-export interface Props {
+interface Props {
className?: string;
currentMonth?: Date;
highlightFrom?: Date;
@@ -64,13 +64,8 @@ interface State {
type Week = [string, string, string, string, string, string, string];
export default class DateInput extends React.PureComponent<Props, State> {
- context!: InjectedIntlProps;
input?: HTMLInputElement | null;
- static contextTypes = {
- intl: intlShape
- };
-
constructor(props: Props) {
super(props);
this.state = { currentMonth: props.value || props.currentMonth || new Date(), open: false };
@@ -129,10 +124,7 @@ export default class DateInput extends React.PureComponent<Props, State> {
render() {
const { highlightFrom, highlightTo, minDate, value } = this.props;
- const { formatDate } = this.context.intl;
const { lastHovered } = this.state;
- const formattedValue =
- value && formatDate(value, { year: 'numeric', month: 'short', day: 'numeric' });
const after = this.props.maxDate || new Date();
@@ -158,17 +150,17 @@ export default class DateInput extends React.PureComponent<Props, State> {
return (
<OutsideClickHandler onClickOutside={this.closeCalendar}>
<span className={classNames('date-input-control', this.props.className)}>
- <input
+ <InputWrapper
className={classNames('date-input-control-input', this.props.inputClassName, {
'is-filled': this.props.value !== undefined
})}
+ innerRef={node => (this.input = node)}
name={this.props.name}
onFocus={this.openCalendar}
placeholder={this.props.placeholder}
readOnly={true}
- ref={node => (this.input = node)}
type="text"
- value={formattedValue || ''}
+ value={value}
/>
<CalendarIcon className="date-input-control-icon" fill="" />
{this.props.value !== undefined && (
@@ -230,3 +222,15 @@ export default class DateInput extends React.PureComponent<Props, State> {
function NullComponent() {
return null;
}
+
+type InputWrapperProps = T.Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value'> &
+ InjectedIntlProps & {
+ innerRef: React.Ref<HTMLInputElement>;
+ value: Date | undefined;
+ };
+
+const InputWrapper = injectIntl(({ innerRef, intl, value, ...other }: InputWrapperProps) => {
+ const formattedValue =
+ value && intl.formatDate(value, { year: 'numeric', month: 'short', day: 'numeric' });
+ return <input {...other} ref={innerRef} value={formattedValue || ''} />;
+});
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/DateInput-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/DateInput-test.tsx
index 4a009334233..9cb742d98ad 100644
--- a/server/sonar-web/src/main/js/components/controls/__tests__/DateInput-test.tsx
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/DateInput-test.tsx
@@ -18,13 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { shallow } from 'enzyme';
import * as addDays from 'date-fns/add_days';
import * as setMonth from 'date-fns/set_month';
import * as setYear from 'date-fns/set_year';
import * as subDays from 'date-fns/sub_days';
import * as subMonths from 'date-fns/sub_months';
-import DateInput, { Props } from '../DateInput';
-import { shallowWithIntl } from '../../../helpers/testUtils';
+import DateInput from '../DateInput';
import { parseDate } from '../../../helpers/dates';
jest.mock('../../lazyLoad', () => ({
@@ -111,8 +111,8 @@ it('should hightlightTo range', () => {
expect(dayPicker.prop('selectedDays')).toEqual([dateB]);
});
-function shallowRender(props?: Partial<Props>) {
- const wrapper = shallowWithIntl<DateInput>(
+function shallowRender(props?: Partial<DateInput['props']>) {
+ const wrapper = shallow<DateInput>(
<DateInput
currentMonth={dateA}
maxDate={dateB}
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/DateInput-test.tsx.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/DateInput-test.tsx.snap
index 5485b05984e..f4dc4048d63 100644
--- a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/DateInput-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/DateInput-test.tsx.snap
@@ -7,13 +7,13 @@ exports[`should render 1`] = `
<span
className="date-input-control"
>
- <input
+ <InjectIntl(Component)
className="date-input-control-input"
+ innerRef={[Function]}
onFocus={[Function]}
placeholder="placeholder"
readOnly={true}
type="text"
- value=""
/>
<CalendarIcon
className="date-input-control-icon"
@@ -30,13 +30,14 @@ exports[`should render 2`] = `
<span
className="date-input-control"
>
- <input
+ <InjectIntl(Component)
className="date-input-control-input is-filled"
+ innerRef={[Function]}
onFocus={[Function]}
placeholder="placeholder"
readOnly={true}
type="text"
- value="Jan 17, 2018"
+ value={2018-01-17T00:00:00.000Z}
/>
<CalendarIcon
className="date-input-control-icon"
@@ -62,13 +63,14 @@ exports[`should render 3`] = `
<span
className="date-input-control"
>
- <input
+ <InjectIntl(Component)
className="date-input-control-input is-filled"
+ innerRef={[Function]}
onFocus={[Function]}
placeholder="placeholder"
readOnly={true}
type="text"
- value="Jan 17, 2018"
+ value={2018-01-17T00:00:00.000Z}
/>
<CalendarIcon
className="date-input-control-icon"
@@ -269,13 +271,13 @@ exports[`should select a day 1`] = `
<span
className="date-input-control"
>
- <input
+ <InjectIntl(Component)
className="date-input-control-input"
+ innerRef={[Function]}
onFocus={[Function]}
placeholder="placeholder"
readOnly={true}
type="text"
- value=""
/>
<CalendarIcon
className="date-input-control-icon"
diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueMessage.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueMessage.tsx
index 226a6547d5f..8a6e4934ca5 100644
--- a/server/sonar-web/src/main/js/components/issue/components/IssueMessage.tsx
+++ b/server/sonar-web/src/main/js/components/issue/components/IssueMessage.tsx
@@ -22,25 +22,20 @@ import EllipsisIcon from '../../icons-components/EllipsisIcon';
import Tooltip from '../../controls/Tooltip';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { Button } from '../../ui/buttons';
-import { WorkspaceContext } from '../../workspace/context';
+import { WorkspaceContextShape } from '../../workspace/context';
interface Props {
engine?: string;
manualVulnerability: boolean;
message: string;
+ openRule: WorkspaceContextShape['openRule'];
organization: string;
rule: string;
}
export default class IssueMessage extends React.PureComponent<Props> {
- context!: { workspace: WorkspaceContext };
-
- static contextTypes = {
- workspace: () => null
- };
-
handleClick = () => {
- this.context.workspace.openRule({
+ this.props.openRule({
key: this.props.rule,
organization: this.props.organization
});
diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx
index 5f0f7d585cd..c6989ce39e4 100644
--- a/server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx
+++ b/server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx
@@ -29,6 +29,7 @@ import { getBranchLikeQuery } from '../../../helpers/branches';
import { getComponentIssuesUrl } from '../../../helpers/urls';
import { formatMeasure } from '../../../helpers/measures';
import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { WorkspaceContext } from '../../workspace/context';
interface Props {
branchLike?: T.BranchLike;
@@ -69,13 +70,18 @@ export default function IssueTitleBar(props: Props) {
return (
<div className="issue-row">
- <IssueMessage
- engine={issue.externalRuleEngine}
- manualVulnerability={issue.fromHotspot && issue.type === 'VULNERABILITY'}
- message={issue.message}
- organization={issue.organization}
- rule={issue.rule}
- />
+ <WorkspaceContext.Consumer>
+ {({ openRule }) => (
+ <IssueMessage
+ engine={issue.externalRuleEngine}
+ manualVulnerability={issue.fromHotspot && issue.type === 'VULNERABILITY'}
+ message={issue.message}
+ openRule={openRule}
+ organization={issue.organization}
+ rule={issue.rule}
+ />
+ )}
+ </WorkspaceContext.Consumer>
<div className="issue-row-meta">
<ul className="issue-meta-list">
diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueMessage-test.tsx b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueMessage-test.tsx
index 55b5241bacb..25c7a39daeb 100644
--- a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueMessage-test.tsx
+++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueMessage-test.tsx
@@ -26,6 +26,7 @@ it('should render with the message and a link to open the rule', () => {
<IssueMessage
manualVulnerability={false}
message="Reduce the number of conditional operators (4) used in the expression"
+ openRule={jest.fn()}
organization="myorg"
rule="javascript:S1067"
/>
diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.tsx.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.tsx.snap
index 09c82801eea..3fc0b16bfe3 100644
--- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.tsx.snap
@@ -10,12 +10,9 @@ exports[`should render the titlebar correctly 1`] = `
<div
className="issue-row"
>
- <IssueMessage
- manualVulnerability={false}
- message="Reduce the number of conditional operators (4) used in the expression"
- organization="myorg"
- rule="javascript:S1067"
- />
+ <ContextConsumer>
+ <Component />
+ </ContextConsumer>
<div
className="issue-row-meta"
>
@@ -109,12 +106,9 @@ exports[`should render the titlebar with the filter 1`] = `
<div
className="issue-row"
>
- <IssueMessage
- manualVulnerability={false}
- message="Reduce the number of conditional operators (4) used in the expression"
- organization="myorg"
- rule="javascript:S1067"
- />
+ <ContextConsumer>
+ <Component />
+ </ContextConsumer>
<div
className="issue-row-meta"
>
diff --git a/server/sonar-web/src/main/js/components/workspace/Workspace.tsx b/server/sonar-web/src/main/js/components/workspace/Workspace.tsx
index 3f35a5fdf9b..00896f5e86c 100644
--- a/server/sonar-web/src/main/js/components/workspace/Workspace.tsx
+++ b/server/sonar-web/src/main/js/components/workspace/Workspace.tsx
@@ -18,7 +18,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import * as PropTypes from 'prop-types';
import { omit, uniqBy } from 'lodash';
import { WorkspaceContext, ComponentDescriptor, RuleDescriptor } from './context';
import WorkspacePortal from './WorkspacePortal';
@@ -48,19 +47,11 @@ const TYPE_KEY = '__type__';
export default class Workspace extends React.PureComponent<{}, State> {
mounted = false;
- static childContextTypes = {
- workspace: PropTypes.object
- };
-
constructor(props: {}) {
super(props);
this.state = { height: INITIAL_HEIGHT, open: {}, ...this.loadWorkspace() };
}
- getChildContext = (): { workspace: WorkspaceContext } => {
- return { workspace: { openComponent: this.openComponent, openRule: this.openRule } };
- };
-
componentDidMount() {
this.mounted = true;
}
@@ -187,7 +178,8 @@ export default class Workspace extends React.PureComponent<{}, State> {
const height = this.state.maximized ? window.innerHeight * MAX_HEIGHT : this.state.height;
return (
- <>
+ <WorkspaceContext.Provider
+ value={{ openComponent: this.openComponent, openRule: this.openRule }}>
{this.props.children}
<WorkspacePortal>
{(components.length > 0 || rules.length > 0) && (
@@ -228,7 +220,7 @@ export default class Workspace extends React.PureComponent<{}, State> {
/>
)}
</WorkspacePortal>
- </>
+ </WorkspaceContext.Provider>
);
}
}
diff --git a/server/sonar-web/src/main/js/components/workspace/context.ts b/server/sonar-web/src/main/js/components/workspace/context.ts
index 0193e811238..4de9a9b6c7c 100644
--- a/server/sonar-web/src/main/js/components/workspace/context.ts
+++ b/server/sonar-web/src/main/js/components/workspace/context.ts
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { createContext } from 'react';
export interface ComponentDescriptor {
branchLike: T.BranchLike | undefined;
@@ -32,7 +33,12 @@ export interface RuleDescriptor {
organization: string;
}
-export interface WorkspaceContext {
+export interface WorkspaceContextShape {
openComponent: (component: ComponentDescriptor) => void;
openRule: (rule: RuleDescriptor) => void;
}
+
+export const WorkspaceContext = createContext<WorkspaceContextShape>({
+ openComponent: () => {},
+ openRule: () => {}
+});
diff --git a/server/sonar-web/src/main/js/helpers/testUtils.ts b/server/sonar-web/src/main/js/helpers/testUtils.ts
index ed1f5f42a4b..399363f6418 100644
--- a/server/sonar-web/src/main/js/helpers/testUtils.ts
+++ b/server/sonar-web/src/main/js/helpers/testUtils.ts
@@ -17,9 +17,8 @@
* 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, ShallowRendererProps, ShallowWrapper, ReactWrapper } from 'enzyme';
+import { ShallowWrapper, ReactWrapper } from 'enzyme';
import { InjectedRouter } from 'react-router';
-import { IntlProvider } from 'react-intl';
export const mockEvent = {
target: { blur() {} },
@@ -112,16 +111,6 @@ export function doAsync(fn?: Function): Promise<void> {
});
}
-// Create the IntlProvider to retrieve context for wrapping around.
-const intlProvider = new IntlProvider({ locale: 'en' }, {});
-const { intl } = intlProvider.getChildContext();
-export function shallowWithIntl<C extends React.Component>(
- node: React.ReactElement<any>,
- options: ShallowRendererProps = {}
-) {
- return shallow<C>(node, { ...options, context: { intl, ...options.context } });
-}
-
export async function waitAndUpdate(wrapper: ShallowWrapper<any, any> | ReactWrapper<any, any>) {
await new Promise(setImmediate);
wrapper.update();