aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2018-10-23 08:17:14 +0200
committerSonarTech <sonartech@sonarsource.com>2018-11-16 20:21:04 +0100
commitf0e05b42339d0736bf117c631bde4530cc1f356a (patch)
treec25d4714869dfcf3d61b6382e7b5221e52b2dc41 /server/sonar-web/src/main/js
parent9a64997f8446afe86429895b3795b2d61a448221 (diff)
downloadsonarqube-f0e05b42339d0736bf117c631bde4530cc1f356a.tar.gz
sonarqube-f0e05b42339d0736bf117c631bde4530cc1f356a.zip
SONAR-11324 Bind remote orgs to existing SonarCloud orgs
* Split personal org binding and public org binding * Remove BETA flag on tabs
Diffstat (limited to 'server/sonar-web/src/main/js')
-rw-r--r--server/sonar-web/src/main/js/app/styles/sonarcloud.css16
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/OrganizationAvatarInput.tsx (renamed from server/sonar-web/src/main/js/apps/create/organization/components/OrganizationAvatarInput.tsx)6
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/OrganizationDescriptionInput.tsx (renamed from server/sonar-web/src/main/js/apps/create/organization/components/OrganizationDescriptionInput.tsx)4
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/OrganizationKeyInput.tsx (renamed from server/sonar-web/src/main/js/apps/create/organization/components/OrganizationKeyInput.tsx)48
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/OrganizationNameInput.tsx (renamed from server/sonar-web/src/main/js/apps/create/organization/components/OrganizationNameInput.tsx)4
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/OrganizationSelect.tsx70
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/OrganizationUrlInput.tsx (renamed from server/sonar-web/src/main/js/apps/create/organization/components/OrganizationUrlInput.tsx)4
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationAvatarInput-test.tsx (renamed from server/sonar-web/src/main/js/apps/create/organization/components/__tests__/OrganizationAvatarInput-test.tsx)0
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationDescriptionInput-test.tsx (renamed from server/sonar-web/src/main/js/apps/create/organization/components/__tests__/OrganizationDescriptionInput-test.tsx)0
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationKeyInput-test.tsx (renamed from server/sonar-web/src/main/js/apps/create/organization/components/__tests__/OrganizationKeyInput-test.tsx)6
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationNameInput-test.tsx (renamed from server/sonar-web/src/main/js/apps/create/organization/components/__tests__/OrganizationNameInput-test.tsx)0
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationSelect-test.tsx (renamed from server/sonar-web/src/main/js/apps/create/project/__tests__/OrganizationSelect-test.tsx)12
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationUrlInput-test.tsx (renamed from server/sonar-web/src/main/js/apps/create/organization/components/__tests__/OrganizationUrlInput-test.tsx)0
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationAvatarInput-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/create/organization/components/__tests__/__snapshots__/OrganizationAvatarInput-test.tsx.snap)0
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationDescriptionInput-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/create/organization/components/__tests__/__snapshots__/OrganizationDescriptionInput-test.tsx.snap)0
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationKeyInput-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/create/organization/components/__tests__/__snapshots__/OrganizationKeyInput-test.tsx.snap)2
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationNameInput-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/create/organization/components/__tests__/__snapshots__/OrganizationNameInput-test.tsx.snap)0
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationSelect-test.tsx.snap68
-rw-r--r--server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationUrlInput-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/create/organization/components/__tests__/__snapshots__/OrganizationUrlInput-test.tsx.snap)0
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/AlmApplicationInstalling.tsx25
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationBind.tsx95
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx142
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/AutoPersonalOrganizationBind.tsx102
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx10
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx124
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/ManualOrganizationCreate.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsForm.tsx184
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsStep.tsx162
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationBind-test.tsx50
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx54
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoPersonalOrganizationBind-test.tsx69
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx17
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/ManualOrganizationCreate-test.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsForm-test.tsx84
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsStep-test.tsx96
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationBind-test.tsx.snap33
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap120
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoPersonalOrganizationBind-test.tsx.snap60
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap10
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap130
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ManualOrganizationCreate-test.tsx.snap27
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsForm-test.tsx.snap72
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsStep-test.tsx.snap235
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/AutoProjectCreate.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx13
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/ManualProjectCreate.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/OrganizationInput.tsx (renamed from server/sonar-web/src/main/js/apps/create/project/OrganizationSelect.tsx)39
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/ManualProjectCreate-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/OrganizationInput-test.tsx47
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AutoProjectCreate-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap18
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/ManualProjectCreate-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/OrganizationInput-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/OrganizationSelect-test.tsx.snap)51
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingPage.tsx10
54 files changed, 1458 insertions, 897 deletions
diff --git a/server/sonar-web/src/main/js/app/styles/sonarcloud.css b/server/sonar-web/src/main/js/app/styles/sonarcloud.css
index 748afde887a..3ad81d1225a 100644
--- a/server/sonar-web/src/main/js/app/styles/sonarcloud.css
+++ b/server/sonar-web/src/main/js/app/styles/sonarcloud.css
@@ -32,19 +32,3 @@
font-size: var(--hugeFontSize);
font-weight: bold;
}
-
-.beta-badge {
- display: inline-block;
- padding: 2px 4px;
- border: 1px solid var(--alertBorderInfo);
- border-radius: 2px;
- background-color: var(--alertBackgroundInfo);
- color: var(--alertTextInfo);
- font-size: 10px;
-}
-
-.beta-badge.is-muted {
- border-color: var(--disableGrayBorder);
- background-color: var(--disableGrayBg);
- color: var(--disableGrayText);
-}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/components/OrganizationAvatarInput.tsx b/server/sonar-web/src/main/js/apps/create/components/OrganizationAvatarInput.tsx
index 7d02df3735c..c0bcd44df9b 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/components/OrganizationAvatarInput.tsx
+++ b/server/sonar-web/src/main/js/apps/create/components/OrganizationAvatarInput.tsx
@@ -20,9 +20,9 @@
import * as React from 'react';
import * as classNames from 'classnames';
import { isWebUri } from 'valid-url';
-import ValidationInput from '../../../../components/controls/ValidationInput';
-import { translate } from '../../../../helpers/l10n';
-import OrganizationAvatar from '../../../../components/common/OrganizationAvatar';
+import ValidationInput from '../../../components/controls/ValidationInput';
+import { translate } from '../../../helpers/l10n';
+import OrganizationAvatar from '../../../components/common/OrganizationAvatar';
interface Props {
initialValue?: string;
diff --git a/server/sonar-web/src/main/js/apps/create/organization/components/OrganizationDescriptionInput.tsx b/server/sonar-web/src/main/js/apps/create/components/OrganizationDescriptionInput.tsx
index eaea25f97ad..567da282995 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/components/OrganizationDescriptionInput.tsx
+++ b/server/sonar-web/src/main/js/apps/create/components/OrganizationDescriptionInput.tsx
@@ -19,8 +19,8 @@
*/
import * as React from 'react';
import * as classNames from 'classnames';
-import ValidationInput from '../../../../components/controls/ValidationInput';
-import { translate } from '../../../../helpers/l10n';
+import ValidationInput from '../../../components/controls/ValidationInput';
+import { translate } from '../../../helpers/l10n';
interface Props {
initialValue?: string;
diff --git a/server/sonar-web/src/main/js/apps/create/organization/components/OrganizationKeyInput.tsx b/server/sonar-web/src/main/js/apps/create/components/OrganizationKeyInput.tsx
index a4fcc91d979..b88d6380411 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/components/OrganizationKeyInput.tsx
+++ b/server/sonar-web/src/main/js/apps/create/components/OrganizationKeyInput.tsx
@@ -20,10 +20,10 @@
import * as React from 'react';
import * as classNames from 'classnames';
import { debounce } from 'lodash';
-import { getOrganization } from '../../../../api/organizations';
-import ValidationInput from '../../../../components/controls/ValidationInput';
-import { translate } from '../../../../helpers/l10n';
-import { getHostUrl } from '../../../../helpers/urls';
+import { getOrganization } from '../../../api/organizations';
+import ValidationInput from '../../../components/controls/ValidationInput';
+import { translate } from '../../../helpers/l10n';
+import { getHostUrl } from '../../../helpers/urls';
interface Props {
initialValue?: string;
@@ -64,25 +64,27 @@ export default class OrganizationKeyInput extends React.PureComponent<Props, Sta
checkFreeKey = (key: string) => {
this.setState({ validating: true });
return getOrganization(key)
- .then(organization => organization === undefined, () => true)
- .then(
- free => {
- if (this.mounted) {
- if (!free) {
- this.setState({
- error: translate('onboarding.create_organization.organization_name.taken'),
- touched: true,
- validating: false
- });
- this.props.onChange(undefined);
- } else {
- this.setState({ error: undefined, validating: false });
- this.props.onChange(key);
- }
+ .then(organization => {
+ if (this.mounted) {
+ if (organization === undefined) {
+ this.setState({ error: undefined, validating: false });
+ this.props.onChange(key);
+ } else {
+ this.setState({
+ error: translate('onboarding.create_organization.organization_name.taken'),
+ touched: true,
+ validating: false
+ });
+ this.props.onChange(undefined);
}
- },
- () => {}
- );
+ }
+ })
+ .catch(() => {
+ if (this.mounted) {
+ this.setState({ error: undefined, validating: false });
+ this.props.onChange(key);
+ }
+ });
};
handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
@@ -130,7 +132,7 @@ export default class OrganizationKeyInput extends React.PureComponent<Props, Sta
{!this.props.readOnly && (
<input
autoFocus={true}
- className={classNames('input-super-large', 'text-middle', {
+ className={classNames('input-super-large', {
'is-invalid': isInvalid,
'is-valid': isValid
})}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/components/OrganizationNameInput.tsx b/server/sonar-web/src/main/js/apps/create/components/OrganizationNameInput.tsx
index 9e50b0cbb2e..3194befe9ac 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/components/OrganizationNameInput.tsx
+++ b/server/sonar-web/src/main/js/apps/create/components/OrganizationNameInput.tsx
@@ -19,8 +19,8 @@
*/
import * as React from 'react';
import * as classNames from 'classnames';
-import ValidationInput from '../../../../components/controls/ValidationInput';
-import { translate } from '../../../../helpers/l10n';
+import ValidationInput from '../../../components/controls/ValidationInput';
+import { translate } from '../../../helpers/l10n';
interface Props {
initialValue?: string;
diff --git a/server/sonar-web/src/main/js/apps/create/components/OrganizationSelect.tsx b/server/sonar-web/src/main/js/apps/create/components/OrganizationSelect.tsx
new file mode 100644
index 00000000000..8f7defd6730
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/components/OrganizationSelect.tsx
@@ -0,0 +1,70 @@
+/*
+ * 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 * as React from 'react';
+import { sortBy } from 'lodash';
+import Select from '../../../components/controls/Select';
+import { Organization } from '../../../app/types';
+import { translate } from '../../../helpers/l10n';
+import { sanitizeAlmId } from '../../../helpers/almIntegrations';
+import { getBaseUrl } from '../../../helpers/urls';
+
+interface Props {
+ onChange: (organization: Organization) => void;
+ organization: string;
+ organizations: Organization[];
+}
+
+export default function OrganizationSelect({ onChange, organization, organizations }: Props) {
+ return (
+ <Select
+ autoFocus={true}
+ className="input-super-large"
+ clearable={false}
+ id="select-organization"
+ labelKey="name"
+ onChange={onChange}
+ optionRenderer={optionRenderer}
+ options={sortBy(organizations, o => o.name.toLowerCase())}
+ placeholder={translate('onboarding.import_organization.choose_organization')}
+ required={true}
+ value={organization}
+ valueKey="key"
+ valueRenderer={optionRenderer}
+ />
+ );
+}
+
+export function optionRenderer(organization: Organization) {
+ const icon = organization.alm
+ ? `sonarcloud/${sanitizeAlmId(organization.alm.key)}`
+ : 'sonarcloud-square-logo';
+ return (
+ <span>
+ <img
+ alt={organization.alm ? organization.alm.key : 'SonarCloud'}
+ className="spacer-right"
+ height={14}
+ src={`${getBaseUrl()}/images/${icon}.svg`}
+ />
+ {organization.name}
+ <span className="note little-spacer-left">{organization.key}</span>
+ </span>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/components/OrganizationUrlInput.tsx b/server/sonar-web/src/main/js/apps/create/components/OrganizationUrlInput.tsx
index a77bdc99832..098224f7638 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/components/OrganizationUrlInput.tsx
+++ b/server/sonar-web/src/main/js/apps/create/components/OrganizationUrlInput.tsx
@@ -20,8 +20,8 @@
import * as React from 'react';
import * as classNames from 'classnames';
import { isWebUri } from 'valid-url';
-import ValidationInput from '../../../../components/controls/ValidationInput';
-import { translate } from '../../../../helpers/l10n';
+import ValidationInput from '../../../components/controls/ValidationInput';
+import { translate } from '../../../helpers/l10n';
interface Props {
initialValue?: string;
diff --git a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/OrganizationAvatarInput-test.tsx b/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationAvatarInput-test.tsx
index c7d7c24d01e..c7d7c24d01e 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/OrganizationAvatarInput-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationAvatarInput-test.tsx
diff --git a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/OrganizationDescriptionInput-test.tsx b/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationDescriptionInput-test.tsx
index eab1e2ca818..eab1e2ca818 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/OrganizationDescriptionInput-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationDescriptionInput-test.tsx
diff --git a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/OrganizationKeyInput-test.tsx b/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationKeyInput-test.tsx
index d559b30e4d0..ef1b459fbdc 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/OrganizationKeyInput-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationKeyInput-test.tsx
@@ -20,10 +20,10 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import OrganizationKeyInput from '../OrganizationKeyInput';
-import { getOrganization } from '../../../../../api/organizations';
-import { waitAndUpdate } from '../../../../../helpers/testUtils';
+import { getOrganization } from '../../../../api/organizations';
+import { waitAndUpdate } from '../../../../helpers/testUtils';
-jest.mock('../../../../../api/organizations', () => ({
+jest.mock('../../../../api/organizations', () => ({
getOrganization: jest.fn().mockResolvedValue(undefined)
}));
diff --git a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/OrganizationNameInput-test.tsx b/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationNameInput-test.tsx
index ecbfdb1f190..ecbfdb1f190 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/OrganizationNameInput-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationNameInput-test.tsx
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/OrganizationSelect-test.tsx b/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationSelect-test.tsx
index cc7e426bbc4..c78fede288a 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/OrganizationSelect-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationSelect-test.tsx
@@ -32,18 +32,6 @@ it('should render correctly', () => {
<OrganizationSelect onChange={jest.fn()} organization="bar" organizations={organizations} />
)
).toMatchSnapshot();
- expect(
- shallow(
- <OrganizationSelect
- autoImport={true}
- onChange={jest.fn()}
- organization="bar"
- organizations={organizations}
- />
- )
- .find('.js-new-org')
- .contains('onboarding.create_project.import_new_org')
- ).toBe(true);
});
it('should render options correctly', () => {
diff --git a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/OrganizationUrlInput-test.tsx b/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationUrlInput-test.tsx
index 357a912eda1..357a912eda1 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/OrganizationUrlInput-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationUrlInput-test.tsx
diff --git a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/__snapshots__/OrganizationAvatarInput-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationAvatarInput-test.tsx.snap
index 292c7b24b87..292c7b24b87 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/__snapshots__/OrganizationAvatarInput-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationAvatarInput-test.tsx.snap
diff --git a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/__snapshots__/OrganizationDescriptionInput-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationDescriptionInput-test.tsx.snap
index 80e11c04f11..80e11c04f11 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/__snapshots__/OrganizationDescriptionInput-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationDescriptionInput-test.tsx.snap
diff --git a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/__snapshots__/OrganizationKeyInput-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationKeyInput-test.tsx.snap
index 05d2e74dd68..c4441f50319 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/__snapshots__/OrganizationKeyInput-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationKeyInput-test.tsx.snap
@@ -18,7 +18,7 @@ exports[`should render correctly 1`] = `
</span>
<input
autoFocus={true}
- className="input-super-large text-middle"
+ className="input-super-large"
id="organization-key"
maxLength={255}
onBlur={[Function]}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/__snapshots__/OrganizationNameInput-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationNameInput-test.tsx.snap
index 1af9dc98684..1af9dc98684 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/__snapshots__/OrganizationNameInput-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationNameInput-test.tsx.snap
diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationSelect-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationSelect-test.tsx.snap
new file mode 100644
index 00000000000..0208b7589ba
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationSelect-test.tsx.snap
@@ -0,0 +1,68 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Select
+ autoFocus={true}
+ className="input-super-large"
+ clearable={false}
+ id="select-organization"
+ labelKey="name"
+ onChange={[MockFunction]}
+ optionRenderer={[Function]}
+ options={
+ Array [
+ Object {
+ "alm": Object {
+ "key": "github",
+ "url": "",
+ },
+ "key": "bar",
+ "name": "Bar",
+ },
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ },
+ ]
+ }
+ placeholder="onboarding.import_organization.choose_organization"
+ required={true}
+ value="bar"
+ valueKey="key"
+ valueRenderer={[Function]}
+/>
+`;
+
+exports[`should render options correctly 1`] = `
+<span>
+ <img
+ alt="SonarCloud"
+ className="spacer-right"
+ height={14}
+ src="/images/sonarcloud-square-logo.svg"
+ />
+ Foo
+ <span
+ className="note little-spacer-left"
+ >
+ foo
+ </span>
+</span>
+`;
+
+exports[`should render options correctly 2`] = `
+<span>
+ <img
+ alt="github"
+ className="spacer-right"
+ height={14}
+ src="/images/sonarcloud/github.svg"
+ />
+ Bar
+ <span
+ className="note little-spacer-left"
+ >
+ bar
+ </span>
+</span>
+`;
diff --git a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/__snapshots__/OrganizationUrlInput-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationUrlInput-test.tsx.snap
index d3f571b4db8..d3f571b4db8 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/components/__tests__/__snapshots__/OrganizationUrlInput-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationUrlInput-test.tsx.snap
diff --git a/server/sonar-web/src/main/js/apps/create/organization/AlmApplicationInstalling.tsx b/server/sonar-web/src/main/js/apps/create/organization/AlmApplicationInstalling.tsx
index 72c6dc0197d..86b8d778b5f 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/AlmApplicationInstalling.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/AlmApplicationInstalling.tsx
@@ -19,18 +19,23 @@
*/
import * as React from 'react';
import { translate } from '../../../helpers/l10n';
+import DeferredSpinner from '../../../components/common/DeferredSpinner';
export default function AlmApplicationInstalling({ almKey }: { almKey?: string }) {
return (
- <div className="sonarcloud page page-limited">
- <div className="huge-spacer-top text-center">
- <i className="spinner" />
- <p className="big-spacer-top">
- {almKey
- ? translate('onboarding.import_organization.installing', almKey)
- : translate('onboarding.import_organization.installing')}
- </p>
- </div>
- </div>
+ <DeferredSpinner
+ customSpinner={
+ <div className="sonarcloud page page-limited">
+ <div className="huge-spacer-top text-center">
+ <i className="spinner" />
+ <p className="big-spacer-top">
+ {almKey
+ ? translate('onboarding.import_organization.installing', almKey)
+ : translate('onboarding.import_organization.installing')}
+ </p>
+ </div>
+ </div>
+ }
+ />
);
}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationBind.tsx b/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationBind.tsx
new file mode 100644
index 00000000000..9511f639648
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationBind.tsx
@@ -0,0 +1,95 @@
+/*
+ * 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 * as React from 'react';
+import { Organization } from '../../../app/types';
+import OrganizationSelect from '../components/OrganizationSelect';
+import { SubmitButton } from '../../../components/ui/buttons';
+import { translate } from '../../../helpers/l10n';
+
+interface Props {
+ onBindOrganization: (organization: string) => Promise<void>;
+ unboundOrganizations: Organization[];
+}
+
+interface State {
+ organization: string;
+ submitting: boolean;
+}
+
+export default class AutoOrganizationBind extends React.PureComponent<Props, State> {
+ mounted = false;
+
+ constructor(props: Props) {
+ super(props);
+ this.state = { organization: this.getInitialSelectedOrganization(props), submitting: false };
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ getInitialSelectedOrganization(props: Props) {
+ if (props.unboundOrganizations.length === 1) {
+ return props.unboundOrganizations[0].key;
+ }
+ return '';
+ }
+
+ handleChange = ({ key }: Organization) => {
+ this.setState({ organization: key });
+ };
+
+ handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
+ event.preventDefault();
+ const { organization } = this.state;
+ if (organization) {
+ this.setState({ submitting: true });
+ this.props.onBindOrganization(organization).then(this.stopSubmitting, this.stopSubmitting);
+ }
+ };
+
+ stopSubmitting = () => {
+ if (this.mounted) {
+ this.setState({ submitting: false });
+ }
+ };
+
+ render() {
+ const { organization, submitting } = this.state;
+ return (
+ <form id="bind-organization-form" onSubmit={this.handleSubmit}>
+ <OrganizationSelect
+ onChange={this.handleChange}
+ organization={organization}
+ organizations={this.props.unboundOrganizations}
+ />
+ <div className="big-spacer-top">
+ <SubmitButton disabled={submitting || !organization}>
+ {translate('onboarding.import_organization.bind')}
+ </SubmitButton>
+ </div>
+ </form>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx b/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx
index 14fc63bdbf8..fc2b246d428 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx
@@ -19,7 +19,9 @@
*/
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
+import AutoOrganizationBind from './AutoOrganizationBind';
import ChooseRemoteOrganizationStep from './ChooseRemoteOrganizationStep';
+import OrganizationDetailsForm from './OrganizationDetailsForm';
import OrganizationDetailsStep from './OrganizationDetailsStep';
import {
AlmApplication,
@@ -27,10 +29,17 @@ import {
OrganizationBase,
Organization
} from '../../../app/types';
+import { bindAlmOrganization } from '../../../api/alm-integration';
+import { sanitizeAlmId } from '../../../helpers/almIntegrations';
import { getBaseUrl } from '../../../helpers/urls';
import { translate } from '../../../helpers/l10n';
-import { sanitizeAlmId } from '../../../helpers/almIntegrations';
-import OrganizationAvatar from '../../../components/common/OrganizationAvatar';
+import RadioToggle from '../../../components/controls/RadioToggle';
+
+export enum Filters {
+ Bind = 'bind',
+ Create = 'create',
+ None = 'none'
+}
interface Props {
almApplication: AlmApplication;
@@ -39,59 +48,68 @@ interface Props {
createOrganization: (
organization: OrganizationBase & { installationId?: string }
) => Promise<Organization>;
- importPersonalOrg?: Organization;
- onOrgCreated: (organization: string) => void;
- updateOrganization: (
- organization: OrganizationBase & { installationId?: string }
- ) => Promise<Organization>;
+ onOrgCreated: (organization: string, justCreated?: boolean) => void;
+ unboundOrganizations: Organization[];
+}
+
+interface State {
+ filter: Filters;
}
-export default class AutoOrganizationCreate extends React.PureComponent<Props> {
+export default class AutoOrganizationCreate extends React.PureComponent<Props, State> {
+ constructor(props: Props) {
+ super(props);
+ this.state = {
+ filter: props.unboundOrganizations.length === 0 ? Filters.Create : Filters.None
+ };
+ }
+
+ handleOptionChange = (filter: Filters) => {
+ this.setState({ filter });
+ };
+
handleCreateOrganization = (organization: Required<OrganizationBase>) => {
if (organization) {
- const { importPersonalOrg } = this.props;
- let promise: Promise<Organization>;
- if (importPersonalOrg) {
- promise = this.props.updateOrganization({
- avatar: organization.avatar,
- description: organization.description,
- installationId: this.props.almInstallId,
- key: importPersonalOrg.key,
- name: organization.name || organization.key,
- url: organization.url
- });
- } else {
- promise = this.props.createOrganization({
+ return this.props
+ .createOrganization({
avatar: organization.avatar,
description: organization.description,
installationId: this.props.almInstallId,
key: organization.key,
name: organization.name || organization.key,
url: organization.url
- });
- }
- return promise.then(({ key }) => this.props.onOrgCreated(key));
- } else {
- return Promise.reject();
+ })
+ .then(({ key }) => this.props.onOrgCreated(key));
+ }
+ return Promise.reject();
+ };
+
+ handleBindOrganization = (organization: string) => {
+ if (this.props.almInstallId) {
+ return bindAlmOrganization({
+ organization,
+ installationId: this.props.almInstallId
+ }).then(() => this.props.onOrgCreated(organization, false));
}
+ return Promise.reject();
};
render() {
- const { almApplication, almInstallId, almOrganization, importPersonalOrg } = this.props;
+ const { almApplication, almInstallId, almOrganization, unboundOrganizations } = this.props;
if (almInstallId && almOrganization) {
- const description = importPersonalOrg
- ? translate('onboarding.import_personal_organization_x')
- : translate('onboarding.import_organization_x');
- const submitText = importPersonalOrg
- ? translate('onboarding.import_organization.bind')
- : translate('my_account.create_organization');
+ const { filter } = this.state;
+ const hasUnboundOrgs = unboundOrganizations.length > 0;
return (
<OrganizationDetailsStep
- description={
- <p className="huge-spacer-bottom">
+ finished={false}
+ onOpen={() => {}}
+ open={true}
+ organization={almOrganization}>
+ <div className="huge-spacer-bottom">
+ <p className="big-spacer-bottom">
<FormattedMessage
- defaultMessage={description}
- id={description}
+ defaultMessage={translate('onboarding.import_organization_x')}
+ id="onboarding.import_organization_x"
values={{
avatar: (
<img
@@ -103,25 +121,47 @@ export default class AutoOrganizationCreate extends React.PureComponent<Props> {
width={16}
/>
),
- name: <strong>{almOrganization.name}</strong>,
- personalAvatar: importPersonalOrg && (
- <OrganizationAvatar organization={importPersonalOrg} small={true} />
- ),
- personalName: importPersonalOrg && <strong>{importPersonalOrg.name}</strong>
+ name: <strong>{almOrganization.name}</strong>
}}
/>
</p>
- }
- finished={false}
- keyReadOnly={Boolean(importPersonalOrg)}
- onContinue={this.handleCreateOrganization}
- onOpen={() => {}}
- open={true}
- organization={importPersonalOrg || almOrganization}
- submitText={submitText}
- />
+
+ {hasUnboundOrgs && (
+ <RadioToggle
+ name="filter"
+ onCheck={this.handleOptionChange}
+ options={[
+ {
+ label: translate('onboarding.import_organization.create_new'),
+ value: Filters.Create
+ },
+ {
+ label: translate('onboarding.import_organization.bind_existing'),
+ value: Filters.Bind
+ }
+ ]}
+ value={filter}
+ />
+ )}
+ </div>
+
+ {filter === Filters.Create && (
+ <OrganizationDetailsForm
+ onContinue={this.handleCreateOrganization}
+ organization={almOrganization}
+ submitText={translate('onboarding.import_organization.import')}
+ />
+ )}
+ {filter === Filters.Bind && (
+ <AutoOrganizationBind
+ onBindOrganization={this.handleBindOrganization}
+ unboundOrganizations={unboundOrganizations}
+ />
+ )}
+ </OrganizationDetailsStep>
);
}
+
return (
<ChooseRemoteOrganizationStep
almApplication={this.props.almApplication}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/AutoPersonalOrganizationBind.tsx b/server/sonar-web/src/main/js/apps/create/organization/AutoPersonalOrganizationBind.tsx
new file mode 100644
index 00000000000..47a65c594d6
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/organization/AutoPersonalOrganizationBind.tsx
@@ -0,0 +1,102 @@
+/*
+ * 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 * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import OrganizationDetailsForm from './OrganizationDetailsForm';
+import OrganizationDetailsStep from './OrganizationDetailsStep';
+import {
+ AlmApplication,
+ AlmOrganization,
+ OrganizationBase,
+ Organization
+} from '../../../app/types';
+import { getBaseUrl } from '../../../helpers/urls';
+import { translate } from '../../../helpers/l10n';
+import { sanitizeAlmId } from '../../../helpers/almIntegrations';
+import OrganizationAvatar from '../../../components/common/OrganizationAvatar';
+
+interface Props {
+ almApplication: AlmApplication;
+ almInstallId?: string;
+ almOrganization: AlmOrganization;
+ importPersonalOrg: Organization;
+ onOrgCreated: (organization: string) => void;
+ updateOrganization: (
+ organization: OrganizationBase & { installationId?: string }
+ ) => Promise<Organization>;
+}
+
+export default class AutoPersonalOrganizationBind extends React.PureComponent<Props> {
+ handleCreateOrganization = (organization: Required<OrganizationBase>) => {
+ if (organization) {
+ return this.props
+ .updateOrganization({
+ avatar: organization.avatar,
+ description: organization.description,
+ installationId: this.props.almInstallId,
+ key: this.props.importPersonalOrg.key,
+ name: organization.name || organization.key,
+ url: organization.url
+ })
+ .then(({ key }) => this.props.onOrgCreated(key));
+ } else {
+ return Promise.reject();
+ }
+ };
+
+ render() {
+ const { almApplication, importPersonalOrg } = this.props;
+ return (
+ <OrganizationDetailsStep
+ finished={false}
+ onOpen={() => {}}
+ open={true}
+ organization={importPersonalOrg}>
+ <p className="huge-spacer-bottom">
+ <FormattedMessage
+ defaultMessage={translate('onboarding.import_personal_organization_x')}
+ id="onboarding.import_personal_organization_x"
+ values={{
+ avatar: (
+ <img
+ alt={almApplication.name}
+ className="little-spacer-left"
+ src={`${getBaseUrl()}/images/sonarcloud/${sanitizeAlmId(almApplication.key)}.svg`}
+ width={16}
+ />
+ ),
+ name: <strong>{this.props.almOrganization.name}</strong>,
+ personalAvatar: importPersonalOrg && (
+ <OrganizationAvatar organization={importPersonalOrg} small={true} />
+ ),
+ personalName: importPersonalOrg && <strong>{importPersonalOrg.name}</strong>
+ }}
+ />
+ </p>
+ <OrganizationDetailsForm
+ keyReadOnly={true}
+ onContinue={this.handleCreateOrganization}
+ organization={importPersonalOrg}
+ submitText={translate('onboarding.import_organization.bind')}
+ />
+ </OrganizationDetailsStep>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx b/server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx
index 96318218442..80e80a5f193 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx
@@ -36,10 +36,10 @@ export default class ChooseRemoteOrganizationStep extends React.PureComponent<Pr
<div className="boxed-group-inner">
{almInstallId && (
<Alert className="markdown big-spacer-bottom width-60" variant="error">
- {translate('onboarding.create_organization.import_org_not_found')}
+ {translate('onboarding.import_organization.org_not_found')}
<ul>
- <li>{translate('onboarding.create_organization.import_org_not_found.tips_1')}</li>
- <li>{translate('onboarding.create_organization.import_org_not_found.tips_2')}</li>
+ <li>{translate('onboarding.import_organization.org_not_found.tips_1')}</li>
+ <li>{translate('onboarding.import_organization.org_not_found.tips_2')}</li>
</ul>
</Alert>
)}
@@ -49,7 +49,7 @@ export default class ChooseRemoteOrganizationStep extends React.PureComponent<Pr
small={true}
url={almApplication.installationUrl}>
{translate(
- 'onboarding.create_organization.choose_organization_button',
+ 'onboarding.import_organization.choose_organization_button',
almApplication.key
)}
</IdentityProviderLink>
@@ -70,7 +70,7 @@ export default class ChooseRemoteOrganizationStep extends React.PureComponent<Pr
renderForm={this.renderForm}
renderResult={this.renderResult}
stepNumber={1}
- stepTitle={translate('onboarding.create_organization.import_org_details')}
+ stepTitle={translate('onboarding.import_organization.import_org_details')}
/>
);
}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx b/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx
index f3301cf3e6b..81b219784cd 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx
@@ -18,7 +18,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import * as classNames from 'classnames';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { Helmet } from 'react-helmet';
@@ -27,6 +26,7 @@ import { Link, withRouter, WithRouterProps } from 'react-router';
import { formatPrice, parseQuery } from './utils';
import AlmApplicationInstalling from './AlmApplicationInstalling';
import AutoOrganizationCreate from './AutoOrganizationCreate';
+import AutoPersonalOrganizationBind from './AutoPersonalOrganizationBind';
import ManualOrganizationCreate from './ManualOrganizationCreate';
import DeferredSpinner from '../../../components/common/DeferredSpinner';
import Tabs from '../../../components/controls/Tabs';
@@ -141,10 +141,10 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
});
};
- handleOrgCreated = (organization: string) => {
+ handleOrgCreated = (organization: string, justCreated = true) => {
this.props.router.push({
pathname: getOrganizationUrl(organization),
- state: { justCreated: true }
+ state: { justCreated }
});
};
@@ -166,16 +166,78 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
});
};
- render() {
+ renderContent = (almInstallId?: string, importPersonalOrg?: Organization) => {
const { currentUser, location } = this.props;
+ const { almApplication, almOrganization } = this.state;
const state = (location.state || {}) as LocationState;
+
+ if (importPersonalOrg && almOrganization && almApplication) {
+ return (
+ <AutoPersonalOrganizationBind
+ almApplication={almApplication}
+ almInstallId={almInstallId}
+ almOrganization={almOrganization}
+ importPersonalOrg={importPersonalOrg}
+ onOrgCreated={this.handleOrgCreated}
+ updateOrganization={this.props.updateOrganization}
+ />
+ );
+ }
+
+ const showManualTab = state.tab === 'manual' && !almOrganization;
+ return (
+ <>
+ {almApplication && (
+ <Tabs<TabKeys>
+ onChange={this.onTabChange}
+ selected={showManualTab ? 'manual' : 'auto'}
+ tabs={[
+ {
+ key: 'auto',
+ node: translate('onboarding.import_organization', almApplication.key)
+ },
+ {
+ disabled: Boolean(almOrganization),
+ key: 'manual',
+ node: translate('onboarding.create_organization.create_manually')
+ }
+ ]}
+ />
+ )}
+
+ {showManualTab || !almApplication ? (
+ <ManualOrganizationCreate
+ createOrganization={this.props.createOrganization}
+ deleteOrganization={this.props.deleteOrganization}
+ onOrgCreated={this.handleOrgCreated}
+ onlyPaid={state.paid}
+ subscriptionPlans={this.state.subscriptionPlans}
+ />
+ ) : (
+ <AutoOrganizationCreate
+ almApplication={almApplication}
+ almInstallId={almInstallId}
+ almOrganization={almOrganization}
+ createOrganization={this.props.createOrganization}
+ onOrgCreated={this.handleOrgCreated}
+ unboundOrganizations={this.props.userOrganizations.filter(
+ o => !o.alm && o.key !== currentUser.personalOrganization
+ )}
+ />
+ )}
+ </>
+ );
+ };
+
+ render() {
+ const { currentUser, location } = this.props;
const query = parseQuery(location.query);
if (this.state.almOrgLoading) {
return <AlmApplicationInstalling almKey={query.almKey} />;
}
- const { almApplication, almOrganization, subscriptionPlans } = this.state;
+ const { almOrganization, subscriptionPlans } = this.state;
const importPersonalOrg = isPersonal(almOrganization)
? this.props.userOrganizations.find(o => o.key === currentUser.personalOrganization)
: undefined;
@@ -187,7 +249,6 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
: translate('onboarding.create_organization.page.description');
const startedPrice = subscriptionPlans && subscriptionPlans[0] && subscriptionPlans[0].price;
const formattedPrice = formatPrice(startedPrice);
- const showManualTab = state.tab === 'manual' && !almOrganization;
return (
<>
@@ -216,56 +277,7 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
{this.state.loading ? (
<DeferredSpinner />
) : (
- <>
- {almApplication &&
- !importPersonalOrg && (
- <Tabs<TabKeys>
- onChange={this.onTabChange}
- selected={showManualTab ? 'manual' : 'auto'}
- tabs={[
- {
- key: 'auto',
- node: (
- <>
- {translate('onboarding.import_organization', almApplication.key)}
- <span
- className={classNames('beta-badge spacer-left', {
- 'is-muted': showManualTab
- })}>
- {translate('beta')}
- </span>
- </>
- )
- },
- {
- disabled: Boolean(almOrganization),
- key: 'manual',
- node: translate('onboarding.create_organization.create_manually')
- }
- ]}
- />
- )}
-
- {showManualTab || !almApplication ? (
- <ManualOrganizationCreate
- createOrganization={this.props.createOrganization}
- deleteOrganization={this.props.deleteOrganization}
- onOrgCreated={this.handleOrgCreated}
- onlyPaid={state.paid}
- subscriptionPlans={this.state.subscriptionPlans}
- />
- ) : (
- <AutoOrganizationCreate
- almApplication={almApplication}
- almInstallId={query.almInstallId}
- almOrganization={almOrganization}
- createOrganization={this.props.createOrganization}
- importPersonalOrg={importPersonalOrg}
- onOrgCreated={this.handleOrgCreated}
- updateOrganization={this.props.updateOrganization}
- />
- )}
- </>
+ this.renderContent(query.almInstallId, importPersonalOrg)
)}
</div>
</>
diff --git a/server/sonar-web/src/main/js/apps/create/organization/ManualOrganizationCreate.tsx b/server/sonar-web/src/main/js/apps/create/organization/ManualOrganizationCreate.tsx
index 6bf245e9088..cda7e48af9e 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/ManualOrganizationCreate.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/ManualOrganizationCreate.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import OrganizationDetailsForm from './OrganizationDetailsForm';
import OrganizationDetailsStep from './OrganizationDetailsStep';
import PlanStep from './PlanStep';
import { formatPrice } from './utils';
@@ -108,12 +109,15 @@ export default class ManualOrganizationCreate extends React.PureComponent<Props,
<>
<OrganizationDetailsStep
finished={this.state.organization !== undefined}
- onContinue={this.handleOrganizationDetailsFinish}
onOpen={this.handleOrganizationDetailsStepOpen}
open={this.state.step === Step.OrganizationDetails}
- organization={this.state.organization}
- submitText={translate('continue')}
- />
+ organization={this.state.organization}>
+ <OrganizationDetailsForm
+ onContinue={this.handleOrganizationDetailsFinish}
+ organization={this.state.organization}
+ submitText={translate('continue')}
+ />
+ </OrganizationDetailsStep>
{subscriptionPlans !== undefined && (
<PlanStep
diff --git a/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsForm.tsx b/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsForm.tsx
new file mode 100644
index 00000000000..f6b4b5ac91b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsForm.tsx
@@ -0,0 +1,184 @@
+/*
+ * 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 * as React from 'react';
+import OrganizationAvatarInput from '../components/OrganizationAvatarInput';
+import OrganizationDescriptionInput from '../components/OrganizationDescriptionInput';
+import OrganizationKeyInput from '../components/OrganizationKeyInput';
+import OrganizationNameInput from '../components/OrganizationNameInput';
+import OrganizationUrlInput from '../components/OrganizationUrlInput';
+import DropdownIcon from '../../../components/icons-components/DropdownIcon';
+import { OrganizationBase } from '../../../app/types';
+import { ResetButtonLink, SubmitButton } from '../../../components/ui/buttons';
+import { translate } from '../../../helpers/l10n';
+
+type RequiredOrganization = Required<OrganizationBase>;
+
+interface Props {
+ keyReadOnly?: boolean;
+ onContinue: (organization: RequiredOrganization) => Promise<void>;
+ organization?: OrganizationBase & { key: string };
+ submitText: string;
+}
+
+interface State {
+ additional: boolean;
+ avatar?: string;
+ description?: string;
+ key?: string;
+ name?: string;
+ submitting: boolean;
+ url?: string;
+}
+
+type ValidState = Pick<State, Exclude<keyof State, RequiredOrganization>> & RequiredOrganization;
+
+export default class OrganizationDetailsForm extends React.PureComponent<Props, State> {
+ mounted = false;
+
+ constructor(props: Props) {
+ super(props);
+ const { organization } = props;
+ this.state = {
+ additional: false,
+ avatar: (organization && organization.avatar) || '',
+ description: (organization && organization.description) || '',
+ key: (organization && organization.key) || undefined,
+ name: (organization && organization.name) || '',
+ submitting: false,
+ url: (organization && organization.url) || ''
+ };
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ canSubmit(state: State): state is ValidState {
+ return Boolean(
+ state.key !== undefined &&
+ state.name !== undefined &&
+ state.description !== undefined &&
+ state.avatar !== undefined &&
+ state.url !== undefined
+ );
+ }
+
+ handleAdditionalClick = () => {
+ this.setState(state => ({ additional: !state.additional }));
+ };
+
+ handleKeyUpdate = (key: string | undefined) => {
+ this.setState({ key });
+ };
+
+ handleNameUpdate = (name: string | undefined) => {
+ this.setState({ name });
+ };
+
+ handleDescriptionUpdate = (description: string | undefined) => {
+ this.setState({ description });
+ };
+
+ handleAvatarUpdate = (avatar: string | undefined) => {
+ this.setState({ avatar });
+ };
+
+ handleUrlUpdate = (url: string | undefined) => {
+ this.setState({ url });
+ };
+
+ handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
+ event.preventDefault();
+ const { state } = this;
+ if (this.canSubmit(state)) {
+ this.setState({ submitting: true });
+ this.props
+ .onContinue({
+ avatar: state.avatar,
+ description: state.description,
+ key: state.key,
+ name: state.name,
+ url: state.url
+ })
+ .then(this.stopSubmitting, this.stopSubmitting);
+ }
+ };
+
+ stopSubmitting = () => {
+ if (this.mounted) {
+ this.setState({ submitting: false });
+ }
+ };
+
+ render() {
+ return (
+ <form id="organization-form" onSubmit={this.handleSubmit}>
+ <OrganizationKeyInput
+ initialValue={this.state.key}
+ onChange={this.handleKeyUpdate}
+ readOnly={this.props.keyReadOnly}
+ />
+ <div className="big-spacer-top">
+ <ResetButtonLink onClick={this.handleAdditionalClick}>
+ {translate(
+ this.state.additional
+ ? 'onboarding.create_organization.hide_additional_info'
+ : 'onboarding.create_organization.add_additional_info'
+ )}
+ <DropdownIcon className="little-spacer-left" turned={this.state.additional} />
+ </ResetButtonLink>
+ </div>
+ <div className="js-additional-info" hidden={!this.state.additional}>
+ <div className="big-spacer-top">
+ <OrganizationNameInput
+ initialValue={this.state.name}
+ onChange={this.handleNameUpdate}
+ />
+ </div>
+ <div className="big-spacer-top">
+ <OrganizationAvatarInput
+ initialValue={this.state.avatar}
+ name={this.state.name}
+ onChange={this.handleDescriptionUpdate}
+ />
+ </div>
+ <div className="big-spacer-top">
+ <OrganizationDescriptionInput
+ initialValue={this.state.description}
+ onChange={this.handleAvatarUpdate}
+ />
+ </div>
+ <div className="big-spacer-top">
+ <OrganizationUrlInput initialValue={this.state.url} onChange={this.handleUrlUpdate} />
+ </div>
+ </div>
+ <div className="big-spacer-top">
+ <SubmitButton disabled={this.state.submitting || !this.canSubmit(this.state)}>
+ {this.props.submitText}
+ </SubmitButton>
+ </div>
+ </form>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsStep.tsx b/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsStep.tsx
index d6e5f0696a1..b10abe71323 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsStep.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsStep.tsx
@@ -18,177 +18,21 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import OrganizationAvatarInput from './components/OrganizationAvatarInput';
-import OrganizationDescriptionInput from './components/OrganizationDescriptionInput';
-import OrganizationKeyInput from './components/OrganizationKeyInput';
-import OrganizationNameInput from './components/OrganizationNameInput';
-import OrganizationUrlInput from './components/OrganizationUrlInput';
import Step from '../../tutorials/components/Step';
import { translate } from '../../../helpers/l10n';
-import { ResetButtonLink, SubmitButton } from '../../../components/ui/buttons';
import AlertSuccessIcon from '../../../components/icons-components/AlertSuccessIcon';
-import DropdownIcon from '../../../components/icons-components/DropdownIcon';
import { OrganizationBase } from '../../../app/types';
-type RequiredOrganization = Required<OrganizationBase>;
-
interface Props {
- description?: React.ReactNode;
+ children: React.ReactNode;
finished: boolean;
- keyReadOnly?: boolean;
- onContinue: (organization: RequiredOrganization) => Promise<void>;
onOpen: () => void;
open: boolean;
organization?: OrganizationBase & { key: string };
- submitText: string;
-}
-
-interface State {
- additional: boolean;
- avatar?: string;
- description?: string;
- key?: string;
- name?: string;
- submitting: boolean;
- url?: string;
}
-
-type ValidState = Pick<State, Exclude<keyof State, RequiredOrganization>> & RequiredOrganization;
-
-export default class OrganizationDetailsStep extends React.PureComponent<Props, State> {
- mounted = false;
-
- constructor(props: Props) {
- super(props);
- const { organization } = props;
- this.state = {
- additional: false,
- avatar: (organization && organization.avatar) || '',
- description: (organization && organization.description) || '',
- key: (organization && organization.key) || undefined,
- name: (organization && organization.name) || '',
- submitting: false,
- url: (organization && organization.url) || ''
- };
- }
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- canSubmit(state: State): state is ValidState {
- return Boolean(
- state.key !== undefined &&
- state.name !== undefined &&
- state.description !== undefined &&
- state.avatar !== undefined &&
- state.url !== undefined
- );
- }
-
- handleAdditionalClick = () => {
- this.setState(state => ({ additional: !state.additional }));
- };
-
- handleKeyUpdate = (key: string | undefined) => {
- this.setState({ key });
- };
-
- handleNameUpdate = (name: string | undefined) => {
- this.setState({ name });
- };
-
- handleDescriptionUpdate = (description: string | undefined) => {
- this.setState({ description });
- };
-
- handleAvatarUpdate = (avatar: string | undefined) => {
- this.setState({ avatar });
- };
-
- handleUrlUpdate = (url: string | undefined) => {
- this.setState({ url });
- };
-
- handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
- event.preventDefault();
- const { state } = this;
- if (this.canSubmit(state)) {
- this.setState({ submitting: true });
- this.props
- .onContinue({
- avatar: state.avatar,
- description: state.description,
- key: state.key,
- name: state.name,
- url: state.url
- })
- .then(this.stopSubmitting, this.stopSubmitting);
- }
- };
-
- stopSubmitting = () => {
- if (this.mounted) {
- this.setState({ submitting: false });
- }
- };
-
+export default class OrganizationDetailsStep extends React.PureComponent<Props> {
renderForm = () => {
- return (
- <div className="boxed-group-inner">
- <form id="organization-form" onSubmit={this.handleSubmit}>
- {this.props.description}
- <OrganizationKeyInput
- initialValue={this.state.key}
- onChange={this.handleKeyUpdate}
- readOnly={this.props.keyReadOnly}
- />
- <div className="big-spacer-top">
- <ResetButtonLink onClick={this.handleAdditionalClick}>
- {translate(
- this.state.additional
- ? 'onboarding.create_organization.hide_additional_info'
- : 'onboarding.create_organization.add_additional_info'
- )}
- <DropdownIcon className="little-spacer-left" turned={this.state.additional} />
- </ResetButtonLink>
- </div>
- <div className="js-additional-info" hidden={!this.state.additional}>
- <div className="big-spacer-top">
- <OrganizationNameInput
- initialValue={this.state.name}
- onChange={this.handleNameUpdate}
- />
- </div>
- <div className="big-spacer-top">
- <OrganizationAvatarInput
- initialValue={this.state.avatar}
- name={this.state.name}
- onChange={this.handleDescriptionUpdate}
- />
- </div>
- <div className="big-spacer-top">
- <OrganizationDescriptionInput
- initialValue={this.state.description}
- onChange={this.handleAvatarUpdate}
- />
- </div>
- <div className="big-spacer-top">
- <OrganizationUrlInput initialValue={this.state.url} onChange={this.handleUrlUpdate} />
- </div>
- </div>
- <div className="big-spacer-top">
- <SubmitButton disabled={this.state.submitting || !this.canSubmit(this.state)}>
- {this.props.submitText}
- </SubmitButton>
- </div>
- </form>
- </div>
- );
+ return <div className="boxed-group-inner">{this.props.children}</div>;
};
renderResult = () => {
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationBind-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationBind-test.tsx
new file mode 100644
index 00000000000..fc88575215c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationBind-test.tsx
@@ -0,0 +1,50 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import AutoOrganizationBind from '../AutoOrganizationBind';
+import { submit } from '../../../../helpers/testUtils';
+
+const organization = {
+ avatar: 'http://example.com/avatar',
+ description: 'description-foo',
+ key: 'key-foo',
+ name: 'name-foo',
+ url: 'http://example.com/foo'
+};
+
+it('should render correctly', () => {
+ const onBindOrganization = jest.fn().mockResolvedValue({});
+ const wrapper = shallowRender({ onBindOrganization });
+ expect(wrapper).toMatchSnapshot();
+
+ submit(wrapper.find('form'));
+ expect(onBindOrganization).toHaveBeenCalled();
+});
+
+function shallowRender(props: Partial<AutoOrganizationBind['props']> = {}) {
+ return shallow(
+ <AutoOrganizationBind
+ onBindOrganization={jest.fn()}
+ unboundOrganizations={[organization]}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx
index 17e57b70a94..4d6539e500c 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx
@@ -21,6 +21,11 @@ import * as React from 'react';
import { shallow } from 'enzyme';
import AutoOrganizationCreate from '../AutoOrganizationCreate';
import { waitAndUpdate } from '../../../../helpers/testUtils';
+import { bindAlmOrganization } from '../../../../api/alm-integration';
+
+jest.mock('../../../../api/alm-integration', () => ({
+ bindAlmOrganization: jest.fn().mockResolvedValue({})
+}));
const organization = {
avatar: 'http://example.com/avatar',
@@ -39,42 +44,55 @@ it('should render prefilled and create org', async () => {
const onOrgCreated = jest.fn();
const wrapper = shallowRender({
almInstallId: 'id-foo',
- almOrganization: {
- ...organization,
- type: 'ORGANIZATION'
- },
+ almOrganization: { ...organization, type: 'ORGANIZATION' },
createOrganization,
onOrgCreated
});
expect(wrapper).toMatchSnapshot();
- wrapper.find('OrganizationDetailsStep').prop<Function>('onContinue')(organization);
+ wrapper.find('OrganizationDetailsForm').prop<Function>('onContinue')(organization);
await waitAndUpdate(wrapper);
expect(createOrganization).toBeCalledWith({ ...organization, installationId: 'id-foo' });
expect(onOrgCreated).toBeCalledWith('foo');
});
-it('should render for personal organizations', async () => {
- const personalOrg = { key: 'personal-org', name: 'personal-org' };
- const updateOrganization = jest.fn().mockResolvedValue({ key: personalOrg.key });
+it('should display choice between import or creation', () => {
+ const wrapper = shallowRender({
+ almInstallId: 'id-foo',
+ almOrganization: { ...organization, type: 'ORGANIZATION' },
+ unboundOrganizations: [organization]
+ });
+ expect(wrapper).toMatchSnapshot();
+
+ wrapper.find('RadioToggle').prop<Function>('onCheck')('create');
+ wrapper.update();
+ expect(wrapper.find('OrganizationDetailsForm').exists()).toBe(true);
+
+ wrapper.find('RadioToggle').prop<Function>('onCheck')('bind');
+ wrapper.update();
+ expect(wrapper.find('AutoOrganizationBind').exists()).toBe(true);
+});
+
+it('should bind existing organization', async () => {
const onOrgCreated = jest.fn();
const wrapper = shallowRender({
almInstallId: 'id-foo',
- almOrganization: { ...organization, type: 'USER' },
- importPersonalOrg: personalOrg,
+ almOrganization: { ...organization, type: 'ORGANIZATION' },
onOrgCreated,
- updateOrganization
+ unboundOrganizations: [organization]
});
- expect(wrapper).toMatchSnapshot();
-
- wrapper.find('OrganizationDetailsStep').prop<Function>('onContinue')(personalOrg);
+ wrapper.find('RadioToggle').prop<Function>('onCheck')('bind');
+ wrapper.update();
+ wrapper.find('AutoOrganizationBind').prop<Function>('onBindOrganization')('foo');
+ expect(bindAlmOrganization as jest.Mock<any>).toHaveBeenCalledWith({
+ installationId: 'id-foo',
+ organization: 'foo'
+ });
await waitAndUpdate(wrapper);
-
- expect(updateOrganization).toBeCalledWith({ ...personalOrg, installationId: 'id-foo' });
- expect(onOrgCreated).toBeCalledWith(personalOrg.key);
+ expect(onOrgCreated).toHaveBeenCalledWith('foo', false);
});
function shallowRender(props: Partial<AutoOrganizationCreate['props']> = {}) {
@@ -89,7 +107,7 @@ function shallowRender(props: Partial<AutoOrganizationCreate['props']> = {}) {
}}
createOrganization={jest.fn()}
onOrgCreated={jest.fn()}
- updateOrganization={jest.fn()}
+ unboundOrganizations={[]}
{...props}
/>
);
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoPersonalOrganizationBind-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoPersonalOrganizationBind-test.tsx
new file mode 100644
index 00000000000..d735122f941
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoPersonalOrganizationBind-test.tsx
@@ -0,0 +1,69 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import AutoPersonalOrganizationBind from '../AutoPersonalOrganizationBind';
+import { waitAndUpdate } from '../../../../helpers/testUtils';
+
+it('should render correctly', async () => {
+ const personalOrg = { key: 'personalorg', name: 'Personal Org' };
+ const updateOrganization = jest.fn().mockResolvedValue({ key: personalOrg.key });
+ const onOrgCreated = jest.fn();
+ const wrapper = shallowRender({
+ almInstallId: 'id-foo',
+ importPersonalOrg: personalOrg,
+ onOrgCreated,
+ updateOrganization
+ });
+
+ expect(wrapper).toMatchSnapshot();
+
+ wrapper.find('OrganizationDetailsForm').prop<Function>('onContinue')(personalOrg);
+ await waitAndUpdate(wrapper);
+
+ expect(updateOrganization).toBeCalledWith({ ...personalOrg, installationId: 'id-foo' });
+ expect(onOrgCreated).toBeCalledWith(personalOrg.key);
+});
+
+function shallowRender(props: Partial<AutoPersonalOrganizationBind['props']> = {}) {
+ return shallow(
+ <AutoPersonalOrganizationBind
+ almApplication={{
+ backgroundColor: '#0052CC',
+ iconPath: '"/static/authbitbucket/bitbucket.svg"',
+ installationUrl: 'https://bitbucket.org/install/app',
+ key: 'bitbucket',
+ name: 'BitBucket'
+ }}
+ almOrganization={{
+ avatar: 'http://example.com/avatar',
+ description: 'description-foo',
+ key: 'key-foo',
+ name: 'name-foo',
+ type: 'USER',
+ url: 'http://example.com/foo'
+ }}
+ importPersonalOrg={{ key: 'personalorg', name: 'Personal Org' }}
+ onOrgCreated={jest.fn()}
+ updateOrganization={jest.fn()}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx
index 7db9ebf2636..8aa237fc1d0 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx
@@ -23,6 +23,7 @@ import { shallow } from 'enzyme';
import { CreateOrganization } from '../CreateOrganization';
import { mockRouter, waitAndUpdate } from '../../../../helpers/testUtils';
import { LoggedInUser } from '../../../../app/types';
+import { getAlmOrganization } from '../../../../api/alm-integration';
jest.mock('../../../../api/billing', () => ({
getSubscriptionPlans: jest
@@ -89,6 +90,22 @@ it('should render with auto tab selected and manual disabled', async () => {
expect(wrapper).toMatchSnapshot();
});
+it('should render with auto personal organization bind page', async () => {
+ (getAlmOrganization as jest.Mock<any>).mockResolvedValueOnce({
+ key: 'foo',
+ name: 'Foo',
+ avatar: 'https://avatars3.githubusercontent.com/u/37629810?v=4',
+ type: 'USER'
+ });
+ const wrapper = shallowRender({
+ currentUser: { ...user, externalProvider: 'github', personalOrganization: 'foo' },
+ location: { query: { installation_id: 'foo' } } as Location // eslint-disable-line camelcase
+ });
+ expect(wrapper).toMatchSnapshot();
+ await waitAndUpdate(wrapper);
+ expect(wrapper).toMatchSnapshot();
+});
+
it('should switch tabs', async () => {
const replace = jest.fn();
const wrapper = shallowRender({
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/ManualOrganizationCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/ManualOrganizationCreate-test.tsx
index 6226f8173b7..1682a146013 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/ManualOrganizationCreate-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/ManualOrganizationCreate-test.tsx
@@ -38,7 +38,7 @@ it('should render and create organization', async () => {
await waitAndUpdate(wrapper);
expect(wrapper).toMatchSnapshot();
- wrapper.find('OrganizationDetailsStep').prop<Function>('onContinue')(organization);
+ wrapper.find('OrganizationDetailsForm').prop<Function>('onContinue')(organization);
await waitAndUpdate(wrapper);
expect(wrapper).toMatchSnapshot();
@@ -52,7 +52,7 @@ it('should preselect paid plan', async () => {
const wrapper = shallowRender({ onlyPaid: true });
await waitAndUpdate(wrapper);
- wrapper.find('OrganizationDetailsStep').prop<Function>('onContinue')(organization);
+ wrapper.find('OrganizationDetailsForm').prop<Function>('onContinue')(organization);
await waitAndUpdate(wrapper);
expect(wrapper.find('PlanStep').prop('onlyPaid')).toBe(true);
});
@@ -63,7 +63,7 @@ it('should roll back after upgrade failure', async () => {
const wrapper = shallowRender({ createOrganization, deleteOrganization });
await waitAndUpdate(wrapper);
- wrapper.find('OrganizationDetailsStep').prop<Function>('onContinue')(organization);
+ wrapper.find('OrganizationDetailsForm').prop<Function>('onContinue')(organization);
await waitAndUpdate(wrapper);
wrapper.find('PlanStep').prop<Function>('createOrganization')();
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsForm-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsForm-test.tsx
new file mode 100644
index 00000000000..bc7845d4caf
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsForm-test.tsx
@@ -0,0 +1,84 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import OrganizationDetailsForm from '../OrganizationDetailsForm';
+import { click, submit } from '../../../../helpers/testUtils';
+
+it('should render form', () => {
+ const wrapper = shallowRender();
+ expect(wrapper).toMatchSnapshot();
+ expect(wrapper.find('.js-additional-info').prop('hidden')).toBe(true);
+
+ click(wrapper.find('ResetButtonLink'));
+ wrapper.update();
+ expect(wrapper.find('.js-additional-info').prop('hidden')).toBe(false);
+});
+
+it('should validate before submit', () => {
+ const wrapper = shallowRender();
+ const instance = wrapper.instance() as OrganizationDetailsForm;
+
+ expect(
+ instance.canSubmit({
+ additional: false,
+ avatar: '',
+ description: '',
+ name: '',
+ key: 'foo',
+ submitting: false,
+ url: ''
+ })
+ ).toBe(true);
+
+ expect(
+ instance.canSubmit({
+ additional: false,
+ avatar: '',
+ description: '',
+ name: '',
+ key: undefined,
+ submitting: false,
+ url: ''
+ })
+ ).toBe(false);
+
+ expect(
+ instance.canSubmit({
+ additional: false,
+ avatar: undefined,
+ description: '',
+ name: '',
+ key: 'foo',
+ submitting: false,
+ url: ''
+ })
+ ).toBe(false);
+
+ instance.canSubmit = jest.fn() as any;
+ submit(wrapper.find('form'));
+ expect(instance.canSubmit).toHaveBeenCalled();
+});
+
+function shallowRender(props: Partial<OrganizationDetailsForm['props']> = {}) {
+ return shallow(
+ <OrganizationDetailsForm onContinue={jest.fn()} submitText="continue" {...props} />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsStep-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsStep-test.tsx
index ac756ffdc8b..e905b769872 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsStep-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsStep-test.tsx
@@ -20,109 +20,31 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import OrganizationDetailsStep from '../OrganizationDetailsStep';
-import { click, submit } from '../../../../helpers/testUtils';
-import { getOrganization } from '../../../../api/organizations';
-
-jest.mock('../../../../api/organizations', () => ({
- getOrganization: jest.fn()
-}));
-
-beforeEach(() => {
- (getOrganization as jest.Mock).mockResolvedValue(undefined);
-});
it('should render form', () => {
const wrapper = shallow(
- <OrganizationDetailsStep
- finished={false}
- onContinue={jest.fn()}
- onOpen={jest.fn()}
- open={true}
- submitText="continue"
- />
+ <OrganizationDetailsStep finished={false} onOpen={jest.fn()} open={true}>
+ <form />
+ </OrganizationDetailsStep>
);
expect(wrapper).toMatchSnapshot();
- expect(wrapper.dive()).toMatchSnapshot();
- expect(
- wrapper
- .dive()
- .find('.js-additional-info')
- .prop('hidden')
- ).toBe(true);
-
- click(wrapper.dive().find('ResetButtonLink'));
- wrapper.update();
expect(
wrapper
.dive()
- .find('.js-additional-info')
- .prop('hidden')
- ).toBe(false);
-});
-
-it('should validate before submit', () => {
- const wrapper = shallow(
- <OrganizationDetailsStep
- finished={false}
- onContinue={jest.fn()}
- onOpen={jest.fn()}
- open={true}
- submitText="continue"
- />
- );
- const instance = wrapper.instance() as OrganizationDetailsStep;
-
- expect(
- instance.canSubmit({
- additional: false,
- avatar: '',
- description: '',
- name: '',
- key: 'foo',
- submitting: false,
- url: ''
- })
+ .find('form')
+ .exists()
).toBe(true);
-
- expect(
- instance.canSubmit({
- additional: false,
- avatar: '',
- description: '',
- name: '',
- key: undefined,
- submitting: false,
- url: ''
- })
- ).toBe(false);
-
- expect(
- instance.canSubmit({
- additional: false,
- avatar: undefined,
- description: '',
- name: '',
- key: 'foo',
- submitting: false,
- url: ''
- })
- ).toBe(false);
-
- instance.canSubmit = jest.fn() as any;
- submit(wrapper.dive().find('form'));
- expect(instance.canSubmit).toHaveBeenCalled();
});
-it.only('should render result', () => {
+it('should render result', () => {
const wrapper = shallow(
<OrganizationDetailsStep
finished={true}
- onContinue={jest.fn()}
onOpen={jest.fn()}
open={false}
- organization={{ avatar: '', description: '', key: 'org', name: 'Organization', url: '' }}
- submitText="continue"
- />
+ organization={{ avatar: '', description: '', key: 'org', name: 'Organization', url: '' }}>
+ <div />
+ </OrganizationDetailsStep>
);
expect(wrapper.dive().find('.boxed-group-actions')).toMatchSnapshot();
expect(
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationBind-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationBind-test.tsx.snap
new file mode 100644
index 00000000000..612312edf9d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationBind-test.tsx.snap
@@ -0,0 +1,33 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<form
+ id="bind-organization-form"
+ onSubmit={[Function]}
+>
+ <OrganizationSelect
+ onChange={[Function]}
+ organization="key-foo"
+ organizations={
+ Array [
+ Object {
+ "avatar": "http://example.com/avatar",
+ "description": "description-foo",
+ "key": "key-foo",
+ "name": "name-foo",
+ "url": "http://example.com/foo",
+ },
+ ]
+ }
+ />
+ <div
+ className="big-spacer-top"
+ >
+ <SubmitButton
+ disabled={false}
+ >
+ onboarding.import_organization.bind
+ </SubmitButton>
+ </div>
+</form>
+`;
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap
index 423b5f2181b..f11e164d3a1 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap
@@ -1,14 +1,30 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`should render for personal organizations 1`] = `
+exports[`should display choice between import or creation 1`] = `
<OrganizationDetailsStep
- description={
+ finished={false}
+ onOpen={[Function]}
+ open={true}
+ organization={
+ Object {
+ "avatar": "http://example.com/avatar",
+ "description": "description-foo",
+ "key": "key-foo",
+ "name": "name-foo",
+ "type": "ORGANIZATION",
+ "url": "http://example.com/foo",
+ }
+ }
+>
+ <div
+ className="huge-spacer-bottom"
+ >
<p
- className="huge-spacer-bottom"
+ className="big-spacer-bottom"
>
<FormattedMessage
- defaultMessage="onboarding.import_personal_organization_x"
- id="onboarding.import_personal_organization_x"
+ defaultMessage="onboarding.import_organization_x"
+ id="onboarding.import_organization_x"
values={
Object {
"avatar": <img
@@ -20,43 +36,53 @@ exports[`should render for personal organizations 1`] = `
"name": <strong>
name-foo
</strong>,
- "personalAvatar": <OrganizationAvatar
- organization={
- Object {
- "key": "personal-org",
- "name": "personal-org",
- }
- }
- small={true}
- />,
- "personalName": <strong>
- personal-org
- </strong>,
}
}
/>
</p>
- }
+ <RadioToggle
+ disabled={false}
+ name="filter"
+ onCheck={[Function]}
+ options={
+ Array [
+ Object {
+ "label": "onboarding.import_organization.create_new",
+ "value": "create",
+ },
+ Object {
+ "label": "onboarding.import_organization.bind_existing",
+ "value": "bind",
+ },
+ ]
+ }
+ value="none"
+ />
+ </div>
+</OrganizationDetailsStep>
+`;
+
+exports[`should render prefilled and create org 1`] = `
+<OrganizationDetailsStep
finished={false}
- keyReadOnly={true}
- onContinue={[Function]}
onOpen={[Function]}
open={true}
organization={
Object {
- "key": "personal-org",
- "name": "personal-org",
+ "avatar": "http://example.com/avatar",
+ "description": "description-foo",
+ "key": "key-foo",
+ "name": "name-foo",
+ "type": "ORGANIZATION",
+ "url": "http://example.com/foo",
}
}
- submitText="onboarding.import_organization.bind"
-/>
-`;
-
-exports[`should render prefilled and create org 1`] = `
-<OrganizationDetailsStep
- description={
+>
+ <div
+ className="huge-spacer-bottom"
+ >
<p
- className="huge-spacer-bottom"
+ className="big-spacer-bottom"
>
<FormattedMessage
defaultMessage="onboarding.import_organization_x"
@@ -72,30 +98,26 @@ exports[`should render prefilled and create org 1`] = `
"name": <strong>
name-foo
</strong>,
- "personalAvatar": undefined,
- "personalName": undefined,
}
}
/>
</p>
- }
- finished={false}
- keyReadOnly={false}
- onContinue={[Function]}
- onOpen={[Function]}
- open={true}
- organization={
- Object {
- "avatar": "http://example.com/avatar",
- "description": "description-foo",
- "key": "key-foo",
- "name": "name-foo",
- "type": "ORGANIZATION",
- "url": "http://example.com/foo",
+ </div>
+ <OrganizationDetailsForm
+ onContinue={[Function]}
+ organization={
+ Object {
+ "avatar": "http://example.com/avatar",
+ "description": "description-foo",
+ "key": "key-foo",
+ "name": "name-foo",
+ "type": "ORGANIZATION",
+ "url": "http://example.com/foo",
+ }
}
- }
- submitText="my_account.create_organization"
-/>
+ submitText="onboarding.import_organization.import"
+ />
+</OrganizationDetailsStep>
`;
exports[`should render with import org button 1`] = `
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoPersonalOrganizationBind-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoPersonalOrganizationBind-test.tsx.snap
new file mode 100644
index 00000000000..f29f5f9d008
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoPersonalOrganizationBind-test.tsx.snap
@@ -0,0 +1,60 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<OrganizationDetailsStep
+ finished={false}
+ onOpen={[Function]}
+ open={true}
+ organization={
+ Object {
+ "key": "personalorg",
+ "name": "Personal Org",
+ }
+ }
+>
+ <p
+ className="huge-spacer-bottom"
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.import_personal_organization_x"
+ id="onboarding.import_personal_organization_x"
+ values={
+ Object {
+ "avatar": <img
+ alt="BitBucket"
+ className="little-spacer-left"
+ src="/images/sonarcloud/bitbucket.svg"
+ width={16}
+ />,
+ "name": <strong>
+ name-foo
+ </strong>,
+ "personalAvatar": <OrganizationAvatar
+ organization={
+ Object {
+ "key": "personalorg",
+ "name": "Personal Org",
+ }
+ }
+ small={true}
+ />,
+ "personalName": <strong>
+ Personal Org
+ </strong>,
+ }
+ }
+ />
+ </p>
+ <OrganizationDetailsForm
+ keyReadOnly={true}
+ onContinue={[Function]}
+ organization={
+ Object {
+ "key": "personalorg",
+ "name": "Personal Org",
+ }
+ }
+ submitText="onboarding.import_organization.bind"
+ />
+</OrganizationDetailsStep>
+`;
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap
index 227a1be6a19..6a1c633b1a2 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap
@@ -5,13 +5,13 @@ exports[`should display an alert message 1`] = `
className="markdown big-spacer-bottom width-60"
variant="error"
>
- onboarding.create_organization.import_org_not_found
+ onboarding.import_organization.org_not_found
<ul>
<li>
- onboarding.create_organization.import_org_not_found.tips_1
+ onboarding.import_organization.org_not_found.tips_1
</li>
<li>
- onboarding.create_organization.import_org_not_found.tips_2
+ onboarding.import_organization.org_not_found.tips_2
</li>
</ul>
</Alert>
@@ -30,7 +30,7 @@ exports[`should render 1`] = `
className="boxed-group-header"
>
<h2>
- onboarding.create_organization.import_org_details
+ onboarding.import_organization.import_org_details
</h2>
</div>
<div
@@ -53,7 +53,7 @@ exports[`should render 1`] = `
small={true}
url="https://alm.application.url"
>
- onboarding.create_organization.choose_organization_button.github
+ onboarding.import_organization.choose_organization_button.github
</IdentityProviderLink>
</div>
</div>
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap
index 9d3dcf6dbc8..d43925de3dc 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap
@@ -1,5 +1,84 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`should render with auto personal organization bind page 1`] = `
+<AlmApplicationInstalling
+ almKey="github"
+/>
+`;
+
+exports[`should render with auto personal organization bind page 2`] = `
+<Fragment>
+ <HelmetWrapper
+ defer={true}
+ encodeSpecialCharacters={true}
+ title="onboarding.import_organization.personal.page.header"
+ titleTemplate="%s"
+ />
+ <div
+ className="sonarcloud page page-limited"
+ >
+ <header
+ className="page-header"
+ >
+ <h1
+ className="page-title big-spacer-bottom"
+ >
+ onboarding.import_organization.personal.page.header
+ </h1>
+ <p
+ className="page-description"
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.import_organization.personal.page.description"
+ id="onboarding.import_organization.personal.page.description"
+ values={
+ Object {
+ "break": <br />,
+ "more": <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ target="_blank"
+ to="/documentation/sonarcloud-pricing/"
+ >
+ learn_more
+ </Link>,
+ "price": "billing.price_format.10",
+ }
+ }
+ />
+ </p>
+ </header>
+ <AutoPersonalOrganizationBind
+ almApplication={
+ Object {
+ "backgroundColor": "blue",
+ "iconPath": "icon/path",
+ "installationUrl": "https://alm.installation.url",
+ "key": "github",
+ "name": "GitHub",
+ }
+ }
+ almInstallId="foo"
+ almOrganization={
+ Object {
+ "avatar": "https://avatars3.githubusercontent.com/u/37629810?v=4",
+ "key": "foo",
+ "name": "Foo",
+ "type": "USER",
+ }
+ }
+ importPersonalOrg={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ }
+ }
+ onOrgCreated={[Function]}
+ />
+ </div>
+</Fragment>
+`;
+
exports[`should render with auto tab displayed 1`] = `
<Fragment>
<HelmetWrapper
@@ -49,14 +128,7 @@ exports[`should render with auto tab displayed 1`] = `
Array [
Object {
"key": "auto",
- "node": <React.Fragment>
- onboarding.import_organization.github
- <span
- className="beta-badge spacer-left"
- >
- beta
- </span>
- </React.Fragment>,
+ "node": "onboarding.import_organization.github",
},
Object {
"disabled": false,
@@ -77,6 +149,14 @@ exports[`should render with auto tab displayed 1`] = `
}
}
onOrgCreated={[Function]}
+ unboundOrganizations={
+ Array [
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ },
+ ]
+ }
/>
</div>
</Fragment>
@@ -137,14 +217,7 @@ exports[`should render with auto tab selected and manual disabled 2`] = `
Array [
Object {
"key": "auto",
- "node": <React.Fragment>
- onboarding.import_organization.github
- <span
- className="beta-badge spacer-left"
- >
- beta
- </span>
- </React.Fragment>,
+ "node": "onboarding.import_organization.github",
},
Object {
"disabled": true,
@@ -176,6 +249,14 @@ exports[`should render with auto tab selected and manual disabled 2`] = `
}
}
onOrgCreated={[Function]}
+ unboundOrganizations={
+ Array [
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ },
+ ]
+ }
/>
</div>
</Fragment>
@@ -291,14 +372,7 @@ exports[`should switch tabs 1`] = `
Array [
Object {
"key": "auto",
- "node": <React.Fragment>
- onboarding.import_organization.github
- <span
- className="beta-badge spacer-left"
- >
- beta
- </span>
- </React.Fragment>,
+ "node": "onboarding.import_organization.github",
},
Object {
"disabled": false,
@@ -319,6 +393,14 @@ exports[`should switch tabs 1`] = `
}
}
onOrgCreated={[Function]}
+ unboundOrganizations={
+ Array [
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ },
+ ]
+ }
/>
</div>
</Fragment>
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ManualOrganizationCreate-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ManualOrganizationCreate-test.tsx.snap
index be548a156d6..6d36c3862b3 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ManualOrganizationCreate-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ManualOrganizationCreate-test.tsx.snap
@@ -4,11 +4,14 @@ exports[`should render and create organization 1`] = `
<Fragment>
<OrganizationDetailsStep
finished={false}
- onContinue={[Function]}
onOpen={[Function]}
open={true}
- submitText="continue"
- />
+ >
+ <OrganizationDetailsForm
+ onContinue={[Function]}
+ submitText="continue"
+ />
+ </OrganizationDetailsStep>
<PlanStep
createOrganization={[Function]}
deleteOrganization={[Function]}
@@ -36,7 +39,6 @@ exports[`should render and create organization 2`] = `
<Fragment>
<OrganizationDetailsStep
finished={true}
- onContinue={[Function]}
onOpen={[Function]}
open={false}
organization={
@@ -48,8 +50,21 @@ exports[`should render and create organization 2`] = `
"url": "http://example.com/foo",
}
}
- submitText="continue"
- />
+ >
+ <OrganizationDetailsForm
+ onContinue={[Function]}
+ organization={
+ Object {
+ "avatar": "http://example.com/avatar",
+ "description": "description-foo",
+ "key": "key-foo",
+ "name": "name-foo",
+ "url": "http://example.com/foo",
+ }
+ }
+ submitText="continue"
+ />
+ </OrganizationDetailsStep>
<PlanStep
createOrganization={[Function]}
deleteOrganization={[Function]}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsForm-test.tsx.snap
new file mode 100644
index 00000000000..3acf5cb5e3a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsForm-test.tsx.snap
@@ -0,0 +1,72 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render form 1`] = `
+<form
+ id="organization-form"
+ onSubmit={[Function]}
+>
+ <OrganizationKeyInput
+ onChange={[Function]}
+ />
+ <div
+ className="big-spacer-top"
+ >
+ <ResetButtonLink
+ onClick={[Function]}
+ >
+ onboarding.create_organization.add_additional_info
+ <DropdownIcon
+ className="little-spacer-left"
+ turned={false}
+ />
+ </ResetButtonLink>
+ </div>
+ <div
+ className="js-additional-info"
+ hidden={true}
+ >
+ <div
+ className="big-spacer-top"
+ >
+ <OrganizationNameInput
+ initialValue=""
+ onChange={[Function]}
+ />
+ </div>
+ <div
+ className="big-spacer-top"
+ >
+ <OrganizationAvatarInput
+ initialValue=""
+ name=""
+ onChange={[Function]}
+ />
+ </div>
+ <div
+ className="big-spacer-top"
+ >
+ <OrganizationDescriptionInput
+ initialValue=""
+ onChange={[Function]}
+ />
+ </div>
+ <div
+ className="big-spacer-top"
+ >
+ <OrganizationUrlInput
+ initialValue=""
+ onChange={[Function]}
+ />
+ </div>
+ </div>
+ <div
+ className="big-spacer-top"
+ >
+ <SubmitButton
+ disabled={true}
+ >
+ continue
+ </SubmitButton>
+ </div>
+</form>
+`;
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsStep-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsStep-test.tsx.snap
index 169967f9e4f..3b15b770b40 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsStep-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsStep-test.tsx.snap
@@ -12,241 +12,6 @@ exports[`should render form 1`] = `
/>
`;
-exports[`should render form 2`] = `
-<div
- className="boxed-group onboarding-step is-open"
->
- <div
- className="onboarding-step-number"
- >
- 1
- </div>
- <div
- className="boxed-group-header"
- >
- <h2>
- onboarding.create_organization.enter_org_details
- </h2>
- </div>
- <div
- className=""
- >
- <div
- className="boxed-group-inner"
- >
-<<<<<<< HEAD
- <ValidationForm
- initialValues={
- Object {
- "avatar": "",
- "description": "",
- "key": "",
- "name": "",
- "url": "",
- }
- }
- isInitialValid={false}
- onSubmit={[MockFunction]}
- validate={[Function]}
- >
- <Component />
- </ValidationForm>
- </div>
- </div>
-</div>
-`;
-
-exports[`should render form 3`] = `
-<form
- onSubmit={[Function]}
->
- <OrganizationDetailsInput
- description="onboarding.create_organization.organization_name.description"
- dirty={false}
- id="organization-key"
- isSubmitting={false}
- isValidating={false}
- label="onboarding.create_organization.organization_name"
- name="key"
- onBlur={[Function]}
- onChange={[Function]}
- required={true}
- value=""
- >
- <Component />
- </OrganizationDetailsInput>
- <div
- className="big-spacer-top"
- >
- <ResetButtonLink
- onClick={[Function]}
- >
- onboarding.create_organization.add_additional_info
- <DropdownIcon
- className="little-spacer-left"
- turned={false}
- />
- </ResetButtonLink>
- </div>
- <div
- className="js-additional-info"
- hidden={true}
- >
- <div
- className="big-spacer-top"
- >
- <OrganizationDetailsInput
- description="onboarding.create_organization.display_name.description"
- dirty={false}
- id="organization-display-name"
- isSubmitting={false}
- isValidating={false}
- label="onboarding.create_organization.display_name"
- name="name"
- onBlur={[Function]}
- onChange={[Function]}
- value=""
- >
- <Component />
- </OrganizationDetailsInput>
- </div>
- <div
- className="big-spacer-top"
- >
- <OrganizationDetailsInput
- description="onboarding.create_organization.avatar.description"
- dirty={false}
- id="organization-avatar"
- isSubmitting={false}
- isValidating={false}
- label="onboarding.create_organization.avatar"
- name="avatar"
- onBlur={[Function]}
- onChange={[Function]}
- value=""
- >
- <Component />
- </OrganizationDetailsInput>
- </div>
- <div
- className="big-spacer-top"
- >
- <OrganizationDetailsInput
- dirty={false}
- id="organization-description"
- isSubmitting={false}
- isValidating={false}
- label="description"
- name="description"
- onBlur={[Function]}
- onChange={[Function]}
- value=""
- >
- <Component />
- </OrganizationDetailsInput>
- </div>
- <div
- className="big-spacer-top"
- >
- <OrganizationDetailsInput
- dirty={false}
- id="organization-url"
- isSubmitting={false}
- isValidating={false}
- label="onboarding.create_organization.url"
- name="url"
- onBlur={[Function]}
- onChange={[Function]}
- value=""
- >
- <Component />
- </OrganizationDetailsInput>
- </div>
- </div>
- <div
- className="big-spacer-top"
- >
- <SubmitButton
- disabled={true}
- >
- continue
- </SubmitButton>
- </div>
-</form>
-=======
- <form
- id="organization-form"
- onSubmit={[Function]}
- >
- <OrganizationKeyInput
- onChange={[Function]}
- />
- <div
- className="big-spacer-top"
- >
- <ResetButtonLink
- onClick={[Function]}
- >
- onboarding.create_organization.add_additional_info
- <DropdownIcon
- className="little-spacer-left"
- turned={false}
- />
- </ResetButtonLink>
- </div>
- <div
- className="js-additional-info"
- hidden={true}
- >
- <div
- className="big-spacer-top"
- >
- <OrganizationNameInput
- initialOrgName=""
- onChange={[Function]}
- />
- </div>
- <div
- className="big-spacer-top"
- >
- <OrganizationAvatarInput
- initialOrgAvatar=""
- onChange={[Function]}
- />
- </div>
- <div
- className="big-spacer-top"
- >
- <OrganizationDescriptionInput
- initialOrgDescription=""
- onChange={[Function]}
- />
- </div>
- <div
- className="big-spacer-top"
- >
- <OrganizationUrlInput
- initialOrgUrl=""
- onChange={[Function]}
- />
- </div>
- </div>
- <div
- className="big-spacer-top"
- >
- <SubmitButton
- disabled={true}
- >
- continue
- </SubmitButton>
- </div>
- </form>
- </div>
- </div>
-</div>
->>>>>>> 116a4ec872... SONAR-11322 Import repos from bound organizations
-`;
-
exports[`should render result 1`] = `
<div
className="boxed-group-actions display-flex-center"
diff --git a/server/sonar-web/src/main/js/apps/create/project/AutoProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/AutoProjectCreate.tsx
index 4460f222b8f..ad6f002914f 100644
--- a/server/sonar-web/src/main/js/apps/create/project/AutoProjectCreate.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/AutoProjectCreate.tsx
@@ -19,7 +19,7 @@
*/
import * as React from 'react';
import RemoteRepositories from './RemoteRepositories';
-import OrganizationSelect from './OrganizationSelect';
+import OrganizationInput from './OrganizationInput';
import IdentityProviderLink from '../../../components/ui/IdentityProviderLink';
import { AlmApplication, Organization } from '../../../app/types';
import { translate } from '../../../helpers/l10n';
@@ -69,7 +69,7 @@ export default class AutoProjectCreate extends React.PureComponent<Props, State>
small={true}
url={almApplication.installationUrl}>
{translate(
- 'onboarding.create_organization.choose_organization_button',
+ 'onboarding.import_organization.choose_organization_button',
almApplication.key
)}
</IdentityProviderLink>
@@ -80,7 +80,7 @@ export default class AutoProjectCreate extends React.PureComponent<Props, State>
const { selectedOrganization } = this.state;
return (
<>
- <OrganizationSelect
+ <OrganizationInput
autoImport={true}
onChange={this.handleOrganizationSelect}
organization={selectedOrganization}
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 53382bfc34e..7afb0d1ca10 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
@@ -18,7 +18,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import * as classNames from 'classnames';
import { connect } from 'react-redux';
import { WithRouterProps } from 'react-router';
import Helmet from 'react-helmet';
@@ -142,17 +141,7 @@ export class CreateProjectPage extends React.PureComponent<Props & WithRouterPro
tabs={[
{
key: 'auto',
- node: (
- <>
- {translate('onboarding.create_project.select_repositories')}
- <span
- className={classNames('beta-badge spacer-left', {
- 'is-muted': showManualTab
- })}>
- {translate('beta')}
- </span>
- </>
- )
+ node: translate('onboarding.create_project.select_repositories')
},
{ key: 'manual', node: translate('onboarding.create_project.create_manually') }
]}
diff --git a/server/sonar-web/src/main/js/apps/create/project/ManualProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/ManualProjectCreate.tsx
index 2820af7c8dc..83b0a6ab269 100644
--- a/server/sonar-web/src/main/js/apps/create/project/ManualProjectCreate.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/ManualProjectCreate.tsx
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import OrganizationSelect from './OrganizationSelect';
+import OrganizationInput from './OrganizationInput';
import DeferredSpinner from '../../../components/common/DeferredSpinner';
import { SubmitButton } from '../../../components/ui/buttons';
import { LoggedInUser, Organization } from '../../../app/types';
@@ -113,7 +113,7 @@ export default class ManualProjectCreate extends React.PureComponent<Props, Stat
return (
<>
<form onSubmit={this.handleFormSubmit}>
- <OrganizationSelect
+ <OrganizationInput
onChange={this.handleOrganizationSelect}
organization={this.state.selectedOrganization}
organizations={this.props.userOrganizations}
diff --git a/server/sonar-web/src/main/js/apps/create/project/OrganizationSelect.tsx b/server/sonar-web/src/main/js/apps/create/project/OrganizationInput.tsx
index ed7cf5d30ee..b917efd6125 100644
--- a/server/sonar-web/src/main/js/apps/create/project/OrganizationSelect.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/OrganizationInput.tsx
@@ -19,12 +19,9 @@
*/
import * as React from 'react';
import { Link } from 'react-router';
-import { sortBy } from 'lodash';
-import Select from '../../../components/controls/Select';
+import OrganizationSelect from '../components/OrganizationSelect';
import { Organization } from '../../../app/types';
import { translate } from '../../../helpers/l10n';
-import { sanitizeAlmId } from '../../../helpers/almIntegrations';
-import { getBaseUrl } from '../../../helpers/urls';
interface Props {
autoImport?: boolean;
@@ -33,7 +30,7 @@ interface Props {
organizations: Organization[];
}
-export default function OrganizationSelect({
+export default function OrganizationInput({
autoImport,
onChange,
organization,
@@ -45,19 +42,10 @@ export default function OrganizationSelect({
{translate('onboarding.create_project.organization')}
<em className="mandatory">*</em>
</label>
- <Select
- autoFocus={true}
- className="input-super-large"
- clearable={false}
- id="select-organization"
- labelKey="name"
+ <OrganizationSelect
onChange={onChange}
- optionRenderer={optionRenderer}
- options={sortBy(organizations, o => o.name.toLowerCase())}
- required={true}
- value={organization}
- valueKey="key"
- valueRenderer={optionRenderer}
+ organization={organization}
+ organizations={organizations}
/>
<Link className="big-spacer-left js-new-org" to="/create-organization">
{autoImport
@@ -67,20 +55,3 @@ export default function OrganizationSelect({
</div>
);
}
-
-export function optionRenderer(organization: Organization) {
- return (
- <span>
- {organization.alm && (
- <img
- alt={organization.alm.key}
- className="spacer-right"
- height={14}
- src={`${getBaseUrl()}/images/sonarcloud/${sanitizeAlmId(organization.alm.key)}.svg`}
- />
- )}
- {organization.name}
- <span className="note little-spacer-left">{organization.key}</span>
- </span>
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/ManualProjectCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/ManualProjectCreate-test.tsx
index 52d56a87e38..e8e33a81ac1 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/ManualProjectCreate-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/ManualProjectCreate-test.tsx
@@ -38,7 +38,7 @@ it('should render correctly', () => {
it('should correctly create a project', async () => {
const onProjectCreate = jest.fn();
const wrapper = getWrapper({ onProjectCreate });
- wrapper.find('OrganizationSelect').prop<Function>('onChange')({ key: 'foo' });
+ wrapper.find('OrganizationInput').prop<Function>('onChange')({ key: 'foo' });
change(wrapper.find('#project-name'), 'Bar');
expect(wrapper.find('SubmitButton')).toMatchSnapshot();
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/OrganizationInput-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/OrganizationInput-test.tsx
new file mode 100644
index 00000000000..f6fcd45a248
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/OrganizationInput-test.tsx
@@ -0,0 +1,47 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import OrganizationInput from '../OrganizationInput';
+
+const organizations = [
+ { key: 'foo', name: 'Foo' },
+ { alm: { key: 'github', url: '' }, key: 'bar', name: 'Bar' }
+];
+
+it('should render correctly', () => {
+ expect(
+ shallow(
+ <OrganizationInput onChange={jest.fn()} organization="bar" organizations={organizations} />
+ )
+ ).toMatchSnapshot();
+ expect(
+ shallow(
+ <OrganizationInput
+ autoImport={true}
+ onChange={jest.fn()}
+ organization="bar"
+ organizations={organizations}
+ />
+ )
+ .find('.js-new-org')
+ .contains('onboarding.create_project.import_new_org')
+ ).toBe(true);
+});
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AutoProjectCreate-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AutoProjectCreate-test.tsx.snap
index a96a37b53b5..6623016dd4f 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AutoProjectCreate-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AutoProjectCreate-test.tsx.snap
@@ -2,7 +2,7 @@
exports[`should display the bounded organizations dropdown with the list of repositories 1`] = `
<Fragment>
- <OrganizationSelect
+ <OrganizationInput
autoImport={true}
onChange={[Function]}
organization="foo"
@@ -59,7 +59,7 @@ exports[`should display the provider app install button 1`] = `
small={true}
url="https://alm.installation.url"
>
- onboarding.create_organization.choose_organization_button.github
+ onboarding.import_organization.choose_organization_button.github
</IdentityProviderLink>
</Fragment>
`;
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 5e2c2e1a150..c0ff4ce7535 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
@@ -54,14 +54,7 @@ exports[`should render correctly 2`] = `
Array [
Object {
"key": "auto",
- "node": <React.Fragment>
- onboarding.create_project.select_repositories
- <span
- className="beta-badge spacer-left"
- >
- beta
- </span>
- </React.Fragment>,
+ "node": "onboarding.create_project.select_repositories",
},
Object {
"key": "manual",
@@ -178,14 +171,7 @@ exports[`should switch tabs 1`] = `
Array [
Object {
"key": "auto",
- "node": <React.Fragment>
- onboarding.create_project.select_repositories
- <span
- className="beta-badge spacer-left"
- >
- beta
- </span>
- </React.Fragment>,
+ "node": "onboarding.create_project.select_repositories",
},
Object {
"key": "manual",
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/ManualProjectCreate-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/ManualProjectCreate-test.tsx.snap
index 53fde97ce31..4f38672a053 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/ManualProjectCreate-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/ManualProjectCreate-test.tsx.snap
@@ -21,7 +21,7 @@ exports[`should render correctly 1`] = `
<form
onSubmit={[Function]}
>
- <OrganizationSelect
+ <OrganizationInput
onChange={[Function]}
organization=""
organizations={
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/OrganizationSelect-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/OrganizationInput-test.tsx.snap
index 367f0265e72..fe6c8d4e3f6 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/OrganizationSelect-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/OrganizationInput-test.tsx.snap
@@ -14,17 +14,16 @@ exports[`should render correctly 1`] = `
*
</em>
</label>
- <Select
- autoFocus={true}
- className="input-super-large"
- clearable={false}
- id="select-organization"
- labelKey="name"
+ <OrganizationSelect
onChange={[MockFunction]}
- optionRenderer={[Function]}
- options={
+ organization="bar"
+ organizations={
Array [
Object {
+ "key": "foo",
+ "name": "Foo",
+ },
+ Object {
"alm": Object {
"key": "github",
"url": "",
@@ -32,16 +31,8 @@ exports[`should render correctly 1`] = `
"key": "bar",
"name": "Bar",
},
- Object {
- "key": "foo",
- "name": "Foo",
- },
]
}
- required={true}
- value="bar"
- valueKey="key"
- valueRenderer={[Function]}
/>
<Link
className="big-spacer-left js-new-org"
@@ -53,31 +44,3 @@ exports[`should render correctly 1`] = `
</Link>
</div>
`;
-
-exports[`should render options correctly 1`] = `
-<span>
- Foo
- <span
- className="note little-spacer-left"
- >
- foo
- </span>
-</span>
-`;
-
-exports[`should render options correctly 2`] = `
-<span>
- <img
- alt="github"
- className="spacer-right"
- height={14}
- src="/images/sonarcloud/github.svg"
- />
- Bar
- <span
- className="note little-spacer-left"
- >
- bar
- </span>
-</span>
-`;
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 039574a03fb..ae23029734e 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
@@ -22,13 +22,12 @@ import * as PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { InjectedRouter } from 'react-router';
import OnboardingModal from './OnboardingModal';
-import { skipOnboarding } from '../../../api/users';
-import { skipOnboarding as skipOnboardingAction } from '../../../store/users';
+import { skipOnboarding } from '../../../store/users';
import TeamOnboardingModal from '../teamOnboarding/TeamOnboardingModal';
import { Organization } from '../../../app/types';
interface DispatchProps {
- skipOnboardingAction: () => void;
+ skipOnboarding: () => void;
}
interface OwnProps {
@@ -52,8 +51,7 @@ export class OnboardingPage extends React.PureComponent<OwnProps & DispatchProps
state: State = { modal: ModalKey.onboarding };
closeOnboarding = () => {
- skipOnboarding();
- this.props.skipOnboardingAction();
+ this.props.skipOnboarding();
this.props.router.replace('/');
};
@@ -90,7 +88,7 @@ export class OnboardingPage extends React.PureComponent<OwnProps & DispatchProps
}
}
-const mapDispatchToProps: DispatchProps = { skipOnboardingAction };
+const mapDispatchToProps: DispatchProps = { skipOnboarding };
export default connect(
null,